#include <filesystem> #include <iostream> #include <utility> #include <fggl/gfx/window.hpp> #include <fggl/gfx/camera.hpp> #include <fggl/input/camera_input.h> #include <fggl/gfx/compat.hpp> #include <fggl/gfx/ogl/compat.hpp> #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> #include <PerlinNoise.hpp> constexpr bool showNormals = false; // prototype of resource discovery void discover(const std::filesystem::path& base) { std::vector< std::filesystem::path > contentPacks; for ( auto& item : std::filesystem::directory_iterator(base) ) { // content pack detection if ( std::filesystem::is_directory( item ) ) { auto manifest = item.path() / "manifest.yml"; if ( std::filesystem::exists( manifest ) ) { contentPacks.push_back( item.path() ); } } } // what did we find? std::cerr << "found pack(s): " << std::endl; for ( auto& pack : contentPacks ) { std::cerr << pack << std::endl; } } enum camera_type { cam_free, cam_arcball }; camera_type cam_mode = cam_free; //TODO proper input system using InputManager = std::shared_ptr<fggl::input::Input>; void process_camera(fggl::ecs3::World& ecs, const InputManager& input) { auto cameras = ecs.findMatching<fggl::gfx::Camera>(); fggl::ecs3::entity_t cam = cameras[0]; auto camTransform = ecs.get<fggl::math::Transform>(cam); auto camComp = ecs.get<fggl::gfx::Camera>(cam); const glm::vec3 dir = ( camTransform->origin() - camComp->target ); const glm::vec3 forward = glm::normalize( dir ); // scroll wheel glm::vec3 motion(0.0f); float delta = input->mouse.axis( fggl::input::MouseAxis::SCROLL_Y ); if ( (glm::length( dir ) < 25.0f && delta < 0.0f) || (glm::length( dir ) > 2.5f && delta > 0.0f) ) motion -= (forward * delta); camTransform->origin( camTransform->origin() + motion ); if ( cam_mode == cam_arcball || input->mouse.button( fggl::input::MouseButton::MIDDLE ) ) { fggl::input::process_arcball(ecs, *input, cam); } else if ( cam_mode == cam_free ) { fggl::input::process_freecam(ecs, *input, cam); } fggl::input::process_edgescroll( ecs, *input, cam ); } void placeObject(fggl::ecs3::World& world, fggl::ecs::entity_t parent, fggl::ecs::entity_t prototype, glm::vec3 targetPos) { auto obj = world.copy(prototype); auto result = world.get<fggl::math::Transform>(obj); int xPos = (int)targetPos.x; int zPos = (int)targetPos.z * -1; // figure out the floor height auto heightMap = world.get<fggl::data::HeightMap>(parent); targetPos.y = heightMap->getValue(xPos, zPos); // TODO should really be the gradient at the required point result->origin( targetPos ); } class MenuScene : public fggl::scenes::Scene { public: explicit MenuScene(InputManager inputs) : m_inputs(std::move(inputs)) {} ~MenuScene() override = default; void setup() override { } void cleanup() override { } 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"); } } void render() override { } private: InputManager m_inputs; }; 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; void setup() override { auto types = m_world.types(); // 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)); m_world.add(prototype, types.find(fggl::input::FreeCamKeys::name)); auto camTf = m_world.get<fggl::math::Transform>(prototype); camTf->origin( glm::vec3(10.0f, 3.0f, 10.0f) ); auto cameraKeys = m_world.get<fggl::input::FreeCamKeys>(prototype); cameraKeys->forward = glfwGetKeyScancode(GLFW_KEY_W); cameraKeys->backward = glfwGetKeyScancode(GLFW_KEY_S); cameraKeys->left = glfwGetKeyScancode(GLFW_KEY_A); cameraKeys->right = glfwGetKeyScancode(GLFW_KEY_D); cameraKeys->rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q); cameraKeys->rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E); } fggl::ecs3::entity_t terrain; { terrain = m_world.create(false); m_world.add(terrain, types.find(fggl::math::Transform::name)); auto camTf = m_world.get<fggl::math::Transform>(terrain); camTf->origin( glm::vec3(0.0f, 0.0f, 0.0f) ); //auto terrainData = m_world.get<fggl::data::HeightMap>(terrain); fggl::data::HeightMap terrainData{}; terrainData.clear(); const siv::PerlinNoise::seed_type seed = 123456u; const siv::PerlinNoise perlin{ seed }; for (int y = 0; y < 255; ++y) { for (int x = 0; x < 255; ++x) { const double noise = perlin.octave2D_11( (x * 0.01), (y * 0.01) , 4) * 10.f; terrainData.heightValues[x * 255 +y] = (float)noise; } } m_world.set<fggl::data::HeightMap>(terrain, &terrainData); } // create foundation object fggl::ecs3::entity_t foundation; { foundation = m_world.create(true); m_world.add(foundation, types.find(fggl::math::Transform::name)); // plot rendering fggl::data::Mesh mesh; fggl::data::make_cube(mesh); mesh.removeDups(); // add mesh as a component constexpr char shader[] = "phong"; fggl::gfx::StaticMesh staticMesh{mesh, shader}; m_world.set<fggl::gfx::StaticMesh>(foundation, &staticMesh); } // 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.5f, (float)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(); fggl::gfx::StaticMesh staticMesh{mesh, shader}; m_world.set<fggl::gfx::StaticMesh>(bunker, &staticMesh); } for (int i=0; i<3; ++i) { glm::vec3 location(i * 6.5f + 1.0f, 0.0f, -7.0f); placeObject(m_world, terrain, foundation, location); } int nCubes = 3; for ( int i=0; i<nCubes; i++ ) { glm::vec3 location; location.x = i * 6.f + 1.0f; location.z = -5.0f + 1.0f; placeObject(m_world, terrain, bunker, location); } } void cleanup() override { } void update() override { process_camera(m_world, m_inputs); // JWR - this doesn't really seem like it belongs in the game scene... if ( m_inputs->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_F10)) ) { auto dbgui = fggl::util::ServiceLocator::instance().providePtr<fggl::debug::DebugUI>(); dbgui->visible( !dbgui->visible() ); } } void render() override { debugInspector(); } // entity inspector void debugInspector() { auto types = m_world.types(); ImGui::Begin("Entities"); 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 = m_world.getComponents(entity); for ( auto comp : entComp ){ auto meta = types.meta(comp); ImGui::Text("%s (%d) - %lu bytes", meta->name(), comp, meta->size()); } ImGui::TreePop(); } } ImGui::End(); }; private: fggl::ecs3::World& m_world; InputManager m_inputs; }; void gamepadDebug(bool* visible) { auto inputs = fggl::util::ServiceLocator::instance().providePtr<fggl::input::Input>(); auto &gamepads = inputs->gamepads; ImGui::Begin("GamePad", visible); for (int i = 0; i < 16; i++) { std::string title = gamepads.name(i); bool present = gamepads.present(i); if (ImGui::TreeNode(title.c_str())) { ImGui::Text("present: %s", present ? "yes" : "no"); if (present) { if (ImGui::TreeNode("buttons##2")) { for (auto &btn: fggl::input::GamepadButtonsMicrosoft) { ImGui::Text("%s: %i %i %i", btn.name, gamepads.button(i, btn.id), gamepads.buttonPressed(i, btn.id), gamepads.buttonReleased(i, btn.id) ); } ImGui::TreePop(); } if (ImGui::TreeNode("axes##2")) { for (auto &axis: fggl::input::GamepadAxes) { ImGui::Text("%s: %f %f", axis.name, gamepads.axis(i, axis.id), gamepads.axisDelta(i, axis.id) ); } ImGui::TreePop(); } } ImGui::TreePop(); ImGui::Separator(); } } ImGui::End(); } int main(int argc, char* argv[]) { auto& locator = fggl::util::ServiceLocator::instance(); // setup ECS Types fggl::ecs3::TypeRegistry types; types.make<fggl::math::Transform>(); fggl::ecs3::ModuleManager modules(types); // setup ECS fggl::ecs3::World ecs(types); // input management auto inputs = std::make_shared<fggl::input::Input>(); locator.supply<fggl::input::Input>(inputs); // window auto glfwModule = modules.load<fggl::gfx::ecsGlfwModule>(inputs); auto window = glfwModule->createWindow("Demo Game"); window->fullscreen( true ); // storage API auto storage = std::make_shared<fggl::data::Storage>(); locator.supply<fggl::data::Storage>(storage); //discover( storage.resolvePath(fggl::data::Data, "../../packs") ); // Opengl APIs auto glModule = modules.load<fggl::gfx::ecsOpenGLModule>(window, storage); fggl::gfx::loadPipeline(glModule, "unlit", false); fggl::gfx::loadPipeline(glModule, "phong", false); fggl::gfx::loadPipeline(glModule, "normals", false); // debug layer std::shared_ptr<fggl::debug::DebugUI> debug = std::make_shared<fggl::debug::DebugUI>(window); locator.supply<fggl::debug::DebugUI>(debug); debug->addWindow("gamepad", gamepadDebug); debug->addWindow("imgui-demo", ImGui::ShowDemoWindow); debug->addWindow("imgui-about", ImGui::ShowAboutWindow); debug->addWindow("imgui-help", [](bool* val) { ImGui::ShowUserGuide(); } ); debug->visible(true); // Scene management 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)); scenes->activate("main_menu"); // Main game/event loop fggl::util::Timer time{}; time.frequency( glfwGetTimerFrequency() ); time.setup( glfwGetTimerValue() ); while( !window->closeRequested() ) { // // Setup setup // time.tick( glfwGetTimerValue() ); inputs->frame( time.delta() ); glfwModule->context.pollEvents(); debug->frameStart(); // // update step // scenes->update(); // render the scene window->activate(); glModule->ogl.clear(); // allow the scene to do stuff, then actually render scenes->render(); fggl::gfx::renderMeshes(glModule, ecs, time.delta()); debug->draw(); window->swap(); } return 0; }