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.
pull/3750/head
Stenzek 4 months ago
parent cae25968fe
commit 1e6a006bd0
No known key found for this signature in database

@ -186,40 +186,36 @@ void AnalogController::SetBindState(u32 index, float value)
m_half_axis_state[sub_index] = u8_value; m_half_axis_state[sub_index] = u8_value;
#define MERGE(pos, neg) \
((m_half_axis_state[static_cast<u32>(pos)] != 0) ? (127u + ((m_half_axis_state[static_cast<u32>(pos)] + 1u) / 2u)) : \
(127u - (m_half_axis_state[static_cast<u32>(neg)] / 2u)))
const auto prev_axis_state = m_axis_state; const auto prev_axis_state = m_axis_state;
switch (static_cast<HalfAxis>(sub_index)) switch (static_cast<HalfAxis>(sub_index))
{ {
case HalfAxis::LLeft: case HalfAxis::LLeft:
case HalfAxis::LRight: case HalfAxis::LRight:
m_axis_state[static_cast<u8>(Axis::LeftX)] = ((m_invert_left_stick & 1u) != 0u) ? m_axis_state[static_cast<u8>(Axis::LeftX)] =
MERGE(HalfAxis::LLeft, HalfAxis::LRight) : MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)],
MERGE(HalfAxis::LRight, HalfAxis::LLeft); m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)], (m_invert_left_stick & 1));
break; break;
case HalfAxis::LDown: case HalfAxis::LDown:
case HalfAxis::LUp: case HalfAxis::LUp:
m_axis_state[static_cast<u8>(Axis::LeftY)] = ((m_invert_left_stick & 2u) != 0u) ? m_axis_state[static_cast<u8>(Axis::LeftY)] =
MERGE(HalfAxis::LUp, HalfAxis::LDown) : MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)],
MERGE(HalfAxis::LDown, HalfAxis::LUp); m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)], (m_invert_left_stick & 2));
break; break;
case HalfAxis::RLeft: case HalfAxis::RLeft:
case HalfAxis::RRight: case HalfAxis::RRight:
m_axis_state[static_cast<u8>(Axis::RightX)] = ((m_invert_right_stick & 1u) != 0u) ? m_axis_state[static_cast<u8>(Axis::RightX)] =
MERGE(HalfAxis::RLeft, HalfAxis::RRight) : MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)],
MERGE(HalfAxis::RRight, HalfAxis::RLeft); m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)], (m_invert_right_stick & 1));
break; break;
case HalfAxis::RDown: case HalfAxis::RDown:
case HalfAxis::RUp: case HalfAxis::RUp:
m_axis_state[static_cast<u8>(Axis::RightY)] = ((m_invert_right_stick & 2u) != 0u) ? m_axis_state[static_cast<u8>(Axis::RightY)] =
MERGE(HalfAxis::RUp, HalfAxis::RDown) : MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)],
MERGE(HalfAxis::RDown, HalfAxis::RUp); m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)], (m_invert_right_stick & 2));
break; break;
default: default:
@ -228,43 +224,38 @@ void AnalogController::SetBindState(u32 index, float value)
if (m_analog_deadzone > 0.0f) if (m_analog_deadzone > 0.0f)
{ {
#define MERGE_F(pos, neg) \
((m_half_axis_state[static_cast<u32>(pos)] != 0) ? \
(static_cast<float>(m_half_axis_state[static_cast<u32>(pos)]) / 255.0f) : \
(static_cast<float>(m_half_axis_state[static_cast<u32>(neg)]) / -255.0f))
float pos_x, pos_y; float pos_x, pos_y;
if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft) if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft)
{ {
pos_x = ((m_invert_left_stick & 1u) != 0u) ? MERGE_F(HalfAxis::LLeft, HalfAxis::LRight) : pos_x =
MERGE_F(HalfAxis::LRight, HalfAxis::LLeft); MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)],
pos_y = ((m_invert_left_stick & 2u) != 0u) ? MERGE_F(HalfAxis::LUp, HalfAxis::LDown) : m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)], (m_invert_left_stick & 1));
MERGE_F(HalfAxis::LDown, HalfAxis::LUp); pos_y =
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)],
m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)], (m_invert_left_stick & 2));
} }
else else
{ {
pos_x = ((m_invert_right_stick & 1u) != 0u) ? MERGE_F(HalfAxis::RLeft, HalfAxis::RRight) : pos_x =
MERGE_F(HalfAxis::RRight, HalfAxis::RLeft); MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)],
pos_y = ((m_invert_right_stick & 2u) != 0u) ? MERGE_F(HalfAxis::RUp, HalfAxis::RDown) : m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)], (m_invert_right_stick & 1));
MERGE_F(HalfAxis::RDown, HalfAxis::RUp); pos_y =
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)],
m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)], (m_invert_right_stick & 2));
} }
if (InCircularDeadzone(m_analog_deadzone, pos_x, pos_y)) if (InCircularDeadzone(m_analog_deadzone, pos_x, pos_y))
{ {
// Set to 127 (center).
if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft) if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft)
m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = 127; m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = AXIS_CENTER;
else else
m_axis_state[static_cast<u8>(Axis::RightX)] = m_axis_state[static_cast<u8>(Axis::RightY)] = 127; m_axis_state[static_cast<u8>(Axis::RightX)] = m_axis_state[static_cast<u8>(Axis::RightY)] = AXIS_CENTER;
} }
#undef MERGE_F
} }
if (std::memcmp(m_axis_state.data(), prev_axis_state.data(), m_axis_state.size()) != 0) if (std::memcmp(m_axis_state.data(), prev_axis_state.data(), m_axis_state.size()) != 0)
System::SetRunaheadReplayFlag(true); System::SetRunaheadReplayFlag(true);
#undef MERGE
return; return;
} }

@ -106,40 +106,36 @@ void AnalogJoystick::SetBindState(u32 index, float value)
m_half_axis_state[sub_index] = u8_value; m_half_axis_state[sub_index] = u8_value;
#define MERGE(pos, neg) \
((m_half_axis_state[static_cast<u32>(pos)] != 0) ? (127u + ((m_half_axis_state[static_cast<u32>(pos)] + 1u) / 2u)) : \
(127u - (m_half_axis_state[static_cast<u32>(neg)] / 2u)))
const auto prev_axis_state = m_axis_state; const auto prev_axis_state = m_axis_state;
switch (static_cast<HalfAxis>(sub_index)) switch (static_cast<HalfAxis>(sub_index))
{ {
case HalfAxis::LLeft: case HalfAxis::LLeft:
case HalfAxis::LRight: case HalfAxis::LRight:
m_axis_state[static_cast<u8>(Axis::LeftX)] = ((m_invert_left_stick & 1u) != 0u) ? m_axis_state[static_cast<u8>(Axis::LeftX)] =
MERGE(HalfAxis::LLeft, HalfAxis::LRight) : MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)],
MERGE(HalfAxis::LRight, HalfAxis::LLeft); m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)], (m_invert_left_stick & 1));
break; break;
case HalfAxis::LDown: case HalfAxis::LDown:
case HalfAxis::LUp: case HalfAxis::LUp:
m_axis_state[static_cast<u8>(Axis::LeftY)] = ((m_invert_left_stick & 2u) != 0u) ? m_axis_state[static_cast<u8>(Axis::LeftY)] =
MERGE(HalfAxis::LUp, HalfAxis::LDown) : MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)],
MERGE(HalfAxis::LDown, HalfAxis::LUp); m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)], (m_invert_left_stick & 2));
break; break;
case HalfAxis::RLeft: case HalfAxis::RLeft:
case HalfAxis::RRight: case HalfAxis::RRight:
m_axis_state[static_cast<u8>(Axis::RightX)] = ((m_invert_right_stick & 1u) != 0u) ? m_axis_state[static_cast<u8>(Axis::RightX)] =
MERGE(HalfAxis::RLeft, HalfAxis::RRight) : MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)],
MERGE(HalfAxis::RRight, HalfAxis::RLeft); m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)], (m_invert_right_stick & 1));
break; break;
case HalfAxis::RDown: case HalfAxis::RDown:
case HalfAxis::RUp: case HalfAxis::RUp:
m_axis_state[static_cast<u8>(Axis::RightY)] = ((m_invert_right_stick & 2u) != 0u) ? m_axis_state[static_cast<u8>(Axis::RightY)] =
MERGE(HalfAxis::RUp, HalfAxis::RDown) : MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)],
MERGE(HalfAxis::RDown, HalfAxis::RUp); m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)], (m_invert_right_stick & 2));
break; break;
default: default:
@ -148,43 +144,38 @@ void AnalogJoystick::SetBindState(u32 index, float value)
if (m_analog_deadzone > 0.0f) if (m_analog_deadzone > 0.0f)
{ {
#define MERGE_F(pos, neg) \
((m_half_axis_state[static_cast<u32>(pos)] != 0) ? \
(static_cast<float>(m_half_axis_state[static_cast<u32>(pos)]) / 255.0f) : \
(static_cast<float>(m_half_axis_state[static_cast<u32>(neg)]) / -255.0f))
float pos_x, pos_y; float pos_x, pos_y;
if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft) if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft)
{ {
pos_x = ((m_invert_left_stick & 1u) != 0u) ? MERGE_F(HalfAxis::LLeft, HalfAxis::LRight) : pos_x =
MERGE_F(HalfAxis::LRight, HalfAxis::LLeft); MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LLeft)],
pos_y = ((m_invert_left_stick & 2u) != 0u) ? MERGE_F(HalfAxis::LUp, HalfAxis::LDown) : m_half_axis_state[static_cast<size_t>(HalfAxis::LRight)], (m_invert_left_stick & 1));
MERGE_F(HalfAxis::LDown, HalfAxis::LUp); pos_y =
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::LUp)],
m_half_axis_state[static_cast<size_t>(HalfAxis::LDown)], (m_invert_left_stick & 2));
} }
else else
{ {
pos_x = ((m_invert_right_stick & 1u) != 0u) ? MERGE_F(HalfAxis::RLeft, HalfAxis::RRight) : pos_x =
MERGE_F(HalfAxis::RRight, HalfAxis::RLeft); MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RLeft)],
pos_y = ((m_invert_right_stick & 2u) != 0u) ? MERGE_F(HalfAxis::RUp, HalfAxis::RDown) : m_half_axis_state[static_cast<size_t>(HalfAxis::RRight)], (m_invert_right_stick & 1));
MERGE_F(HalfAxis::RDown, HalfAxis::RUp); pos_y =
MergeHalfAxesToFloat(m_half_axis_state[static_cast<size_t>(HalfAxis::RUp)],
m_half_axis_state[static_cast<size_t>(HalfAxis::RDown)], (m_invert_right_stick & 2));
} }
if (InCircularDeadzone(m_analog_deadzone, pos_x, pos_y)) if (InCircularDeadzone(m_analog_deadzone, pos_x, pos_y))
{ {
// Set to 127 (center).
if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft) if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft)
m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = 127; m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = AXIS_CENTER;
else else
m_axis_state[static_cast<u8>(Axis::RightX)] = m_axis_state[static_cast<u8>(Axis::RightY)] = 127; m_axis_state[static_cast<u8>(Axis::RightX)] = m_axis_state[static_cast<u8>(Axis::RightY)] = AXIS_CENTER;
} }
#undef MERGE_F
} }
if (std::memcmp(m_axis_state.data(), prev_axis_state.data(), m_axis_state.size()) != 0) if (std::memcmp(m_axis_state.data(), prev_axis_state.data(), m_axis_state.size()) != 0)
System::SetRunaheadReplayFlag(true); System::SetRunaheadReplayFlag(true);
#undef MERGE
return; return;
} }

@ -239,3 +239,17 @@ bool Controller::CanStartInAnalogMode(ControllerType ctype)
return ((dbentry->supported_controllers & (1u << static_cast<u8>(ctype))) != 0 && return ((dbentry->supported_controllers & (1u << static_cast<u8>(ctype))) != 0 &&
!dbentry->HasTrait(GameDatabase::Trait::DisableAutoAnalogMode)); !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<u8>(128 + (static_cast<u32>(pos_value) / 2) - ((static_cast<u32>(neg_value) + 1) / 2));
}
float Controller::MergeHalfAxesToFloat(u8 neg_value, u8 pos_value, bool invert)
{
const float result = (static_cast<s32>(pos_value) - static_cast<s32>(neg_value)) / 255.0f;
return (invert ? -result : result);
}

@ -53,6 +53,9 @@ public:
static constexpr float DEFAULT_STICK_SENSITIVITY = 1.33f; static constexpr float DEFAULT_STICK_SENSITIVITY = 1.33f;
static constexpr float DEFAULT_BUTTON_DEADZONE = 0.25f; 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); explicit Controller(u32 index);
virtual ~Controller(); virtual ~Controller();
@ -119,6 +122,12 @@ public:
/// Returns true if automatic analog mode can be used. /// Returns true if automatic analog mode can be used.
static bool CanStartInAnalogMode(ControllerType ctype); 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: protected:
u32 m_index; u32 m_index;
}; };

@ -171,10 +171,9 @@ void NeGconRumble::SetBindState(u32 index, float value)
m_half_axis_state[index - static_cast<u32>(Button::Count)] = m_half_axis_state[index - static_cast<u32>(Button::Count)] =
static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f)); static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
// Merge left/right. Seems to be inverted.
m_axis_state[static_cast<u32>(Axis::Steering)] = m_axis_state[static_cast<u32>(Axis::Steering)] =
((m_half_axis_state[1] != 0) ? (127u + ((m_half_axis_state[1] + 1u) / 2u)) : MergeHalfAxes(m_half_axis_state[static_cast<size_t>(HalfAxis::SteeringLeft)],
(127u - (m_half_axis_state[0] / 2u))); m_half_axis_state[static_cast<size_t>(HalfAxis::SteeringRight)], false);
} }
else if (index >= static_cast<u32>(Button::Count)) else if (index >= static_cast<u32>(Button::Count))
{ {

Loading…
Cancel
Save