From 788a6958ba4d78a04044cadb4de3d207f4229f34 Mon Sep 17 00:00:00 2001 From: Archie Jaskowicz Date: Mon, 13 May 2024 20:02:46 +0100 Subject: [PATCH] feat: added controller support --- include/engine/utilities/input_manager.h | 79 +++++++ include/engine/window.h | 7 +- src/engine/utilities/input_manager.cpp | 131 +++++++++++ src/engine/window.cpp | 283 ++++++++++++++++------- 4 files changed, 410 insertions(+), 90 deletions(-) diff --git a/include/engine/utilities/input_manager.h b/include/engine/utilities/input_manager.h index 6f70f09..5cd073c 100644 --- a/include/engine/utilities/input_manager.h +++ b/include/engine/utilities/input_manager.h @@ -1 +1,80 @@ #pragma once + +#include +#include "GLFW/glfw3.h" + +namespace input { + +enum controller_inputs { + INPUT_UP = 0, + INPUT_DOWN = 1, + INPUT_LEFT = 2, + INPUT_RIGHT = 3, + CONTROLLER_BUTTON_UP = 4, + CONTROLLER_BUTTON_DOWN = 5, + CONTROLLER_BUTTON_LEFT = 6, + CONTROLLER_BUTTON_RIGHT = 7, + CONTROLLER_BUTTON_OPTION = 8, + CONTROLLER_BUTTON_START = 9, + CONTROLLER_BUTTON_LEFT_JOYSTICK = 10, + CONTROLLER_BUTTON_RIGHT_JOYSTICK = 11, + CONTROLLER_BUTTON_LEFT_BUMPER = 12, + CONTROLLER_BUTTON_LB = 12, + CONTROLLER_BUTTON_RIGHT_BUMPER = 13, + CONTROLLER_BUTTON_RB = 13, + CONTROLLER_BUTTON_DPAD_UP = 14, + CONTROLLER_BUTTON_DPAD_DOWN = 15, + CONTROLLER_BUTTON_DPAD_LEFT = 16, + CONTROLLER_BUTTON_DPAD_RIGHT = 17, +}; + +enum input_types { + KeyboardAndMouse = 0, + Controller = 1, +}; + +class input_manager { + +public: + /** + * @brief input_manager constructor. + * @param window The window reference to setup the input_manager. + */ + input_manager(GLFWwindow* window); + + ~input_manager(); + + void input_loop(); + + /** + * @brief Keyboard press event + * + * @param key The key that was pressed (look at `GLFW_KEY_`). + * + */ + std::function on_keyboard_press{}; + + /** + * @brief Controller input event + * + * @param input The input of the controller as `controller_inputs`. + */ + std::function on_controller_input{}; + + /** + * @brief Controller button press event + * + * @param key The controller button as `controller_inputs`. + */ + std::function on_controller_button_press{}; + + std::vector keys_held{}; + + input_types current_input_type{}; + +private: + + float deadzone = 0.2; +}; + +} diff --git a/include/engine/window.h b/include/engine/window.h index 839b89b..a1c4dae 100644 --- a/include/engine/window.h +++ b/include/engine/window.h @@ -3,6 +3,7 @@ #include #include "video_manager.h" #include "glm/vec2.hpp" +#include "utilities/input_manager.h" #include class window { @@ -18,6 +19,8 @@ class window { video_manager manager{}; + std::unique_ptr input_man{nullptr}; + private: struct GLFWwindow* glfw_window{nullptr}; @@ -25,10 +28,10 @@ class window { uint16_t frame_width{0}; // vid_reader.height uint16_t frame_height{0}; // vid_reader.height - bool testing_export{false}; - GLuint video_texture; glm::vec2 vertices[4] = {{-1, -1}, {-1, 1}, {1, 1}, {1, -1}}; + void render_window_bar(); + }; \ No newline at end of file diff --git a/src/engine/utilities/input_manager.cpp b/src/engine/utilities/input_manager.cpp index faf8234..a2cb9a3 100644 --- a/src/engine/utilities/input_manager.cpp +++ b/src/engine/utilities/input_manager.cpp @@ -1 +1,132 @@ +#include #include "utilities/input_manager.h" + +input::input_manager::input_manager(GLFWwindow* window) { + if(!window) { + return; + } + + std::cout << "Setting up input manager..." << "\n"; + + static input_manager *mgr = this; + + glfwSetKeyCallback(window, [](GLFWwindow *window, int key, int scancode, int action, int mods) { + mgr->current_input_type = input_types::KeyboardAndMouse; + if(action == GLFW_PRESS) { + mgr->keys_held.emplace_back(key); + mgr->on_keyboard_press(key); + } + }); + + std::cout << "Input manager has setup successfully!" << "\n"; +} + +input::input_manager::~input_manager() { + std::cout << "Input manager killed." << "\n"; +} + +void input::input_manager::input_loop() { + if(glfwJoystickPresent(GLFW_JOYSTICK_1) == GLFW_TRUE) { + int count; + const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &count); + + const float x_axis = axes[0]; + const float y_axis = axes[1]; + + if(on_controller_input) { + if(abs(x_axis) >= deadzone) { + if(x_axis < 0) { + on_controller_input(controller_inputs::INPUT_LEFT); + } else { + on_controller_input(controller_inputs::INPUT_RIGHT); + } + + current_input_type = input_types::Controller; + } + + if(abs(y_axis) >= deadzone) { + if(y_axis < 0) { + on_controller_input(controller_inputs::INPUT_UP); + } else { + on_controller_input(controller_inputs::INPUT_DOWN); + } + + current_input_type = input_types::Controller; + } + } + + const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &count); + + if(on_controller_button_press) { + if(buttons[0] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_DOWN); + current_input_type = input_types::Controller; + } + + if(buttons[1] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_RIGHT); + current_input_type = input_types::Controller; + } + + if(buttons[2] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_LEFT); + current_input_type = input_types::Controller; + } + + if(buttons[3] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_UP); + current_input_type = input_types::Controller; + } + + if(buttons[4] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_LEFT_BUMPER); + current_input_type = input_types::Controller; + } + + if(buttons[5] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_RIGHT_BUMPER); + current_input_type = input_types::Controller; + } + + if(buttons[6] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_OPTION); + current_input_type = input_types::Controller; + } + + if(buttons[7] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_START); + current_input_type = input_types::Controller; + } + + if(buttons[8] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_LEFT_JOYSTICK); + current_input_type = input_types::Controller; + } + + if(buttons[9] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_RIGHT_JOYSTICK); + current_input_type = input_types::Controller; + } + + if(buttons[10] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_DPAD_UP); + current_input_type = input_types::Controller; + } + + if(buttons[11] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_DPAD_RIGHT); + current_input_type = input_types::Controller; + } + + if(buttons[12] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_DPAD_DOWN); + current_input_type = input_types::Controller; + } + + if(buttons[13] == GLFW_PRESS) { + on_controller_button_press(controller_inputs::CONTROLLER_BUTTON_DPAD_LEFT); + current_input_type = input_types::Controller; + } + } + } +} diff --git a/src/engine/window.cpp b/src/engine/window.cpp index 1c42ee3..d4af26b 100644 --- a/src/engine/window.cpp +++ b/src/engine/window.cpp @@ -57,20 +57,28 @@ window::window() { glfwSetWindowIcon(glfw_window, 1, images); stbi_image_free(images[0].pixels); - - glfwSetKeyCallback(glfw_window, [](GLFWwindow* window, int key, int scancode, int action, int mods) { - //std::cout << "key pressed: " << key << "\n"; - /* - if (action == GLFW_PRESS) { - if (key == GLFW_KEY_F11) { - glfw_window - glfwSetWindowSize(glfw_window, desktopWidth, desktopHeight); + // Initialise Input Manager + input_man = std::make_unique(glfw_window); + + input_man->on_keyboard_press = [this](int key) { + if(std::find(input_man->keys_held.begin(), input_man->keys_held.end(), GLFW_KEY_LEFT_CONTROL) != input_man->keys_held.end()) { + if(std::find(input_man->keys_held.begin(), input_man->keys_held.end(), GLFW_KEY_O) != input_man->keys_held.end()) { + bool loaded = utilities::load_project(manager); + + if(loaded) + ImGui::InsertNotification({ImGuiToastType::Success, 3000, "Project loaded successfully!"}); + else + ImGui::InsertNotification({ImGuiToastType::Error, 5000, "Failed to load project. Check the file is right and project isn't corrupted. If the problem persists, report this issue!"}); + } else if(std::find(input_man->keys_held.begin(), input_man->keys_held.end(), GLFW_KEY_S) != input_man->keys_held.end()) { + bool saved = utilities::save_project(manager); + + if(saved) + ImGui::InsertNotification({ImGuiToastType::Success, 3000, "Project saved successfully!"}); + else + ImGui::InsertNotification({ImGuiToastType::Error, 5000, "Failed to save project. Check the file path you're saving to. If the problem persists, report this issue!"}); } } - */ - return; - }); - + }; // glfwSetWindowMonitor @@ -140,6 +148,9 @@ window::window() { } window::~window() { + // Free input_man + input_man.reset(); + ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); @@ -171,12 +182,9 @@ void window::window_loop() { static bool first_frame{ true }; static bool show_choices{ false }; - if(glfwJoystickPresent(GLFW_JOYSTICK_1) == GLFW_TRUE) { - int count; - const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &count); + static std::string selected_option{}; - //std::cout << "controller funnies: " << axes[0] << "\n"; - } + input_man->input_loop(); //const auto& render_start = std::chrono::high_resolution_clock::now(); @@ -367,80 +375,10 @@ void window::window_loop() { //ImGui::ShowDemoWindow(); - if (ImGui::BeginMainMenuBar()) { - if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("New Project")) { - manager.remove_all_videos(); - - ImGui::InsertNotification({ImGuiToastType::Success, 3000, "New project started successfully!"}); - } else if (ImGui::MenuItem("Open", "Ctrl+O")) { - bool loaded = utilities::load_project(manager); - - if(loaded) - ImGui::InsertNotification({ImGuiToastType::Success, 3000, "Project loaded successfully!"}); - else - ImGui::InsertNotification({ImGuiToastType::Error, 5000, "Failed to load project. Check the file is right and project isn't corrupted. If the problem persists, report this issue!"}); - } else if (ImGui::MenuItem("Save", "Ctrl+S")) { - bool saved = utilities::save_project(manager); - - if(saved) - ImGui::InsertNotification({ImGuiToastType::Success, 3000, "Project saved successfully!"}); - else - ImGui::InsertNotification({ImGuiToastType::Error, 5000, "Failed to save project. Check the file path you're saving to. If the problem persists, report this issue!"}); - } else if (ImGui::MenuItem("Quit")) { - close_window(); - } - - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Edit")) { - if (ImGui::MenuItem("Project Settings")) { - ImGui::InsertNotification({ ImGuiToastType::Error, 3000, "Not currently implemented." }); - } - - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Packaging")) { - if (ImGui::BeginMenu("Export")) { - if (ImGui::MenuItem("Windows")) { - ImGui::InsertNotification({ImGuiToastType::Error, 3000, "Not currently implemented."}); - /* do stuff */ - } else if (ImGui::MenuItem("Linux")) { - ImGui::InsertNotification({ImGuiToastType::Error, 3000, "Not currently implemented."}); - /* do stuff */ - } else if (ImGui::MenuItem("OSX")) { - ImGui::InsertNotification({ImGuiToastType::Error, 3000, "Not currently implemented."}); - /* do stuff */ - } - - ImGui::EndMenu(); - } - - ImGui::Checkbox("Release Mode", &testing_export); - - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Play")) { - if (ImGui::MenuItem("In-Engine")) { - ImGui::InsertNotification({ImGuiToastType::Error, 3000, "Not currently implemented."}); - /* do stuff */ - } else if (ImGui::MenuItem("Standalone")) { - ImGui::InsertNotification({ImGuiToastType::Error, 3000, "Not currently implemented."}); - /* do stuff */ - } - - ImGui::EndMenu(); - } - - ImGui::EndMainMenuBar(); - } + render_window_bar(); if (show_choices) { if (!manager.current_video.options.empty() && !first_frame) { - ImVec2 WindowPos(window_width/2, window_height - 40); ImGui::SetNextWindowPos(WindowPos, ImGuiCond_None, ImVec2(0.5f, 0.5f)); // ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground (This is to prevent the window from having any looks) @@ -454,6 +392,10 @@ void window::window_loop() { continue; } + if(selected_option.empty()) { + selected_option = opt.first; + } + std::string option_text(opt.second.name); if (ImGui::Button(option_text.c_str(), ImVec2(300, 30))) { first_frame = true; @@ -471,13 +413,111 @@ void window::window_loop() { } show_choices = false; + selected_option = ""; break; } + + if(input_man->current_input_type == input::Controller) { + if(selected_option == opt.first) { + ImGui::SetKeyboardFocusHere(); + } + } + ImGui::SameLine(); } ImGui::End(); } + + input_man->on_controller_button_press = [&](input::controller_inputs key) { + if(key == input::controller_inputs::CONTROLLER_BUTTON_DOWN) { + first_frame = true; + delete[] frame_data; + frame_data = nullptr; + + manager.current_video.name = ""; + + //std::cout << "attempting to play: " << opt.second.video_id << "\n"; + video vid = manager.get_videos()[manager.current_video.options[selected_option].video_id]; + //std::cout << "video name: " << vid.name << "\n"; + if (!manager.open_video(&vid_reader, vid)) { + std::cout << "Failed to read frame data." << "\n"; + } + + show_choices = false; + selected_option = ""; + } else if(key == input::controller_inputs::CONTROLLER_BUTTON_DPAD_RIGHT) { + bool found{false}; + for (const auto& opt : manager.current_video.options) { + if(found) { + selected_option = opt.first; + break; + } else { + // if the current option is this option, then set "found" to true + // so the next option can get selected. + if(selected_option == opt.first) { + found = true; + } + } + } + + if(!found) { + for (const auto& opt : manager.current_video.options) { + selected_option = opt.first; + break; + } + } + + } else if(key == input::controller_inputs::CONTROLLER_BUTTON_DPAD_LEFT) { + std::string previous_id{}; + for (const auto& opt : manager.current_video.options) { + if(selected_option == opt.first) { + if(!previous_id.empty()) + selected_option = previous_id; + break; + } else { + previous_id = opt.first; + } + } + } + }; + + input_man->on_controller_input = [&](input::controller_inputs input){ + if(input == input::INPUT_RIGHT) { + bool found{false}; + for (const auto& opt : manager.current_video.options) { + if(found) { + selected_option = opt.first; + break; + } else { + // if the current option is this option, then set "found" to true + // so the next option can get selected. + if(selected_option == opt.first) { + found = true; + } + } + } + + if(!found) { + for (const auto& opt : manager.current_video.options) { + selected_option = opt.first; + break; + } + } + + } else if(input == input::INPUT_LEFT) { + std::string previous_id{}; + for (const auto& opt : manager.current_video.options) { + if(selected_option == opt.first) { + if(!previous_id.empty()) + selected_option = previous_id; + break; + } else { + previous_id = opt.first; + } + } + } + }; } manager.render_window(vid_reader); @@ -519,3 +559,70 @@ void window::window_loop() { void window::close_window() { glfwSetWindowShouldClose(glfw_window, true); } + +void window::render_window_bar() { + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("New Project")) { + manager.remove_all_videos(); + + ImGui::InsertNotification({ImGuiToastType::Success, 3000, "New project started successfully!"}); + } else if (ImGui::MenuItem("Open", "Ctrl+O")) { + bool loaded = utilities::load_project(manager); + + if(loaded) + ImGui::InsertNotification({ImGuiToastType::Success, 3000, "Project loaded successfully!"}); + else + ImGui::InsertNotification({ImGuiToastType::Error, 5000, "Failed to load project. Check the file is right and project isn't corrupted. If the problem persists, report this issue!"}); + } else if (ImGui::MenuItem("Save", "Ctrl+S")) { + bool saved = utilities::save_project(manager); + + if(saved) + ImGui::InsertNotification({ImGuiToastType::Success, 3000, "Project saved successfully!"}); + else + ImGui::InsertNotification({ImGuiToastType::Error, 5000, "Failed to save project. Check the file path you're saving to. If the problem persists, report this issue!"}); + } else if (ImGui::MenuItem("Quit")) { + close_window(); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Edit")) { + if (ImGui::MenuItem("Project Settings")) { + ImGui::InsertNotification({ ImGuiToastType::Error, 3000, "Not currently implemented." }); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Export")) { + if (ImGui::MenuItem("Windows")) { + ImGui::InsertNotification({ImGuiToastType::Error, 3000, "Not currently implemented."}); + /* do stuff */ + } else if (ImGui::MenuItem("Linux")) { + ImGui::InsertNotification({ImGuiToastType::Error, 3000, "Not currently implemented."}); + /* do stuff */ + } else if (ImGui::MenuItem("OSX")) { + ImGui::InsertNotification({ImGuiToastType::Error, 3000, "Not currently implemented."}); + /* do stuff */ + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Play")) { + if (ImGui::MenuItem("In-Engine")) { + ImGui::InsertNotification({ImGuiToastType::Error, 3000, "Not currently implemented."}); + /* do stuff */ + } else if (ImGui::MenuItem("Standalone")) { + ImGui::InsertNotification({ImGuiToastType::Error, 3000, "Not currently implemented."}); + /* do stuff */ + } + + ImGui::EndMenu(); + } + + ImGui::EndMainMenuBar(); + } +}