Add independent settings for NeGcon axes (#3094)

* Add independent settings for NeGcon axes

* TOSQUASH coverage of the full NeGcon twisting range
This commit is contained in:
athenavr 2024-01-22 05:37:38 +01:00 committed by Stenzek
parent daa8445331
commit 5e2c6d24b9
No known key found for this signature in database
2 changed files with 159 additions and 20 deletions

View File

@ -56,7 +56,15 @@ float NeGcon::GetBindState(u32 index) const
if (index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringLeft)) ||
index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringRight)))
{
return static_cast<float>(m_half_axis_state[index - static_cast<u32>(Button::Count)]) * (1.0f / 255.0f);
float value = m_axis_state[static_cast<u32>(Axis::Steering)];
value = value - 128.0f;
value /= value < 0.0f ? 128.0f : 127.0f;
value = std::clamp(value, -1.0f, 1.0f);
if (index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringLeft)))
{
value *= -1.0f;
}
return std::max(0.0f, value);
}
else if (index >= static_cast<u32>(Button::Count))
{
@ -74,23 +82,32 @@ float NeGcon::GetBindState(u32 index) const
}
}
static float apply_axis_modifier(float value, const NeGcon::AxisModifier& axis_modifier)
{
value = (value - axis_modifier.deadzone) / (axis_modifier.saturation - axis_modifier.deadzone);
value = std::clamp(value, 0.0f, 1.0f);
value = std::pow(value, std::exp(axis_modifier.linearity));
return value;
}
static u8 get_scaled_value(float value, const NeGcon::AxisModifier& axis_modifier)
{
value = axis_modifier.scaling * axis_modifier.unit * value + axis_modifier.zero;
return std::clamp(std::round(value), 0.0f, 255.0f);
}
void NeGcon::SetBindState(u32 index, float value)
{
// Steering Axis: -1..1 -> 0..255
if (index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringLeft)) ||
index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringRight)))
{
value *= m_steering_sensitivity;
if (value < m_steering_deadzone)
value = 0.0f;
value = apply_axis_modifier(value, m_steering_modifier);
m_half_axis_state[index - static_cast<u32>(Button::Count)] =
static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
m_half_axis_state[index - static_cast<u32>(Button::Count)] = std::clamp(value, 0.0f, 1.0f);
// Merge left/right. Seems to be inverted.
m_axis_state[static_cast<u32>(Axis::Steering)] =
((m_half_axis_state[1] != 0) ? (127u + ((m_half_axis_state[1] + 1u) / 2u)) :
(127u - (m_half_axis_state[0] / 2u)));
float merged = m_half_axis_state[1] - m_half_axis_state[0];
m_axis_state[static_cast<u32>(Axis::Steering)] = get_scaled_value(merged, m_steering_modifier);
}
else if (index >= static_cast<u32>(Button::Count))
{
@ -99,7 +116,18 @@ void NeGcon::SetBindState(u32 index, float value)
if (sub_index >= m_axis_state.size())
return;
m_axis_state[sub_index] = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
if (index >= (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::I)) &&
index <= (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::L)))
{
const AxisModifier& axis_modifier =
m_half_axis_modifiers[index - (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::I))];
value = apply_axis_modifier(value, axis_modifier);
m_axis_state[sub_index] = get_scaled_value(value, axis_modifier);
}
else
{
m_axis_state[sub_index] = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
}
}
else if (index < static_cast<u32>(Button::Count))
{
@ -264,11 +292,53 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Float, "SteeringDeadzone", TRANSLATE_NOOP("NeGcon", "Steering Axis Deadzone"),
TRANSLATE_NOOP("NeGcon", "Sets deadzone size for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%",
nullptr, 100.0f},
{SettingInfo::Type::Float, "SteeringSensitivity", TRANSLATE_NOOP("NeGcon", "Steering Axis Sensitivity"),
TRANSLATE_NOOP("NeGcon", "Sets the steering axis scaling factor."), "1.00f", "0.01f", "2.00f", "0.01f", "%.0f%%",
TRANSLATE_NOOP("NeGcon", "Sets deadzone for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::Float, "SteeringSaturation", TRANSLATE_NOOP("NeGcon", "Steering Axis Saturation"),
TRANSLATE_NOOP("NeGcon", "Sets saturation for steering axis."), "1.00f", "0.01f", "1.00f", "0.01f", "%.0f%%",
nullptr, 100.0f},
{SettingInfo::Type::Float, "SteeringLinearity", TRANSLATE_NOOP("NeGcon", "Steering Axis Linearity"),
TRANSLATE_NOOP("NeGcon", "Sets linearity for steering axis."), "0.00f", "-2.00f", "2.00f", "0.05f", "%.2f", nullptr,
1.0f},
{SettingInfo::Type::Float, "SteeringScaling", TRANSLATE_NOOP("NeGcon", "Steering Scaling"),
TRANSLATE_NOOP("NeGcon", "Sets scaling for steering axis."), "1.00f", "0.01f", "10.00f", "0.01f", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::Float, "IDeadzone", TRANSLATE_NOOP("NeGcon", "I Button Deadzone"),
TRANSLATE_NOOP("NeGcon", "Sets deadzone for button I."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::Float, "ISaturation", TRANSLATE_NOOP("NeGcon", "I Button Saturation"),
TRANSLATE_NOOP("NeGcon", "Sets saturation for button I."), "1.00f", "0.01f", "1.00f", "0.01f", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::Float, "ILinearity", TRANSLATE_NOOP("NeGcon", "I Button Linearity"),
TRANSLATE_NOOP("NeGcon", "Sets linearity for button I."), "0.00f", "-2.00f", "2.00f", "0.01f", "%.2f", nullptr,
1.0f},
{SettingInfo::Type::Float, "IScaling", TRANSLATE_NOOP("NeGcon", "I Scaling"),
TRANSLATE_NOOP("NeGcon", "Sets scaling for button I."), "1.00f", "0.01f", "10.00f", "0.01f", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::Float, "IIDeadzone", TRANSLATE_NOOP("NeGcon", "II Button Deadzone"),
TRANSLATE_NOOP("NeGcon", "Sets deadzone for button II."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::Float, "IISaturation", TRANSLATE_NOOP("NeGcon", "II Button Saturation"),
TRANSLATE_NOOP("NeGcon", "Sets saturation for button II."), "1.00f", "0.01f", "1.00f", "0.01f", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::Float, "IILinearity", TRANSLATE_NOOP("NeGcon", "II Button Linearity"),
TRANSLATE_NOOP("NeGcon", "Sets linearity for button II."), "0.00f", "-2.00f", "2.00f", "0.01f", "%.2f", nullptr,
1.0f},
{SettingInfo::Type::Float, "IIScaling", TRANSLATE_NOOP("NeGcon", "II Scaling"),
TRANSLATE_NOOP("NeGcon", "Sets scaling for button II."), "1.00f", "0.01f", "10.00f", "0.01f", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::Float, "LDeadzone", TRANSLATE_NOOP("NeGcon", "Left Trigger Deadzone"),
TRANSLATE_NOOP("NeGcon", "Sets deadzone for left trigger."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::Float, "LSaturation", TRANSLATE_NOOP("NeGcon", "Left Trigger Saturation"),
TRANSLATE_NOOP("NeGcon", "Sets saturation for left trigger."), "1.00f", "0.01f", "1.00f", "0.01f", "%.0f%%", nullptr,
100.0f},
{SettingInfo::Type::Float, "LLinearity", TRANSLATE_NOOP("NeGcon", "Left Trigger Linearity"),
TRANSLATE_NOOP("NeGcon", "Sets linearity for left trigger."), "0.00f", "-2.00f", "2.00f", "0.01f", "%.2f", nullptr,
1.0f},
{SettingInfo::Type::Float, "LScaling", TRANSLATE_NOOP("NeGcon", "Left Trigger Scaling"),
TRANSLATE_NOOP("NeGcon", "Sets scaling for left trigger."), "1.00f", "0.01f", "10.00f", "0.01f", "%.0f%%", nullptr,
100.0f},
};
const Controller::ControllerInfo NeGcon::INFO = {
@ -278,6 +348,36 @@ const Controller::ControllerInfo NeGcon::INFO = {
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);
m_steering_modifier = {
.deadzone = si.GetFloatValue(section, "SteeringDeadzone", DEFAULT_STEERING_MODIFIER.deadzone),
.saturation = si.GetFloatValue(section, "SteeringSaturation", DEFAULT_STEERING_MODIFIER.saturation),
.linearity = si.GetFloatValue(section, "SteeringLinearity", DEFAULT_STEERING_MODIFIER.linearity),
.scaling = si.GetFloatValue(section, "SteeringScaling", DEFAULT_STEERING_MODIFIER.scaling),
.zero = DEFAULT_STEERING_MODIFIER.zero,
.unit = DEFAULT_STEERING_MODIFIER.unit,
};
m_half_axis_modifiers[0] = {
.deadzone = si.GetFloatValue(section, "IDeadzone", DEFAULT_PEDAL_MODIFIER.deadzone),
.saturation = si.GetFloatValue(section, "ISaturation", DEFAULT_PEDAL_MODIFIER.saturation),
.linearity = si.GetFloatValue(section, "ILinearity", DEFAULT_PEDAL_MODIFIER.linearity),
.scaling = si.GetFloatValue(section, "IScaling", DEFAULT_PEDAL_MODIFIER.scaling),
.zero = DEFAULT_PEDAL_MODIFIER.zero,
.unit = DEFAULT_PEDAL_MODIFIER.unit,
};
m_half_axis_modifiers[1] = {
.deadzone = si.GetFloatValue(section, "IIDeadzone", DEFAULT_PEDAL_MODIFIER.deadzone),
.saturation = si.GetFloatValue(section, "IISaturation", DEFAULT_PEDAL_MODIFIER.saturation),
.linearity = si.GetFloatValue(section, "IILinearity", DEFAULT_PEDAL_MODIFIER.linearity),
.scaling = si.GetFloatValue(section, "IIScaling", DEFAULT_PEDAL_MODIFIER.scaling),
.zero = DEFAULT_PEDAL_MODIFIER.zero,
.unit = DEFAULT_PEDAL_MODIFIER.unit,
};
m_half_axis_modifiers[2] = {
.deadzone = si.GetFloatValue(section, "LDeadzone", DEFAULT_PEDAL_MODIFIER.deadzone),
.saturation = si.GetFloatValue(section, "LSaturation", DEFAULT_PEDAL_MODIFIER.saturation),
.linearity = si.GetFloatValue(section, "LLinearity", DEFAULT_PEDAL_MODIFIER.linearity),
.scaling = si.GetFloatValue(section, "LScaling", DEFAULT_PEDAL_MODIFIER.scaling),
.zero = DEFAULT_PEDAL_MODIFIER.zero,
.unit = DEFAULT_PEDAL_MODIFIER.unit,
};
}

View File

@ -43,6 +43,41 @@ public:
Count
};
struct AxisModifier
{
float deadzone;
float saturation;
float linearity;
float scaling;
float zero;
float unit;
};
static constexpr float DEFAULT_DEADZONE = 0.00f;
static constexpr float DEFAULT_SATURATION = 1.00f;
static constexpr float DEFAULT_LINEARITY = 0.00f;
static constexpr float DEFAULT_SCALING = 1.00f;
static constexpr float DEFAULT_STEERING_ZERO = 128.0f;
static constexpr float DEFAULT_STEERING_UNIT = 128.0f;
static constexpr float DEFAULT_PEDAL_ZERO = 0.0f;
static constexpr float DEFAULT_PEDAL_UNIT = 255.0f;
static constexpr AxisModifier DEFAULT_STEERING_MODIFIER = {
.deadzone = DEFAULT_DEADZONE,
.saturation = DEFAULT_SATURATION,
.linearity = DEFAULT_LINEARITY,
.scaling = DEFAULT_SCALING,
.zero = DEFAULT_STEERING_ZERO,
.unit = DEFAULT_STEERING_UNIT,
};
static constexpr AxisModifier DEFAULT_PEDAL_MODIFIER = {
.deadzone = DEFAULT_DEADZONE,
.saturation = DEFAULT_SATURATION,
.linearity = DEFAULT_LINEARITY,
.scaling = DEFAULT_SCALING,
.zero = DEFAULT_PEDAL_ZERO,
.unit = DEFAULT_PEDAL_UNIT,
};
static const Controller::ControllerInfo INFO;
NeGcon(u32 index);
@ -83,13 +118,17 @@ private:
std::array<u8, static_cast<u8>(Axis::Count)> m_axis_state{};
// steering, merged to m_axis_state
std::array<u8, 2> m_half_axis_state{};
std::array<float, 2> m_half_axis_state;
// buttons are active low; bits 0-2, 8-10, 14-15 are not used and are always high
u16 m_button_state = UINT16_C(0xFFFF);
TransferState m_transfer_state = TransferState::Idle;
float m_steering_deadzone = 0.00f;
float m_steering_sensitivity = 1.00f;
AxisModifier m_steering_modifier = DEFAULT_STEERING_MODIFIER;
std::array<AxisModifier, 3> m_half_axis_modifiers = {
DEFAULT_PEDAL_MODIFIER,
DEFAULT_PEDAL_MODIFIER,
DEFAULT_PEDAL_MODIFIER,
};
};