WE GOT TEXTURES BABY
This commit is contained in:
@@ -1,8 +1,38 @@
|
||||
#include <destrum/Input/InputManager.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
static float NormalizeAxis(Sint16 v, int deadzone) {
|
||||
// Deadzone in raw units. Return [-1..1]
|
||||
const int iv = (int)v;
|
||||
|
||||
if (std::abs(iv) <= deadzone) return 0.0f;
|
||||
|
||||
// Normalize to [-1..1] while preserving sign.
|
||||
// SDL axis range is roughly [-32768..32767]
|
||||
const float denom = (iv < 0) ? 32768.0f : 32767.0f;
|
||||
float f = (float)iv / denom;
|
||||
|
||||
// Optional: re-scale so that value starts at 0 outside deadzone
|
||||
// (nice for sticks; comment out if you prefer raw normalize)
|
||||
const float dz = (float)deadzone / 32767.0f;
|
||||
const float sign = (f < 0.0f) ? -1.0f : 1.0f;
|
||||
const float af = std::abs(f);
|
||||
const float scaled = (af - dz) / (1.0f - dz);
|
||||
return sign * std::clamp(scaled, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void InputManager::Init() {
|
||||
// Nothing mandatory here. You can also enable relative mouse mode, etc.
|
||||
// SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
// Make sure gamecontroller subsystem is active (safe even if already)
|
||||
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||
|
||||
// Open any controllers already connected
|
||||
const int num = SDL_NumJoysticks();
|
||||
for (int i = 0; i < num; ++i) {
|
||||
if (SDL_IsGameController(i)) {
|
||||
AddController(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::BeginFrame() {
|
||||
@@ -21,74 +51,160 @@ void InputManager::BeginFrame() {
|
||||
m_mouseDY = m_mouseY - m_prevMouseY;
|
||||
m_prevMouseX = m_mouseX;
|
||||
m_prevMouseY = m_mouseY;
|
||||
|
||||
// Clear controller "edge" sets + snapshot axes
|
||||
for (auto& [id, pad]: m_pads) {
|
||||
pad.buttonsPressed.clear();
|
||||
pad.buttonsReleased.clear();
|
||||
pad.prevAxes = pad.axes;
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::AddController(int deviceIndex) {
|
||||
if (!SDL_IsGameController(deviceIndex)) return;
|
||||
|
||||
SDL_GameController* gc = SDL_GameControllerOpen(deviceIndex);
|
||||
if (!gc) return;
|
||||
|
||||
SDL_Joystick* joy = SDL_GameControllerGetJoystick(gc);
|
||||
SDL_JoystickID instanceId = SDL_JoystickInstanceID(joy);
|
||||
|
||||
PadState ps;
|
||||
ps.controller = gc;
|
||||
ps.instanceId = instanceId;
|
||||
ps.axes.fill(0);
|
||||
ps.prevAxes.fill(0);
|
||||
|
||||
m_pads[instanceId] = ps;
|
||||
|
||||
// Track order (padIndex mapping)
|
||||
m_padOrder.push_back(instanceId);
|
||||
}
|
||||
|
||||
void InputManager::RemoveController(SDL_JoystickID instanceId) {
|
||||
auto it = m_pads.find(instanceId);
|
||||
if (it != m_pads.end()) {
|
||||
if (it->second.controller) {
|
||||
SDL_GameControllerClose(it->second.controller);
|
||||
}
|
||||
m_pads.erase(it);
|
||||
}
|
||||
|
||||
// Remove from order list
|
||||
m_padOrder.erase(
|
||||
std::remove(m_padOrder.begin(), m_padOrder.end(), instanceId),
|
||||
m_padOrder.end()
|
||||
);
|
||||
}
|
||||
|
||||
void InputManager::ProcessEvent(const SDL_Event& e) {
|
||||
switch (e.type) {
|
||||
case SDL_KEYDOWN: {
|
||||
if (e.key.repeat) break; // avoid repeat spam for "Pressed"
|
||||
if (e.key.repeat) break;
|
||||
SDL_Scancode sc = e.key.keysym.scancode;
|
||||
m_keysDown.insert(sc);
|
||||
m_keysPressed.insert(sc);
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_KEYUP: {
|
||||
SDL_Scancode sc = e.key.keysym.scancode;
|
||||
m_keysDown.erase(sc);
|
||||
m_keysReleased.insert(sc);
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN: {
|
||||
Uint8 b = e.button.button;
|
||||
m_mouseDown.insert(b);
|
||||
m_mousePressed.insert(b);
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
Uint8 b = e.button.button;
|
||||
m_mouseDown.erase(b);
|
||||
m_mouseReleased.insert(b);
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION: {
|
||||
m_mouseX = e.motion.x;
|
||||
m_mouseY = e.motion.y;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEWHEEL: {
|
||||
// Accumulate wheel per frame
|
||||
m_wheelX += e.wheel.x;
|
||||
m_wheelY += e.wheel.y;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_TEXTINPUT: {
|
||||
// text typed this frame
|
||||
m_textInput += e.text.text;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
// ---- Controller hotplug ----
|
||||
case SDL_CONTROLLERDEVICEADDED: {
|
||||
// e.cdevice.which is the device index here
|
||||
AddController(e.cdevice.which);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERDEVICEREMOVED: {
|
||||
// e.cdevice.which is instanceId here
|
||||
RemoveController((SDL_JoystickID)e.cdevice.which);
|
||||
}
|
||||
break;
|
||||
|
||||
// ---- Controller buttons ----
|
||||
case SDL_CONTROLLERBUTTONDOWN: {
|
||||
SDL_JoystickID id = (SDL_JoystickID)e.cbutton.which;
|
||||
auto it = m_pads.find(id);
|
||||
if (it == m_pads.end()) break;
|
||||
|
||||
uint8_t btn = (uint8_t)e.cbutton.button;
|
||||
it->second.buttonsDown.insert(btn);
|
||||
it->second.buttonsPressed.insert(btn);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERBUTTONUP: {
|
||||
SDL_JoystickID id = (SDL_JoystickID)e.cbutton.which;
|
||||
auto it = m_pads.find(id);
|
||||
if (it == m_pads.end()) break;
|
||||
|
||||
uint8_t btn = (uint8_t)e.cbutton.button;
|
||||
it->second.buttonsDown.erase(btn);
|
||||
it->second.buttonsReleased.insert(btn);
|
||||
}
|
||||
break;
|
||||
|
||||
// ---- Controller axes ----
|
||||
case SDL_CONTROLLERAXISMOTION: {
|
||||
SDL_JoystickID id = (SDL_JoystickID)e.caxis.which;
|
||||
auto it = m_pads.find(id);
|
||||
if (it == m_pads.end()) break;
|
||||
|
||||
const int axis = (int)e.caxis.axis;
|
||||
if (axis >= 0 && axis < SDL_CONTROLLER_AXIS_MAX) {
|
||||
it->second.axes[(size_t)axis] = e.caxis.value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
bool InputManager::IsKeyDown(SDL_Scancode sc) const {
|
||||
return m_keysDown.find(sc) != m_keysDown.end();
|
||||
}
|
||||
bool InputManager::WasKeyPressed(SDL_Scancode sc) const {
|
||||
return m_keysPressed.find(sc) != m_keysPressed.end();
|
||||
}
|
||||
bool InputManager::WasKeyReleased(SDL_Scancode sc) const {
|
||||
return m_keysReleased.find(sc) != m_keysReleased.end();
|
||||
}
|
||||
bool InputManager::IsKeyDown(SDL_Scancode sc) const { return m_keysDown.count(sc) != 0; }
|
||||
bool InputManager::WasKeyPressed(SDL_Scancode sc) const { return m_keysPressed.count(sc) != 0; }
|
||||
bool InputManager::WasKeyReleased(SDL_Scancode sc) const { return m_keysReleased.count(sc) != 0; }
|
||||
|
||||
bool InputManager::IsMouseDown(Uint8 button) const {
|
||||
return m_mouseDown.find(button) != m_mouseDown.end();
|
||||
}
|
||||
bool InputManager::WasMousePressed(Uint8 button) const {
|
||||
return m_mousePressed.find(button) != m_mousePressed.end();
|
||||
}
|
||||
bool InputManager::WasMouseReleased(Uint8 button) const {
|
||||
return m_mouseReleased.find(button) != m_mouseReleased.end();
|
||||
}
|
||||
bool InputManager::IsMouseDown(Uint8 button) const { return m_mouseDown.count(button) != 0; }
|
||||
bool InputManager::WasMousePressed(Uint8 button) const { return m_mousePressed.count(button) != 0; }
|
||||
bool InputManager::WasMouseReleased(Uint8 button) const { return m_mouseReleased.count(button) != 0; }
|
||||
|
||||
void InputManager::StartTextInput() {
|
||||
if (!m_textInputActive) {
|
||||
@@ -96,6 +212,7 @@ void InputManager::StartTextInput() {
|
||||
m_textInputActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::StopTextInput() {
|
||||
if (m_textInputActive) {
|
||||
SDL_StopTextInput();
|
||||
@@ -103,6 +220,56 @@ void InputManager::StopTextInput() {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Controller direct queries ----
|
||||
SDL_JoystickID InputManager::GetPadInstanceId(uint8_t padIndex) const {
|
||||
if (padIndex >= m_padOrder.size()) return -1;
|
||||
return m_padOrder[padIndex];
|
||||
}
|
||||
|
||||
bool InputManager::IsPadButtonDown(SDL_JoystickID id, SDL_GameControllerButton btn) const {
|
||||
auto it = m_pads.find(id);
|
||||
if (it == m_pads.end()) return false;
|
||||
return it->second.buttonsDown.count((uint8_t)btn) != 0;
|
||||
}
|
||||
|
||||
bool InputManager::WasPadButtonPressed(SDL_JoystickID id, SDL_GameControllerButton btn) const {
|
||||
auto it = m_pads.find(id);
|
||||
if (it == m_pads.end()) return false;
|
||||
return it->second.buttonsPressed.count((uint8_t)btn) != 0;
|
||||
}
|
||||
|
||||
bool InputManager::WasPadButtonReleased(SDL_JoystickID id, SDL_GameControllerButton btn) const {
|
||||
auto it = m_pads.find(id);
|
||||
if (it == m_pads.end()) return false;
|
||||
return it->second.buttonsReleased.count((uint8_t)btn) != 0;
|
||||
}
|
||||
|
||||
float InputManager::GetPadAxis(SDL_JoystickID id, SDL_GameControllerAxis axis) const {
|
||||
auto it = m_pads.find(id);
|
||||
if (it == m_pads.end()) return 0.0f;
|
||||
const int a = (int)axis;
|
||||
if (a < 0 || a >= SDL_CONTROLLER_AXIS_MAX) return 0.0f;
|
||||
return NormalizeAxis(it->second.axes[(size_t)a], m_axisDeadzone);
|
||||
}
|
||||
|
||||
// ---- Binding helpers ----
|
||||
InputManager::Binding InputManager::PadButton(uint8_t padIndex, SDL_GameControllerButton btn) {
|
||||
Binding b;
|
||||
b.device = Device::GamepadButton;
|
||||
b.code = ((int)padIndex << 8) | ((int)btn & 0xFF);
|
||||
return b;
|
||||
}
|
||||
|
||||
InputManager::Binding InputManager::PadAxis(uint8_t padIndex, SDL_GameControllerAxis axis, int sign, float threshold) {
|
||||
Binding b;
|
||||
b.device = Device::GamepadAxis;
|
||||
b.code = ((int)padIndex << 8) | ((int)axis & 0xFF);
|
||||
b.sign = (sign < 0) ? -1 : +1;
|
||||
b.threshold = threshold;
|
||||
return b;
|
||||
}
|
||||
|
||||
// ---- Action mapping ----
|
||||
void InputManager::BindAction(const std::string& action, const Binding& binding) {
|
||||
m_bindings[action].push_back(binding);
|
||||
}
|
||||
@@ -111,28 +278,67 @@ bool InputManager::QueryBinding(const Binding& b, ButtonState state) const {
|
||||
switch (b.device) {
|
||||
case Device::Keyboard: {
|
||||
auto sc = static_cast<SDL_Scancode>(b.code);
|
||||
if (state == ButtonState::Down) return IsKeyDown(sc);
|
||||
if (state == ButtonState::Pressed) return WasKeyPressed(sc);
|
||||
if (state == ButtonState::Down) return IsKeyDown(sc);
|
||||
if (state == ButtonState::Pressed) return WasKeyPressed(sc);
|
||||
if (state == ButtonState::Released) return WasKeyReleased(sc);
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Device::MouseButton: {
|
||||
auto mb = static_cast<Uint8>(b.code);
|
||||
if (state == ButtonState::Down) return IsMouseDown(mb);
|
||||
if (state == ButtonState::Pressed) return WasMousePressed(mb);
|
||||
if (state == ButtonState::Down) return IsMouseDown(mb);
|
||||
if (state == ButtonState::Pressed) return WasMousePressed(mb);
|
||||
if (state == ButtonState::Released) return WasMouseReleased(mb);
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Device::MouseWheel: {
|
||||
// Encoding:
|
||||
// +1/-1 => wheel Y (up/down), +2/-2 => wheel X (right/left)
|
||||
const int c = b.code;
|
||||
if (state != ButtonState::Pressed) return false; // wheel is "impulse"
|
||||
if (c == 1) return m_wheelY > 0;
|
||||
if (state != ButtonState::Pressed) return false;
|
||||
if (c == 1) return m_wheelY > 0;
|
||||
if (c == -1) return m_wheelY < 0;
|
||||
if (c == 2) return m_wheelX > 0;
|
||||
if (c == 2) return m_wheelX > 0;
|
||||
if (c == -2) return m_wheelX < 0;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Device::GamepadButton: {
|
||||
const uint8_t padIndex = GetPadIndexFromCode(b.code);
|
||||
const uint8_t btn = GetLow8FromCode(b.code);
|
||||
|
||||
const SDL_JoystickID id = GetPadInstanceId(padIndex);
|
||||
if (id < 0) return false;
|
||||
|
||||
if (state == ButtonState::Down) return IsPadButtonDown(id, (SDL_GameControllerButton)btn);
|
||||
if (state == ButtonState::Pressed) return WasPadButtonPressed(id, (SDL_GameControllerButton)btn);
|
||||
if (state == ButtonState::Released) return WasPadButtonReleased(id, (SDL_GameControllerButton)btn);
|
||||
}
|
||||
break;
|
||||
|
||||
case Device::GamepadAxis: {
|
||||
// Axis bindings are treated as "digital" by thresholding
|
||||
const uint8_t padIndex = GetPadIndexFromCode(b.code);
|
||||
const uint8_t axis = GetLow8FromCode(b.code);
|
||||
|
||||
const SDL_JoystickID id = GetPadInstanceId(padIndex);
|
||||
if (id < 0) return false;
|
||||
|
||||
auto it = m_pads.find(id);
|
||||
if (it == m_pads.end()) return false;
|
||||
|
||||
if (axis >= SDL_CONTROLLER_AXIS_MAX) return false;
|
||||
|
||||
const float cur = NormalizeAxis(it->second.axes[axis], m_axisDeadzone) * (float)b.sign;
|
||||
const float prev = NormalizeAxis(it->second.prevAxes[axis], m_axisDeadzone) * (float)b.sign;
|
||||
|
||||
const bool curOn = cur >= b.threshold;
|
||||
const bool prevOn = prev >= b.threshold;
|
||||
|
||||
if (state == ButtonState::Down) return curOn;
|
||||
if (state == ButtonState::Pressed) return (curOn && !prevOn);
|
||||
if (state == ButtonState::Released) return (!curOn && prevOn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -141,7 +347,7 @@ bool InputManager::GetAction(const std::string& action, ButtonState state) const
|
||||
auto it = m_bindings.find(action);
|
||||
if (it == m_bindings.end()) return false;
|
||||
|
||||
for (const auto& b : it->second) {
|
||||
for (const auto& b: it->second) {
|
||||
if (QueryBinding(b, state)) return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user