diff --git a/demo/main.cpp b/demo/main.cpp index f58a6351a3fcf66ac05e68ec6afd9c947cb8f87f..e2481d8a894d89e4c4881ef26357604daa16876b 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -1,6 +1,7 @@ #include <filesystem> #include <iostream> +#include <utility> #include <fggl/gfx/window.hpp> #include <fggl/gfx/camera.hpp> @@ -10,10 +11,12 @@ #include <fggl/data/procedural.hpp> #include <fggl/data/storage.hpp> #include <fggl/util/chrono.hpp> +#include <fggl/util/service.h> #include <fggl/ecs3/ecs.hpp> #include <fggl/debug/debug.h> #include <imgui.h> +#include <fggl/scenes/Scene.h> constexpr bool showNormals = false; @@ -104,6 +107,7 @@ void process_freecam(fggl::ecs3::World& ecs, InputManager input, fggl::ecs::enti auto code_d = glfwGetKeyScancode(GLFW_KEY_D); auto code_a = glfwGetKeyScancode(GLFW_KEY_A); + // calulate rotation (user input) if ( keyboard.down( code_q ) ) { rotationValue = ROT_SPEED; @@ -206,7 +210,7 @@ void process_edgescroll(fggl::ecs3::World& ecs, InputManager input, fggl::ecs::e } -void process_camera(fggl::gfx::Window& window, fggl::ecs3::World& ecs, InputManager input) { +void process_camera(fggl::ecs3::World& ecs, InputManager input) { auto cameras = ecs.findMatching<fggl::gfx::Camera>(); fggl::ecs3::entity_t cam = cameras[0]; @@ -232,126 +236,114 @@ void process_camera(fggl::gfx::Window& window, fggl::ecs3::World& ecs, InputMana process_edgescroll( ecs, input, cam ); } -int main(int argc, char* argv[]) { - // setup ECS - fggl::ecs3::TypeRegistry types; - fggl::ecs3::ModuleManager modules(types); - fggl::ecs3::World ecs(types); +class MenuScene : public fggl::scenes::Scene { - auto inputs = std::make_shared<fggl::input::Input>(); - auto glfwModule = modules.load<fggl::gfx::ecsGlfwModule>(inputs); +public: + explicit MenuScene(InputManager inputs) : m_inputs(std::move(inputs)) {} + ~MenuScene() override = default; - fggl::gfx::Window win{}; - win.title("FGGL Demo"); - win.fullscreen( true ); + void setup() override { + } - // storage API - fggl::data::Storage storage; - //discover( storage.resolvePath(fggl::data::Data, "../../packs") ); + void cleanup() override { - // Opengl APIs - auto glModule = modules.load<fggl::gfx::ecsOpenGLModule>(win, storage); + } - fggl::gfx::loadPipeline(glModule, "unlit", false); - fggl::gfx::loadPipeline(glModule, "phong", false); - fggl::gfx::loadPipeline(glModule, "normals", false); + void update() override { + bool leftMouse = m_inputs->mouse.button(fggl::input::MouseButton::LEFT); + if (leftMouse) { + auto scenes = fggl::util::ServiceLocator::instance().providePtr<fggl::scenes::SceneManager>(); + scenes->activate("game"); + } + } - // debug layer - fggl::debug::DebugUI debug(win); - debug.visible(true); + void render() override { - // create ECS - types.make<fggl::math::Transform>(); + } - // create camera using strings - { - auto prototype = ecs.create(false); - ecs.add(prototype, types.find(fggl::math::Transform::name)); - ecs.add(prototype, types.find(fggl::gfx::Camera::name)); + private: + InputManager m_inputs; - auto camTf = ecs.get<fggl::math::Transform>(prototype); - camTf->origin( glm::vec3(0.0f, 3.0f, 3.0f) ); - } +}; - // create building prototype - fggl::ecs3::entity_t bunker; - { - bunker = ecs.create(true); - ecs.add(bunker, types.find(fggl::math::Transform::name)); - - // mesh - int nSections = 2; - constexpr float HALF_PI = M_PI / 2.0f; - constexpr char shader[] = "phong"; - - fggl::data::Mesh mesh; - for (int j=-(nSections/2); j<=nSections/2; j++) { - const auto shapeOffset = glm::vec3( 0.0f, 0.0f, j * 1.0f ); - - const auto cubeMat = glm::translate( fggl::math::mat4( 1.0f ) , shapeOffset ); - const auto leftSlope = fggl::math::modelMatrix( - glm::vec3(-1.0f, 0.0f, 0.0f) + shapeOffset, - glm::vec3( 0.0f, -HALF_PI, 0.0f) ); - const auto rightSlope = fggl::math::modelMatrix( - glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset, - glm::vec3( 0.0f, HALF_PI, 0.0f) ); - - fggl::data::make_cube( mesh, cubeMat ); - fggl::data::make_slope( mesh, leftSlope ); - fggl::data::make_slope( mesh, rightSlope ); - } - mesh.removeDups(); +class GameScene : public fggl::scenes::Scene { +public: + explicit GameScene(fggl::ecs3::World& world, InputManager inputs) : m_world(world), m_inputs(std::move(inputs)) { }; + ~GameScene() override = default; - fggl::gfx::StaticMesh staticMesh{mesh, shader}; - ecs.set<fggl::gfx::StaticMesh>(bunker, &staticMesh); - } + void setup() override { + auto types = m_world.types(); - auto floorEnt = ecs.create(false); - { - ecs.add<fggl::math::Transform>( floorEnt ); - fggl::data::Mesh mesh = fggl::data::make_quad_xz(); + // create camera using strings + { + auto prototype = m_world.create(false); + m_world.add(prototype, types.find(fggl::math::Transform::name)); + m_world.add(prototype, types.find(fggl::gfx::Camera::name)); - fggl::gfx::StaticMesh sMesh{mesh, "phong"}; - ecs.set<fggl::gfx::StaticMesh>(floorEnt, &sMesh); - } + auto camTf = m_world.get<fggl::math::Transform>(prototype); + camTf->origin( glm::vec3(0.0f, 3.0f, 3.0f) ); + } - int nCubes = 3; - for ( int i=0; i<nCubes; i++ ) { - auto bunkerClone = ecs.copy(bunker); - auto result = ecs.get<fggl::math::Transform>(bunkerClone); - result->origin( glm::vec3( i * 5.0f, 0.0f, 0.0f) ); - } + // create building prototype + fggl::ecs3::entity_t bunker; + { + bunker = m_world.create(true); + m_world.add(bunker, types.find(fggl::math::Transform::name)); + + // mesh + int nSections = 2; + constexpr float HALF_PI = M_PI / 2.0f; + constexpr char shader[] = "phong"; + + fggl::data::Mesh mesh; + for (int j=-(nSections/2); j<=nSections/2; j++) { + const auto shapeOffset = glm::vec3( 0.0f, 0.0f, j * 1.0f ); + + const auto cubeMat = glm::translate( fggl::math::mat4( 1.0f ) , shapeOffset ); + const auto leftSlope = fggl::math::modelMatrix( + glm::vec3(-1.0f, 0.0f, 0.0f) + shapeOffset, + glm::vec3( 0.0f, -HALF_PI, 0.0f) ); + const auto rightSlope = fggl::math::modelMatrix( + glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset, + glm::vec3( 0.0f, HALF_PI, 0.0f) ); + + fggl::data::make_cube( mesh, cubeMat ); + fggl::data::make_slope( mesh, leftSlope ); + fggl::data::make_slope( mesh, rightSlope ); + } + mesh.removeDups(); - bool gamepadWindow = true; + fggl::gfx::StaticMesh staticMesh{mesh, shader}; + m_world.set<fggl::gfx::StaticMesh>(bunker, &staticMesh); + } - fggl::util::Timer time{}; - time.frequency( glfwGetTimerFrequency() ); - time.setup( glfwGetTimerValue() ); + int nCubes = 3; + for ( int i=0; i<nCubes; i++ ) { + auto bunkerClone = m_world.copy(bunker); + auto result = m_world.get<fggl::math::Transform>(bunkerClone); + result->origin( glm::vec3( i * 5.0f, 0.0f, 0.0f) ); + } + } - while( !win.closeRequested() ) { + void cleanup() override { - // - // Setup setup - // - time.tick( glfwGetTimerValue() ); - inputs->frame( time.delta() ); + } - glfwModule->context.pollEvents(); - debug.frameStart(); + void update() override { + process_camera(m_world, m_inputs); + } - // - // update step - // - process_camera(win, ecs, inputs); + void render() override { + auto types = m_world.types(); // ECS3 inspector ImGui::Begin("Entities"); - auto entityItr = ecs.all(); + auto entityItr = m_world.all(); for (auto& entity : entityItr) { std::string label = "entity-" + std::to_string(entity); if ( ImGui::TreeNode(label.c_str()) ){ - auto entComp = ecs.getComponents(entity); + auto entComp = m_world.getComponents(entity); for ( auto comp : entComp ){ auto meta = types.meta(comp); ImGui::Text("%s (%d) - %lu bytes", meta->name(), comp, meta->size()); @@ -361,10 +353,10 @@ int main(int argc, char* argv[]) { } ImGui::End(); - // imgui gamepad debug - if ( gamepadWindow ) { - auto &gamepads = inputs->gamepads; - ImGui::Begin("GamePad", &gamepadWindow); + // imgui gamepad debug + if ( m_gamepadWindow ) { + auto &gamepads = m_inputs->gamepads; + ImGui::Begin("GamePad", &m_gamepadWindow); for (int i = 0; i < 16; i++) { std::string title = gamepads.name(i); @@ -405,20 +397,92 @@ int main(int argc, char* argv[]) { } ImGui::End(); } + } + +private: + fggl::ecs3::World& m_world; + InputManager m_inputs; + bool m_gamepadWindow = false; +}; +int main(int argc, char* argv[]) { + auto& locator = fggl::util::ServiceLocator::instance(); + + // setup ECS + fggl::ecs3::TypeRegistry types; + types.make<fggl::math::Transform>(); + fggl::ecs3::ModuleManager modules(types); + + fggl::ecs3::World ecs(types); + + // input management + auto inputs = std::make_shared<fggl::input::Input>(); + locator.supply<fggl::input::Input>(inputs); + + auto glfwModule = modules.load<fggl::gfx::ecsGlfwModule>(inputs); + + // window + fggl::gfx::Window win{}; + win.title("FGGL Demo"); + win.fullscreen( true ); + + // storage API + fggl::data::Storage storage; + //discover( storage.resolvePath(fggl::data::Data, "../../packs") ); + + // Opengl APIs + auto glModule = modules.load<fggl::gfx::ecsOpenGLModule>(win, storage); + + fggl::gfx::loadPipeline(glModule, "unlit", false); + fggl::gfx::loadPipeline(glModule, "phong", false); + fggl::gfx::loadPipeline(glModule, "normals", false); + + // debug layer + fggl::debug::DebugUI debug(win); + debug.visible(true); + + // Create scene manager + auto scenes = std::make_shared<fggl::scenes::SceneManager>(); + locator.supply<fggl::scenes::SceneManager>(scenes); + + scenes->create("main_menu", std::make_shared<MenuScene>(inputs)); + scenes->create("game", std::make_shared<GameScene>(ecs, inputs)); + + // activate the main menu + scenes->activate("main_menu"); + + // create ECS + + fggl::util::Timer time{}; + time.frequency( glfwGetTimerFrequency() ); + time.setup( glfwGetTimerValue() ); + + while( !win.closeRequested() ) { + // + // Setup setup // - // render step - // - win.activate(); - glModule->ogl.clear(); + time.tick( glfwGetTimerValue() ); + inputs->frame( time.delta() ); - // model rendering system - fggl::gfx::renderMeshes(glModule, ecs, time.delta()); - debug.draw(); + glfwModule->context.pollEvents(); + debug.frameStart(); - // swap the windows - frame rendering over - win.swap(); - } + // + // update step + // + scenes->update(); + + // render the scene + win.activate(); + glModule->ogl.clear(); + + // allow the scene to do stuff, then actually render + scenes->render(); + fggl::gfx::renderMeshes(glModule, ecs, time.delta()); + + debug.draw(); + win.swap(); + } return 0; } diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt index 1bd8aec5da984df76d65488a9b019a21d15734be..155d5bd41d5aee4e6a85447ab64a543f9485c462 100644 --- a/fggl/CMakeLists.txt +++ b/fggl/CMakeLists.txt @@ -6,7 +6,7 @@ add_library(fggl fggl.cpp data/procedural.cpp ecs3/fast/Container.cpp ecs3/prototype/world.cpp - ecs3/module/module.cpp) + ecs3/module/module.cpp scenes/Scene.cpp scenes/Scene.h util/service.h) target_include_directories(fggl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../) # Graphics backend diff --git a/fggl/ecs3/prototype/world.h b/fggl/ecs3/prototype/world.h index 74b59b2463ef0e53912f4c25b5d7062b05b06946..5111928fb674e4b1642a1ca30c996be18ce19ff7 100644 --- a/fggl/ecs3/prototype/world.h +++ b/fggl/ecs3/prototype/world.h @@ -122,6 +122,10 @@ namespace fggl::ecs3::prototype { m_entities.erase(entity); } + inline TypeRegistry& types() { + return m_types; + } + std::vector<entity_t> all() { std::vector<entity_t> entities{}; for( auto& [eid, entity] : m_entities) { diff --git a/fggl/scenes/Scene.cpp b/fggl/scenes/Scene.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b3d2dab5b94d442991a820ebde09f4c45b34d96e --- /dev/null +++ b/fggl/scenes/Scene.cpp @@ -0,0 +1,25 @@ +// +// Created by webpigeon on 20/11/2021. +// + +#include "Scene.h" + +#include <utility> + +namespace fggl::scenes { + + void SceneManager::create(const std::string &name, std::shared_ptr<Scene> scene) { + m_scenes[name] = std::move(scene); + } + + void SceneManager::activate(const std::string &name) { + auto newScene = m_scenes.at(name); + if ( m_active != nullptr ) { + m_active->cleanup(); + m_active = nullptr; + } + newScene->setup(); + m_active = newScene; + } + +} \ No newline at end of file diff --git a/fggl/scenes/Scene.h b/fggl/scenes/Scene.h new file mode 100644 index 0000000000000000000000000000000000000000..d4c9daf1aa98d6ed6ad40e2788a5ed8ef0cea01a --- /dev/null +++ b/fggl/scenes/Scene.h @@ -0,0 +1,49 @@ +// +// Created by webpigeon on 20/11/2021. +// + +#ifndef FGGL_SCENE_H +#define FGGL_SCENE_H + +#include <memory> +#include <string> +#include <unordered_map> + +namespace fggl::scenes { + + class Scene { + public: + virtual ~Scene() = default; + + virtual void setup() = 0; + virtual void cleanup() = 0; + + virtual void update() = 0; + virtual void render() = 0; + }; + + class SceneManager { + public: + SceneManager() = default; + + void create(const std::string& name, std::shared_ptr<Scene> scene); + void activate(const std::string& name); + + inline void update() { + if ( m_active == nullptr ) { return; } + m_active->update(); + } + + inline void render() { + if ( m_active == nullptr ) { return; } + m_active->render(); + } + + private: + std::shared_ptr<Scene> m_active; + std::unordered_map<std::string, std::shared_ptr<Scene>> m_scenes; + }; + +} + +#endif //FGGL_SCENE_H diff --git a/fggl/util/service.h b/fggl/util/service.h new file mode 100644 index 0000000000000000000000000000000000000000..9d1c26108c224409a204bbbed41d2462d02629ae --- /dev/null +++ b/fggl/util/service.h @@ -0,0 +1,47 @@ +// +// Created by webpigeon on 20/11/2021. +// + +#ifndef FGGL_SERVICE_H +#define FGGL_SERVICE_H + +#include <memory> +#include <typeindex> +#include <unordered_map> + +namespace fggl::util { + + class ServiceLocator { + inline static ServiceLocator *s_instance; + std::unordered_map<std::type_index, std::shared_ptr<void>> m_services; + ServiceLocator() = default; + + public: + // don't allow copying or moving + ServiceLocator(ServiceLocator& other) = delete; + ServiceLocator(ServiceLocator&& other) = delete; + + static ServiceLocator& instance() { + if ( s_instance == nullptr ) { + s_instance = new ServiceLocator(); + } + return *s_instance; + } + + template<typename T> + void supply(std::shared_ptr<T> ptr) { + auto info = std::type_index(typeid(T)); + m_services[info] = ptr; + } + + template<typename T> + std::shared_ptr<T> providePtr() { + auto info = std::type_index(typeid(T)); + return std::static_pointer_cast<T>(m_services.at(info)); + } + + }; + +} + +#endif //FGGL_SERVICE_H