RioProfile gains a nullable AxisRouting section mapping each calibrated axis (X/Y/Z/Rx/Ry/Rz) to a pad target (thumbs, triggers, or None) with a Centered or UnipolarPositive conversion; the default reproduces the old hardcoded routing exactly, so existing profiles are untouched. Routing resolution lives in a pure, ViGEm-free AxisRouter for testability; the sink neutralizes the pad on routing change so stale trigger state cannot leak across profile switches. Motivation: Descent reads the pad via SDL GameController, where the triggers are its stock fire axis-buttons - the old fixed routing put throttle on LeftTrigger (fires) and detent would have read as full reverse. descent-d1x.json now routes Z->RightThumbY (UnipolarPositive, detent = center) and Rz->RightThumbX, triggers untargeted; guarded by tests that parse the shipped JSON through the real deserializer and byte-compare the dxx-rebirth reference copy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
83 lines
2.9 KiB
C#
83 lines
2.9 KiB
C#
using RioJoy.Core.Calibration;
|
|
|
|
namespace RioJoy.Core.Output;
|
|
|
|
/// <summary>
|
|
/// Where a calibrated axis lands on the virtual Xbox 360 pad. Our own enum (no
|
|
/// ViGEm types — these are shared config types, serialized into profiles and
|
|
/// compiled for net40 too); <see cref="ViGEmJoystickSink"/> translates to the
|
|
/// ViGEm equivalents. <see cref="None"/> = the axis is not emitted at all.
|
|
/// </summary>
|
|
public enum PadTarget
|
|
{
|
|
LeftThumbX,
|
|
LeftThumbY,
|
|
RightThumbX,
|
|
RightThumbY,
|
|
LeftTrigger,
|
|
RightTrigger,
|
|
None,
|
|
}
|
|
|
|
/// <summary>
|
|
/// How a calibrated axis value (<c>0..32766</c>, center 16383) converts to a
|
|
/// thumb-stick value. Meaningless for trigger targets (triggers always use the
|
|
/// legacy <c>value*255/32766</c> byte conversion).
|
|
/// </summary>
|
|
public enum AxisOutputMode
|
|
{
|
|
/// <summary>Legacy bipolar mapping: <c>(value - 16383) * 2</c> → center 16383 = thumb 0.</summary>
|
|
Centered,
|
|
|
|
/// <summary>
|
|
/// Unipolar mapping for axes whose calibrated rest is 0 (the ratcheted
|
|
/// throttle): <c>clamp(value, 0, 32767)</c> → calibrated 0 = thumb center 0,
|
|
/// 32766 = thumb max. Only the upper half of the thumb range is used.
|
|
/// </summary>
|
|
UnipolarPositive,
|
|
}
|
|
|
|
/// <summary>One axis route: which pad control it drives, and how the value converts.</summary>
|
|
public sealed record AxisRoute
|
|
{
|
|
public PadTarget Target { get; init; } = PadTarget.None;
|
|
|
|
public AxisOutputMode Mode { get; init; } = AxisOutputMode.Centered;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Per-profile routing of the six calibrated axes onto the ViGEm Xbox 360 pad
|
|
/// (<see cref="RioJoy.Core.Profiles.RioProfile.AxisRouting"/>; null there = this
|
|
/// default). The defaults reproduce the historical hardcoded sink routing
|
|
/// exactly — X→LeftThumbX, Y→LeftThumbY, Rx→RightThumbX, Ry→RightThumbY,
|
|
/// Z→LeftTrigger, Rz→RightTrigger, all <see cref="AxisOutputMode.Centered"/> —
|
|
/// so existing profiles behave identically. Two axes routed to the same target
|
|
/// are not arbitrated: the last <c>SetAxis</c> write wins.
|
|
/// </summary>
|
|
public sealed record AxisRoutingConfig
|
|
{
|
|
public AxisRoute X { get; init; } = new() { Target = PadTarget.LeftThumbX };
|
|
|
|
public AxisRoute Y { get; init; } = new() { Target = PadTarget.LeftThumbY };
|
|
|
|
public AxisRoute Z { get; init; } = new() { Target = PadTarget.LeftTrigger };
|
|
|
|
public AxisRoute Rx { get; init; } = new() { Target = PadTarget.RightThumbX };
|
|
|
|
public AxisRoute Ry { get; init; } = new() { Target = PadTarget.RightThumbY };
|
|
|
|
public AxisRoute Rz { get; init; } = new() { Target = PadTarget.RightTrigger };
|
|
|
|
/// <summary>The route for <paramref name="axis"/>.</summary>
|
|
public AxisRoute RouteFor(JoyAxis axis) => axis switch
|
|
{
|
|
JoyAxis.X => X,
|
|
JoyAxis.Y => Y,
|
|
JoyAxis.Z => Z,
|
|
JoyAxis.Rx => Rx,
|
|
JoyAxis.Ry => Ry,
|
|
JoyAxis.Rz => Rz,
|
|
_ => new AxisRoute(), // unknown axis → None (not emitted)
|
|
};
|
|
}
|