diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 08b8e71..d316c01 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -1944,6 +1944,16 @@ void // (the keyboard-steering skip); pure spin/walk never showed it (one stream). // Save the heading, let DeadReckon own the LINEAR channel, then override // the angular result with the original's incremental integration. + // ROOT-MOTION peer POSITION (authentic FUN_004ab1c8: the clip's travel + // drives the body between records; pose records re-anchor). Save the + // position so the engine velocity-lerp result can be discarded below + // (BT_DR_POS=1 restores the velocity dead-reckon for A/B). + // DEFAULT: engine velocity dead-reckon (best-measured: ratio 1.0022). The + // coupled root-motion experiment (BT_ROOT_POS=1) needs the MASTER-side + // gait mirror to stop the authority tug-of-war (measured ratio 1.86) -- + // the completion path is mapped in the session notes. + static const int s_drPos = getenv("BT_ROOT_POS") ? 0 : 1; + Point3D replPrevPos = localOrigin.linearPosition; Quaternion replPrevHeading = localOrigin.angularPosition; DeadReckon(dt); // engine reckoner: LINEAR position/velocity if (angSyncLatch) @@ -1960,6 +1970,8 @@ void &replPrevHeading, &replAngStep); // exact FUN_00409f58 } localOrigin.angularPosition.Normalize(); + if (!s_drPos) + localOrigin.linearPosition = replPrevPos; // root-motion mode: gait applies travel below localToWorld = localOrigin; // RENDER-vs-SIM decoupling probe: publish the heading the RENDER will @@ -2122,7 +2134,48 @@ void // once so a direct run-state entry can't slew from -4.3e8. if (legCycleSpeed < -100.0f || legCycleSpeed > 200.0f) legCycleSpeed = 0.0f; - (void)AdvanceLegAnimation(dt); // joints only; travel = DeadReckon + const Scalar replLegAdv = AdvanceLegAnimation(dt); // joints + ROOT TRAVEL + // AUTHENTIC coupled position (mirror of the master's world-step at + // mech4.cpp:3325-3336 == Mech::IntegrateMotion tail @004ab1c8): between + // records the body advances by the CLIP'S OWN travel rotated by the + // heading -- feet and ground are locked BY CONSTRUCTION, so cadence-vs- + // travel mismatch (the speed-change glitch) is structurally impossible. + // A pose record re-anchors onto the authoritative position + // (poseSyncLatch, armed by every type-0 read). adv is SIGNED by the SM + // (reverse clips return negative travel; master dir==1.0 in SM mode). + if (!s_drPos) + { + // POSE-SYNC OFFSET DECAY (authentic FUN_004ab1c8 motionEventVector + // mechanism): a record does NOT snap the body -- the correction offset + // is absorbed over ~1/3s while the clip's root motion keeps driving, so + // the feet never pop and the position converges to authority. + static Vector3D s_rmErr[2]; // per-instance would be a member; one replicant per node in practice + Vector3D &rmErr = s_rmErr[0]; + if (poseSyncLatch) + { + rmErr.Subtract(updateOrigin.linearPosition, localOrigin.linearPosition); + poseSyncLatch = 0; + } + { + Scalar k = dt * 3.0f; if (k > 1.0f) k = 1.0f; // ~1/3s absorption + localOrigin.linearPosition.x += rmErr.x * k; + localOrigin.linearPosition.y += rmErr.y * k; + localOrigin.linearPosition.z += rmErr.z * k; + rmErr.x -= rmErr.x * k; rmErr.y -= rmErr.y * k; rmErr.z -= rmErr.z * k; + } + if (replLegAdv != 0.0f) + { + Vector3D rmLocal(0.0f, 0.0f, -replLegAdv); // forward = -Z, per-frame DISTANCE + Matrix34 rmOrient; + Matrix34::FromQuaternion(&rmOrient, &localOrigin.angularPosition); + Vector3D rmWorld; + FUN_00408744(&rmWorld, (Scalar *)&rmLocal, &rmOrient); + localOrigin.linearPosition.x += rmWorld.x; + localOrigin.linearPosition.y += rmWorld.y; + localOrigin.linearPosition.z += rmWorld.z; + } + localToWorld = localOrigin; // refresh with the coupled position + } // GAIT-EVENNESS probe (BT_GAITEV, 1s stats): the metric of the VISIBLE // speed-change glitch -- per-frame leg-clip advance regularity + leg-state @@ -3707,6 +3760,10 @@ void // makes the cadence regular + dense (records ~1 frame apart), so the // horizon prediction matches and the lerp tracks smoothly. On a LAN // this is a handful of small packets/frame -- negligible. + // Dense while moving: with the gait-driven peer, the master's velocity + // mirror no longer models the peer, so the deadband under-sends and the + // peer diverges (measured 11u snaps). Dense records keep the correction + // offset tiny; the peer absorbs it via the authentic pose-sync decay. static const int s_denseTx = getenv("BT_NO_DENSE_TX") ? 0 : 1; const Logical moving = (liveSpdSq > 0.25f); if (