MP: peer true-mirror cadence -- drive the body clip from actual replicated motion (task #50)
Toward a truer mirror (user: peer 'hesitates/skips on accel/decel, master has
smoother transitions'). The body channel FOLLOWS the master's replicated STATE
(96a896a) but its CADENCE (bodyCycleSpeed) is still slewed LOCALLY toward the
replicated commanded speed -- which drifts from the master's actual cadence, so
the animation phase wanders until a record snaps it (the hesitation).
FIX (replicant-gated, single-player untouched): peerMirrorSpeed member = the
actual replicated ground speed (|updateVelocity|, set in the peer branch); the
body channel walk (6/7) + reverse (0xc/0xd) cases override bodyCycleSpeed with
it (clamped to the state's band) right before the clip advance, so the peer's
clip advances at the master's REAL rate instead of a locally-drifting slew.
peerMirrorSpeed = -1 on the master/single-player -> the authentic slew runs.
BT_NO_MIRROR_CAD / BT_PEER_LEGCH revert.
Verified autonomous (through-zero sweep): no crash, body channel advances forward
(back-steps ~0), feet track (position ratio 1.05, maxStep 1.68u). Perceived
smoothness is the visual test (harness saturates).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f094d78df1
commit
23f1532fb0
@@ -916,6 +916,7 @@ Mech::Mech(
|
|||||||
poseSyncLatch = 0; // @0x77c (was absorbed Wword(0x1df))
|
poseSyncLatch = 0; // @0x77c (was absorbed Wword(0x1df))
|
||||||
heatLevelSnapshot = 0; // @0x780 (type-7 deadband baseline)
|
heatLevelSnapshot = 0; // @0x780 (type-7 deadband baseline)
|
||||||
mirrorBodyAdv = 0.0f; // master send-mirror gait travel
|
mirrorBodyAdv = 0.0f; // master send-mirror gait travel
|
||||||
|
peerMirrorSpeed = -1.0f; // peer true-mirror cadence (disabled by default)
|
||||||
angMirrorYaw = 0.0f; // scalar peer-yaw mirror (port addition)
|
angMirrorYaw = 0.0f; // scalar peer-yaw mirror (port addition)
|
||||||
angMirrorRate = 0.0f;
|
angMirrorRate = 0.0f;
|
||||||
angMirrorValid = 0;
|
angMirrorValid = 0;
|
||||||
|
|||||||
@@ -631,6 +631,12 @@ public:
|
|||||||
// predicts (gait), instead of the constant-velocity deadReckoner. This is the
|
// predicts (gait), instead of the constant-velocity deadReckoner. This is the
|
||||||
// authentic FUN_004a9b5c mirror = IntegrateMotion(mj=0) on projectedOrigin.
|
// authentic FUN_004a9b5c mirror = IntegrateMotion(mj=0) on projectedOrigin.
|
||||||
Scalar mirrorBodyAdv; // body cycleDistance this frame (master)
|
Scalar mirrorBodyAdv; // body cycleDistance this frame (master)
|
||||||
|
// Peer true-mirror cadence: the replicant sets this to the actual replicated
|
||||||
|
// ground speed (|updateVelocity|) each frame so the body clip advances at the
|
||||||
|
// master's real rate instead of the locally-slewed bodyCycleSpeed (which drifts
|
||||||
|
// and makes the animation phase wander -> the accel/decel hesitation). -1 =
|
||||||
|
// disabled (master/single-player always -1, so the authentic slew runs).
|
||||||
|
Scalar peerMirrorSpeed;
|
||||||
Scalar angMirrorYaw; // yaw at the last type-4 send (rad)
|
Scalar angMirrorYaw; // yaw at the last type-4 send (rad)
|
||||||
Scalar angMirrorRate; // yaw rate sent with it (rad/s)
|
Scalar angMirrorRate; // yaw rate sent with it (rad/s)
|
||||||
Time angMirrorTime; // lastPerformance at that send
|
Time angMirrorTime; // lastPerformance at that send
|
||||||
|
|||||||
@@ -999,6 +999,9 @@ Scalar
|
|||||||
if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
||||||
if (bodyCycleSpeed > walkStrideLength) bodyCycleSpeed = walkStrideLength;
|
if (bodyCycleSpeed > walkStrideLength) bodyCycleSpeed = walkStrideLength;
|
||||||
}
|
}
|
||||||
|
if (peerMirrorSpeed >= 0.0f) // peer: cadence == actual mirrored ground speed
|
||||||
|
bodyCycleSpeed = (peerMirrorSpeed < standSpeed) ? standSpeed
|
||||||
|
: ((peerMirrorSpeed > walkStrideLength) ? walkStrideLength : peerMirrorSpeed);
|
||||||
distance = bodyAnimation.Advance(
|
distance = bodyAnimation.Advance(
|
||||||
time_slice * (bodyCycleSpeed / walkStrideLength) * globalTimeScale, loop);
|
time_slice * (bodyCycleSpeed / walkStrideLength) * globalTimeScale, loop);
|
||||||
break;
|
break;
|
||||||
@@ -1019,6 +1022,9 @@ Scalar
|
|||||||
if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
||||||
if (bodyCycleSpeed > reverseSpeedMax2) bodyCycleSpeed = reverseSpeedMax2;
|
if (bodyCycleSpeed > reverseSpeedMax2) bodyCycleSpeed = reverseSpeedMax2;
|
||||||
}
|
}
|
||||||
|
if (peerMirrorSpeed >= 0.0f) // peer: cadence == actual mirrored ground speed
|
||||||
|
bodyCycleSpeed = (peerMirrorSpeed < reverseSpeedMax) ? reverseSpeedMax
|
||||||
|
: ((peerMirrorSpeed > reverseSpeedMax2) ? reverseSpeedMax2 : peerMirrorSpeed);
|
||||||
distance = bodyAnimation.Advance(
|
distance = bodyAnimation.Advance(
|
||||||
time_slice * (bodyCycleSpeed / reverseStrideLength) * globalTimeScale, loop);
|
time_slice * (bodyCycleSpeed / reverseStrideLength) * globalTimeScale, loop);
|
||||||
break;
|
break;
|
||||||
@@ -1159,6 +1165,9 @@ Scalar
|
|||||||
if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
||||||
if (bodyCycleSpeed > walkStrideLength) bodyCycleSpeed = walkStrideLength;
|
if (bodyCycleSpeed > walkStrideLength) bodyCycleSpeed = walkStrideLength;
|
||||||
}
|
}
|
||||||
|
if (peerMirrorSpeed >= 0.0f) // peer: cadence == actual mirrored ground speed
|
||||||
|
bodyCycleSpeed = (peerMirrorSpeed < standSpeed) ? standSpeed
|
||||||
|
: ((peerMirrorSpeed > walkStrideLength) ? walkStrideLength : peerMirrorSpeed);
|
||||||
distance = bodyAnimation.Advance(
|
distance = bodyAnimation.Advance(
|
||||||
time_slice * (bodyCycleSpeed / walkStrideLength) * globalTimeScale, loop);
|
time_slice * (bodyCycleSpeed / walkStrideLength) * globalTimeScale, loop);
|
||||||
break;
|
break;
|
||||||
@@ -1179,6 +1188,9 @@ Scalar
|
|||||||
if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
||||||
if (bodyCycleSpeed > reverseSpeedMax2) bodyCycleSpeed = reverseSpeedMax2;
|
if (bodyCycleSpeed > reverseSpeedMax2) bodyCycleSpeed = reverseSpeedMax2;
|
||||||
}
|
}
|
||||||
|
if (peerMirrorSpeed >= 0.0f) // peer: cadence == actual mirrored ground speed
|
||||||
|
bodyCycleSpeed = (peerMirrorSpeed < reverseSpeedMax) ? reverseSpeedMax
|
||||||
|
: ((peerMirrorSpeed > reverseSpeedMax2) ? reverseSpeedMax2 : peerMirrorSpeed);
|
||||||
distance = bodyAnimation.Advance(
|
distance = bodyAnimation.Advance(
|
||||||
time_slice * (bodyCycleSpeed / reverseStrideLength) * globalTimeScale, loop);
|
time_slice * (bodyCycleSpeed / reverseStrideLength) * globalTimeScale, loop);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -2148,6 +2148,17 @@ void
|
|||||||
// master's exact clips. Swapping the peer to the body channel RESTORES the
|
// master's exact clips. Swapping the peer to the body channel RESTORES the
|
||||||
// original single-channel design (no new netcode). BT_PEER_LEGCH=1 = old.
|
// original single-channel design (no new netcode). BT_PEER_LEGCH=1 = old.
|
||||||
static const int s_peerLegCh = getenv("BT_PEER_LEGCH") ? 1 : 0;
|
static const int s_peerLegCh = getenv("BT_PEER_LEGCH") ? 1 : 0;
|
||||||
|
// TRUE-MIRROR CADENCE: drive the body clip from the ACTUAL replicated ground
|
||||||
|
// speed so the animation phase tracks the master exactly instead of drifting
|
||||||
|
// via the local bodyCycleSpeed slew (the accel/decel hesitation). Consumed in
|
||||||
|
// AdvanceBodyAnimation walk/reverse. -1 disables -> authentic slew.
|
||||||
|
if (!s_peerLegCh && !getenv("BT_NO_MIRROR_CAD"))
|
||||||
|
{
|
||||||
|
const Vector3D &mcv = updateVelocity.linearMotion;
|
||||||
|
peerMirrorSpeed = (Scalar)sqrtf((float)(mcv.x * mcv.x + mcv.z * mcv.z));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
peerMirrorSpeed = -1.0f;
|
||||||
// TURN-STEP ARMING for the body channel: the body Standing case only enters
|
// TURN-STEP ARMING for the body channel: the body Standing case only enters
|
||||||
// walk/reverse/stand -- it never arms the turn-in-place clip (state 4); on the
|
// walk/reverse/stand -- it never arms the turn-in-place clip (state 4); on the
|
||||||
// master that arming comes from the LEG Standing case cross-arming both
|
// master that arming comes from the LEG Standing case cross-arming both
|
||||||
|
|||||||
Reference in New Issue
Block a user