Skip to content

Commit

Permalink
Merge pull request #18654 from hrydgard/analog-axis-to-buttons-improv…
Browse files Browse the repository at this point in the history
…ement

Joystick: Bias down the axis-to-button threshold if the "co-axis" is active.
  • Loading branch information
hrydgard committed Dec 31, 2023
2 parents eabcd5d + 706b98a commit b36cf59
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 5 deletions.
52 changes: 47 additions & 5 deletions Core/ControlMapper.cpp
Expand Up @@ -18,20 +18,56 @@ using KeyMap::MultiInputMapping;
const float AXIS_BIND_THRESHOLD = 0.75f;
const float AXIS_BIND_THRESHOLD_MOUSE = 0.01f;

float GetDeviceAxisThreshold(int device, const InputMapping &mapping) {

// We reduce the threshold of some axes when another axis on the same stick is active.
// This makes it easier to hit diagonals if you bind an analog stick to four face buttons or D-Pad.
static InputAxis GetCoAxis(InputAxis axis) {
switch (axis) {
case JOYSTICK_AXIS_X: return JOYSTICK_AXIS_Y;
case JOYSTICK_AXIS_Y: return JOYSTICK_AXIS_X;

// This looks weird, but it's simply how XInput axes are mapped.
case JOYSTICK_AXIS_Z: return JOYSTICK_AXIS_RZ;
case JOYSTICK_AXIS_RZ: return JOYSTICK_AXIS_Z;

// Not sure if these two are used.
case JOYSTICK_AXIS_RX: return JOYSTICK_AXIS_RY;
case JOYSTICK_AXIS_RY: return JOYSTICK_AXIS_RX;

default:
return JOYSTICK_AXIS_MAX; // invalid
}
}

float ControlMapper::GetDeviceAxisThreshold(int device, const InputMapping &mapping) {
if (device == DEVICE_ID_MOUSE) {
return AXIS_BIND_THRESHOLD_MOUSE;
}
if (mapping.IsAxis()) {
switch (KeyMap::GetAxisType((InputAxis)mapping.Axis(nullptr))) {
case KeyMap::AxisType::TRIGGER:
return g_Config.fAnalogTriggerThreshold;
case KeyMap::AxisType::STICK:
{
// Co-axis processing, see GetCoAxes comment.
InputAxis axis = (InputAxis)mapping.Axis(nullptr);
InputAxis coAxis = GetCoAxis(axis);
if (coAxis != JOYSTICK_AXIS_MAX) {
float absCoValue = fabsf(rawAxisValue_[(int)coAxis]);
if (absCoValue > 0.0f) {
// Bias down the threshold if the other axis is active.
float biasedThreshold = AXIS_BIND_THRESHOLD * (1.0f - absCoValue * 0.2f);
// INFO_LOG(SYSTEM, "coValue: %f threshold: %f", absCoValue, biasedThreshold);
return biasedThreshold;
}
}
break;
}
default:
return AXIS_BIND_THRESHOLD;
break;
}
} else {
return AXIS_BIND_THRESHOLD;
}
return AXIS_BIND_THRESHOLD;
}

static int GetOppositeVKey(int vkey) {
Expand Down Expand Up @@ -325,7 +361,7 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping, double no
} else {
curTime = iter->second.timestamp;
}
bool down = iter->second.value > GetDeviceAxisThreshold(iter->first.deviceId, mapping);
bool down = iter->second.value > 0.0f && iter->second.value > GetDeviceAxisThreshold(iter->first.deviceId, mapping);
if (!down)
all = false;
}
Expand Down Expand Up @@ -541,10 +577,16 @@ void ControlMapper::Axis(const AxisInput *axes, size_t count) {
KeyMap::LockMappings();
for (size_t i = 0; i < count; i++) {
const AxisInput &axis = axes[i];

if (axis.deviceId == DEVICE_ID_MOUSE && !g_Config.bMouseControl) {
continue;
}

size_t deviceIndex = (size_t)axis.deviceId; // this wraps -1 up high, so will get rejected on the next line.
if (deviceIndex < (size_t)DEVICE_ID_COUNT) {
deviceTimestamps_[deviceIndex] = now;
}
rawAxisValue_[axis.axisId] = axis.value; // these are only used for co-axis mapping
if (axis.value >= 0.0f) {
InputMapping mapping(axis.deviceId, axis.axisId, 1);
InputMapping opposite(axis.deviceId, axis.axisId, -1);
Expand Down
5 changes: 5 additions & 0 deletions Core/ControlMapper.h
Expand Up @@ -59,11 +59,16 @@ class ControlMapper {
void onVKeyAnalog(int deviceId, int vkey, float value);

void UpdateCurInputAxis(const InputMapping &mapping, float value, double timestamp);
float GetDeviceAxisThreshold(int device, const InputMapping &mapping);

// To track mappable virtual keys. We can have as many as we want.
float virtKeys_[VIRTKEY_COUNT]{};
bool virtKeyOn_[VIRTKEY_COUNT]{}; // Track boolean output separaately since thresholds may differ.

// This is only used for co-axis (analog stick to buttons), so not bothering to track separately
// per device.
float rawAxisValue_[JOYSTICK_AXIS_MAX]{};

double deviceTimestamps_[(size_t)DEVICE_ID_COUNT]{};

int lastNonDeadzoneDeviceID_[2]{};
Expand Down

0 comments on commit b36cf59

Please sign in to comment.