Add basic input system

This commit is contained in:
2020-05-16 00:56:22 +02:00
parent 3e3cbad9c4
commit fc529b0694
29 changed files with 1926 additions and 493 deletions

View File

@@ -0,0 +1,103 @@
#pragma once
#include "../Base/ICloseable.hpp"
#include "InputKey.hpp"
namespace openVulkanoCpp
{
namespace Input
{
class InputDevice : public ICloseable
{
InputDeviceType deviceType = InputDeviceType::UNKNOWN;
int index = -1;
std::string name;
float axisAsButtonThreshold = 0.5f;
float buttonAsAxisValue = 1.0f;
protected:
InputDevice() = default;
void Init(InputDeviceType type, int index, const std::string& name)
{
this->deviceType = type;
this->index = index;
this->name = name;
}
virtual float ReadAxis(int16_t key) const = 0;
virtual bool ReadButton(int16_t key) const = 0;
virtual bool ReadButtonUp(int16_t key) const = 0;
virtual bool ReadButtonDown(int16_t key) const = 0;
public:
virtual ~InputDevice() = default;
virtual void Close() override
{
this->deviceType = InputDeviceType::UNKNOWN;
this->index = -1;
this->name = "";
}
InputDeviceType GetType() const { return deviceType; }
int GetIndex() const { return index; }
const std::string& GetName() const { return name; }
float GetAxisAsButtonThreshold() const { return axisAsButtonThreshold; }
void SetAxisAsButtonThreshold(float value) { axisAsButtonThreshold = value; }
float GetButtonAsAxisValue() const { return buttonAsAxisValue; }
void SetButtonAsAxisValue(float value) { buttonAsAxisValue = value; }
bool GetButton(InputKey key) const
{
if (key.GetInputDeviceType() != deviceType) return false;
if (key.GetInputType() == InputKey::InputType::AXIS)
{
return ReadAxis(key.GetInputKey()) > axisAsButtonThreshold;
}
return ReadButton(key.GetInputKey());
}
bool GetButtonUp(InputKey key) const
{
if (key.GetInputDeviceType() != deviceType) return false;
if (key.GetInputType() == InputKey::InputType::AXIS)
{
//TODO handle
return ReadAxis(key.GetInputKey()) > axisAsButtonThreshold;
}
return ReadButtonUp(key.GetInputKey());
}
bool GetButtonDown(InputKey key) const
{
if (key.GetInputDeviceType() != deviceType) return false;
if (key.GetInputType() == InputKey::InputType::AXIS)
{
//TODO handle
return ReadAxis(key.GetInputKey()) > axisAsButtonThreshold;
}
return ReadButtonDown(key.GetInputKey());
}
float GetAxis(InputKey key) const
{
if (key.GetInputDeviceType() != deviceType) return 0;
if (key.GetInputType() == InputKey::InputType::BUTTON)
{
return ReadButton(key.GetInputKey()) ? buttonAsAxisValue : 0;
}
return ReadAxis(key.GetInputKey());
}
float GetAxis(InputKey keyPositive, InputKey keyNegative) const
{
return GetAxis(keyPositive) - GetAxis(keyNegative);
}
};
}
}

View File

@@ -0,0 +1,110 @@
#pragma once
#include "../Base/Logger.hpp"
#include "InputDevice.hpp"
#include <cstring>
namespace openVulkanoCpp
{
namespace Input
{
class ControllerType
{
public:
enum Type { GENERIC_JOYSTICK, XBOX, DUAL_SHOCK, DUAL_SENSE };
private:
Type type;
public:
ControllerType(Type type) : type(type) {}
};
class InputDeviceController : public InputDevice
{
float axes[InputKey::Controller::Axis::AXIS_LAST + 1] = {0};
uint32_t pressedButtons = 0, lastPressedButtons = 0;
const ControllerType controllerType = ControllerType::GENERIC_JOYSTICK;
bool GetLastButton(InputKey::Controller::Button button) const
{
return lastPressedButtons & (1 << button);
}
protected:
InputDeviceController() = default;
void Init(const int index, const std::string& name)
{
InputDevice::Init(InputDeviceType::CONTROLLER, index, name);
pressedButtons = 0;
lastPressedButtons = 0;
for(float& axis : axes)
{
axis = 0;
}
// TODO find controller type from name
Logger::INPUT->info("Initialized controller: id: {0}, name: {1}", index, name);
}
void SetAxis(InputKey::Controller::Axis axisId, float value)
{
axes[axisId] = value;
}
void SetButtons(uint32_t buttonStates)
{
lastPressedButtons = pressedButtons;
pressedButtons = buttonStates;
}
float ReadAxis(int16_t key) const override final
{
return GetAxis(static_cast<InputKey::Controller::Axis>(key));
}
bool ReadButton(int16_t key) const override final
{
return GetButton(static_cast<InputKey::Controller::Button>(key));
}
bool ReadButtonUp(int16_t key) const override final
{
return GetButtonUp(static_cast<InputKey::Controller::Button>(key));
}
bool ReadButtonDown(int16_t key) const override final
{
return GetButtonDown(static_cast<InputKey::Controller::Button>(key));
}
public:
bool GetButton(const InputKey::Controller::Button button) const
{
return pressedButtons & (1 << button);
}
bool GetButtonUp(const InputKey::Controller::Button button) const
{
return !GetButton(button) && GetLastButton(button);
}
bool GetButtonDown(const InputKey::Controller::Button button) const
{
return GetButton(button) && !GetLastButton(button);
}
float GetAxis(const InputKey::Controller::Axis axis) const
{
return axes[axis];
}
ControllerType GetControllerType() const
{
return controllerType;
}
};
}
}

View File

@@ -0,0 +1,87 @@
#pragma once
#include "InputDevice.hpp"
#include <bitset>
namespace openVulkanoCpp
{
class IWindow;
namespace Input
{
class InputDeviceKeyboard : public InputDevice
{
std::bitset<InputKey::Keyboard::KEY_LAST + 1> keyState;
std::bitset<InputKey::Keyboard::KEY_LAST + 1> lastKeyState;
IWindow* lastWindow;
protected:
InputDeviceKeyboard() = default;
void Init(int index, const std::string& name)
{
InputDevice::Init(InputDeviceType::KEYBOARD, index, name);
}
void PreTick()
{
lastKeyState = keyState;
}
void UpdateKey(InputKey::Keyboard::Key key, bool state)
{
keyState[key] = state;
}
void UpdateActiveWindow(IWindow* window)
{
lastWindow = window;
}
float ReadAxis(int16_t key) const override final
{
return 0;
}
bool ReadButton(int16_t key) const override final
{
return GetButton(static_cast<InputKey::Keyboard::Key>(key));
}
bool ReadButtonUp(int16_t key) const override final
{
return GetButtonUp(static_cast<InputKey::Keyboard::Key>(key));
}
bool ReadButtonDown(int16_t key) const override final
{
return GetButtonDown(static_cast<InputKey::Keyboard::Key>(key));
}
public:
bool GetButton(const InputKey::Keyboard::Key button) const
{
return keyState[button] > 0;
}
bool GetButtonUp(const InputKey::Keyboard::Key button) const
{
return !keyState[button] && lastKeyState[button];
}
bool GetButtonDown(const InputKey::Keyboard::Key button) const
{
return keyState[button] && !lastKeyState[button];
}
IWindow* GetActiveWindow() const
{
return lastWindow;
}
bool IsInWindow(const IWindow* window) const
{
return window == lastWindow;
}
};
}
}

View File

@@ -0,0 +1,180 @@
#pragma once
#include "InputDevice.hpp"
#include "../Base/Logger.hpp"
namespace openVulkanoCpp
{
class IWindow;
namespace Input
{
class InputDeviceMouse : public InputDevice
{
float axes[InputKey::Mouse::Axis::AXIS_LAST + 2] = { 0 };
uint8_t pressedButtons = 0, lastPressedButtons = 0;
double mousePosX = 0, mousePosY = 0;
IWindow* lastWindow = nullptr;
protected:
InputDeviceMouse() = default;
void Init(const int index, const std::string& name)
{
InputDevice::Init(InputDeviceType::MOUSE, index, name);
ClearAxes();
pressedButtons = 0;
lastPressedButtons = 0;
mousePosX = 0;
mousePosY = 0;
lastWindow = nullptr;
}
void UpdateButtons(uint8_t newPressedButtons)
{
lastPressedButtons = pressedButtons;
pressedButtons = newPressedButtons;
if (lastPressedButtons != pressedButtons)
Logger::INPUT->info("Mouse button state changed {0:08b}", pressedButtons);
}
void UpdatePosition(double posX, double posY)
{
axes[InputKey::Mouse::AXIS_X] = static_cast<float>(posX - mousePosX);
axes[InputKey::Mouse::AXIS_Y] = static_cast<float>(posY - mousePosY);
mousePosX = posX;
mousePosY = posY;
Logger::INPUT->info("Mouse moved posX: {0} posY: {1}, relativeX: {2}, relativeY: {3}", posX, posY, axes[InputKey::Mouse::AXIS_X], axes[InputKey::Mouse::AXIS_Y]);
}
void UpdateWheel(float wheelX, float wheelY)
{
axes[InputKey::Mouse::AXIS_WHEEL_X] = wheelX;
axes[InputKey::Mouse::AXIS_WHEEL_Y] = wheelY;
Logger::INPUT->info("Mouse scrolled x: {0} y: {1}", wheelX, wheelY);
}
void UpdateActiveWindow(IWindow* window)
{
axes[InputKey::Mouse::AXIS_X] = 0;
axes[InputKey::Mouse::AXIS_Y] = 0;
lastWindow = window;
}
void ClearAxes()
{
for (float& axis : axes)
{
axis = 0;
}
}
private:
bool GetLastButton(InputKey::Mouse::Button button) const
{
return lastPressedButtons & (1 << button);
}
protected:
float ReadAxis(int16_t key) const override final
{
return GetAxis(static_cast<InputKey::Mouse::Axis>(key));
}
bool ReadButton(int16_t key) const override final
{
return GetButton(static_cast<InputKey::Mouse::Button>(key));
}
bool ReadButtonUp(int16_t key) const override final
{
return GetButtonUp(static_cast<InputKey::Mouse::Button>(key));
}
bool ReadButtonDown(int16_t key) const override final
{
return GetButtonDown(static_cast<InputKey::Mouse::Button>(key));
}
public:
bool GetButton(const InputKey::Mouse::Button button) const
{
return pressedButtons & (1 << button);
}
bool GetButtonUp(const InputKey::Mouse::Button button) const
{
return !GetButton(button) && GetLastButton(button);
}
bool GetButtonDown(const InputKey::Mouse::Button button) const
{
return GetButton(button) && !GetLastButton(button);
}
float GetAxis(const InputKey::Mouse::Axis axis) const
{
return axes[axis];
}
float GetWheel() const
{
return GetAxis(InputKey::Mouse::Axis::AXIS_WHEEL_Y);
}
float GetWheelX() const
{
return GetAxis(InputKey::Mouse::Axis::AXIS_WHEEL_X);
}
float GetWheelY() const
{
return GetAxis(InputKey::Mouse::Axis::AXIS_WHEEL_Y);
}
glm::vec2 GetMousePosition() const
{
return { mousePosX, mousePosY };
}
void GetMousePosition(double& posX, double& posY) const
{
posX = mousePosX;
posY = mousePosY;
}
glm::vec2 GetMousePosition(const IWindow* window) const
{
if (window == lastWindow)
{
return { mousePosX, mousePosY };
}
return { std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN() };
}
void GetMousePosition(const IWindow* window, double& posX, double& posY) const
{
if (window == lastWindow)
{
posX = mousePosX;
posY = mousePosY;
}
else
{
posX = std::numeric_limits<double>::quiet_NaN();
posY = std::numeric_limits<double>::quiet_NaN();
}
}
IWindow* GetActiveWindow() const
{
return lastWindow;
}
bool IsInWindow(const IWindow* window) const
{
return window == lastWindow;
}
};
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <cstdint>
#include <string>
namespace openVulkanoCpp
{
namespace Input
{
enum class InputDeviceType : uint8_t
{
UNKNOWN, KEYBOARD, MOUSE, CONTROLLER
};
}
}

View File

@@ -0,0 +1,273 @@
#pragma once
#include "InputDeviceType.hpp"
namespace openVulkanoCpp
{
namespace Input
{
class InputKey
{
public:
enum class InputType : uint8_t
{
BUTTON = 0, AXIS
};
class Keyboard
{
public:
enum Key : int16_t
{
UNKNOWN = -1,
// Numpad
KEY_NUMPAD_0 = 0,
KEY_NUMPAD_1 = 1,
KEY_NUMPAD_2 = 2,
KEY_NUMPAD_3 = 3,
KEY_NUMPAD_4 = 4,
KEY_NUMPAD_5 = 5,
KEY_NUMPAD_6 = 6,
KEY_NUMPAD_7 = 7,
KEY_NUMPAD_8 = 8,
KEY_NUMPAD_9 = 9,
KEY_NUMPAD_DECIMAL = 10,
KEY_NUMPAD_DIVIDE = 11,
KEY_NUMPAD_MULTIPLY = 12,
KEY_NUMPAD_SUBTRACT = 13,
KEY_NUMPAD_ADD = 14,
KEY_NUMPAD_ENTER = 15,
KEY_NUMPAD_EQUAL = 16,
KEY_NUM_LOCK = 17,
KEY_ESCAPE = 18,
KEY_ENTER = 19,
KEY_TAB = 20,
KEY_BACKSPACE = 21,
KEY_INSERT = 22,
KEY_DELETE = 23,
KEY_RIGHT = 24,
KEY_LEFT = 25,
KEY_DOWN = 26,
KEY_UP = 27,
KEY_PAGE_UP = 28,
KEY_PAGE_DOWN = 29,
KEY_HOME = 30,
KEY_END = 31,
KEY_CAPS_LOCK = 32,
KEY_SCROLL_LOCK = 33,
KEY_PRINT_SCREEN = 34,
KEY_PAUSE = 35,
KEY_LEFT_SHIFT = 36,
KEY_LEFT_CONTROL = 37,
KEY_LEFT_ALT = 38,
KEY_LEFT_SUPER = 39,
KEY_RIGHT_SHIFT = 40,
KEY_RIGHT_CONTROL = 41,
KEY_RIGHT_ALT = 42,
KEY_RIGHT_SUPER = 43,
// Printable keys, most of them are mapped to their ascii codes
KEY_COMMA = 44,
KEY_MINUS = 45,
KEY_PERIOD = 46,
KEY_SLASH = 47,
KEY_0 = 48,
KEY_1 = 49,
KEY_2 = 50,
KEY_3 = 51,
KEY_4 = 52,
KEY_5 = 53,
KEY_6 = 54,
KEY_7 = 55,
KEY_8 = 56,
KEY_9 = 57,
KEY_WORLD_1 = 58,
KEY_SEMICOLON = 59,
KEY_WORLD_2 = 60,
KEY_EQUAL = 61,
KEY_SPACE = 62,
KEY_APOSTROPHE = 63, //'
KEY_GRAVE_ACCENT = 64, // `
KEY_A = 65,
KEY_B = 66,
KEY_C = 67,
KEY_D = 68,
KEY_E = 69,
KEY_F = 70,
KEY_G = 71,
KEY_H = 72,
KEY_I = 73,
KEY_J = 74,
KEY_K = 75,
KEY_L = 76,
KEY_M = 77,
KEY_N = 78,
KEY_O = 79,
KEY_P = 80,
KEY_Q = 81,
KEY_R = 82,
KEY_S = 83,
KEY_T = 84,
KEY_U = 85,
KEY_V = 86,
KEY_W = 87,
KEY_X = 88,
KEY_Y = 89,
KEY_Z = 90,
KEY_LEFT_BRACKET = 91, // [
KEY_BACKSLASH = 92,
KEY_RIGHT_BRACKET = 93, // ]
KEY_MENU = 99,
// Function keys
KEY_F1 = 101,
KEY_F2 = 102,
KEY_F3 = 103,
KEY_F4 = 104,
KEY_F5 = 105,
KEY_F6 = 106,
KEY_F7 = 107,
KEY_F8 = 108,
KEY_F9 = 109,
KEY_F10 = 110,
KEY_F11 = 111,
KEY_F12 = 112,
KEY_F13 = 113,
KEY_F14 = 114,
KEY_F15 = 115,
KEY_F16 = 116,
KEY_F17 = 117,
KEY_F18 = 118,
KEY_F19 = 119,
KEY_F20 = 120,
KEY_F21 = 121,
KEY_F22 = 122,
KEY_F23 = 123,
KEY_F24 = 124,
KEY_F25 = 125,
KEY_LAST = KEY_F25
};
};
class Mouse
{
public:
enum Button : int16_t
{
BUTTON_1 = 0,
BUTTON_2,
BUTTON_3,
BUTTON_4,
BUTTON_5,
BUTTON_6,
BUTTON_7,
BUTTON_8,
BUTTON_LAST = BUTTON_8,
BUTTON_LEFT = BUTTON_1,
BUTTON_RIGHT = BUTTON_2,
BUTTON_MIDDLE = BUTTON_3
};
enum Axis : int16_t
{
AXIS_X = 0,
AXIS_Y,
AXIS_WHEEL_X,
AXIS_WHEEL_Y,
AXIS_LAST = AXIS_WHEEL_Y
};
};
class Controller
{
public:
enum Button : int16_t
{
JOY_1 = 0,
JOY_2,
JOY_3,
JOY_4,
JOY_5,
JOY_6,
JOY_7,
JOY_8,
JOY_9,
JOY_10,
JOY_11,
JOY_12,
JOY_13,
JOY_14,
JOY_15,
JOY_16,
JOY_LAST = JOY_16,
BUTTON_A = JOY_1,
BUTTON_B = JOY_2,
BUTTON_X = JOY_3,
BUTTON_Y = JOY_4,
BUTTON_LEFT_BUMPER = JOY_5,
BUTTON_RIGHT_BUMPER = JOY_6,
BUTTON_BACK = JOY_7,
BUTTON_START = JOY_8,
BUTTON_GUIDE = JOY_9,
BUTTON_LEFT_THUMB = JOY_10,
BUTTON_RIGHT_THUMB = JOY_11,
BUTTON_DPAD_UP = JOY_12,
BUTTON_DPAD_RIGHT = JOY_13,
BUTTON_DPAD_DOWN = JOY_14,
BUTTON_DPAD_LEFT = JOY_15,
PS_BUTTON_CROSS = BUTTON_A,
PS_BUTTON_CIRCLE = BUTTON_B,
PS_BUTTON_SQUARE = BUTTON_X,
PS_BUTTON_TRIANGLE = BUTTON_Y
};
enum Axis : int16_t
{
AXIS_LEFT_X = 0,
AXIS_LEFT_Y,
AXIS_RIGHT_X,
AXIS_RIGHT_Y,
AXIS_LEFT_TRIGGER,
AXIS_RIGHT_TRIGGER,
AXIS_LAST = AXIS_RIGHT_TRIGGER
};
};
private:
InputDeviceType deviceType;
InputType type;
int16_t key;
public:
InputKey(Keyboard::Key keyboardKey) :
deviceType(InputDeviceType::KEYBOARD), type(InputType::BUTTON), key(keyboardKey)
{}
InputKey(Mouse::Button mouseButton) :
deviceType(InputDeviceType::MOUSE), type(InputType::BUTTON), key(mouseButton)
{}
InputKey(Mouse::Axis mouseAxis) :
deviceType(InputDeviceType::MOUSE), type(InputType::AXIS), key(mouseAxis)
{}
InputKey(Controller::Button controllerButton) :
deviceType(InputDeviceType::CONTROLLER), type(InputType::BUTTON), key(controllerButton)
{}
InputKey(Controller::Axis controllerAxis) :
deviceType(InputDeviceType::CONTROLLER), type(InputType::AXIS), key(controllerAxis)
{}
InputDeviceType GetInputDeviceType() const { return deviceType; }
InputType GetInputType() const { return type; }
int16_t GetInputKey() const { return key; }
};
}
}

View File

@@ -0,0 +1,182 @@
#pragma once
#include "InputKey.hpp"
#include "InputDevice.hpp"
#include "../Base/Utils.hpp"
#include <functional>
#include <unordered_map>
#include <vector>
namespace openVulkanoCpp
{
namespace Input
{
class BaseInputAction
{
std::string name;
std::vector<InputDevice*> devices;
bool enabled;
protected:
BaseInputAction(const std::string& name) : name(name), enabled(true) {}
public:
virtual ~BaseInputAction() = default;
const std::string& GetName() const
{
return name;
}
const std::vector<InputDevice*>& GetDevices() const
{
return devices;
}
bool IsEnabled() const
{
return enabled;
}
void SetEnabled(bool enabled)
{
this->enabled = enabled;
}
};
class InputAction : public BaseInputAction
{
std::vector<InputKey> keys;
std::vector<std::pair<InputKey, InputKey>> axisButtons;
public:
InputAction(const std::string& name) : BaseInputAction(name)
{}
const std::vector<InputKey>& GetKeys() const
{
return keys;
}
const std::vector<std::pair<InputKey, InputKey>>& GetAxisButtons() const
{
return axisButtons;
}
void BindKey(InputKey key)
{
keys.push_back(key);
}
void BindAxisButtons(InputKey keyPositive, InputKey keyNegative)
{
axisButtons.emplace_back(keyPositive, keyNegative);
}
};
class InputShortcut : public BaseInputAction
{
std::string name;
std::vector<InputDevice*> devices;
std::vector<std::vector<InputKey>> buttonBindings;
public:
InputShortcut(const std::string& name) : BaseInputAction(name)
{}
};
class InputManager
{
InputManager() = default;
public:
static InputManager* GetInstace()
{
static InputManager* instance = new InputManager();
return instance;
}
void RegisterInputDevice(InputDevice* device)
{
devices.push_back(device);
if (!lastActiveDevice) lastActiveDevice = device;
}
void UnregisterInputDevice(InputDevice* device)
{
Utils::Remove(devices, device);
}
InputAction* GetAction(const std::string& actionName)
{
InputAction*& action = actionNameMapping[actionName];
if(!action)
{
action = new InputAction(actionName);
}
return action;
}
float GetAxis(const InputAction* action) const
{
float value = 0;
const std::vector<InputDevice*>& testDevices = action->GetDevices().empty() ? devices : action->GetDevices();
for (const InputDevice* device : testDevices)
{
for(InputKey key : action->GetKeys())
{
value += device->GetAxis(key);
}
for(const auto& keys : action->GetAxisButtons())
{
value += GetAxis(keys.first) - GetAxis(keys.second);
}
}
return value;
}
float GetAxis(InputKey key) const
{
float value = 0;
for (const InputDevice* device : devices)
{
value += device->GetAxis(key);
}
return value;
}
bool GetButton(InputAction* action) const
{
const std::vector<InputDevice*>& testDevices = action->GetDevices().empty() ? devices : action->GetDevices();
for (const InputDevice* device : testDevices)
{
for (const InputKey key : action->GetKeys())
{
if (device->GetButton(key)) return true;
}
}
return false;
}
bool GetButton(InputKey key) const
{
for(const InputDevice* device : devices)
{
if (device->GetButton(key)) return true;
}
return false;
}
InputDevice* GetLastActiveDevice() const
{
return lastActiveDevice;
}
private:
//std::unordered_map<InputKey, std::vector<InputAction*>> inputActionMapping;
std::unordered_map<std::string, InputAction*> actionNameMapping;
std::vector<InputDevice*> devices;
InputDevice* lastActiveDevice = nullptr;
};
}
}