Windows: Normalize left and right analog stick.

This commit is contained in:
Unknown W. Brackets 2021-04-04 08:16:26 -07:00
parent 540a5e3a3b
commit c9428975fe
3 changed files with 108 additions and 53 deletions

View file

@ -26,15 +26,11 @@
#include "Common/System/NativeApp.h"
#include "Core/Config.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/KeyMap.h"
#include "Core/Reporting.h"
#include "Windows/DinputDevice.h"
#pragma comment(lib,"dinput8.lib")
#ifdef min
#undef min
#undef max
#endif
//initialize static members of DinputDevice
unsigned int DinputDevice::pInstances = 0;
LPDIRECTINPUT8 DinputDevice::pDI = NULL;
@ -227,6 +223,69 @@ inline float LinearMaps(float val, float a0, float a1, float b0, float b1) {
return b0 + (((val - a0) * (b1 - b0)) / (a1 - a0));
}
static void ApplyNormalization(LONG &jsx, LONG &jsy) {
// Circle to Square mapping, cribbed from XInputDevice
float sx = (float)jsx;
float sy = (float)jsy;
float scaleFactor = sqrtf((sx * sx + sy * sy) / std::max(sx * sx, sy * sy));
jsx = (int)(sx * scaleFactor);
jsy = (int)(sy * scaleFactor);
// 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 st = g_Config.fDInputAnalogSensitivity;
float magnitude = sqrtf((float)(jsx * jsx + jsy * jsy));
if (magnitude > dz * 10000.0f) {
if (idzm == 1) {
int xSign = Signs(jsx);
if (xSign != 0) {
jsx = LinearMaps(jsx, xSign * (int)(dz * 10000), xSign * 10000, xSign * (int)(md * 10000), xSign * (int)(st * 10000));
}
} else if (idzm == 2) {
int ySign = Signs(jsy);
if (ySign != 0) {
jsy = LinearMaps(jsy, ySign * (int)(dz * 10000.0f), ySign * 10000, ySign * (int)(md * 10000.0f), ySign * (int)(st * 10000));
}
} else if (idzm == 3) {
float xNorm = (float)jsx / magnitude;
float yNorm = (float)jsy / magnitude;
float mapMag = LinearMaps(magnitude, dz * 10000.0f, 10000.0f, md * 10000.0f, 10000.0f * st);
jsx = (short)(xNorm * mapMag);
jsy = (short)(yNorm * mapMag);
}
} else {
jsx = 0;
jsy = 0;
}
jsx = (short)std::min(10000.0f, std::max((float)jsx, -10000.0f));
jsy = (short)std::min(10000.0f, std::max((float)jsy, -10000.0f));
}
static LONG *ValueForAxisId(DIJOYSTATE2 &js, int axisId) {
switch (axisId) {
case JOYSTICK_AXIS_X: return &js.lX;
case JOYSTICK_AXIS_Y: return &js.lY;
case JOYSTICK_AXIS_Z: return &js.lZ;
case JOYSTICK_AXIS_RX: return &js.lRx;
case JOYSTICK_AXIS_RY: return &js.lRy;
case JOYSTICK_AXIS_RZ: return &js.lRz;
default: return nullptr;
}
}
static void ApplyNormalization(DIJOYSTATE2 &js, int xAxisId, int yAxisId) {
LONG *nrmX = ValueForAxisId(js, xAxisId);
LONG *nrmY = ValueForAxisId(js, yAxisId);
if (nrmX != nullptr && nrmY != nullptr) {
ApplyNormalization(*nrmX, *nrmY);
}
}
int DinputDevice::UpdateState() {
if (!pJoystick) return -1;
@ -246,53 +305,11 @@ int DinputDevice::UpdateState() {
AxisInput axis;
axis.deviceId = DEVICE_ID_PAD_0 + pDevNum;
// Circle to Square mapping, cribbed from XInputDevice
float sx = (float)js.lX;
float sy = (float)js.lY;
float scaleFactor = sqrtf((sx * sx + sy * sy) / std::max(sx * sx, sy * sy));
js.lX = (int)(sx * scaleFactor);
js.lY = (int)(sy * scaleFactor);
// 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 st = g_Config.fDInputAnalogSensitivity;
float magnitude = sqrtf((float)(js.lX * js.lX + js.lY * js.lY));
if (magnitude > dz * 10000.0f) {
if (idzm == 1)
{
int xSign = Signs(js.lX);
if (xSign != 0) {
js.lX = LinearMaps(js.lX, xSign * (int)(dz * 10000), xSign * 10000, xSign * (int)(md * 10000), xSign * (int)(st * 10000));
}
}
else if (idzm == 2)
{
int ySign = Signs(js.lY);
if (ySign != 0) {
js.lY = LinearMaps(js.lY, ySign * (int)(dz * 10000.0f), ySign * 10000, ySign * (int)(md * 10000.0f), ySign * (int)(st * 10000));
}
}
else if (idzm == 3)
{
float xNorm = (float)js.lX / magnitude;
float yNorm = (float)js.lY / magnitude;
float mapMag = LinearMaps(magnitude, dz * 10000.0f, 10000.0f, md * 10000.0f, 10000.0f * st);
js.lX = (short)(xNorm * mapMag);
js.lY = (short)(yNorm * mapMag);
}
}
else
{
js.lX = 0;
js.lY = 0;
}
js.lX = (short)std::min(10000.0f, std::max((float)js.lX, -10000.0f));
js.lY = (short)std::min(10000.0f, std::max((float)js.lY, -10000.0f));
auto axesToSquare = KeyMap::MappedAxesForDevice(axis.deviceId);
ApplyNormalization(js, axesToSquare.leftXAxisId, axesToSquare.leftYAxisId);
// Prevent double normalization.
if (axesToSquare.leftXAxisId != axesToSquare.rightXAxisId && axesToSquare.leftXAxisId != axesToSquare.rightYAxisId)
ApplyNormalization(js, axesToSquare.rightXAxisId, axesToSquare.rightYAxisId);
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);