From 2f194e47ac8eee20332d266ce14b47540710096b Mon Sep 17 00:00:00 2001 From: Joseph Walton-Rivers <joseph@walton-rivers.uk> Date: Sun, 18 Jul 2021 21:22:07 +0100 Subject: [PATCH] basic controller support --- .gitignore | 1 + demo/imgui.ini | 5 --- demo/main.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++- fggl/gfx/input.cpp | 47 +++++++++++++++++++-- fggl/gfx/input.hpp | 99 ++++++++++++++++++++++++++++++++++++++++++++- fggl/gfx/window.cpp | 58 ++++++++++++++++++++++---- 6 files changed, 290 insertions(+), 18 deletions(-) delete mode 100644 demo/imgui.ini diff --git a/.gitignore b/.gitignore index cd44b8d..a326e39 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build/ cmake-build-debug/ cmake-build-debug-coverage/ .idea/ +imgui.ini diff --git a/demo/imgui.ini b/demo/imgui.ini deleted file mode 100644 index 4a5c201..0000000 --- a/demo/imgui.ini +++ /dev/null @@ -1,5 +0,0 @@ -[Window][Debug##Default] -Pos=60,60 -Size=400,400 -Collapsed=0 - diff --git a/demo/main.cpp b/demo/main.cpp index 2c168f7..932f0bf 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -13,6 +13,8 @@ #include <fggl/debug/debug.h> #include <fggl/data/storage.hpp> +#include <imgui.h> + template <typename T> int sgn(T val) { return (T(0) < val) - (val < T(0)); } @@ -109,6 +111,7 @@ int main(int argc, char* argv[]) { // build our main window fggl::gfx::Window win( fggl::gfx::Input::instance() ); + win.title("FGGL Demo"); win.fullscreen( true ); // opengl time @@ -117,7 +120,7 @@ int main(int argc, char* argv[]) { // debug layer fggl::debug::DebugUI debug(win); - debug.visible(false); + debug.visible(true); // storage API fggl::data::Storage storage; @@ -194,6 +197,8 @@ int main(int argc, char* argv[]) { fggl::gfx::Input& input = fggl::gfx::Input::instance(); + bool joystickWindow = true; + float time = 0.0f; float dt = 16.0f; while( !win.closeRequested() ) { @@ -210,6 +215,97 @@ int main(int argc, char* argv[]) { process_arcball(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(); + + // imgui gamepad debug + ImGui::Begin("GamePad", &joystickWindow); + for ( int i=0; i<16; i++ ) { + std::string title = "GamePad " + std::to_string(i); + + bool present = input.hasJoystick(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) + ); + } + 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) + ); + + } + ImGui::TreePop(); + } + + } + + ImGui::TreePop(); + ImGui::Separator(); + } + + } + ImGui::End(); + /* float amount = glm::radians( time / 2048.0f * 360.0f ); auto spinners = ecs.getEntityWith<fggl::math::Transform>(); for ( auto entity : spinners ) { diff --git a/fggl/gfx/input.cpp b/fggl/gfx/input.cpp index 23dc9b9..09d8df8 100644 --- a/fggl/gfx/input.cpp +++ b/fggl/gfx/input.cpp @@ -6,23 +6,36 @@ using fggl::gfx::Input; -Input::Input() : m_mouse_curr(), m_mouse_last(), m_joydata(), m_joysticks() { +Input::Input() : m_mouse_curr(), m_mouse_last(), m_joydata(), m_joysticks(), m_pad_last(), m_pad_curr() { clear(); } void Input::clear() { + // reset mouse data 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; + + // reset pad data + for ( auto& pad : m_pad_curr ) { + pad.present = false; + pad.buttons.reset(); + for (int i=0; i<6; i++){ + pad.axes[i] = 0.0f; + } + } + m_pad_last = m_pad_curr; } void Input::frame() { m_mouse_last = m_mouse_curr; m_mouse_curr.scroll[0] = 0.0; m_mouse_curr.scroll[1] = 0.0; + + m_pad_last = m_pad_curr; } void Input::mousePos(double x, double y) { @@ -81,7 +94,7 @@ bool Input::mouseReleased(const MouseButton btn) const { } void Input::joystickConnect(int id, Joystick &data){ - std::cout << "JOYSTICK TIME: " << data.name << std::endl; + // joystick data is polled, so we need to call this every frame >.< m_joysticks[id] = true; m_joydata[id] = data; } @@ -92,6 +105,34 @@ void Input::joystickDisconnect( int id ){ m_joydata[id] = Joystick(); } -bool Input::joystick(int id) const { +bool Input::hasJoystick(int id) const { return m_joysticks[id]; } + +const fggl::gfx::Joystick& Input::joystick(int id) const { + return m_joydata[id]; +} + +void Input::padState(int id, const PadState& state) { + m_pad_curr[id] = state; +} + +bool Input::padDown(int id, PadButton btn) { + return m_pad_curr[id].buttons[(int)btn]; +} + +bool Input::padPressed(int id, PadButton btn) { + return m_pad_curr[id].buttons[(int)btn] && !m_pad_last[id].buttons[(int)btn]; +} + +bool Input::padReleased(int id, PadButton btn) { + return !m_pad_curr[id].buttons[(int)btn] && m_pad_last[id].buttons[(int)btn]; +} + +float Input::padAxis(int id, PadAxis axis) { + return m_pad_curr[id].axes[(int)axis]; +} + +float Input::padAxisDelta(int id, PadAxis axis) { + return m_pad_last[id].axes[(int)axis] - m_pad_curr[id].axes[(int)axis]; +} diff --git a/fggl/gfx/input.hpp b/fggl/gfx/input.hpp index 4922848..5e29c8b 100644 --- a/fggl/gfx/input.hpp +++ b/fggl/gfx/input.hpp @@ -23,6 +23,89 @@ namespace fggl::gfx { char buttons; }; + enum class PadButton { + A, + B, + X, + Y, + BUMPER_LEFT, + BUMPER_RIGHT, + BACK, + START, + GUIDE, + THUMB_LEFT, + THUMB_RIGHT, + DPAD_UP, + DPAD_RIGHT, + DPAD_DOWN, + DPAD_LEFT + }; + constexpr std::array<PadButton, 15> PadButtons = { + PadButton::A, + PadButton::B, + PadButton::X, + PadButton::Y, + PadButton::BUMPER_LEFT, + PadButton::BUMPER_RIGHT, + PadButton::BACK, + PadButton::START, + PadButton::GUIDE, + PadButton::THUMB_LEFT, + PadButton::THUMB_RIGHT, + PadButton::DPAD_UP, + PadButton::DPAD_RIGHT, + PadButton::DPAD_DOWN, + PadButton::DPAD_LEFT + }; + const std::array<const std::string, 15> PadButtonLabels = { + "A", + "B", + "X", + "Y", + "Left Bumper", + "Right Bumper", + "Back", + "Start", + "Guide", + "Left Thumb", + "Right Thumb", + "DPad Up", + "DPad Right", + "DPad Down", + "DPad left" + }; + + enum class PadAxis { + LEFT_X, + LEFT_Y, + RIGHT_X, + RIGHT_Y, + TRIGGER_LEFT, + TRIGGER_RIGHT + }; + constexpr std::array<PadAxis,6> PadAxes = { + PadAxis::LEFT_X, + PadAxis::LEFT_Y, + PadAxis::RIGHT_X, + PadAxis::RIGHT_Y, + PadAxis::TRIGGER_LEFT, + PadAxis::TRIGGER_RIGHT + }; + const std::array<const std::string, 15> PadAxisLabels = { + "Left (x)", + "Left (y)", + "Right (x)", + "Right (y)", + "Left Trigger", + "Right Trigger" + }; + + struct PadState { + std::bitset<PadButtons.size()> buttons; + float axes[PadAxes.size()]; + bool present; + }; + struct Joystick { const char* name = nullptr; const float* axes = nullptr; @@ -31,6 +114,7 @@ namespace fggl::gfx { int hatCount = 0; int axisCount = 0; int buttonCount = 0; + bool gamepad = false; }; class Input { @@ -84,7 +168,18 @@ namespace fggl::gfx { void joystickConnect(int id, Joystick& data); void joystickDisconnect(int id); - bool joystick(int id) const; + bool hasJoystick(int id) const; + const Joystick& joystick(int id) const; + + // gamepads + void padState(int id, const PadState& state); + + bool padDown(int id, PadButton btn); + bool padPressed(int id, PadButton btn); + bool padReleased(int id, PadButton btn); + + float padAxis(int id, PadAxis axis); + float padAxisDelta(int id, PadAxis axis); private: Input(); @@ -92,6 +187,8 @@ namespace fggl::gfx { MouseInputState m_mouse_last; std::bitset<16> m_joysticks; std::array<Joystick, 16> m_joydata; + std::array<PadState, 16> m_pad_curr; + std::array<PadState, 16> m_pad_last; }; }; diff --git a/fggl/gfx/window.cpp b/fggl/gfx/window.cpp index 9cde13b..f1aa1b5 100644 --- a/fggl/gfx/window.cpp +++ b/fggl/gfx/window.cpp @@ -38,16 +38,54 @@ static void fggl_input_mouse_btn(GLFWwindow* window, int btn, int action, int mo 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 ) { + GLFWgamepadstate state; + glfwGetGamepadState(jid, &state); + + fggl::gfx::PadState padState; + for (int i=0; i<=GLFW_GAMEPAD_BUTTON_LAST; i++) { + padState.buttons[i] = state.buttons[i]; + } + + for (int i=0; i<=GLFW_GAMEPAD_AXIS_LAST; i++) { + padState.axes[i] = state.axes[i]; + } + + input.padState(jid, padState); + } +} + +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() { + Input& input = Input::instance(); + for (int jid=0; jid<16; jid++) { + if ( glfwJoystickPresent(jid) ) { + fggl_activate_joystick(input, jid); + } + } +} + 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); + fggl_activate_joystick(input, jid); } else if ( state == GLFW_DISCONNECTED ) { input.joystickDisconnect(jid); } @@ -62,6 +100,10 @@ Context::Context() { int code = glfwGetError(error); throw std::runtime_error( *error ); } + + // joysticks are global + fggl_joystick_init(); + glfwSetJoystickCallback(fggl_joystick); } Context::~Context() { @@ -70,6 +112,7 @@ Context::~Context() { void Context::pollEvents() { glfwPollEvents(); + fggl_joystick_poll(); } Window::Window(Input& input) : m_window(nullptr), m_input(input) { @@ -91,7 +134,6 @@ 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); - glfwSetJoystickCallback(fggl_joystick); } Window::~Window() { -- GitLab