diff --git a/demo/main.cpp b/demo/main.cpp index 820e84875d8787f9be4dd44637e8adc2a313afd3..64d444ed02c2f9c059ffa39740cd551ccff0dfa5 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -152,8 +152,9 @@ public: auto& locator = fggl::util::ServiceLocator::instance(); m_inputs = locator.providePtr<fggl::input::Input>(); - auto types = locator.providePtr<fggl::ecs3::TypeRegistry>(); - m_world = std::make_unique<fggl::ecs3::World>(*types); + // setup the world using the global type registry + // FIXME: type registry probably doesn't need to be application-global - will make savegame/mod support complicated + m_world = std::make_unique<fggl::ecs3::World>( *m_owner.registry() ); m_sceneTime = std::make_unique<fggl::util::Timer>(); m_sceneTime->frequency( glfwGetTimerFrequency() ); @@ -173,15 +174,19 @@ public: 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) ); + if ( camTf != nullptr) { + 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); + if ( cameraKeys != nullptr ) { + 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; @@ -286,7 +291,7 @@ public: } void render(fggl::gfx::Paint& paint) override { - debugInspector(); + //debugInspector(); fggl::gfx::renderMeshes(glModule, *m_world, m_sceneTime->delta()); } @@ -368,6 +373,7 @@ int main(int argc, const char* argv[]) { auto& locator = fggl::util::ServiceLocator::instance(); auto inputs = locator.supply<fggl::input::Input>(std::make_shared<fggl::input::Input>()); auto storage = locator.supply<fggl::data::Storage>(std::make_shared<fggl::data::Storage>()); + locator.supply<fggl::ecs3::TypeRegistry>(std::make_shared<fggl::ecs3::TypeRegistry>()); // Would be nice to not take args like this, it messes with lifetimes auto& windowing = app.use<fggl::gfx::ecsGlfwModule>(inputs); @@ -378,15 +384,22 @@ int main(int argc, const char* argv[]) { window->fullscreen( true ); app.setWindow( std::move(window) ); + // load a bunch of modules to provide game functionality + app.use<fggl::ecs3::ecsTypes>(); + app.use<fggl::gfx::ecsOpenGLModule>(storage); + // atlas testing std::vector< fggl::gfx::ImageAtlas<char>::SubImage > images; auto *atlas = fggl::gfx::ImageAtlas<char>::pack(images); // and now our states auto *menu = app.add_state<fggl::scenes::BasicMenu>("menu"); + menu->add("start", [&app]() { app.change_state("game"); }); + menu->add("options", [&app]() { app.change_state("game"); }); + menu->add("quit", [&app]() { app.change_state("game"); }); - // game state + // game state app.add_state<GameScene>("game"); return app.run(argc, argv); diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt index 244f3bf49760ca97e5cb1579e377fc9c6ca215cf..0ff4692f0cd80b5a8b782c34a9f24e5d8c9bf87e 100644 --- a/fggl/CMakeLists.txt +++ b/fggl/CMakeLists.txt @@ -22,6 +22,9 @@ target_sources(${PROJECT_NAME} input/input.cpp input/mouse.cpp data/heightmap.cpp + gui/widget.cpp + gui/widgets.cpp + gui/containers.cpp ) # spdlog for cleaner logging diff --git a/fggl/gfx/ogl/renderer.cpp b/fggl/gfx/ogl/renderer.cpp index b4d18e8a3c37a0a10dbdc7d28e546de1a975613a..baf0a00f95315a7fd9442bb07ed83d0ece87e1f7 100644 --- a/fggl/gfx/ogl/renderer.cpp +++ b/fggl/gfx/ogl/renderer.cpp @@ -299,11 +299,10 @@ namespace fggl::gfx { return token; } -// TODO(webpigeon): this shouldn't be hard-coded + // TODO(webpigeon): this shouldn't be hard-coded constexpr glm::vec3 DEFAULT_LIGHTPOS = glm::vec3(20.0F, 20.0F, 15.0F); - void MeshRenderer::render(fggl::ecs3::World &ecs, ecs3::entity_t camera, - float dt) { + void MeshRenderer::render(fggl::ecs3::World &ecs, ecs3::entity_t camera, float dt) { if (camera == ecs::NULL_ENTITY) { spdlog::warn("tried to render a scene, but no camera exists!"); return; @@ -334,8 +333,7 @@ namespace fggl::gfx { glm::vec3 lightPos = DEFAULT_LIGHTPOS; // TODO(webpigeon): better performance if grouped by vao first - // TODO(webpigeon): the nvidia performance presentation said I shouldn't use - // uniforms for large data + // TODO(webpigeon): the nvidia performance presentation said I shouldn't use uniforms for large data for (auto &entity : entities) { const auto &transform = ecs.get<fggl::math::Transform>(entity); const auto &mesh = ecs.get<GlRenderToken>(entity); diff --git a/fggl/gui/containers.cpp b/fggl/gui/containers.cpp index 33f83e24160e12e9108154949d1721d3f0a4dffa..ed36f1ad1c7b7cb03ac34fcc4485358af53282f2 100644 --- a/fggl/gui/containers.cpp +++ b/fggl/gui/containers.cpp @@ -2,6 +2,32 @@ namespace fggl::gui { + Widget *Container::getChildAt(const math::vec2 &point) { + for ( auto& child : m_children ){ + if ( child->contains(point) ){ + return child->getChildAt(point); + } + } + + return nullptr; + } + + bool Container::contains(const math::vec2 &point) { + return true; + } + + void Container::render(gfx::Paint &paint) { + for( auto& child : m_children ){ + child->render( paint ); + } + } + + void Container::add(std::unique_ptr<Widget> widget) { + m_children.push_back( std::move(widget) ); + m_dirty = true; + } + + /* Box::Box( LayoutAxis axis ) : m_axis( axis ) {} void Box::layout() { @@ -24,6 +50,6 @@ namespace fggl::gui { setSize(lineSum, biggestCross); else setSize(biggestCross, lineSum); - } + }*/ }; diff --git a/fggl/gui/widget.cpp b/fggl/gui/widget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..837a42c63683ad88fea86e5eddb72619844dd90c --- /dev/null +++ b/fggl/gui/widget.cpp @@ -0,0 +1,146 @@ +// +// Created by webpigeon on 17/04/22. +// + +#include <fggl/gui/widget.hpp> + +namespace fggl::gui { + + void buttonBorder( gfx::Path2D& path, glm::vec2 pos, glm::vec2 size ) { + // outer box + path.colour( {1.0f, 0.0f, 0.0f} ); + path.pathTo( { pos.x + size.x, pos.y } ); + path.pathTo( { pos.x + size.x, pos.y + size.y } ); + path.pathTo( { pos.x, pos.y + size.y } ); + path.close(); + + // inner box + math::vec2 innerTop { pos.x + 5, pos.y + 5 }; + math::vec2 innerBottom { pos.x + size.x - 5, pos.y + size.y - 5 }; + + path.colour( {1.0f, 1.0f, 0.0f} ); + path.moveTo( { innerTop.x, innerTop.y } ); + path.pathTo( { innerBottom.x, innerTop.y } ); + path.pathTo( { innerBottom.x, innerBottom.y } ); + path.pathTo( { innerTop.x, innerBottom.y } ); + path.pathTo( { innerTop.x, innerTop.y } ); + } + + void makeBox( gfx::Path2D& path, glm::vec2 topLeft, glm::vec2 bottomRight ) { + path.moveTo( { topLeft.x, topLeft.y } ); + path.pathTo( { bottomRight.x, topLeft.y } ); + path.pathTo( { bottomRight.x, bottomRight.y } ); + path.pathTo( { topLeft.x, bottomRight.y } ); + path.pathTo( { topLeft.x, topLeft.y } ); + } + + void draw_progress( gfx::Path2D& path, glm::vec2 topLeft, glm::vec2 size, float value ) { + const auto bottomRight { topLeft + size }; + + // background + path.colour( {0.5f, 0.5f, 0.5f} ); + makeBox( path, topLeft, bottomRight ); + + // fill + math::vec2 innerTop { topLeft.x + 5, topLeft.y + 5 }; + math::vec2 innerBottom { bottomRight.x - 5, bottomRight.y - 5 }; + + // figure out how wide the bar should be + float barWidth = (innerBottom.x - innerTop.x) * value; + float trueBottom = innerBottom.x; + innerBottom.x = innerTop.x + barWidth; + + // draw the bar + path.colour( {0.8f, 0.0f, 0.0f} ); + makeBox( path, innerTop, innerBottom ); + + // part of the bar that's not filled in + math::vec2 emptyTop { innerBottom.x, innerTop.y }; + math::vec2 emptyBottom { trueBottom, innerBottom.y }; + path.colour( {0.4f, 0.0f, 0.0f} ); + makeBox( path, emptyTop, emptyBottom ); + + } + + void draw_slider( gfx::Path2D& path, glm::vec2 topLeft, glm::vec2 size, float value ) { + draw_progress( path, topLeft, size, value ); + + // dimensions + const auto bottomRight { topLeft + size }; + const math::vec2 innerTop { topLeft.x + 5, topLeft.y + 5 }; + const math::vec2 innerBottom { bottomRight.x - 5, bottomRight.y - 5 }; + + // selector bar + float trackWidth = innerBottom.x - innerTop.x; + float selectorValue = trackWidth * value; + float selectorWidth = 6; + + math::vec2 selectorTop { innerTop.x + selectorValue - ( selectorWidth/2), topLeft.y }; + math::vec2 selectorBottom { selectorTop.x + selectorWidth, bottomRight.y }; + path.colour( {1.0f, 1.0f, 1.0f} ); + makeBox( path, selectorTop, selectorBottom ); + } + + void draw_button( gfx::Path2D& path, glm::vec2 pos, glm::vec2 size, bool active, bool pressed) { + // locations + math::vec2 outerTop { pos }; + math::vec2 outerBottom { pos + size }; + math::vec2 innerTop { pos.x + 5, pos.y + 5 }; + math::vec2 innerBottom { pos.x + size.x - 5, pos.y + size.y - 5 }; + + math::vec3 baseColour{ 0.5f, 0.5f, 0.5f }; + + if ( active ) { + baseColour *= 1.2f; + } + + if ( pressed ) { + baseColour *= 0.8f; + } + + math::vec3 lightColour{ baseColour * 1.2f }; + math::vec3 darkColour{ baseColour * 0.8f }; + if ( pressed ) { + // flip light and dark for selected buttons + auto tmp = darkColour; + darkColour = lightColour; + lightColour = tmp; + } + + // bottom side + path.colour( darkColour ); + path.moveTo( outerTop ); + path.pathTo( innerTop ); + path.pathTo( { innerBottom.x, innerTop.y } ); + path.pathTo( { outerBottom.x, outerTop.y } ); + path.pathTo( outerTop ); + + // left side + path.colour( darkColour ); + path.moveTo( outerTop ); + path.pathTo( innerTop ); + path.pathTo( { innerTop.x, innerBottom.y } ); + path.pathTo( { outerTop.x, outerBottom.y } ); + path.pathTo( outerTop ); + + // top side + path.colour( lightColour ); + path.moveTo( { outerTop.x, outerBottom.y} ); + path.pathTo( { innerTop.x, innerBottom.y} ); + path.pathTo( innerBottom ); + path.pathTo( outerBottom ); + path.pathTo( { outerTop.x, outerBottom.y} ); + + // right side + path.colour( lightColour ); + path.moveTo( outerBottom ); + path.pathTo( innerBottom ); + path.pathTo( { innerBottom.x, innerTop.y } ); + path.pathTo( { outerBottom.x, outerTop.y } ); + path.pathTo( outerBottom ); + + // inner box + path.colour( baseColour ); + makeBox( path, innerTop, innerBottom ); + } +} \ No newline at end of file diff --git a/fggl/gui/widgets.cpp b/fggl/gui/widgets.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c10d07d75d51b9c8ae25fe02bdaece0b4d547908 --- /dev/null +++ b/fggl/gui/widgets.cpp @@ -0,0 +1,40 @@ +// +// Created by webpigeon on 17/04/22. +// + +#include <fggl/gui/widget.hpp> +#include <fggl/gui/widgets.hpp> + +#include <spdlog/spdlog.h> + +namespace fggl::gui { + + Button::Button( math::vec2 pos, math::vec2 size) : Widget(pos, size), m_hover(false), m_active(false) {} + + void Button::render(gfx::Paint &paint) { + gfx::Path2D path{ topLeft() }; + draw_button(path, topLeft(), size(), m_hover, m_active); + paint.fill(path); + } + + void Button::activate() { + m_active = !m_active; + if ( m_active ) { + for( auto& callback : m_callbacks ) { + callback(); + } + } + } + + void Button::onEnter() { + m_hover = true; + } + + void Button::onExit() { + m_hover = false; + } + + void Button::addCallback(Callback cb) { + m_callbacks.push_back( cb ); + } +} // namespace fggl::gui \ No newline at end of file diff --git a/fggl/scenes/menu.cpp b/fggl/scenes/menu.cpp index 8d9936ed2bb3599740d6d50ce2b5ae10cce7f835..386dbf67feab1a9be8eb7a0d0516e12f9204c9d4 100644 --- a/fggl/scenes/menu.cpp +++ b/fggl/scenes/menu.cpp @@ -1,5 +1,6 @@ #include <fggl/scenes/menu.hpp> #include <fggl/util/service.h> +#include <fggl/gui/gui.hpp> #include <spdlog/spdlog.h> @@ -9,150 +10,14 @@ namespace fggl::scenes { using fggl::input::MouseAxis; - static void buttonBorder( gfx::Path2D& path, glm::vec2 pos, glm::vec2 size ) { - // outer box - path.colour( {1.0f, 0.0f, 0.0f} ); - path.pathTo( { pos.x + size.x, pos.y } ); - path.pathTo( { pos.x + size.x, pos.y + size.y } ); - path.pathTo( { pos.x, pos.y + size.y } ); - path.close(); - - // inner box - math::vec2 innerTop { pos.x + 5, pos.y + 5 }; - math::vec2 innerBottom { pos.x + size.x - 5, pos.y + size.y - 5 }; - - path.colour( {1.0f, 1.0f, 0.0f} ); - path.moveTo( { innerTop.x, innerTop.y } ); - path.pathTo( { innerBottom.x, innerTop.y } ); - path.pathTo( { innerBottom.x, innerBottom.y } ); - path.pathTo( { innerTop.x, innerBottom.y } ); - path.pathTo( { innerTop.x, innerTop.y } ); - } - - static void makeBox( gfx::Path2D& path, glm::vec2 topLeft, glm::vec2 bottomRight ) { - path.moveTo( { topLeft.x, topLeft.y } ); - path.pathTo( { bottomRight.x, topLeft.y } ); - path.pathTo( { bottomRight.x, bottomRight.y } ); - path.pathTo( { topLeft.x, bottomRight.y } ); - path.pathTo( { topLeft.x, topLeft.y } ); - } - - static void makeProgress( gfx::Path2D& path, glm::vec2 topLeft, glm::vec2 size, float value ) { - const auto bottomRight { topLeft + size }; - - // background - path.colour( {0.5f, 0.5f, 0.5f} ); - makeBox( path, topLeft, bottomRight ); - - // fill - math::vec2 innerTop { topLeft.x + 5, topLeft.y + 5 }; - math::vec2 innerBottom { bottomRight.x - 5, bottomRight.y - 5 }; - - // figure out how wide the bar should be - float barWidth = (innerBottom.x - innerTop.x) * value; - float trueBottom = innerBottom.x; - innerBottom.x = innerTop.x + barWidth; - - // draw the bar - path.colour( {0.8f, 0.0f, 0.0f} ); - makeBox( path, innerTop, innerBottom ); - - // part of the bar that's not filled in - math::vec2 emptyTop { innerBottom.x, innerTop.y }; - math::vec2 emptyBottom { trueBottom, innerBottom.y }; - path.colour( {0.4f, 0.0f, 0.0f} ); - makeBox( path, emptyTop, emptyBottom ); - - } - - static void makeSlider( gfx::Path2D& path, glm::vec2 topLeft, glm::vec2 size, float value ) { - - makeProgress( path, topLeft, size, value ); - - // dimensions - const auto bottomRight { topLeft + size }; - const math::vec2 innerTop { topLeft.x + 5, topLeft.y + 5 }; - const math::vec2 innerBottom { bottomRight.x - 5, bottomRight.y - 5 }; - - // selector bar - float trackWidth = innerBottom.x - innerTop.x; - float selectorValue = trackWidth * value; - float selectorWidth = 6; - - math::vec2 selectorTop { innerTop.x + selectorValue - ( selectorWidth/2), topLeft.y }; - math::vec2 selectorBottom { selectorTop.x + selectorWidth, bottomRight.y }; - path.colour( {1.0f, 1.0f, 1.0f} ); - makeBox( path, selectorTop, selectorBottom ); - } - - - - static void makeButton( gfx::Path2D& path, glm::vec2 pos, glm::vec2 size, bool active, bool pressed) { - // locations - math::vec2 outerTop { pos }; - math::vec2 outerBottom { pos + size }; - math::vec2 innerTop { pos.x + 5, pos.y + 5 }; - math::vec2 innerBottom { pos.x + size.x - 5, pos.y + size.y - 5 }; - math::vec3 baseColour{ 0.5f, 0.5f, 0.5f }; - - if ( active ) { - baseColour *= 1.2f; - } - - if ( pressed ) { - baseColour *= 0.8f; - } - - math::vec3 lightColour{ baseColour * 1.2f }; - math::vec3 darkColour{ baseColour * 0.8f }; - if ( pressed ) { - // flip light and dark for selected buttons - auto tmp = darkColour; - darkColour = lightColour; - lightColour = tmp; - } - - // bottom side - path.colour( darkColour ); - path.moveTo( outerTop ); - path.pathTo( innerTop ); - path.pathTo( { innerBottom.x, innerTop.y } ); - path.pathTo( { outerBottom.x, outerTop.y } ); - path.pathTo( outerTop ); - - // left side - path.colour( darkColour ); - path.moveTo( outerTop ); - path.pathTo( innerTop ); - path.pathTo( { innerTop.x, innerBottom.y } ); - path.pathTo( { outerTop.x, outerBottom.y } ); - path.pathTo( outerTop ); - - // top side - path.colour( lightColour ); - path.moveTo( { outerTop.x, outerBottom.y} ); - path.pathTo( { innerTop.x, innerBottom.y} ); - path.pathTo( innerBottom ); - path.pathTo( outerBottom ); - path.pathTo( { outerTop.x, outerBottom.y} ); - - // right side - path.colour( lightColour ); - path.moveTo( outerBottom ); - path.pathTo( innerBottom ); - path.pathTo( { innerBottom.x, innerTop.y } ); - path.pathTo( { outerBottom.x, outerTop.y } ); - path.pathTo( outerBottom ); - - // inner box - path.colour( baseColour ); - makeBox( path, innerTop, innerBottom ); - } - - BasicMenu::BasicMenu(fggl::App& app) : AppState(app), m_inputs(nullptr), m_active() { + BasicMenu::BasicMenu(fggl::App& app) : AppState(app), m_inputs(nullptr), m_active(), m_hover(nullptr) { auto& locator = fggl::util::ServiceLocator::instance(); m_inputs = locator.get<input::Input>(); + + math::vec2 pos{ 500.F, 500.F }; + math::vec2 size{ 32.5F, 35.F }; + m_canvas.add(std::make_unique<gui::Button>(pos, size)); } void BasicMenu::update() { @@ -160,45 +25,36 @@ namespace fggl::scenes { m_cursorPos.x = m_inputs->mouse.axis( MouseAxis::X ); m_cursorPos.y = m_inputs->mouse.axis( MouseAxis::Y ); - if ( m_inputs->mouse.pressed( MouseButton::LEFT ) ) { - spdlog::info("clicky clicky: ({}, {})", m_cursorPos.x, m_cursorPos.y); + // in canvas space + math::vec2 projected; + projected.x = math::rescale_ndc(m_cursorPos.x, 0, 1920.f); + projected.y = math::rescale_ndc(m_cursorPos.y, 1080.0f, 0); + + auto* hoverWidget = m_canvas.getChildAt(projected); + if ( hoverWidget != m_hover ){ + if ( m_hover != nullptr ) { + m_hover->onExit(); + } + m_hover = hoverWidget; + if ( m_hover != nullptr ){ + m_hover->onEnter(); + } + } + + if ( m_inputs->mouse.pressed( MouseButton::LEFT ) ) { + spdlog::info("clicky clicky: ({}, {})", projected.x, projected.y); + + auto widget = m_canvas.getChildAt(projected); + if (widget != nullptr) { + widget->activate(); + spdlog::info("ooo! there is a thing there!"); + } } } } void BasicMenu::render(gfx::Paint& paint) { - const math::vec2 btnSize{ 150.0f, 30.0f }; - const float spacing = 5; - - const float padX = 50.0f; - const float padY = 50.0f; - - math::vec2 pos { 1920.0f - ( padX + btnSize.x ), padY }; - for ( const auto& item : m_items ) { - gfx::Path2D btn( pos ); - makeButton( btn, pos, btnSize, m_active == item.first, false ); - paint.fill( btn ); - pos.y += (btnSize.y + spacing); - } - - pos.x = padX; - pos.y = padY; - for ( int i = 0; i <= 10; i++ ) { - gfx::Path2D btn( pos ); - makeProgress( btn, pos, btnSize, i / 10.f ); - paint.fill( btn ); - - pos.y += (btnSize.y + spacing); - } - - for ( int i = 0; i <= 10; i++ ) { - gfx::Path2D btn( pos ); - makeSlider( btn, pos, btnSize, i / 10.0f ); - paint.fill( btn ); - - pos.y += (btnSize.y + spacing); - } - + m_canvas.render( paint ); } void BasicMenu::activate() { @@ -211,6 +67,22 @@ namespace fggl::scenes { void BasicMenu::add(const std::string& name, callback cb) { m_items[name] = cb; + + const math::vec2 btnSize{ 150.0f, 30.0f }; + const float spacing = 5; + const float padX = 50.0f; + const float padY = 50.0f; + + // figure out the position based off the old logic + // FIXME should be the container's job + math::vec2 pos { 1920.0f - ( padX + btnSize.x ), padY }; + auto btnIdx = m_items.size() - 1; + pos.y += (btnIdx * (btnSize.y + spacing)); + + // build the button + auto btn = std::make_unique<gui::Button>(pos, btnSize); + btn->addCallback(cb); + m_canvas.add(std::move(btn)); } }; diff --git a/include/fggl/app.hpp b/include/fggl/app.hpp index 14fb9582c079d3ddc81222e2291277de4a853af7..b3f15053612ede7dfee60ad6a8fb67e7f064ed2f 100644 --- a/include/fggl/app.hpp +++ b/include/fggl/app.hpp @@ -129,6 +129,10 @@ namespace fggl { return m_states.active(); } + inline ecs3::TypeRegistry* registry() { + return m_types.get(); + } + inline bool running() const { return m_running; } diff --git a/include/fggl/ecs3/ecs.hpp b/include/fggl/ecs3/ecs.hpp index bdd1fe06041712c8d3b9748aba394a6609f78531..0619eda3951aa56a2926be3e965ae2c12d2bed2c 100644 --- a/include/fggl/ecs3/ecs.hpp +++ b/include/fggl/ecs3/ecs.hpp @@ -3,11 +3,24 @@ #include <fggl/ecs3/module/module.h> #include <fggl/ecs3/prototype/world.h> +#include <fggl/math/types.hpp> namespace fggl::ecs3 { using World = prototype::World; + class ecsTypes : public Module { + + public: + inline std::string name() const override { + return "ecs::core"; + } + + inline void onLoad(ModuleManager& manager, TypeRegistry& types) override { + types.make<math::Transform>(); + } + }; + } #endif \ No newline at end of file diff --git a/include/fggl/ecs3/prototype/world.h b/include/fggl/ecs3/prototype/world.h index 314668d197e3617aed65f132186251f712e07b91..97e2d10d2d96812c5f8519677c11c553c9387837 100644 --- a/include/fggl/ecs3/prototype/world.h +++ b/include/fggl/ecs3/prototype/world.h @@ -201,8 +201,18 @@ namespace fggl::ecs3::prototype { template<typename C> C *get(entity_t entity_id) { - auto &entity = m_entities.at(entity_id); - return entity.get<C>(); + try { + auto &entity = m_entities.at(entity_id); + try { + return entity.get<C>(); + } catch ( std::out_of_range& e ) { + std::cerr << "entity " << entity_id << " does not have component "<< C::name << std::endl; + return nullptr; + } + } catch ( std::out_of_range& e) { + std::cerr << "someone requested an component that didn't exist, entity was: " << entity_id << std::endl; + return nullptr; + } } void *get(entity_t entity_id, component_type_t t) { diff --git a/include/fggl/ecs3/types.hpp b/include/fggl/ecs3/types.hpp index f6fe4e527bef5481a7bb08504be5bc879a1edd26..868447213b81c0095101bf604f6a21dac1878123 100644 --- a/include/fggl/ecs3/types.hpp +++ b/include/fggl/ecs3/types.hpp @@ -184,6 +184,8 @@ namespace fggl::ecs3 { return type; } } + + std::cerr << "asked for unknown component type: " << name << std::endl; return 0; } diff --git a/include/fggl/gfx/ogl/backend.hpp b/include/fggl/gfx/ogl/backend.hpp index 047d713f3fa1da1513cfd1646a468907a257f3f7..8250f5f0e08ae1c24a0b04a2d8587e253e028336 100644 --- a/include/fggl/gfx/ogl/backend.hpp +++ b/include/fggl/gfx/ogl/backend.hpp @@ -72,6 +72,7 @@ namespace fggl::gfx { private: int m_handle; }; + } #endif diff --git a/include/fggl/gfx/ogl/compat.hpp b/include/fggl/gfx/ogl/compat.hpp index 187f46a854b958e3ff36eed25c290d951b658418..8592b40fd868cdc3eef94ed31ed1fba98bdfb543 100644 --- a/include/fggl/gfx/ogl/compat.hpp +++ b/include/fggl/gfx/ogl/compat.hpp @@ -33,7 +33,7 @@ namespace fggl::gfx { fggl::gfx::MeshRenderer renderer; fggl::gfx::ShaderCache cache; - ecsOpenGLModule(Window &window, std::shared_ptr<fggl::data::Storage> storage) : + explicit ecsOpenGLModule(std::shared_ptr<fggl::data::Storage> storage) : renderer(), cache(std::move(storage)) {} @@ -42,7 +42,7 @@ namespace fggl::gfx { } void uploadMesh(ecs3::World *world, ecs::entity_t entity) { - auto meshData = world->get<gfx::StaticMesh>(entity); + auto *meshData = world->get<gfx::StaticMesh>(entity); auto pipeline = cache.get(meshData->pipeline); auto glMesh = renderer.upload(meshData->mesh); @@ -52,7 +52,7 @@ namespace fggl::gfx { } void uploadHeightmap(ecs3::World *world, ecs::entity_t entity) { - const auto heightmap = world->get<data::HeightMap>(entity); + auto *const heightmap = world->get<data::HeightMap>(entity); data::Mesh tmpMesh{}; data::generateHeightMesh(heightmap, tmpMesh); diff --git a/include/fggl/gfx/ogl/models.hpp b/include/fggl/gfx/ogl/models.hpp new file mode 100644 index 0000000000000000000000000000000000000000..afcaff86abd52a8a1b5168417565eb7b9cb39fcb --- /dev/null +++ b/include/fggl/gfx/ogl/models.hpp @@ -0,0 +1,48 @@ +// +// Created by webpigeon on 17/04/22. +// + +#ifndef FGGL_GFX_OGL_MODELS_HPP +#define FGGL_GFX_OGL_MODELS_HPP + +#include <string> +#include <unordered_map> + +#include <fggl/gfx/ogl/backend.hpp> +#include <fggl/ecs3/ecs.hpp> + +namespace fgg::gfx::ogl { + + namespace { + using fggl::ecs3::World; + using fggl::gfx::Shader; + } + + class StaticModelRenderer { + public: + StaticModelRenderer(); + ~StaticModelRenderer() = default; + + void render(World& world) { + resolveModels(); + renderModels(); + } + + private: + /** + * Attach any missing render tokens to models. + */ + void resolveModels(); + + /** + * Render all visible objects according to their render tokens. + */ + void renderModels(); + + private: + std::unordered_map<std::string, std::shared_ptr<Shader>> m_shaders; + }; + +} + +#endif //FGGL_INCLUDE_FGGL_GFX_OGL_MODELS_HPP diff --git a/include/fggl/gfx/paint.hpp b/include/fggl/gfx/paint.hpp index 6082674daf911d43ac4bca90c0032ed4a8e69b13..01a63cf2daa3adea35189cab7b169e465863f57e 100644 --- a/include/fggl/gfx/paint.hpp +++ b/include/fggl/gfx/paint.hpp @@ -1,6 +1,7 @@ #ifndef FGGL_GFX_PAINT_H #define FGGL_GFX_PAINT_H +#include <string> #include <vector> #include <fggl/math/types.hpp> diff --git a/include/fggl/gui/containers.hpp b/include/fggl/gui/containers.hpp index 813e450908381d40d2e4cce2b4540a4cd5818ff1..d27d59d8a8678ad2e64de0c1f028535b7efc1375 100644 --- a/include/fggl/gui/containers.hpp +++ b/include/fggl/gui/containers.hpp @@ -5,33 +5,26 @@ namespace fggl::gui { - class Widget { - public: - Widget() = default; - - virtual math::vec2 baseSize(); - - void size(math::vec2 size); - math::vec2 size() const; - - virtual bool contains(const math::vec2 &point); - virtual Widget *getChildAt(const math::vec2 &point); - - virtual void render(gfx::Paint &paint) = 0; - }; - class Container : public Widget { public: Container() = default; + virtual ~Container() = default; - void clear(); - Widget *getChildAt(const std::math &point) override; + inline void clear() { + m_children.clear(); + } - void add(Widget &&widget); - virtual void layout() = 0; + void add(std::unique_ptr<Widget> widget); + virtual inline void layout() {} + + bool contains(const math::vec2 &point) override; + Widget *getChildAt(const math::vec2 &point) override; + void render(gfx::Paint &paint) override; private: - bool requireLayout; + bool m_dirty; + + protected: std::vector<std::unique_ptr<Widget>> m_children; }; @@ -50,13 +43,13 @@ namespace fggl::gui { * * QT's QBoxLayout * * Android's XML box things */ - class Box : public Container { + /*class Box : public Container { public: Box(LayoutAxis axis); void layout() override; private: const LayoutAxis m_axis; - } + };*/ }; //namespace fggl::gui diff --git a/include/fggl/gui/gui.hpp b/include/fggl/gui/gui.hpp index e31d655c5b915d80543394012edf3a9a47547c61..a79255258231f042dd6a035815546e429eda0511 100644 --- a/include/fggl/gui/gui.hpp +++ b/include/fggl/gui/gui.hpp @@ -5,5 +5,7 @@ namespace fggl::gui { }; //namespace fggl::gui #include <fggl/gui/widget.hpp> +#include <fggl/gui/containers.hpp> +#include <fggl/gui/widgets.hpp> #endif diff --git a/include/fggl/gui/widget.hpp b/include/fggl/gui/widget.hpp index 50a092d0e8ce2fe4b3c447bafd62b8cfc402b2b9..e74e496f11eda4b429f146e36af84b8d74b44cf3 100644 --- a/include/fggl/gui/widget.hpp +++ b/include/fggl/gui/widget.hpp @@ -4,32 +4,72 @@ #include <fggl/math/types.hpp> #include <fggl/gfx/paint.hpp> +#include <memory> + namespace fggl::gui { + void draw_box( gfx::Path2D& path, math::vec2 topLeft, math::vec2 bottomRight); + void draw_progress( gfx::Path2D& path, math::vec2 topLeft, math::vec2 size, float value); + void draw_slider( gfx::Path2D& path, math::vec2 topLeft, math::vec2 size, float value); + void draw_button( gfx::Path2D& path, math::vec2 topLeft, math::vec2 size, bool active, bool pressed); + + struct Bounds2D { + math::vec2 topLeft; + math::vec2 size; + + Bounds2D() = default; + inline Bounds2D(math::vec2 pos, math::vec2 a_size) : topLeft(pos), size(a_size) {} + + inline bool contains(math::vec2 point) { + return ! ( (point.x > topLeft.x + size.x) || + (point.x < topLeft.x ) || + (point.y > topLeft.y + size.y) || + (point.y < topLeft.y) + ); + } + }; + class Widget { public: Widget() = default; + inline Widget(math::vec2 pos, math::vec2 size) : m_bounds(pos, size) {} - virtual bool contains(const math::vec2 &point); - virtual Widget *getChildAt(const math::vec2 &point); + virtual ~Widget() = default; - virtual void render(gfx::Paint &paint) = 0; - }; + inline math::vec2 topLeft() { + return m_bounds.topLeft; + }; - class Container : public Widget { - public: - Container() = default; + inline math::vec2 bottomRight() { + return m_bounds.topLeft + m_bounds.size; + } - void clear(); - Widget *getChildAt(const std::math &point) override; + inline math::vec2 size() { + return m_bounds.size; + } - void add(Widget &&widget); + virtual inline bool contains(const math::vec2 &point){ + return m_bounds.contains(point); + }; + + virtual inline Widget *getChildAt(const math::vec2 &point) { + if ( !contains(point) ) { + return nullptr; + } + return this; + } + + virtual void render(gfx::Paint &paint) = 0; + inline virtual void activate() {}; + + inline virtual void onEnter() {} + inline virtual void onExit() {} private: - bool requireLayout; - std::vector<std::unique_ptr<Widget>> m_children; + Bounds2D m_bounds; }; + }; //namespace fggl::gui #endif diff --git a/include/fggl/gui/widgets.hpp b/include/fggl/gui/widgets.hpp index 7fff2377e82b9656d3720a8544f537104f95a300..27a620723d7753ad6f78e6acb288eb193e668987 100644 --- a/include/fggl/gui/widgets.hpp +++ b/include/fggl/gui/widgets.hpp @@ -1,16 +1,30 @@ #ifndef FGGL_GUI_WIDGETS_H #define FGGL_GUI_WIDGETS_H +#include <functional> #include <fggl/gui/widget.hpp> namespace fggl::gui { + using Callback = std::function<void(void)>; + class Button : public Widget { public: - Button(); + Button() = default; + Button(math::vec2 pos, math::vec2 size); + void render(gfx::Paint &paint) override; + + void activate() override; + void onEnter() override; + void onExit() override; + + void addCallback(Callback cb); private: const std::string m_value; + std::vector<Callback> m_callbacks; + bool m_hover; + bool m_active; }; class Label : public Widget { diff --git a/include/fggl/math/types.hpp b/include/fggl/math/types.hpp index 4730553f51a0a3948fcbabdb072f31e9d725a50b..9c035fe1177d24c93dcca6d1ce3e7a8f57f85b3d 100644 --- a/include/fggl/math/types.hpp +++ b/include/fggl/math/types.hpp @@ -30,6 +30,27 @@ namespace fggl::math { return x < xi ? xi - 1 : xi; } + inline float rescale_norm(float value, float min, float max) { + return (value - min) / (max - min); + } + + inline float rescale_norm(float value, float min, float max, float newMin, float newMax) { + return newMin + ((value - min) * (newMax - newMin)) / (max - min); + } + + inline float rescale_ndc(float value, float newMin, float newMax){ + return rescale_norm(value, -1, 1, newMin, newMax); + } + + inline float rescale_01(float value, float newMin, float newMax){ + return rescale_norm(value, 0, 1, newMin, newMax); + } + + inline float recale_mean(float value, float avg, float max, float min) { + return (value - avg) / (max - min); + } + + // reference vectors constexpr vec3f UP{0.0f, 1.0f, 0.0f}; constexpr vec3f FORWARD{1.0f, 0.0f, 0.0f}; diff --git a/include/fggl/scenes/menu.hpp b/include/fggl/scenes/menu.hpp index 6e364890f391fc0b1a91824412af131e21073d94..8bb8c2be3a4d7c36e2cfa10ef26f368f8df4b202 100644 --- a/include/fggl/scenes/menu.hpp +++ b/include/fggl/scenes/menu.hpp @@ -8,6 +8,7 @@ #include <fggl/app.hpp> #include <fggl/math/types.hpp> #include <fggl/input/input.hpp> +#include <fggl/gui/gui.hpp> namespace fggl::scenes { @@ -32,6 +33,8 @@ namespace fggl::scenes { // menu state std::string m_active; math::vec2 m_cursorPos; + gui::Container m_canvas; + gui::Widget* m_hover; }; } // namepace fggl::scenes diff --git a/include/fggl/util/service.h b/include/fggl/util/service.h index d80b3bd38be6728f416d0cad88a6a78a472813f8..9bd9f59dc324892dcb8a47e055833f5e50e92f05 100644 --- a/include/fggl/util/service.h +++ b/include/fggl/util/service.h @@ -8,6 +8,8 @@ #include <memory> #include <typeindex> #include <unordered_map> +#include <stdexcept> +#include <iostream> namespace fggl::util { @@ -37,14 +39,18 @@ namespace fggl::util { template<typename T> std::shared_ptr<T> get() { - auto info = std::type_index(typeid(T)); - return std::static_pointer_cast<T>(m_services.at(info)); + try { + auto info = std::type_index(typeid(T)); + return std::static_pointer_cast<T>(m_services.at(info)); + } catch ( std::out_of_range& e ){ + std::cerr << "someone requested a service that doesn't exist!" << std::endl; + return nullptr; + } } 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)); + return get<T>(); } };