diff --git a/demo/main.cpp b/demo/main.cpp index 25aa1c385be68fc7ec23d801170b0b428b5644e0..9fac445c47dd5a14b0729c8ad3a271e155a44c81 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -52,34 +52,14 @@ enum camera_type { cam_free, cam_arcball }; camera_type cam_mode = cam_free; //TODO proper input system -void process_camera(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, fggl::gfx::Input& input, fggl::ecs::entity_t cam) { +using namespace fggl::input; +using InputManager = std::shared_ptr<fggl::input::Input>; - if ( glfwGetKey(window.handle(), GLFW_KEY_F2) == GLFW_PRESS ) { - cam_mode = cam_free; - } - if ( glfwGetKey(window.handle(), GLFW_KEY_F3) == GLFW_PRESS ) { - cam_mode = cam_arcball; - } - - auto camTransform = ecs.getComponent<fggl::math::Transform>(cam); - auto camComp = ecs.getComponent<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 = (float)input.scrollDeltaY(); - 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 ); -} - -void process_arcball(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, fggl::gfx::Input& input, fggl::ecs::entity_t cam) { +void process_arcball(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, InputManager input, fggl::ecs::entity_t cam) { // see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/ auto camTransform = ecs.getComponent<fggl::math::Transform>(cam); auto camComp = ecs.getComponent<fggl::gfx::Camera>(cam); + auto& mouse = input->mouse; glm::vec4 position(camTransform->origin(), 1.0f); glm::vec4 pivot(camComp->target, 1.0f); @@ -89,8 +69,8 @@ void process_arcball(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, fggl::gfx:: float deltaAngleX = ( 2 * M_PI / window.width() ); float deltaAngleY = ( M_PI / window.height() ); - float xAngle = ( input.cursorDeltaX() ) * deltaAngleX; - float yAngle = ( input.cursorDeltaY() ) * deltaAngleY; + float xAngle = ( -mouse.axisDelta(fggl::input::MouseAxis::X) ) * deltaAngleX; + float yAngle = ( -mouse.axisDelta(fggl::input::MouseAxis::Y) ) * deltaAngleY; auto cosAngle = glm::dot( viewDir, fggl::math::UP ); if ( cosAngle * sgn(deltaAngleY) > 0.99f ) { @@ -114,31 +94,40 @@ constexpr float ROT_SPEED = 0.05f; constexpr float PAN_SPEED = 0.05f; constexpr glm::mat4 MAT_IDENTITY(1.0f); -void process_freecam(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, fggl::gfx::Input& input, fggl::ecs::entity_t cam) { + +void process_freecam(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, InputManager input, fggl::ecs::entity_t cam) { float rotationValue = 0.0f; glm::vec3 translation(0.0f); + auto& keyboard = input->keyboard; + auto code_q = glfwGetKeyScancode(GLFW_KEY_Q); + auto code_e = glfwGetKeyScancode(GLFW_KEY_E); + auto code_w = glfwGetKeyScancode(GLFW_KEY_W); + auto code_s = glfwGetKeyScancode(GLFW_KEY_S); + auto code_d = glfwGetKeyScancode(GLFW_KEY_D); + auto code_a = glfwGetKeyScancode(GLFW_KEY_A); + // calulate rotation (user input) - if ( glfwGetKey(window.handle(), GLFW_KEY_Q) == GLFW_PRESS ) { + if ( keyboard.down( code_q ) ) { rotationValue = ROT_SPEED; - } else if ( glfwGetKey(window.handle(), GLFW_KEY_E) == GLFW_PRESS ) { + } else if ( keyboard.down(code_e) ) { rotationValue = -ROT_SPEED; } // calulate movement (user input) - if ( glfwGetKey(window.handle(), GLFW_KEY_W) == GLFW_PRESS ) { + if ( keyboard.down(code_w) ) { translation -= fggl::math::RIGHT; } - if ( glfwGetKey(window.handle(), GLFW_KEY_S) == GLFW_PRESS ) { + if ( keyboard.down(code_s) ) { translation += fggl::math::RIGHT; } - if ( glfwGetKey(window.handle(), GLFW_KEY_D) == GLFW_PRESS ) { + if ( keyboard.down(code_d) ) { translation += fggl::math::FORWARD; } - if ( glfwGetKey(window.handle(), GLFW_KEY_A) == GLFW_PRESS ) { + if ( keyboard.down(code_a) ) { translation -= fggl::math::FORWARD; } @@ -172,14 +161,37 @@ void process_freecam(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, fggl::gfx:: camComp->target = pivot; } +void process_camera(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, InputManager input, fggl::ecs::entity_t cam) { + auto camTransform = ecs.getComponent<fggl::math::Transform>(cam); + auto camComp = ecs.getComponent<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.axisDelta( 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 ) ) { + process_arcball(window, ecs, input, cam); + } else if ( cam_mode == cam_free ) { + process_freecam(window, ecs, input, cam); + } +} + int main(int argc, char* argv[]) { // setup ECS fggl::ecs::ECS ecs; + auto inputs = std::make_shared<fggl::input::Input>(); // build our main window - auto glfwModule = fggl::gfx::ecsInitGlfw(ecs); + auto glfwModule = fggl::gfx::ecsInitGlfw(ecs, inputs); - fggl::gfx::Window win( fggl::gfx::Input::instance() ); + fggl::gfx::Window win; win.title("FGGL Demo"); win.fullscreen( true ); @@ -253,9 +265,6 @@ int main(int argc, char* argv[]) { fggl::gfx::onStaticMeshAdded(ecs, entity, glModule); } - fggl::gfx::Input& input = fggl::gfx::Input::instance(); - - bool joystickWindow = true; bool gamepadWindow = true; @@ -269,8 +278,7 @@ int main(int argc, char* argv[]) { // Setup setup // time.tick( glfwGetTimerValue() ); - - input.frame(); + inputs->frame( time.delta() ); glfwModule->context.pollEvents(); debug.frameStart(); @@ -278,98 +286,36 @@ int main(int argc, char* argv[]) { // // update step // - process_camera(win, ecs, input, camEnt); - if ( cam_mode == cam_arcball ) { - if ( input.mouseDown( fggl::gfx::MOUSE_2 ) ) { - process_arcball(win, ecs, input, camEnt); - } - } else if ( cam_mode == cam_free ) { - process_freecam(win, ecs, input, camEnt); - } - - // imgui joystick debug - ImGui::Begin("Joysticks", &joystickWindow); - for ( int i=0; i<16; i++ ) { - bool present = input.hasJoystick(i); - std::string title = "Joystick " + std::to_string(i); - - if ( ImGui::TreeNode(title.c_str()) ) { - ImGui::Text("present: %s", present ? "yes" : "no" ); - - if ( present ) { - const fggl::gfx::Joystick& joyData = input.joystick(i); - ImGui::Text( "%s", joyData.name ); - ImGui::Text( "gamepad: %s", joyData.gamepad ? "yes" : "no" ); - ImGui::Text( "axes: %d, buttons: %d, hats: %d", - joyData.axisCount, - joyData.buttonCount, - joyData.hatCount); - - if (ImGui::TreeNode("axis##2")) { - // dump data - for ( int axid = 0; axid < joyData.axisCount; axid++ ) { - ImGui::Text("axis %d, value: %f", - axid, joyData.axes[axid] ); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("buttons##2")) { - // dump data - for ( int btnid = 0; btnid < joyData.buttonCount; btnid++ ) { - ImGui::Text("button %d, value: %s", btnid, - joyData.buttons[btnid] == GLFW_PRESS - ? "down" : "up" ); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("hats##2")) { - // dump data - for ( int btnid = 0; btnid < joyData.hatCount; btnid++ ) { - ImGui::Text("button %d, value: %d", - btnid, joyData.hats[btnid] ); - } - ImGui::TreePop(); - } - } - - ImGui::TreePop(); - ImGui::Separator(); - } - - } - ImGui::End(); + process_camera(win, ecs, inputs, camEnt); // imgui gamepad debug + auto& gamepads = inputs->gamepads; ImGui::Begin("GamePad", &gamepadWindow); for ( int i=0; i<16; i++ ) { std::string title = "GamePad " + std::to_string(i); - bool present = input.hasJoystick(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::gfx::PadButtons ) { - auto label = fggl::gfx::PadButtonLabels[ (int) btn ]; - ImGui::Text( "%s: %i %i %i", label.c_str(), - input.padDown(i, btn), - input.padPressed(i, btn), - input.padReleased(i, btn) + 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::gfx::PadAxes ) { - auto label = fggl::gfx::PadAxisLabels[ (int) axis ]; - ImGui::Text("%s: %f %f", label.c_str(), - input.padAxis(i, axis), - input.padAxisDelta(i, axis) + for ( auto& axis : fggl::input::GamepadAxes ) { + ImGui::Text("%s: %f %f", axis.name, + gamepads.axis(i, axis.id), + gamepads.axisDelta(i, axis.id) ); } diff --git a/fggl/gfx/compat.hpp b/fggl/gfx/compat.hpp index bc40713b576c27796915ac4c20e20884cc997a94..62b617d45b28980d036dad4fd2bf12825ecaf62a 100644 --- a/fggl/gfx/compat.hpp +++ b/fggl/gfx/compat.hpp @@ -23,7 +23,7 @@ namespace fggl::gfx { struct ecsGlfwModule { GlfwContext context; - inline ecsGlfwModule() : context() { + inline ecsGlfwModule(std::shared_ptr<fggl::input::Input> inputs) : context(inputs) { } }; @@ -32,8 +32,8 @@ namespace fggl::gfx { // // fake module/callbacks - our ECS doesn't have module/callback support yet. // - inline GlfwModule ecsInitGlfw(ecs::ECS& ecs) { - auto mod = std::make_shared<ecsGlfwModule>(); + inline GlfwModule ecsInitGlfw(ecs::ECS& ecs, std::shared_ptr<fggl::input::Input> inputs) { + auto mod = std::make_shared<ecsGlfwModule>(inputs); return mod; } diff --git a/fggl/gfx/window.cpp b/fggl/gfx/window.cpp index b97e2095acfb8369b24de83383cad4ceb33d6b12..78ab5f904144131bf99fb64e542c6c267465503b 100644 --- a/fggl/gfx/window.cpp +++ b/fggl/gfx/window.cpp @@ -1,5 +1,6 @@ #include "window.hpp" +#include "window_input.hpp" #include <iostream> #include <string> @@ -22,76 +23,86 @@ static void framebuffer_resize(GLFWwindow* window, int width, int height) { } static void fggl_input_cursor(GLFWwindow* window, double x, double y) { - Input& input = Input::instance(); - input.mousePos(x, y); + auto& input = GlfwInputManager::instance(); + input.onMouseMove(x, y); } static void fggl_input_scroll(GLFWwindow* window, double x, double y) { - Input& input = Input::instance(); - input.mouseScroll(x, y); + auto& input = GlfwInputManager::instance(); + input.onMouseScroll(x, y); } static void fggl_input_mouse_btn(GLFWwindow* window, int btn, int action, int mods) { - // as we need the singleton for joysticks, might as well use it for these as well... - Input& input = Input::instance(); - fggl::gfx::MouseButton buttonBit = (fggl::gfx::MouseButton)(1 << btn); - input.mouseBtn( buttonBit, action == GLFW_PRESS ); -} - -static void fggl_activate_joystick(Input& input, int jid) { - fggl::gfx::Joystick data; - data.name = glfwGetJoystickName(jid); - data.axes = glfwGetJoystickAxes(jid, &data.axisCount); - data.buttons = glfwGetJoystickButtons(jid, &data.buttonCount); - data.hats = glfwGetJoystickHats(jid, &data.hatCount); - data.gamepad = glfwJoystickIsGamepad(jid); - input.joystickConnect(jid, data); - - if ( data.gamepad ) { + auto& input = GlfwInputManager::instance(); + input.onMouseButton(btn, action == GLFW_PRESS); +} + +static void fggl_input_keyboard(GLFWwindow* window, int key, int scancode, int action, int mods) { + auto& input = GlfwInputManager::instance(); + input.onKeyEvent( scancode, action == GLFW_PRESS || action == GLFW_REPEAT); +} + +static void fggl_update_joystick(fggl::input::GamepadInput& input, int jid) { + //glfwGetJoystickName(jid); + bool isGamepad = glfwJoystickIsGamepad(jid); + + if ( isGamepad ) { + if( !input.present(jid) ) { + input.setActive(jid, true); + } + GLFWgamepadstate state; glfwGetGamepadState(jid, &state); - fggl::gfx::PadState padState; + fggl::input::GamepadState gamepadState; for (int i=0; i<=GLFW_GAMEPAD_BUTTON_LAST; i++) { - padState.buttons[i] = state.buttons[i]; + gamepadState.buttons[i] = state.buttons[i]; } for (int i=0; i<=GLFW_GAMEPAD_AXIS_LAST; i++) { - padState.axes[i] = state.axes[i]; + gamepadState.axes[i] = state.axes[i]; } - input.padState(jid, padState); + input.update(jid, gamepadState); + } else { + input.setActive(jid, false); } } -static void fggl_joystick_init() { - Input& input = Input::instance(); - for (int jid=0; jid<16; jid++) { - if ( glfwJoystickPresent(jid) ) { - fggl_activate_joystick(input, jid); - } +static void fggl_joystick_poll() { + auto& input = GlfwInputManager::instance(); + if ( !input.alive() ) { + return; } -} -static void fggl_joystick_poll() { - Input& input = Input::instance(); - for (int jid=0; jid<16; jid++) { + auto& gamepadCtl = input.gamepads(); + for (int jid=0; jid < GLFW_JOYSTICK_LAST; jid++) { if ( glfwJoystickPresent(jid) ) { - fggl_activate_joystick(input, jid); + fggl_update_joystick(gamepadCtl, jid); + } else { + gamepadCtl.setActive(jid, false); } } } static void fggl_joystick(int jid, int state) { - Input& input = Input::instance(); + auto& input = GlfwInputManager::instance(); + if ( !input.alive() ) { + return; + } + + auto& gamepadCtl = input.gamepads(); if ( state == GLFW_CONNECTED ) { - fggl_activate_joystick(input, jid); - } else if ( state == GLFW_DISCONNECTED ) { - input.joystickDisconnect(jid); + fggl_update_joystick( gamepadCtl, jid ); + } else { + gamepadCtl.setActive(jid, false); } } -GlfwContext::GlfwContext() { +GlfwContext::GlfwContext( std::shared_ptr<fggl::input::Input> input ) { + auto& glfwCallbacks = GlfwInputManager::instance(); + glfwCallbacks.setup(input); + glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); int state = glfwInit(); @@ -102,7 +113,7 @@ GlfwContext::GlfwContext() { } // joysticks are global - fggl_joystick_init(); + fggl_joystick_poll(); glfwSetJoystickCallback(fggl_joystick); } @@ -115,7 +126,7 @@ void GlfwContext::pollEvents() { fggl_joystick_poll(); } -Window::Window(Input& input) : m_window(nullptr), m_input(input) { +Window::Window() : m_window(nullptr) { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); @@ -134,6 +145,7 @@ Window::Window(Input& input) : m_window(nullptr), m_input(input) { glfwSetScrollCallback(m_window, fggl_input_scroll); glfwSetCursorPosCallback(m_window, fggl_input_cursor); glfwSetMouseButtonCallback(m_window, fggl_input_mouse_btn); + glfwSetKeyCallback(m_window, fggl_input_keyboard); } Window::~Window() { diff --git a/fggl/gfx/window.hpp b/fggl/gfx/window.hpp index ebc5db23be8a16d0a911c03ae05fc1a59e7a1b60..d6bb8d9c63b56a607846cd13844fecdf856bc2a9 100644 --- a/fggl/gfx/window.hpp +++ b/fggl/gfx/window.hpp @@ -3,7 +3,9 @@ #include <cassert> #include <string> +#include <memory> +#include <fggl/input/input.hpp> #include <fggl/gfx/common.hpp> #include <fggl/math/types.hpp> #include <fggl/gfx/input.hpp> @@ -12,7 +14,7 @@ namespace fggl::gfx { class GlfwContext { public: - GlfwContext(); + GlfwContext(std::shared_ptr<fggl::input::Input> input); ~GlfwContext(); void pollEvents(); @@ -39,7 +41,7 @@ namespace fggl::gfx { }; class Window { public: - Window(Input& input); + Window(); ~Window(); // window <-> opengl stuff @@ -120,17 +122,8 @@ namespace fggl::gfx { return m_window; } - inline Input& input() { - return m_input; - } - -/* inline const Input* input() const { - return m_input; - }*/ - private: GLFWwindow* m_window; - Input& m_input; math::vec2 m_framesize; inline void set_hint(int hint, bool state) const { diff --git a/fggl/gfx/window_input.hpp b/fggl/gfx/window_input.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6583efd28428beeca81b6027cb6411c894705a84 --- /dev/null +++ b/fggl/gfx/window_input.hpp @@ -0,0 +1,73 @@ +#ifndef FGGL_GFX_GLFW_INPUT_H +#define FGGL_GFX_GLFW_INPUT_H + +#include <memory> +#include <string> + +#include <fggl/gfx/window.hpp> +#include <fggl/input/input.hpp> + + +namespace fggl::gfx { + + class GlfwInputManager { + public: + static GlfwInputManager& instance() { + static GlfwInputManager *instance = new GlfwInputManager(); + return *instance; + } + + inline void setup(std::shared_ptr<input::Input> input) { + m_inputs = input; + } + + inline bool alive() { + return m_inputs != nullptr; + } + + inline input::MouseInput& mouse() { + return m_inputs->mouse; + } + + inline input::KeyboardInput& keyboard() { + return m_inputs->keyboard; + } + + inline input::GamepadInput& gamepads() { + return m_inputs->gamepads; + } + + inline void onMouseMove(float x, float y) { + if (m_inputs != nullptr ) { + m_inputs->mouse.axis(input::MouseAxis::X, x); + m_inputs->mouse.axis(input::MouseAxis::Y, y); + } + } + + inline void onMouseScroll(float x, float y) { + if (m_inputs != nullptr) { + m_inputs->mouse.axis(input::MouseAxis::SCROLL_X, x); + m_inputs->mouse.axis(input::MouseAxis::SCROLL_Y, y); + } + } + + inline void onMouseButton(int btn, bool state) { + if ( m_inputs != nullptr ) { + m_inputs->mouse.button((fggl::input::MouseButton)btn, state); + } + } + + inline void onKeyEvent(int scancode, bool state) { + if ( m_inputs != nullptr ) { + m_inputs->keyboard.set(scancode, state); + } + } + + private: + std::shared_ptr<input::Input> m_inputs; + + }; + +}; + +#endif diff --git a/fggl/input/gamepad.hpp b/fggl/input/gamepad.hpp new file mode 100644 index 0000000000000000000000000000000000000000..069e249fe81ad7c92f57fd53bc4a3fa40d16df28 --- /dev/null +++ b/fggl/input/gamepad.hpp @@ -0,0 +1,175 @@ +#ifndef FGGL_INPUT_GAMEPAD_H +#define FGGL_INPUT_GAMEPAD_H + +#include <string> +#include <array> +#include <bitset> +#include <cassert> + +namespace fggl::input { + + enum class GamepadButton { + A, + B, + X, + Y, + BUMPER_LEFT, + BUMPER_RIGHT, + BACK, + START, + GUIDE, + THUMB_LEFT, + THUMB_RIGHT, + DPAD_UP, + DPAD_RIGHT, + DPAD_DOWN, + DPAD_LEFT, + SQUARE = 0, + CIRCLE = 1, + TRIANGLE = 2, + CROSS = 3 + }; + + enum class GamepadAxis { + LEFT_X, + LEFT_Y, + RIGHT_X, + RIGHT_Y, + TRIGGER_LEFT, + TRIGGER_RIGHT + }; + + struct GamepadButtonRecord { + GamepadButton id; + char name[15]; + }; + + struct GamepadAxisRecord { + GamepadAxis id; + char name[30]; + }; + + constexpr std::array<GamepadButtonRecord, 15> GamepadButtonsMicrosoft = {{ + {GamepadButton::A, "A"}, + {GamepadButton::B, "B"}, + {GamepadButton::X, "X"}, + {GamepadButton::Y, "Y"}, + {GamepadButton::BUMPER_LEFT, "Left Bumper"}, + {GamepadButton::BUMPER_RIGHT, "Right Bumper"}, + {GamepadButton::BACK, "Back"}, + {GamepadButton::START, "Start"}, + {GamepadButton::GUIDE, "Guide"}, + {GamepadButton::THUMB_LEFT, "Thumb Left"}, + {GamepadButton::THUMB_RIGHT, "Thumb Right"}, + {GamepadButton::DPAD_UP, "Dpad Up"}, + {GamepadButton::DPAD_RIGHT, "Dpad Right"}, + {GamepadButton::DPAD_DOWN, "Dpad Down"}, + {GamepadButton::DPAD_LEFT, "Dpad Left"}, + }}; + + constexpr std::array<GamepadAxisRecord, 6> GamepadAxes = {{ + {GamepadAxis::LEFT_X, "left stick left/right"}, + {GamepadAxis::LEFT_Y, "left stick up/down"}, + {GamepadAxis::RIGHT_X, "right stick left/right"}, + {GamepadAxis::RIGHT_Y, "right stick up/down"}, + {GamepadAxis::TRIGGER_LEFT, "left trigger"}, + {GamepadAxis::TRIGGER_RIGHT, "right trigger"}, + }}; + + constexpr std::array<GamepadButtonRecord, 15> GamepadButtonsPlaystation = {{ + {GamepadButton::SQUARE, "Square"}, + {GamepadButton::CIRCLE, "Circle"}, + {GamepadButton::TRIANGLE, "Triangle"}, + {GamepadButton::CROSS, "Cross"}, + {GamepadButton::BUMPER_LEFT, "Left Bumper"}, + {GamepadButton::BUMPER_RIGHT, "Right Bumper"}, + {GamepadButton::BACK, "Back"}, + {GamepadButton::START, "Start"}, + {GamepadButton::GUIDE, "Guide"}, + {GamepadButton::THUMB_LEFT, "Thumb Left"}, + {GamepadButton::THUMB_RIGHT, "Thumb Right"}, + {GamepadButton::DPAD_UP, "Dpad Up"}, + {GamepadButton::DPAD_RIGHT, "Dpad Right"}, + {GamepadButton::DPAD_DOWN, "Dpad Down"}, + {GamepadButton::DPAD_LEFT, "Dpad Left"}, + }}; + + struct GamepadState { + std::bitset<GamepadButtonsMicrosoft.size()> buttons; + float axes[GamepadAxes.size()]; + }; + + constexpr size_t MaxControllers = 16; + + struct GamepadInput { + public: + inline bool present(size_t id) const { + assert( id < MaxControllers ); + return m_active[id]; + } + + inline float axis(size_t id, GamepadAxis axis) const { + if ( !present(id) ) { + return 0.0f; + } + return m_current[id].axes[(int)axis]; + } + + inline float axisDelta(size_t id, GamepadAxis axis) const { + if ( !present(id) ) { + return 0.0f; + } + return m_current[id].axes[(int)axis] - m_previous[id].axes[(int)axis]; + } + + inline bool button(size_t id, GamepadButton btn) const { + if ( !present(id) ) { + return 0.0f; + } + return m_current[id].buttons[(int)btn]; + } + + inline bool buttonPressed(size_t id, GamepadButton btn) const { + if ( !present(id) ) { + return 0.0f; + } + return m_current[id].buttons[(int)btn] && !m_previous[id].buttons[(int)btn]; + } + + inline bool buttonReleased(size_t id, GamepadButton btn) const { + if ( !present(id) ) { + return 0.0f; + } + return !m_current[id].buttons[(int)btn] && m_previous[id].buttons[(int)btn]; + } + + inline bool buttonChanged(size_t id, GamepadButton btn) const { + if ( !present(id) ) { + return 0.0f; + } + return m_current[id].buttons[(int)btn] != m_previous[id].buttons[(int)btn]; + } + + inline void frame(float dt) { + m_previous = m_current; + } + + inline void update(size_t id, const GamepadState& state) { + assert( present(id) ); + m_current[id] = state; + } + + inline void setActive(size_t id, bool state) { + assert( id < MaxControllers ); + m_active[id] = state; + } + + private: + std::bitset<MaxControllers> m_active; + std::array<GamepadState, MaxControllers> m_current; + std::array<GamepadState, MaxControllers> m_previous; + }; + +}; + +#endif diff --git a/fggl/input/input.hpp b/fggl/input/input.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d57033937c703b96cb3942f844f63a974cb473f4 --- /dev/null +++ b/fggl/input/input.hpp @@ -0,0 +1,29 @@ +#ifndef FGGL_INPUT_H +#define FGGL_INPUT_H + +#include <memory> +#include <fggl/input/keyboard.hpp> +#include <fggl/input/mouse.hpp> +#include <fggl/input/gamepad.hpp> + +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); + } + + KeyboardInput keyboard; + MouseInput mouse; + GamepadInput gamepads; + }; + +} + +#endif diff --git a/fggl/input/keyboard.hpp b/fggl/input/keyboard.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f4f6e828f1f0c3be361732d5c8a20f2773363e01 --- /dev/null +++ b/fggl/input/keyboard.hpp @@ -0,0 +1,64 @@ +#ifndef FGGL_INPUT_KEYBOARD_H +#define FGGL_INPUT_KEYBOARD_H + +#include <array> +#include <vector> +#include <set> +#include <iostream> + +namespace fggl::input { + using scancode_t = int; + + struct KeyboardState { + std::set<scancode_t> m_keys; + + 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 bool down(scancode_t scancode) const { + if ( m_keys.empty() ) + return false; + + return m_keys.find(scancode) != m_keys.end(); + } + }; + + class KeyboardInput { + public: + inline void frame(float dt) { + m_prev = m_curr; + } + + inline void set(scancode_t scancode, bool state) { + m_curr.set(scancode, state); + } + + inline bool down(scancode_t scancode) const { + return m_curr.down(scancode); + } + + inline bool pressed(scancode_t scancode) const { + return m_curr.down(scancode) && !m_prev.down(scancode); + } + + inline bool released(scancode_t scancode) const { + return !m_curr.down(scancode) && m_prev.down(scancode); + } + + private: + KeyboardState m_curr; + KeyboardState m_prev; + }; + +}; + +#endif diff --git a/fggl/input/mouse.hpp b/fggl/input/mouse.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d99160b9b15e61072eeede3102bbf6c4802d032f --- /dev/null +++ b/fggl/input/mouse.hpp @@ -0,0 +1,102 @@ +#ifndef FGGL_INPUT_MOUSE_H +#define FGGL_INPUT_MOUSE_H + +#include <string> +#include <array> +#include <bitset> + +namespace fggl::input { + + enum class MouseButton { + LEFT, + MIDDLE, + RIGHT, + EXTRA_1, + EXTRA_2, + EXTRA_3, + EXTRA_4, + EXTRA_5 + }; + + enum class MouseAxis { + X, + Y, + SCROLL_X, + SCROLL_Y + }; + + struct MouseButtonRecord { + MouseButton id; + char name[15]; + }; + + struct MouseAxisRecord { + MouseAxis id; + char name[15]; + }; + + constexpr std::array<MouseButtonRecord, 8> MouseButtons = {{ + {MouseButton::LEFT, "Left"}, + {MouseButton::MIDDLE, "Middle"}, + {MouseButton::RIGHT, "Right"}, + {MouseButton::EXTRA_1, "Extra 1"}, + {MouseButton::EXTRA_2, "Extra 2"}, + {MouseButton::EXTRA_3, "Extra 3"}, + {MouseButton::EXTRA_4, "Extra 4"}, + {MouseButton::EXTRA_5, "Extra 5"}, + }}; + + constexpr std::array<MouseAxisRecord, 4> MouseAxes = {{ + {MouseAxis::X, "Left/Right"}, + {MouseAxis::Y, "Up/Down"}, + {MouseAxis::SCROLL_X, "Scroll X"}, + {MouseAxis::SCROLL_Y, "Scroll Y"} + }}; + + struct MouseState { + float axis[MouseAxes.size()]; + std::bitset<MouseButtons.size()> buttons; + }; + + class MouseInput { + public: + inline void frame(float dt) { + m_prev = m_curr; + } + + inline void axis(MouseAxis axis, float value) { + m_curr.axis[(int)axis] = value; + } + + inline float axis(MouseAxis axis) const { + return m_curr.axis[(int)axis]; + } + + inline float axisDelta(MouseAxis axis) const { + return m_curr.axis[(int)axis] - m_prev.axis[(int)axis]; + } + + inline void button(MouseButton btn, bool state) { + m_curr.buttons[(int)btn] = state; + } + + inline bool button(MouseButton btn) const { + return m_curr.buttons[(int)btn]; + } + + inline bool pressed(MouseButton btn) const { + return m_curr.buttons[(int)btn] && !m_prev.buttons[(int)btn]; + } + + inline bool released(MouseButton btn) const { + return !m_curr.buttons[(int)btn] && m_prev.buttons[(int)btn]; + } + + private: + MouseState m_curr; + MouseState m_prev; + }; + +}; + +#endif