Newer
Older
ForwardPlusRenderer / src / InputHandler.cpp
#include "stdafx.h"
#include "InputHandler.h"

namespace fpr
{
std::vector<InputHandler*> InputHandler::S_INPUT_HANDLERS;
glm::highp_dvec2           InputHandler::old_cursor_pos;
glm::highp_dvec2           InputHandler::new_cursor_pos;
glm::highp_dvec2           InputHandler::dx_dy;
double                     InputHandler::delta_time;
void                       fpr::InputHandler::BindAction(EKeyCode key, action_callback callback, EKeyState key_state)
{
  m_tracked_key_actions.emplace(key, std::make_tuple(callback, key_state, false));
}

void InputHandler::BindAxis(EKeyCode key, axis_callback callback, EAxis axis, EKeyState key_state)
{
  m_tracked_key_axis.emplace(key, std::make_tuple(callback, key_state, false, axis));
}

void InputHandler::BindMouseAxis(EMouseAxis mouse_axis, mouse_axis_callback callback)
{
  m_mouse_axis_bindings.emplace(mouse_axis, callback);
}

void InputHandler::BindMouseButton(EMouseButton button, action_callback callback, EKeyState state)
{
  m_tracked_mouse_btns.emplace(button, std::make_tuple(callback, state, false));
}

const glm::highp_dvec2& InputHandler::GetCursorDelta()
{
  return dx_dy;
}

void InputHandler::Update([[maybe_unused]] GLFWwindow* window)
{
  //Iterate over each input handler.
  //Iterate over each mapped key and invoke the function that is bound to that key, based on the 'type' of key press or mouse axis change.
  for(InputHandler* instance : S_INPUT_HANDLERS)
  {
    for(auto& [button, callback_data] : instance->m_tracked_mouse_btns)
    {
      auto& [callback, state, is_pressed] = callback_data;
      if(state == fpr::EKeyState::EKS_HOLD && is_pressed)
        callback();
    }

    for(auto& [button, callback_data] : instance->m_tracked_key_actions)
    {
      auto& [callback, state, is_pressed] = callback_data;
      if(state == fpr::EKeyState::EKS_HOLD && is_pressed)
        callback();
    }

    for(auto& [button, callback_data] : instance->m_tracked_key_axis)
    {
      auto& [callback, state, is_pressed, axis] = callback_data;
      if(state == fpr::EKeyState::EKS_HOLD && is_pressed)
        callback((double)axis * delta_time);
    }
  }
  dx_dy = {};
}


InputHandler::~InputHandler() {}

void InputHandler::SetDeltaTime(double delta)
{
  delta_time = delta;
}

void fpr::InputHandler::KeyCallback(
    [[maybe_unused]] GLFWwindow* window,
    int                          key,
    [[maybe_unused]] int         scan_code,
    int                          action,
    [[maybe_unused]] int         mods)
{
  for(InputHandler* instance : S_INPUT_HANDLERS)
  {
    auto action_it = instance->m_tracked_key_actions.find(static_cast<EKeyCode>(key));
    if(action_it != instance->m_tracked_key_actions.end())
    {
      auto& [callback, state, is_pressed] = action_it->second;
      is_pressed                          = static_cast<EKeyState>(action) != EKeyState::EKS_RELEASED;
      if(static_cast<EKeyState>(action) == state)
      {
        callback();
      }
    }

    auto axis_it = instance->m_tracked_key_axis.find(static_cast<EKeyCode>(key));
    if(axis_it != instance->m_tracked_key_axis.end())
    {
      auto& [callback, state, is_pressed, axis] = axis_it->second;
      is_pressed                                = static_cast<EKeyState>(action) != EKeyState::EKS_RELEASED;
    }
  }
}

void InputHandler::MouseBtnCallback(
    [[maybe_unused]] GLFWwindow* window,
    int                          button,
    int                          action,
    [[maybe_unused]] int         mods)
{
  for(InputHandler* instance : S_INPUT_HANDLERS)
  {
    if(!instance->m_enabled)
      continue;
    auto iter = instance->m_tracked_mouse_btns.find(static_cast<EMouseButton>(button));
    if(iter == instance->m_tracked_mouse_btns.end())
      continue;
    else
    {
      auto& [callback, state, is_pressed] = iter->second;

      is_pressed = static_cast<EKeyState>(action) != fpr::EKeyState::EKS_RELEASED;
      if(static_cast<EKeyState>(action) == state || is_pressed)
      {
        callback;
      }
    }
  }
}

void InputHandler::MouseMovementCallback([[maybe_unused]] GLFWwindow* window, double x, double y)
{
  new_cursor_pos.x = x;
  new_cursor_pos.y = y;
  dx_dy            = glm::normalize(old_cursor_pos - new_cursor_pos);
  old_cursor_pos   = new_cursor_pos;

  for(InputHandler* instance : S_INPUT_HANDLERS)
  {
    if(!instance->m_enabled)
      continue;
    if(dx_dy.x != 0)
    {
      auto it = instance->m_mouse_axis_bindings.find(EMouseAxis::EMA_X);
      if(it != instance->m_mouse_axis_bindings.end())
      {
        auto& callback = it->second;
        callback(dx_dy.x * delta_time);
      }
    }

    if(dx_dy.y != 0)
    {
      auto it = instance->m_mouse_axis_bindings.find(EMouseAxis::EMA_Y);
      if(it != instance->m_mouse_axis_bindings.end())
      {
        auto& callback = it->second;
        callback(dx_dy.y * delta_time);
      }
    }
  }
}

void InputHandler::Enable(bool is_enabled)
{
  m_enabled = is_enabled;
};

fpr::InputHandler::InputHandler(GLFWwindow* window): m_window(window)
{
  glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
  glfwSetKeyCallback(window, InputHandler::KeyCallback);
  glfwSetMouseButtonCallback(window, InputHandler::MouseBtnCallback);
  glfwSetCursorPosCallback(window, InputHandler::MouseMovementCallback);
  glfwSetInputMode(window, GLFW_STICKY_MOUSE_BUTTONS, GLFW_TRUE);
  double mouse_x;
  double mouse_y;
  glfwGetCursorPos(window, &mouse_x, &mouse_y);
  S_INPUT_HANDLERS.push_back(this);
}
} // namespace fpr