DInput/XInput: Configurable deadzone + inverter

* Add configurable shared deadzone for all DInput axes
* Add configurable separate deadzones for left & right XInput sticks
* Add configurable deadzone inverter for DInput X/Y axes
* Add configurable deadzone inverter for XInput sticks
This commit is contained in:
Josh Palmer 2015-01-17 16:47:19 +00:00
parent c2500744e1
commit 918b96500c
5 changed files with 135 additions and 12 deletions

View file

@ -562,6 +562,19 @@ static ConfigSetting controlSettings[] = {
ConfigSetting("AnalogStickX", &g_Config.fAnalogStickX, -1.0f, true, true),
ConfigSetting("AnalogStickY", &g_Config.fAnalogStickY, -1.0f, true, true),
ConfigSetting("AnalogStickScale", &g_Config.fAnalogStickScale, defaultControlScale, true, true),
#ifdef _WIN32
ConfigSetting("DInputAnalogDeadzone", &g_Config.fDInputAnalogDeadzone, 0.1f, true, true),
ConfigSetting("DInputAnalogInverseDeadzone", &g_Config.fDInputAnalogInverseDeadzone, 0.0f, true, true),
ConfigSetting("XInputLeftAnalogDeadzone", &g_Config.fXInputLeftAnalogDeadzone, 0.24f, true, true),
ConfigSetting("XInputRightAnalogDeadzone", &g_Config.fXInputRightAnalogDeadzone, 0.27f, true, true),
ConfigSetting("XInputLeftAnalogInverseMode", &g_Config.iXInputLeftAnalogInverseMode, 0, true, true),
ConfigSetting("XInputLeftAnalogInverseDeadzone", &g_Config.fXInputLeftAnalogInverseDeadzone, 0.0f, true, true),
ConfigSetting("XInputRightAnalogInverseMode", &g_Config.iXInputRightAnalogInverseMode, 0, true, true),
ConfigSetting("XInputRightAnalogInverseDeadzone", &g_Config.fXInputRightAnalogInverseDeadzone, 0.0f, true, true),
#endif
ConfigSetting("AnalogLimiterDeadzone", &g_Config.fAnalogLimiterDeadzone, 0.6f, true, true),
ConfigSetting(false),

View file

@ -276,6 +276,19 @@ public:
bool bHapticFeedback;
float fDInputAnalogDeadzone;
int iDInputAnalogInverseMode;
float fDInputAnalogInverseDeadzone;
float fXInputLeftAnalogDeadzone;
float fXInputRightAnalogDeadzone;
int iXInputLeftAnalogInverseMode;
float fXInputLeftAnalogInverseDeadzone;
int iXInputRightAnalogInverseMode;
float fXInputRightAnalogInverseDeadzone;
float fAnalogLimiterDeadzone;
// GLES backend-specific hacks. Not saved to the ini file, do not add checkboxes. Will be made into
// proper options when good enough.

View file

@ -372,6 +372,23 @@ void GameSettingsScreen::CreateViews() {
View *style = controlsSettings->Add(new PopupMultiChoice(&g_Config.iTouchButtonStyle, c->T("Button style"), touchControlStyles, 0, ARRAY_SIZE(touchControlStyles), c, screenManager()));
style->SetEnabledPtr(&g_Config.bShowTouchControls);
static const char *inverseDeadzoneModes[] = { "Off", "X", "Y", "X + Y" };
controlsSettings->Add(new ItemHeader(c->T("DInput Analog Settings", "DInput Analog Settings")));
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fDInputAnalogDeadzone, 0.0f, 1.0f, c->T("Dead Zone"), screenManager()));
controlsSettings->Add(new PopupMultiChoice(&g_Config.iDInputAnalogInverseMode, c->T("Inverse Dead Zone Mode"), inverseDeadzoneModes, 0, ARRAY_SIZE(inverseDeadzoneModes), c, screenManager()));
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fDInputAnalogInverseDeadzone, 0.0f, 1.0f, c->T("Inverse Dead Zone Size"), screenManager()));
controlsSettings->Add(new ItemHeader(c->T("XInput Analog Settings", "XInput Analog Settings")));
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fXInputLeftAnalogDeadzone, 0.0f, 1.0f, c->T("Dead Zone (Left Stick)"), screenManager()));
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fXInputRightAnalogDeadzone, 0.0f, 1.0f, c->T("Dead Zone (Right Stick)"), screenManager()));
controlsSettings->Add(new PopupMultiChoice(&g_Config.iXInputLeftAnalogInverseMode, c->T("Inverse Dead Zone Mode (Left Stick)"), inverseDeadzoneModes, 0, ARRAY_SIZE(inverseDeadzoneModes), c, screenManager()));
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fXInputLeftAnalogInverseDeadzone, 0.0f, 1.0f, c->T("Inverse Dead Zone Size (Left Stick)"), screenManager()));
controlsSettings->Add(new PopupMultiChoice(&g_Config.iXInputRightAnalogInverseMode, c->T("Inverse Dead Zone Mode (Right Stick)"), inverseDeadzoneModes, 0, ARRAY_SIZE(inverseDeadzoneModes), c, screenManager()));
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fXInputRightAnalogInverseDeadzone, 0.0f, 1.0f, c->T("Inverse Dead Zone Size (Right Stick)"), screenManager()));
controlsSettings->Add(new ItemHeader(c->T("Keyboard", "Keyboard Control Settings")));
#if defined(USING_WIN_UI)
controlsSettings->Add(new CheckBox(&g_Config.bIgnoreWindowsKey, c->T("Ignore Windows Key")));

View file

@ -177,8 +177,8 @@ DinputDevice::DinputDevice(int devnum) {
dipw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipw.diph.dwHow = DIPH_DEVICE;
dipw.diph.dwObj = 0;
// dwData 1000 is deadzone(0% - 10%)
dipw.dwData = 1000;
// dwData 10000 is deadzone(0% - 100%), multiply by config scalar
dipw.dwData = (int)(g_Config.fDInputAnalogDeadzone * 10000);
analog |= FAILED(pJoystick->SetProperty(DIPROP_DEADZONE, &dipw.diph)) ? false : true;
}
@ -214,6 +214,14 @@ void SendNativeAxis(int deviceId, short value, short &lastValue, int axisId) {
lastValue = value;
}
inline float Signf(float val) {
return (0.0f < val) - (val < 0.0f);
}
inline float LinearMapf(float val, float a0, float a1, float b0, float b1) {
return b0 + (((val - a0) * (b1 - b0)) / (a1 - a0));
}
int DinputDevice::UpdateState(InputState &input_state) {
if (!pJoystick) return -1;
@ -232,6 +240,36 @@ int DinputDevice::UpdateState(InputState &input_state) {
if (analog) {
AxisInput axis;
axis.deviceId = DEVICE_ID_PAD_0 + pDevNum;
// Linear range mapping (used to invert deadzones)
float dz = g_Config.fDInputAnalogDeadzone;
int idzm = g_Config.iDInputAnalogInverseMode;
float idz = g_Config.fDInputAnalogInverseDeadzone;
float md = std::max(dz, idz);
float magnitude = sqrtf(js.lX * js.lX + js.lY * js.lY);
if (idzm == 1)
{
float xSign = Signf(js.lX);
if (xSign != 0.0f) {
js.lX = LinearMapf(js.lX, xSign * dz, xSign, xSign * md, xSign);
}
}
else if (idzm == 2)
{
float ySign = Signf(js.lY);
if (ySign != 0.0f) {
js.lY = LinearMapf(js.lY, ySign * dz, ySign, ySign * md, ySign);
}
}
else if (idzm == 3)
{
float xNorm = js.lX / magnitude;
float yNorm = js.lY / magnitude;
float mapMag = LinearMapf(magnitude, dz, 1.0, md, 1.0);
js.lX = xNorm * mapMag;
js.lY = yNorm * mapMag;
}
SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lX, last_lX_, JOYSTICK_AXIS_X);
SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lY, last_lY_, JOYSTICK_AXIS_Y);

View file

@ -120,12 +120,20 @@ inline float Clampf(float val, float min, float max) {
return val;
}
static Stick NormalizedDeadzoneFilter(short x, short y, short thresh) {
static const float DEADZONE = (float)thresh / 32767.0f;
inline float Signf(float val) {
return (0.0f < val) - (val < 0.0f);
}
inline float LinearMapf(float val, float a0, float a1, float b0, float b1) {
return b0 + (((val - a0) * (b1 - b0)) / (a1 - a0));
}
static Stick NormalizedDeadzoneFilter(short x, short y, float dz, int idzm, float idz) {
Stick s(x, y, 1.0 / 32767.0f);
float magnitude = sqrtf(s.x * s.x + s.y * s.y);
if (magnitude > DEADZONE) {
if (magnitude > dz) {
// Circle to square mapping (the PSP stick outputs the full -1..1 square of values)
#if 1
// Looks way better than the old one, below, in the axis tester.
@ -140,6 +148,33 @@ static Stick NormalizedDeadzoneFilter(short x, short y, short thresh) {
s.y *= 1.41421f;
}
#endif
// Linear range mapping (used to invert deadzones)
float md = std::max(dz, idz);
if (idzm == 1)
{
float xSign = Signf(s.x);
if (xSign != 0.0f) {
s.x = LinearMapf(s.x, xSign * dz, xSign, xSign * md, xSign);
}
}
else if (idzm == 2)
{
float ySign = Signf(s.y);
if (ySign != 0.0f) {
s.y = LinearMapf(s.y, ySign * dz, ySign, ySign * md, ySign);
}
}
else if (idzm == 3)
{
float xNorm = s.x / magnitude;
float yNorm = s.y / magnitude;
float mapMag = LinearMapf(magnitude, dz, 1.0, md, 1.0);
s.x = xNorm * mapMag;
s.y = yNorm * mapMag;
}
s.x = Clampf(s.x, -1.0f, 1.0f);
s.y = Clampf(s.y, -1.0f, 1.0f);
} else {
@ -149,14 +184,13 @@ static Stick NormalizedDeadzoneFilter(short x, short y, short thresh) {
return s;
}
bool NormalizedDeadzoneDiffers(short x1, short y1, short x2, short y2, const short thresh) {
static const float DEADZONE = (float)thresh / 32767.0f;
bool NormalizedDeadzoneDiffers(short x1, short y1, short x2, short y2, const float dz) {
Stick s1(x1, y1, 1.0 / 32767.0f);
Stick s2(x2, y2, 1.0 / 32767.0f);
float magnitude1 = sqrtf(s1.x * s1.x + s1.y * s1.y);
float magnitude2 = sqrtf(s2.x * s2.x + s2.y * s2.y);
if (magnitude1 > DEADZONE || magnitude2 > DEADZONE) {
if (magnitude1 > dz || magnitude2 > dz) {
return x1 != x2 || y1 != y2;
}
return false;
@ -201,8 +235,12 @@ int XinputDevice::UpdateState(InputState &input_state) {
}
ApplyButtons(state, input_state);
if (NormalizedDeadzoneDiffers(prevState.Gamepad.sThumbLX, prevState.Gamepad.sThumbLY, state.Gamepad.sThumbLX, state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)) {
Stick left = NormalizedDeadzoneFilter(state.Gamepad.sThumbLX, state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
const float LEFT_STICK_DEADZONE = g_Config.fXInputLeftAnalogDeadzone;
const int LEFT_STICK_INV_MODE = g_Config.iXInputLeftAnalogInverseMode;
const float LEFT_STICK_INV_DEADZONE = g_Config.fXInputLeftAnalogInverseDeadzone;
if (NormalizedDeadzoneDiffers(prevState.Gamepad.sThumbLX, prevState.Gamepad.sThumbLY, state.Gamepad.sThumbLX, state.Gamepad.sThumbLY, LEFT_STICK_DEADZONE)) {
Stick left = NormalizedDeadzoneFilter(state.Gamepad.sThumbLX, state.Gamepad.sThumbLY, LEFT_STICK_DEADZONE, LEFT_STICK_INV_MODE, LEFT_STICK_INV_DEADZONE);
AxisInput axis;
axis.deviceId = DEVICE_ID_X360_0;
@ -218,8 +256,12 @@ int XinputDevice::UpdateState(InputState &input_state) {
}
}
if (NormalizedDeadzoneDiffers(prevState.Gamepad.sThumbRX, prevState.Gamepad.sThumbRY, state.Gamepad.sThumbRX, state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)) {
Stick right = NormalizedDeadzoneFilter(state.Gamepad.sThumbRX, state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
const float RIGHT_STICK_DEADZONE = g_Config.fXInputRightAnalogDeadzone;
const int RIGHT_STICK_INV_MODE = g_Config.iXInputRightAnalogInverseMode;
const float RIGHT_STICK_INV_DEADZONE = g_Config.fXInputRightAnalogInverseDeadzone;
if (NormalizedDeadzoneDiffers(prevState.Gamepad.sThumbRX, prevState.Gamepad.sThumbRY, state.Gamepad.sThumbRX, state.Gamepad.sThumbRY, RIGHT_STICK_DEADZONE)) {
Stick right = NormalizedDeadzoneFilter(state.Gamepad.sThumbRX, state.Gamepad.sThumbRY, RIGHT_STICK_DEADZONE, RIGHT_STICK_INV_MODE, LEFT_STICK_INV_DEADZONE);
AxisInput axis;
axis.deviceId = DEVICE_ID_X360_0;