From 1e6a006bd02dfbe5c9f5bd186ac7954359642d32 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 8 Jun 2026 19:09:40 +1000 Subject: [PATCH] Controller: Make center a distinct value Instead of attaching 128 to one side of the axis, make 128 the all-zeros/center position, which leaves exactly 127 positions on either side. Fixes the center not being reported as center in padtest. --- src/core/analog_controller.cpp | 61 +++++++++++++++------------------- src/core/analog_joystick.cpp | 61 +++++++++++++++------------------- src/core/controller.cpp | 14 ++++++++ src/core/controller.h | 9 +++++ src/core/negcon_rumble.cpp | 5 ++- 5 files changed, 77 insertions(+), 73 deletions(-) diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index bdc8016c9..6d4478619 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -186,40 +186,36 @@ void AnalogController::SetBindState(u32 index, float value) m_half_axis_state[sub_index] = u8_value; -#define MERGE(pos, neg) \ - ((m_half_axis_state[static_cast(pos)] != 0) ? (127u + ((m_half_axis_state[static_cast(pos)] + 1u) / 2u)) : \ - (127u - (m_half_axis_state[static_cast(neg)] / 2u))) - const auto prev_axis_state = m_axis_state; switch (static_cast(sub_index)) { case HalfAxis::LLeft: case HalfAxis::LRight: - m_axis_state[static_cast(Axis::LeftX)] = ((m_invert_left_stick & 1u) != 0u) ? - MERGE(HalfAxis::LLeft, HalfAxis::LRight) : - MERGE(HalfAxis::LRight, HalfAxis::LLeft); + m_axis_state[static_cast(Axis::LeftX)] = + MergeHalfAxes(m_half_axis_state[static_cast(HalfAxis::LLeft)], + m_half_axis_state[static_cast(HalfAxis::LRight)], (m_invert_left_stick & 1)); break; case HalfAxis::LDown: case HalfAxis::LUp: - m_axis_state[static_cast(Axis::LeftY)] = ((m_invert_left_stick & 2u) != 0u) ? - MERGE(HalfAxis::LUp, HalfAxis::LDown) : - MERGE(HalfAxis::LDown, HalfAxis::LUp); + m_axis_state[static_cast(Axis::LeftY)] = + MergeHalfAxes(m_half_axis_state[static_cast(HalfAxis::LUp)], + m_half_axis_state[static_cast(HalfAxis::LDown)], (m_invert_left_stick & 2)); break; case HalfAxis::RLeft: case HalfAxis::RRight: - m_axis_state[static_cast(Axis::RightX)] = ((m_invert_right_stick & 1u) != 0u) ? - MERGE(HalfAxis::RLeft, HalfAxis::RRight) : - MERGE(HalfAxis::RRight, HalfAxis::RLeft); + m_axis_state[static_cast(Axis::RightX)] = + MergeHalfAxes(m_half_axis_state[static_cast(HalfAxis::RLeft)], + m_half_axis_state[static_cast(HalfAxis::RRight)], (m_invert_right_stick & 1)); break; case HalfAxis::RDown: case HalfAxis::RUp: - m_axis_state[static_cast(Axis::RightY)] = ((m_invert_right_stick & 2u) != 0u) ? - MERGE(HalfAxis::RUp, HalfAxis::RDown) : - MERGE(HalfAxis::RDown, HalfAxis::RUp); + m_axis_state[static_cast(Axis::RightY)] = + MergeHalfAxes(m_half_axis_state[static_cast(HalfAxis::RUp)], + m_half_axis_state[static_cast(HalfAxis::RDown)], (m_invert_right_stick & 2)); break; default: @@ -228,43 +224,38 @@ void AnalogController::SetBindState(u32 index, float value) if (m_analog_deadzone > 0.0f) { -#define MERGE_F(pos, neg) \ - ((m_half_axis_state[static_cast(pos)] != 0) ? \ - (static_cast(m_half_axis_state[static_cast(pos)]) / 255.0f) : \ - (static_cast(m_half_axis_state[static_cast(neg)]) / -255.0f)) - float pos_x, pos_y; if (static_cast(sub_index) < HalfAxis::RLeft) { - pos_x = ((m_invert_left_stick & 1u) != 0u) ? MERGE_F(HalfAxis::LLeft, HalfAxis::LRight) : - MERGE_F(HalfAxis::LRight, HalfAxis::LLeft); - pos_y = ((m_invert_left_stick & 2u) != 0u) ? MERGE_F(HalfAxis::LUp, HalfAxis::LDown) : - MERGE_F(HalfAxis::LDown, HalfAxis::LUp); + pos_x = + MergeHalfAxesToFloat(m_half_axis_state[static_cast(HalfAxis::LLeft)], + m_half_axis_state[static_cast(HalfAxis::LRight)], (m_invert_left_stick & 1)); + pos_y = + MergeHalfAxesToFloat(m_half_axis_state[static_cast(HalfAxis::LUp)], + m_half_axis_state[static_cast(HalfAxis::LDown)], (m_invert_left_stick & 2)); } else { - pos_x = ((m_invert_right_stick & 1u) != 0u) ? MERGE_F(HalfAxis::RLeft, HalfAxis::RRight) : - MERGE_F(HalfAxis::RRight, HalfAxis::RLeft); - pos_y = ((m_invert_right_stick & 2u) != 0u) ? MERGE_F(HalfAxis::RUp, HalfAxis::RDown) : - MERGE_F(HalfAxis::RDown, HalfAxis::RUp); + pos_x = + MergeHalfAxesToFloat(m_half_axis_state[static_cast(HalfAxis::RLeft)], + m_half_axis_state[static_cast(HalfAxis::RRight)], (m_invert_right_stick & 1)); + pos_y = + MergeHalfAxesToFloat(m_half_axis_state[static_cast(HalfAxis::RUp)], + m_half_axis_state[static_cast(HalfAxis::RDown)], (m_invert_right_stick & 2)); } if (InCircularDeadzone(m_analog_deadzone, pos_x, pos_y)) { - // Set to 127 (center). if (static_cast(sub_index) < HalfAxis::RLeft) - m_axis_state[static_cast(Axis::LeftX)] = m_axis_state[static_cast(Axis::LeftY)] = 127; + m_axis_state[static_cast(Axis::LeftX)] = m_axis_state[static_cast(Axis::LeftY)] = AXIS_CENTER; else - m_axis_state[static_cast(Axis::RightX)] = m_axis_state[static_cast(Axis::RightY)] = 127; + m_axis_state[static_cast(Axis::RightX)] = m_axis_state[static_cast(Axis::RightY)] = AXIS_CENTER; } -#undef MERGE_F } if (std::memcmp(m_axis_state.data(), prev_axis_state.data(), m_axis_state.size()) != 0) System::SetRunaheadReplayFlag(true); -#undef MERGE - return; } diff --git a/src/core/analog_joystick.cpp b/src/core/analog_joystick.cpp index 15beafbaf..a8bff5994 100644 --- a/src/core/analog_joystick.cpp +++ b/src/core/analog_joystick.cpp @@ -106,40 +106,36 @@ void AnalogJoystick::SetBindState(u32 index, float value) m_half_axis_state[sub_index] = u8_value; -#define MERGE(pos, neg) \ - ((m_half_axis_state[static_cast(pos)] != 0) ? (127u + ((m_half_axis_state[static_cast(pos)] + 1u) / 2u)) : \ - (127u - (m_half_axis_state[static_cast(neg)] / 2u))) - const auto prev_axis_state = m_axis_state; switch (static_cast(sub_index)) { case HalfAxis::LLeft: case HalfAxis::LRight: - m_axis_state[static_cast(Axis::LeftX)] = ((m_invert_left_stick & 1u) != 0u) ? - MERGE(HalfAxis::LLeft, HalfAxis::LRight) : - MERGE(HalfAxis::LRight, HalfAxis::LLeft); + m_axis_state[static_cast(Axis::LeftX)] = + MergeHalfAxes(m_half_axis_state[static_cast(HalfAxis::LLeft)], + m_half_axis_state[static_cast(HalfAxis::LRight)], (m_invert_left_stick & 1)); break; case HalfAxis::LDown: case HalfAxis::LUp: - m_axis_state[static_cast(Axis::LeftY)] = ((m_invert_left_stick & 2u) != 0u) ? - MERGE(HalfAxis::LUp, HalfAxis::LDown) : - MERGE(HalfAxis::LDown, HalfAxis::LUp); + m_axis_state[static_cast(Axis::LeftY)] = + MergeHalfAxes(m_half_axis_state[static_cast(HalfAxis::LUp)], + m_half_axis_state[static_cast(HalfAxis::LDown)], (m_invert_left_stick & 2)); break; case HalfAxis::RLeft: case HalfAxis::RRight: - m_axis_state[static_cast(Axis::RightX)] = ((m_invert_right_stick & 1u) != 0u) ? - MERGE(HalfAxis::RLeft, HalfAxis::RRight) : - MERGE(HalfAxis::RRight, HalfAxis::RLeft); + m_axis_state[static_cast(Axis::RightX)] = + MergeHalfAxes(m_half_axis_state[static_cast(HalfAxis::RLeft)], + m_half_axis_state[static_cast(HalfAxis::RRight)], (m_invert_right_stick & 1)); break; case HalfAxis::RDown: case HalfAxis::RUp: - m_axis_state[static_cast(Axis::RightY)] = ((m_invert_right_stick & 2u) != 0u) ? - MERGE(HalfAxis::RUp, HalfAxis::RDown) : - MERGE(HalfAxis::RDown, HalfAxis::RUp); + m_axis_state[static_cast(Axis::RightY)] = + MergeHalfAxes(m_half_axis_state[static_cast(HalfAxis::RUp)], + m_half_axis_state[static_cast(HalfAxis::RDown)], (m_invert_right_stick & 2)); break; default: @@ -148,43 +144,38 @@ void AnalogJoystick::SetBindState(u32 index, float value) if (m_analog_deadzone > 0.0f) { -#define MERGE_F(pos, neg) \ - ((m_half_axis_state[static_cast(pos)] != 0) ? \ - (static_cast(m_half_axis_state[static_cast(pos)]) / 255.0f) : \ - (static_cast(m_half_axis_state[static_cast(neg)]) / -255.0f)) - float pos_x, pos_y; if (static_cast(sub_index) < HalfAxis::RLeft) { - pos_x = ((m_invert_left_stick & 1u) != 0u) ? MERGE_F(HalfAxis::LLeft, HalfAxis::LRight) : - MERGE_F(HalfAxis::LRight, HalfAxis::LLeft); - pos_y = ((m_invert_left_stick & 2u) != 0u) ? MERGE_F(HalfAxis::LUp, HalfAxis::LDown) : - MERGE_F(HalfAxis::LDown, HalfAxis::LUp); + pos_x = + MergeHalfAxesToFloat(m_half_axis_state[static_cast(HalfAxis::LLeft)], + m_half_axis_state[static_cast(HalfAxis::LRight)], (m_invert_left_stick & 1)); + pos_y = + MergeHalfAxesToFloat(m_half_axis_state[static_cast(HalfAxis::LUp)], + m_half_axis_state[static_cast(HalfAxis::LDown)], (m_invert_left_stick & 2)); } else { - pos_x = ((m_invert_right_stick & 1u) != 0u) ? MERGE_F(HalfAxis::RLeft, HalfAxis::RRight) : - MERGE_F(HalfAxis::RRight, HalfAxis::RLeft); - pos_y = ((m_invert_right_stick & 2u) != 0u) ? MERGE_F(HalfAxis::RUp, HalfAxis::RDown) : - MERGE_F(HalfAxis::RDown, HalfAxis::RUp); + pos_x = + MergeHalfAxesToFloat(m_half_axis_state[static_cast(HalfAxis::RLeft)], + m_half_axis_state[static_cast(HalfAxis::RRight)], (m_invert_right_stick & 1)); + pos_y = + MergeHalfAxesToFloat(m_half_axis_state[static_cast(HalfAxis::RUp)], + m_half_axis_state[static_cast(HalfAxis::RDown)], (m_invert_right_stick & 2)); } if (InCircularDeadzone(m_analog_deadzone, pos_x, pos_y)) { - // Set to 127 (center). if (static_cast(sub_index) < HalfAxis::RLeft) - m_axis_state[static_cast(Axis::LeftX)] = m_axis_state[static_cast(Axis::LeftY)] = 127; + m_axis_state[static_cast(Axis::LeftX)] = m_axis_state[static_cast(Axis::LeftY)] = AXIS_CENTER; else - m_axis_state[static_cast(Axis::RightX)] = m_axis_state[static_cast(Axis::RightY)] = 127; + m_axis_state[static_cast(Axis::RightX)] = m_axis_state[static_cast(Axis::RightY)] = AXIS_CENTER; } -#undef MERGE_F } if (std::memcmp(m_axis_state.data(), prev_axis_state.data(), m_axis_state.size()) != 0) System::SetRunaheadReplayFlag(true); -#undef MERGE - return; } diff --git a/src/core/controller.cpp b/src/core/controller.cpp index f9f587377..f5a3a0503 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -239,3 +239,17 @@ bool Controller::CanStartInAnalogMode(ControllerType ctype) return ((dbentry->supported_controllers & (1u << static_cast(ctype))) != 0 && !dbentry->HasTrait(GameDatabase::Trait::DisableAutoAnalogMode)); } + +u8 Controller::MergeHalfAxes(u8 neg_value, u8 pos_value, bool invert) +{ + if (invert) + std::swap(neg_value, pos_value); + + return static_cast(128 + (static_cast(pos_value) / 2) - ((static_cast(neg_value) + 1) / 2)); +} + +float Controller::MergeHalfAxesToFloat(u8 neg_value, u8 pos_value, bool invert) +{ + const float result = (static_cast(pos_value) - static_cast(neg_value)) / 255.0f; + return (invert ? -result : result); +} diff --git a/src/core/controller.h b/src/core/controller.h index 7dd1376fc..bf6cfe449 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -53,6 +53,9 @@ public: static constexpr float DEFAULT_STICK_SENSITIVITY = 1.33f; static constexpr float DEFAULT_BUTTON_DEADZONE = 0.25f; + /// Center position when dealing with axis values. + static constexpr u8 AXIS_CENTER = 0x80; + explicit Controller(u32 index); virtual ~Controller(); @@ -119,6 +122,12 @@ public: /// Returns true if automatic analog mode can be used. static bool CanStartInAnalogMode(ControllerType ctype); + /// Converts a 0..255 half-axis value to an unsigned 8-bit value, with 128 indicating center. + static u8 MergeHalfAxes(u8 neg_value, u8 pos_value, bool invert); + + /// Converts a 0..255 half-axis value to a normalized floating-point value, with 0 indicating center. + static float MergeHalfAxesToFloat(u8 neg_value, u8 pos_value, bool invert); + protected: u32 m_index; }; diff --git a/src/core/negcon_rumble.cpp b/src/core/negcon_rumble.cpp index de8a817da..efb7d88d3 100644 --- a/src/core/negcon_rumble.cpp +++ b/src/core/negcon_rumble.cpp @@ -171,10 +171,9 @@ void NeGconRumble::SetBindState(u32 index, float value) m_half_axis_state[index - static_cast(Button::Count)] = static_cast(std::clamp(value * 255.0f, 0.0f, 255.0f)); - // Merge left/right. Seems to be inverted. m_axis_state[static_cast(Axis::Steering)] = - ((m_half_axis_state[1] != 0) ? (127u + ((m_half_axis_state[1] + 1u) / 2u)) : - (127u - (m_half_axis_state[0] / 2u))); + MergeHalfAxes(m_half_axis_state[static_cast(HalfAxis::SteeringLeft)], + m_half_axis_state[static_cast(HalfAxis::SteeringRight)], false); } else if (index >= static_cast(Button::Count)) {