diff --git a/demo/main.cpp b/demo/main.cpp index 857a63d35247ae8dc672e17a088cba4462f9758b..5ea7c1f3cc18f743c455b7a29b468cc3b0a1f79d 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -78,7 +78,7 @@ void process_camera(fggl::ecs3::World& ecs, const InputManager& input) { motion -= (forward * delta); camTransform->origin( camTransform->origin() + motion ); - if ( cam_mode == cam_arcball || input->mouse.button( fggl::input::MouseButton::MIDDLE ) ) { + if ( cam_mode == cam_arcball || input->mouse.down( fggl::input::MouseButton::MIDDLE ) ) { fggl::input::process_arcball(ecs, *input, cam); } else if ( cam_mode == cam_free ) { fggl::input::process_freecam(ecs, *input, cam); @@ -125,7 +125,7 @@ public: return; } - bool leftMouse = m_inputs->mouse.button(fggl::input::MouseButton::LEFT); + bool leftMouse = m_inputs->mouse.down(fggl::input::MouseButton::LEFT); if (leftMouse) { auto scenes = fggl::util::ServiceLocator::instance().providePtr<fggl::scenes::SceneManager>(); scenes->activate("game"); @@ -382,6 +382,7 @@ int main(int argc, const char* argv[]) { // and now our states auto menu = app.add_state<fggl::scenes::BasicMenu>("menu"); + menu->add("start", [&app]() { app.change_state("game"); }); // game state app.add_state<GameScene>("game"); diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt index 20a631910e3a52191e8db22e71278ee7f104f7a1..5cefcc84e32cfb878cf60a226addec327e622612 100644 --- a/fggl/CMakeLists.txt +++ b/fggl/CMakeLists.txt @@ -20,6 +20,8 @@ target_sources(${PROJECT_NAME} scenes/menu.cpp ecs3/module/module.cpp input/camera_input.cpp + input/input.cpp + input/mouse.cpp data/heightmap.cpp ) diff --git a/fggl/app.cpp b/fggl/app.cpp index 11124cd096e7a25a60bae8b9c99afdff4107eba7..ee3c294474b9ebe0abd489bee5a752439ad0ced0 100644 --- a/fggl/app.cpp +++ b/fggl/app.cpp @@ -30,7 +30,8 @@ namespace fggl { while ( m_running ) { auto& state = m_states.active(); - + + m_modules->onUpdate(); state.update(); // window rendering to frame buffer diff --git a/fggl/ecs3/module/module.cpp b/fggl/ecs3/module/module.cpp index d1fd59c33a49a37fb135116b1ed5c47aab3b6d2c..85ce2e5e8e67a1ea1150bc6d2693bd0b5863ebce 100644 --- a/fggl/ecs3/module/module.cpp +++ b/fggl/ecs3/module/module.cpp @@ -6,9 +6,8 @@ namespace fggl::ecs3 { - void Module::onFrameStart() { - } - - void Module::onFrameEnd() { - } + // default empty implementions + void Module::onUpdate() {} + void Module::onFrameStart() {} + void Module::onFrameEnd() {} } diff --git a/fggl/gfx/window.cpp b/fggl/gfx/window.cpp index 199ec90eeaeb5fdb8d1be7ca3238ba91c468e634..4c6b21c9e4bf5c0a19b3fda055ea04408d56c429 100644 --- a/fggl/gfx/window.cpp +++ b/fggl/gfx/window.cpp @@ -159,7 +159,7 @@ static void fggl_joystick(int jid, int state) { GlfwContext::GlfwContext( std::shared_ptr<fggl::input::Input> input ) { spdlog::debug("[glfw] context creation stated"); auto& glfwCallbacks = GlfwInputManager::instance(); - glfwCallbacks.setup(std::move(input) ); + glfwCallbacks.setup( std::move(input) ); glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); glfwSetErrorCallback(glfw_error); @@ -183,6 +183,9 @@ GlfwContext::~GlfwContext() { } void GlfwContext::pollEvents() { + auto& glfwCallbacks = GlfwInputManager::instance(); + glfwCallbacks.frame(); + glfwPollEvents(); fggl_joystick_poll(); } diff --git a/fggl/input/input.cpp b/fggl/input/input.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ab6e2f5e3ebc98ef93e10ea5113ac95295aa08b0 --- /dev/null +++ b/fggl/input/input.cpp @@ -0,0 +1,11 @@ +#include <fggl/input/input.hpp> + +namespace fggl::input { + + void Input::frame(float dt) { + keyboard.frame(dt); + mouse.frame(dt); + gamepads.frame(dt); + } + +} // namespace fggl::input diff --git a/fggl/input/mouse.cpp b/fggl/input/mouse.cpp new file mode 100644 index 0000000000000000000000000000000000000000..114aa5ac6e79165cffd3382b0c5362001143541a --- /dev/null +++ b/fggl/input/mouse.cpp @@ -0,0 +1,13 @@ +#include <fggl/input/mouse.hpp> + +namespace fggl::input { + + void MouseState::operator=(const MouseState& rhs) { + for ( int i=0; i<4; i++ ) { + axis[i] = rhs.axis[i]; + } + buttons = rhs.buttons; + } + + +} // namespace fggl::input diff --git a/fggl/scenes/Scene.h b/fggl/scenes/Scene.h index d4c9daf1aa98d6ed6ad40e2788a5ed8ef0cea01a..b749d91a9d73c9970b175fafe18fedc1cac352f7 100644 --- a/fggl/scenes/Scene.h +++ b/fggl/scenes/Scene.h @@ -14,6 +14,10 @@ namespace fggl::scenes { class Scene { public: virtual ~Scene() = default; + + // no copying + Scene(const Scene&) = delete; + Scene& operator=(const Scene&) = delete; virtual void setup() = 0; virtual void cleanup() = 0; diff --git a/fggl/scenes/menu.cpp b/fggl/scenes/menu.cpp index d99dc3c0fb3921e97ec390b5c6ef5d425d3be6d1..8d9936ed2bb3599740d6d50ce2b5ae10cce7f835 100644 --- a/fggl/scenes/menu.cpp +++ b/fggl/scenes/menu.cpp @@ -1,7 +1,14 @@ #include <fggl/scenes/menu.hpp> +#include <fggl/util/service.h> + +#include <spdlog/spdlog.h> namespace fggl::scenes { + using fggl::input::MouseButton; + 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} ); @@ -143,17 +150,23 @@ namespace fggl::scenes { makeBox( path, innerTop, innerBottom ); } - BasicMenu::BasicMenu(fggl::App& app) : AppState(app) { - + BasicMenu::BasicMenu(fggl::App& app) : AppState(app), m_inputs(nullptr), m_active() { + auto& locator = fggl::util::ServiceLocator::instance(); + m_inputs = locator.get<input::Input>(); } void BasicMenu::update() { + if ( m_inputs != nullptr ) { + 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); + } + } } void BasicMenu::render(gfx::Paint& paint) { - - const math::vec2 btnSize{ 150.0f, 30.0f }; const float spacing = 5; @@ -161,9 +174,9 @@ namespace fggl::scenes { const float padY = 50.0f; math::vec2 pos { 1920.0f - ( padX + btnSize.x ), padY }; - for (int i=0; i<10; i++) { + for ( const auto& item : m_items ) { gfx::Path2D btn( pos ); - makeButton( btn, pos, btnSize, i==2, i==1 ); + makeButton( btn, pos, btnSize, m_active == item.first, false ); paint.fill( btn ); pos.y += (btnSize.y + spacing); } @@ -196,4 +209,8 @@ namespace fggl::scenes { } + void BasicMenu::add(const std::string& name, callback cb) { + m_items[name] = cb; + } + }; diff --git a/include/fggl/app.hpp b/include/fggl/app.hpp index 34145abd67605a4f0d933b406684d3f50c318711..160a58ea82f3ecaf7af18a0cf5f0dee0da432f7f 100644 --- a/include/fggl/app.hpp +++ b/include/fggl/app.hpp @@ -102,7 +102,7 @@ namespace fggl { int run(int argc, const char** argv); template<typename T> - T& add_state(const Identifer& name) { + T* add_state(const Identifer& name) { static_assert( std::is_base_of<AppState,T>::value, "States must be AppStates"); return m_states.put<T>(name, *this); } diff --git a/include/fggl/ecs3/module/module.h b/include/fggl/ecs3/module/module.h index b38d1e3441695e8d1b3b060e8dc8704a1594ad24..a71ae981868dacf3658391f3c5393e155d047b39 100644 --- a/include/fggl/ecs3/module/module.h +++ b/include/fggl/ecs3/module/module.h @@ -21,6 +21,7 @@ namespace fggl::ecs3 { virtual void onLoad(ModuleManager& manager, TypeRegistry& tr) {}; + virtual void onUpdate(); virtual void onFrameStart(); virtual void onFrameEnd(); }; @@ -45,6 +46,12 @@ namespace fggl::ecs3 { m_types.callbackAdd( Component<C>::typeID(), cb); } + void onUpdate() { + for ( auto& [id,ptr] : m_modules ) { + ptr->onUpdate(); + } + } + void onFrameStart() { for ( auto& [id,ptr] : m_modules ) { ptr->onFrameStart(); diff --git a/include/fggl/gfx/compat.hpp b/include/fggl/gfx/compat.hpp index d1fcfa04329cec66d5ae38547abf65f097944851..3a1b370824e5b25cd58177e7557a2094cc050009 100644 --- a/include/fggl/gfx/compat.hpp +++ b/include/fggl/gfx/compat.hpp @@ -35,7 +35,7 @@ namespace fggl::gfx { return window; } - void onFrameStart() override { + void onUpdate() override { context.pollEvents(); } diff --git a/include/fggl/gfx/window.hpp b/include/fggl/gfx/window.hpp index fbb7900896d58d2fd75269f9f6296a9f62fbddf2..3a4c6f2f32e432403afa148ebf279307d8e786f5 100644 --- a/include/fggl/gfx/window.hpp +++ b/include/fggl/gfx/window.hpp @@ -28,7 +28,8 @@ namespace fggl::gfx { AutoIconify = GLFW_AUTO_ICONIFY, FocusOnShow = GLFW_FOCUS_ON_SHOW }; - enum WindowHint { + + enum WindowHint { Focused = GLFW_FOCUSED, Iconified = GLFW_ICONIFIED, Maximised = GLFW_MAXIMIZED, @@ -40,7 +41,8 @@ namespace fggl::gfx { GlDebugContext = GLFW_OPENGL_DEBUG_CONTEXT, NoError = GLFW_CONTEXT_NO_ERROR }; - class GlfwWindow : public Window { + + class GlfwWindow : public Window { public: GlfwWindow(); ~GlfwWindow(); diff --git a/include/fggl/gfx/window_input.hpp b/include/fggl/gfx/window_input.hpp index 6583efd28428beeca81b6027cb6411c894705a84..153fcd9d554946a77e7452db3ad163ba15f39eef 100644 --- a/include/fggl/gfx/window_input.hpp +++ b/include/fggl/gfx/window_input.hpp @@ -21,6 +21,10 @@ namespace fggl::gfx { m_inputs = input; } + inline void frame() { + m_inputs->frame(0.0f); + } + inline bool alive() { return m_inputs != nullptr; } diff --git a/include/fggl/input/input.hpp b/include/fggl/input/input.hpp index d57033937c703b96cb3942f844f63a974cb473f4..a2f03074e2b0194911b910498073a2bade054c70 100644 --- a/include/fggl/input/input.hpp +++ b/include/fggl/input/input.hpp @@ -10,15 +10,9 @@ namespace fggl::input { class Input { public: - inline Input() : keyboard(), mouse(), gamepads() { - } - - inline void frame(float dt) { - keyboard.frame(dt); - mouse.frame(dt); - gamepads.frame(dt); - } - + Input() = default; + void frame(float dt); + KeyboardInput keyboard; MouseInput mouse; GamepadInput gamepads; diff --git a/include/fggl/input/keyboard.hpp b/include/fggl/input/keyboard.hpp index f4f6e828f1f0c3be361732d5c8a20f2773363e01..853e652d647b4c6754134b30330750a3240d0c2c 100644 --- a/include/fggl/input/keyboard.hpp +++ b/include/fggl/input/keyboard.hpp @@ -3,33 +3,37 @@ #include <array> #include <vector> -#include <set> +#include <unordered_set> #include <iostream> namespace fggl::input { using scancode_t = int; - struct KeyboardState { - std::set<scancode_t> m_keys; + class KeyboardState { + public: + KeyboardState() = default; - inline void clear() { - m_keys.clear(); - } + inline void clear() { + m_keys.clear(); + } - inline void set(scancode_t code, bool state) { - if ( state ) { - m_keys.insert( code ); - } else { - m_keys.erase( code ); - } - } + inline void set(scancode_t code, bool state) { + if ( state ) { + m_keys.insert( code ); + } else { + m_keys.erase( code ); + } + } - inline bool down(scancode_t scancode) const { - if ( m_keys.empty() ) - return false; + inline bool down(scancode_t scancode) const { + if ( m_keys.empty() ) + return false; - return m_keys.find(scancode) != m_keys.end(); - } + return m_keys.count(scancode) > 0; + } + + private: + std::unordered_set<scancode_t> m_keys; }; class KeyboardInput { diff --git a/include/fggl/input/mouse.hpp b/include/fggl/input/mouse.hpp index d99160b9b15e61072eeede3102bbf6c4802d032f..09af8e93bd81a8ea549e1787cad85228e4262376 100644 --- a/include/fggl/input/mouse.hpp +++ b/include/fggl/input/mouse.hpp @@ -27,12 +27,12 @@ namespace fggl::input { struct MouseButtonRecord { MouseButton id; - char name[15]; + const char name[15]; }; struct MouseAxisRecord { MouseAxis id; - char name[15]; + const char name[15]; }; constexpr std::array<MouseButtonRecord, 8> MouseButtons = {{ @@ -56,10 +56,16 @@ namespace fggl::input { struct MouseState { float axis[MouseAxes.size()]; std::bitset<MouseButtons.size()> buttons; + + void operator=(const MouseState& rhs); }; class MouseInput { public: + MouseInput() = default; + MouseInput(const MouseInput& rhs) = delete; + void operator=(const MouseInput& rhs) = delete; + inline void frame(float dt) { m_prev = m_curr; } @@ -80,10 +86,14 @@ namespace fggl::input { m_curr.buttons[(int)btn] = state; } - inline bool button(MouseButton btn) const { + inline bool down(MouseButton btn) const { return m_curr.buttons[(int)btn]; } + inline bool downPrev(MouseButton btn) const { + return m_prev.buttons[(int)btn]; + } + inline bool pressed(MouseButton btn) const { return m_curr.buttons[(int)btn] && !m_prev.buttons[(int)btn]; } diff --git a/include/fggl/scenes/menu.hpp b/include/fggl/scenes/menu.hpp index c36040195c0d8229a748c515095daf4dfba8d226..14cd63eda114f9db7f0e81bfbc67b5cc764fc424 100644 --- a/include/fggl/scenes/menu.hpp +++ b/include/fggl/scenes/menu.hpp @@ -1,11 +1,18 @@ #ifndef FGGL_SCENES_MENU_H #define FGGL_SCENES_MENU_H +#include <functional> +#include <map> +#include <memory> + #include <fggl/app.hpp> +#include <fggl/math/types.hpp> #include <fggl/input/input.hpp> namespace fggl::scenes { + using callback = std::function<void(void)>; + class BasicMenu : public AppState { public: BasicMenu(App& owner); @@ -16,8 +23,15 @@ namespace fggl::scenes { void activate() override; void deactivate() override; + void add(const std::string& label, callback cb); + private: - input::Input* m_inputs; + std::shared_ptr<input::Input> m_inputs; + std::map<const std::string, callback> m_items; + + // menu state + std::string m_active; + math::vec2 m_cursorPos; }; } // namepace fggl::scenes diff --git a/include/fggl/util/states.hpp b/include/fggl/util/states.hpp index a72fe314a483b4512046f67514f3db7dd4e1a736..29067a7790e7213a6bf1fa87c0a98739d825711e 100644 --- a/include/fggl/util/states.hpp +++ b/include/fggl/util/states.hpp @@ -42,7 +42,7 @@ namespace fggl::util { StateMachine& operator=(StateMachine other) = delete; template<typename T, typename... Args> - T& put(const Identifer& name, Args&&... args) { + T* put(const Identifer& name, Args&&... args) { static_assert( std::is_base_of<StateType,T>::value, "States must be AppStates"); m_states[name] = std::make_unique<T>( std::forward<Args...>(args...) ); @@ -51,7 +51,7 @@ namespace fggl::util { m_active = name; } - return *(T*)(m_states[name].get()); + return (T*)(m_states[name].get()); } void change(const Identifer& name) {