From 28e5527da25d8a0b7a6a8a5d9eedc5e18ace47d4 Mon Sep 17 00:00:00 2001 From: Joseph Walton-Rivers <joseph@walton-rivers.uk> Date: Sun, 18 Jul 2021 17:28:00 +0100 Subject: [PATCH] add event-based input handling code --- demo/main.cpp | 45 ++++++++------------- fggl/CMakeLists.txt | 2 +- fggl/gfx/input.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++ fggl/gfx/input.hpp | 99 +++++++++++++++++++++++++++++++++++++++++++++ fggl/gfx/window.cpp | 42 ++++++++++++++++++- fggl/gfx/window.hpp | 17 +++++--- 6 files changed, 265 insertions(+), 37 deletions(-) create mode 100644 fggl/gfx/input.cpp create mode 100644 fggl/gfx/input.hpp diff --git a/demo/main.cpp b/demo/main.cpp index 83211bb..2c168f7 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -41,24 +41,8 @@ void discover(std::filesystem::path base) { } -struct InputState { - double currCursor[2]; - double lastCursor[2]; - bool buttons[3]; -}; - -void process_inputs(fggl::gfx::Window& window, InputState& state) { - state.lastCursor[0] = state.currCursor[0]; - state.lastCursor[1] = state.currCursor[1]; - glfwGetCursorPos(window.handle(), &state.currCursor[0], &state.currCursor[1]); - - state.buttons[0] = glfwGetMouseButton(window.handle(), GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS; - state.buttons[1] = glfwGetMouseButton(window.handle(), GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS; - state.buttons[2] = glfwGetMouseButton(window.handle(), GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS; -} - //TODO proper input system -void process_camera(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, InputState& state, fggl::ecs::entity_t cam) { +void process_camera(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, fggl::gfx::Input& input, fggl::ecs::entity_t cam) { auto camTransform = ecs.getComponent<fggl::math::Transform>(cam); auto camComp = ecs.getComponent<fggl::gfx::Camera>(cam); float moveSpeed = 1.0f; @@ -77,14 +61,16 @@ void process_camera(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, InputState& motion += (forward * moveSpeed); } } - if ( glfwGetKey( window.handle(), GLFW_KEY_A ) == GLFW_PRESS ) { - - } + + // scroll wheel + 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, InputState& state, fggl::ecs::entity_t cam) { +void process_arcball(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, fggl::gfx::Input& 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); @@ -97,8 +83,8 @@ void process_arcball(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, InputState& float deltaAngleX = ( 2 * M_PI / window.width() ); float deltaAngleY = ( M_PI / window.height() ); - float xAngle = ( state.lastCursor[0] - state.currCursor[0] ) * deltaAngleX; - float yAngle = ( state.lastCursor[1] - state.currCursor[1] ) * deltaAngleY; + float xAngle = ( input.cursorDeltaX() ) * deltaAngleX; + float yAngle = ( input.cursorDeltaY() ) * deltaAngleY; auto cosAngle = glm::dot( viewDir, fggl::math::UP ); if ( cosAngle * sgn(deltaAngleY) > 0.99f ) { @@ -122,7 +108,7 @@ int main(int argc, char* argv[]) { fggl::gfx::Context ctx; // build our main window - fggl::gfx::Window win; + fggl::gfx::Window win( fggl::gfx::Input::instance() ); win.fullscreen( true ); // opengl time @@ -206,21 +192,22 @@ int main(int argc, char* argv[]) { ecs.addComponent<fggl::gfx::MeshToken>(entity, token); } - InputState inputs; + fggl::gfx::Input& input = fggl::gfx::Input::instance(); float time = 0.0f; float dt = 16.0f; while( !win.closeRequested() ) { + input.frame(); + ctx.pollEvents(); debug.frameStart(); // update step time += dt; - process_inputs(win, inputs); - process_camera(win, ecs, inputs, camEnt); - if ( inputs.buttons[2] ) { - process_arcball(win, ecs, inputs, camEnt); + process_camera(win, ecs, input, camEnt); + if ( input.mouseDown( fggl::gfx::MOUSE_2 ) ) { + process_arcball(win, ecs, input, camEnt); } /* float amount = glm::radians( time / 2048.0f * 360.0f ); diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt index e99b358..3311372 100644 --- a/fggl/CMakeLists.txt +++ b/fggl/CMakeLists.txt @@ -12,7 +12,7 @@ FetchContent_Declare( FetchContent_MakeAvailable( glfw3 ) endif () -add_library(fggl fggl.cpp ecs/ecs.cpp gfx/window.cpp gfx/renderer.cpp gfx/shader.cpp gfx/ogl.cpp data/model.cpp data/procedural.cpp debug/debug.cpp) +add_library(fggl fggl.cpp ecs/ecs.cpp gfx/window.cpp gfx/renderer.cpp gfx/input.cpp gfx/shader.cpp gfx/ogl.cpp data/model.cpp data/procedural.cpp debug/debug.cpp) target_include_directories(fggl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../) # Graphics backend diff --git a/fggl/gfx/input.cpp b/fggl/gfx/input.cpp new file mode 100644 index 0000000..23dc9b9 --- /dev/null +++ b/fggl/gfx/input.cpp @@ -0,0 +1,97 @@ +#include <fggl/gfx/input.hpp> +#include <cassert> + +#include <bitset> +#include <iostream> + +using fggl::gfx::Input; + +Input::Input() : m_mouse_curr(), m_mouse_last(), m_joydata(), m_joysticks() { + clear(); +} + +void Input::clear() { + m_mouse_curr.cursor[0] = 0.0; + m_mouse_curr.cursor[1] = 0.0; + m_mouse_curr.scroll[0] = 0.0; + m_mouse_curr.scroll[1] = 0.0; + m_mouse_curr.buttons = 0x00; + m_mouse_last = m_mouse_curr; +} + +void Input::frame() { + m_mouse_last = m_mouse_curr; + m_mouse_curr.scroll[0] = 0.0; + m_mouse_curr.scroll[1] = 0.0; +} + +void Input::mousePos(double x, double y) { + assert( m_mouse_curr.cursor != nullptr ); + m_mouse_curr.cursor[0] = x; + m_mouse_curr.cursor[1] = y; +} + +double Input::cursorDeltaX() const { + return m_mouse_last.cursor[0] - m_mouse_curr.cursor[0]; +} + +double Input::cursorDeltaY() const { + return m_mouse_last.cursor[1] - m_mouse_curr.cursor[1]; +} + +const double* Input::mousePos() const { + return m_mouse_curr.scroll.data(); +} + +void Input::mouseScroll(double deltaX, double deltaY) { + m_mouse_curr.scroll[0] = deltaX; + m_mouse_curr.scroll[1] = deltaY; +} + +const double* Input::mouseScroll() const { + return m_mouse_curr.scroll.data(); +} + +double Input::scrollDeltaX() const { + return m_mouse_curr.scroll[0]; +} + +double Input::scrollDeltaY() const { + return m_mouse_curr.scroll[1]; +} + +void Input::mouseBtn(const MouseButton btn, bool state) { + if ( state ) { + m_mouse_curr.buttons |= btn; + } else { + m_mouse_curr.buttons &= ~btn; + } +} + +bool Input::mouseDown(const MouseButton btn) const { + return m_mouse_curr.buttons & btn; +} + +bool Input::mousePressed(const MouseButton btn) const { + return (m_mouse_curr.buttons & btn) && !(m_mouse_last.buttons & btn); +} + +bool Input::mouseReleased(const MouseButton btn) const { + return !(m_mouse_curr.buttons & btn) && (m_mouse_last.buttons & btn); +} + +void Input::joystickConnect(int id, Joystick &data){ + std::cout << "JOYSTICK TIME: " << data.name << std::endl; + m_joysticks[id] = true; + m_joydata[id] = data; +} + +void Input::joystickDisconnect( int id ){ + // reset to empty joystick + m_joysticks[id] = false; + m_joydata[id] = Joystick(); +} + +bool Input::joystick(int id) const { + return m_joysticks[id]; +} diff --git a/fggl/gfx/input.hpp b/fggl/gfx/input.hpp new file mode 100644 index 0000000..4922848 --- /dev/null +++ b/fggl/gfx/input.hpp @@ -0,0 +1,99 @@ +#ifndef FGGL_GFX_INPUT_H +#define FGGL_GFX_INPUT_H + +#include <array> +#include <bitset> + +namespace fggl::gfx { + + enum MouseButton { + MOUSE_1 = 0b00000001, + MOUSE_2 = 0b00000010, + MOUSE_3 = 0b00000100, + MOUSE_4 = 0b00001000, + MOUSE_5 = 0b00010000, + MOUSE_6 = 0b00100000, + MOUSE_7 = 0b01000000, + MOUSE_8 = 0b10000000, + }; + + struct MouseInputState { + std::array<double, 2> cursor; + std::array<double, 2> scroll; + char buttons; + }; + + struct Joystick { + const char* name = nullptr; + const float* axes = nullptr; + const unsigned char* buttons = nullptr; + const unsigned char* hats = nullptr; + int hatCount = 0; + int axisCount = 0; + int buttonCount = 0; + }; + + class Input { + public: + // this is a neccerry evil due to glfw's design :'( + static Input& instance() { + static Input *instance = new Input(); + return *instance; + } + + void clear(); + void frame(); + + // state motification + void mousePos(double x, double y); + void mouseScroll(double deltaX, double deltaY); + void mouseBtn(const MouseButton btn, bool state); + + // mouse position + const double* mousePos() const; + double cursorDeltaX() const; + double cursorDeltaY() const; + + // mouse scroll + const double* mouseScroll() const; + double scrollDeltaX() const; + double scrollDeltaY() const; + + /** + * is the mouse button down this frame? + * + * True is the mouse button is down + */ + bool mouseDown(const MouseButton btn) const; + + /** + * Was the mouse button pressed this frame? + * + * True if the mouse button is down, but was up last frame + */ + bool mousePressed(const MouseButton btn) const; + + /** + * Was the mouse button released this frame? + * + * True if the mouse button is up, but was down last frame + */ + bool mouseReleased(const MouseButton btn) const; + + // joysticks + void joystickConnect(int id, Joystick& data); + void joystickDisconnect(int id); + + bool joystick(int id) const; + + private: + Input(); + MouseInputState m_mouse_curr; + MouseInputState m_mouse_last; + std::bitset<16> m_joysticks; + std::array<Joystick, 16> m_joydata; + }; + +}; + +#endif diff --git a/fggl/gfx/window.cpp b/fggl/gfx/window.cpp index f160f03..9cde13b 100644 --- a/fggl/gfx/window.cpp +++ b/fggl/gfx/window.cpp @@ -21,7 +21,41 @@ static void framebuffer_resize(GLFWwindow* window, int width, int height) { fgglWindow->framesize( width, height ); } +static void fggl_input_cursor(GLFWwindow* window, double x, double y) { + Input& input = Input::instance(); + input.mousePos(x, y); +} + +static void fggl_input_scroll(GLFWwindow* window, double x, double y) { + Input& input = Input::instance(); + input.mouseScroll(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_joystick(int jid, int state) { + Input& input = Input::instance(); + if ( state == GLFW_CONNECTED ) { + 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); + + input.joystickConnect(jid, data); + } else if ( state == GLFW_DISCONNECTED ) { + input.joystickDisconnect(jid); + } +} + Context::Context() { + glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); + int state = glfwInit(); if ( state == GLFW_FALSE ) { const char** error; @@ -38,7 +72,7 @@ void Context::pollEvents() { glfwPollEvents(); } -Window::Window() : m_window(nullptr), m_input(nullptr) { +Window::Window(Input& input) : m_window(nullptr), m_input(input) { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); @@ -52,6 +86,12 @@ Window::Window() : m_window(nullptr), m_input(nullptr) { m_framesize = glm::vec2(1920, 1080); glfwSetWindowUserPointer(m_window, this); glfwSetFramebufferSizeCallback( m_window, framebuffer_resize ); + + // input functions + glfwSetScrollCallback(m_window, fggl_input_scroll); + glfwSetCursorPosCallback(m_window, fggl_input_cursor); + glfwSetMouseButtonCallback(m_window, fggl_input_mouse_btn); + glfwSetJoystickCallback(fggl_joystick); } Window::~Window() { diff --git a/fggl/gfx/window.hpp b/fggl/gfx/window.hpp index 3045983..eaff910 100644 --- a/fggl/gfx/window.hpp +++ b/fggl/gfx/window.hpp @@ -6,6 +6,7 @@ #include <fggl/math/types.hpp> #include <fggl/gfx/rendering.hpp> +#include <fggl/gfx/input.hpp> namespace fggl::gfx { @@ -17,10 +18,6 @@ namespace fggl::gfx { void pollEvents(); }; - class Input { - private: - }; - enum MutWindowHint { Decorated = GLFW_DECORATED, Resizable = GLFW_RESIZABLE, @@ -42,7 +39,7 @@ namespace fggl::gfx { }; class Window { public: - Window(); + Window(Input& input); ~Window(); // window <-> opengl stuff @@ -123,9 +120,17 @@ 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; + Input& m_input; math::vec2 m_framesize; inline void set_hint(int hint, bool state) const { -- GitLab