diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index fa939f92c..efbb023c9 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -157,8 +157,7 @@ void AnalogController::SetBindState(u32 index, float value) if (sub_index >= static_cast(m_half_axis_state.size())) return; - value = ApplyAnalogDeadzoneSensitivity(m_analog_deadzone, m_analog_sensitivity, value); - const u8 u8_value = static_cast(std::clamp(value * 255.0f, 0.0f, 255.0f)); + const u8 u8_value = static_cast(std::clamp(value * m_analog_sensitivity * 255.0f, 0.0f, 255.0f)); if (u8_value == m_half_axis_state[sub_index]) return; @@ -168,7 +167,6 @@ void AnalogController::SetBindState(u32 index, float 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))) - switch (static_cast(sub_index)) { case HalfAxis::LLeft: @@ -203,6 +201,41 @@ void AnalogController::SetBindState(u32 index, float value) break; } + 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); + } + 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); + } + + 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; + else + m_axis_state[static_cast(Axis::RightX)] = m_axis_state[static_cast(Axis::RightY)] = 127; + } +#undef MERGE_F + } + #undef MERGE return; @@ -828,7 +861,6 @@ static const SettingInfo s_settings[] = { "Inverts the direction of the left analog stick.", "0", "0", "3", nullptr, nullptr, s_invert_settings, 0.0f}, {SettingInfo::Type::IntegerList, "InvertRightStick", "Invert Right Stick", "Inverts the direction of the right analog stick.", "0", "0", "3", nullptr, nullptr, s_invert_settings, 0.0f}, - }; const Controller::ControllerInfo AnalogController::INFO = {ControllerType::AnalogController, diff --git a/src/core/analog_joystick.cpp b/src/core/analog_joystick.cpp index a0714cf17..4a87cfbee 100644 --- a/src/core/analog_joystick.cpp +++ b/src/core/analog_joystick.cpp @@ -99,8 +99,7 @@ void AnalogJoystick::SetBindState(u32 index, float value) if (sub_index >= static_cast(m_half_axis_state.size())) return; - value = ApplyAnalogDeadzoneSensitivity(m_analog_deadzone, m_analog_sensitivity, value); - const u8 u8_value = static_cast(std::clamp(value * 255.0f, 0.0f, 255.0f)); + const u8 u8_value = static_cast(std::clamp(value * m_analog_sensitivity * 255.0f, 0.0f, 255.0f)); if (u8_value != m_half_axis_state[sub_index]) System::SetRunaheadReplayFlag(); @@ -114,28 +113,71 @@ void AnalogJoystick::SetBindState(u32 index, float value) { case HalfAxis::LLeft: case HalfAxis::LRight: - m_axis_state[static_cast(Axis::LeftX)] = MERGE(HalfAxis::LRight, HalfAxis::LLeft); + m_axis_state[static_cast(Axis::LeftX)] = ((m_invert_left_stick & 1u) != 0u) ? + MERGE(HalfAxis::LLeft, HalfAxis::LRight) : + MERGE(HalfAxis::LRight, HalfAxis::LLeft); break; case HalfAxis::LDown: case HalfAxis::LUp: - m_axis_state[static_cast(Axis::LeftY)] = MERGE(HalfAxis::LDown, 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); break; case HalfAxis::RLeft: case HalfAxis::RRight: - m_axis_state[static_cast(Axis::RightX)] = MERGE(HalfAxis::RRight, HalfAxis::RLeft); + m_axis_state[static_cast(Axis::RightX)] = ((m_invert_right_stick & 1u) != 0u) ? + MERGE(HalfAxis::RLeft, HalfAxis::RRight) : + MERGE(HalfAxis::RRight, HalfAxis::RLeft); break; case HalfAxis::RDown: case HalfAxis::RUp: - m_axis_state[static_cast(Axis::RightY)] = MERGE(HalfAxis::RDown, 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); break; default: break; } + 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); + } + 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); + } + + 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; + else + m_axis_state[static_cast(Axis::RightX)] = m_axis_state[static_cast(Axis::RightY)] = 127; + } +#undef MERGE_F + } + #undef MERGE return; @@ -329,6 +371,11 @@ static const Controller::ControllerBindingInfo s_binding_info[] = { #undef BUTTON }; +static const char* s_invert_settings[] = {TRANSLATABLE("AnalogJoystick", "Not Inverted"), + TRANSLATABLE("AnalogJoystick", "Invert Left/Right"), + TRANSLATABLE("AnalogJoystick", "Invert Up/Down"), + TRANSLATABLE("AnalogJoystick", "Invert Left/Right + Up/Down"), nullptr}; + static const SettingInfo s_settings[] = { {SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogJoystick", "Analog Deadzone"), TRANSLATABLE("AnalogJoystick", @@ -339,7 +386,12 @@ static const SettingInfo s_settings[] = { "AnalogJoystick", "Sets the analog stick axis scaling factor. A value between 130% and 140% is recommended when using recent " "controllers, e.g. DualShock 4, Xbox One Controller."), - "1.33f", "0.01f", "2.00f", "0.01f", "%.0f%%", nullptr, 100.0f}}; + "1.33f", "0.01f", "2.00f", "0.01f", "%.0f%%", nullptr, 100.0f}, + {SettingInfo::Type::IntegerList, "InvertLeftStick", "Invert Left Stick", + "Inverts the direction of the left analog stick.", "0", "0", "3", nullptr, nullptr, s_invert_settings, 0.0f}, + {SettingInfo::Type::IntegerList, "InvertRightStick", "Invert Right Stick", + "Inverts the direction of the right analog stick.", "0", "0", "3", nullptr, nullptr, s_invert_settings, 0.0f}, +}; const Controller::ControllerInfo AnalogJoystick::INFO = {ControllerType::AnalogJoystick, "AnalogJoystick", @@ -356,4 +408,6 @@ void AnalogJoystick::LoadSettings(SettingsInterface& si, const char* section) m_analog_deadzone = std::clamp(si.GetFloatValue(section, "AnalogDeadzone", DEFAULT_STICK_DEADZONE), 0.0f, 1.0f); m_analog_sensitivity = std::clamp(si.GetFloatValue(section, "AnalogSensitivity", DEFAULT_STICK_SENSITIVITY), 0.01f, 3.0f); + m_invert_left_stick = static_cast(si.GetIntValue(section, "InvertLeftStick", 0)); + m_invert_right_stick = static_cast(si.GetIntValue(section, "InvertRightStick", 0)); } diff --git a/src/core/analog_joystick.h b/src/core/analog_joystick.h index 6b1769cf9..d45145070 100644 --- a/src/core/analog_joystick.h +++ b/src/core/analog_joystick.h @@ -94,6 +94,8 @@ private: float m_analog_deadzone = 0.0f; float m_analog_sensitivity = 1.33f; + u8 m_invert_left_stick = 0; + u8 m_invert_right_stick = 0; // On original hardware, the mode toggle is a switch rather than a button, so we'll enable Analog Mode by default bool m_analog_mode = true; diff --git a/src/core/controller.cpp b/src/core/controller.cpp index af83d582b..7f00c5fb8 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -230,3 +230,21 @@ std::string Controller::GetSettingsSection(u32 pad) { return fmt::format("Pad{}", pad + 1u); } + +bool Controller::InCircularDeadzone(float deadzone, float pos_x, float pos_y) +{ + if (pos_x == 0.0f && pos_y == 0.0f) + return false; + + // Compute the angle at the given position in the stick's square bounding box. + const float theta = std::atan2(pos_y, pos_x); + + // Compute the position that the edge of the circle would be at, given the angle. + const float dz_x = std::cos(theta) * deadzone; + const float dz_y = std::sin(theta) * deadzone; + + // We're in the deadzone if our position is less than the circle edge. + const bool in_x = (pos_x < 0.0f) ? (pos_x > dz_x) : (pos_x <= dz_x); + const bool in_y = (pos_y < 0.0f) ? (pos_y > dz_y) : (pos_y <= dz_y); + return (in_x && in_y); +} diff --git a/src/core/controller.h b/src/core/controller.h index a06de77a7..5d375d84e 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -140,6 +140,9 @@ public: return (value < deadzone) ? 0.0f : ((value - deadzone) / (1.0f - deadzone) * sensitivity); } + /// Returns true if the specified coordinates are inside a circular deadzone. + static bool InCircularDeadzone(float deadzone, float pos_x, float pos_y); + protected: u32 m_index; }; diff --git a/src/core/negcon.cpp b/src/core/negcon.cpp index 0d8f0e9f9..907de60d0 100644 --- a/src/core/negcon.cpp +++ b/src/core/negcon.cpp @@ -71,7 +71,9 @@ void NeGcon::SetBindState(u32 index, float value) if (index == (static_cast(Button::Count) + static_cast(HalfAxis::SteeringLeft)) || index == (static_cast(Button::Count) + static_cast(HalfAxis::SteeringRight))) { - value = ApplyAnalogDeadzoneSensitivity(m_steering_deadzone, 1.0f, value); + value *= m_steering_sensitivity; + if (value < m_steering_deadzone) + value = 0.0f; m_half_axis_state[index - static_cast(Button::Count)] = static_cast(std::clamp(value * 255.0f, 0.0f, 255.0f)); @@ -249,10 +251,14 @@ static const Controller::ControllerBindingInfo s_binding_info[] = { #undef BUTTON }; -static const SettingInfo s_settings[] = {{SettingInfo::Type::Float, "SteeringDeadzone", - TRANSLATABLE("NeGcon", "Steering Axis Deadzone"), - TRANSLATABLE("NeGcon", "Sets deadzone size for steering axis."), "0.00f", - "0.00f", "0.99f", "0.01f", "%.0f%%", nullptr, 100.0f}}; +static const SettingInfo s_settings[] = { + {SettingInfo::Type::Float, "SteeringDeadzone", TRANSLATABLE("NeGcon", "Steering Axis Deadzone"), + TRANSLATABLE("NeGcon", "Sets deadzone size for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", + nullptr, 100.0f}, + {SettingInfo::Type::Float, "SteeringSensitivity", TRANSLATABLE("NeGcon", "Steering Axis Sensitivity"), + TRANSLATABLE("NeGcon", "Sets the steering axis scaling factor."), "1.00f", "0.01f", "2.00f", "0.01f", "%.0f%%", + nullptr, 100.0f}, +}; const Controller::ControllerInfo NeGcon::INFO = {ControllerType::NeGcon, "NeGcon", @@ -267,4 +273,5 @@ void NeGcon::LoadSettings(SettingsInterface& si, const char* section) { Controller::LoadSettings(si, section); m_steering_deadzone = si.GetFloatValue(section, "SteeringDeadzone", 0.10f); + m_steering_sensitivity = si.GetFloatValue(section, "SteeringSensitivity", 1.00f); } diff --git a/src/core/negcon.h b/src/core/negcon.h index aad4cd2b4..0c2e1a154 100644 --- a/src/core/negcon.h +++ b/src/core/negcon.h @@ -88,5 +88,5 @@ private: TransferState m_transfer_state = TransferState::Idle; float m_steering_deadzone = 0.00f; - + float m_steering_sensitivity = 1.00f; };