fix(motion): port calc_acceleration + sequencer omega to retail tick (L.3.1+L.3.2)

Visual verification (Task 4) revealed two missing pieces from the
retail per-frame tick port (acclient!CPhysicsObj::update_object
@ 0x00513730):

1. body.calc_acceleration() must run BEFORE UpdatePhysicsInternal so
   gravity (set via PhysicsStateFlags.Gravity in OnLiveVectorUpdated)
   actually decays jump velocity. Without it body.Acceleration stays
   stale or zero → endless rise on jumps.

2. sequencer.CurrentOmega must be applied to body.Orientation per frame.
   Retail's TurnRight/TurnLeft cycles bake angular velocity that drives
   smooth rotation between UPs; we were only snapping orientation on
   UP receipt (~1 Hz), producing visible chop on turning remotes.

Both fixes are part of the retail tick we already started porting in
PositionManager — just missing pieces.

Speed-overshoot bug (sequencer.CurrentVelocity > server's actual
broadcast pace) is still being investigated in a follow-up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-03 10:32:42 +02:00
parent e94e7913fb
commit c1bfd64834

View file

@ -5789,14 +5789,23 @@ public sealed class GameWindow : IDisposable
if (System.Environment.GetEnvironmentVariable("ACDREAM_INTERP_MANAGER") == "1")
{
// ── NEW PATH: PositionManager (animation root motion + InterpolationManager) ──
// (L.3.1+L.3.2 Task 3 — ACDREAM_INTERP_MANAGER=1 gates this path)
// (L.3.1+L.3.2 Task 3/follow-up — ACDREAM_INTERP_MANAGER=1 gates this path)
//
// Always-run-all-steps per retail CPhysicsObj::UpdateObjectInternal
// (acclient @ 0x00513730):
// 1+2. animation root motion + interpolation correction (combined)
// 3. physics integration (gravity for airborne; no-op for grounded)
// 2.5 sequencer omega → body orientation (TurnRight/TurnLeft angular velocity)
// 3. calc_acceleration (gravity flag → body.Acceleration)
// 4. physics integration (gravity for airborne; no-op for grounded)
// Sequencer-driven motion sources: linear velocity (root motion)
// AND angular velocity (turn-cycle omega).
System.Numerics.Vector3 seqVel = ae.Sequencer?.CurrentVelocity
?? System.Numerics.Vector3.Zero;
System.Numerics.Vector3 seqOmega = ae.Sequencer?.CurrentOmega
?? System.Numerics.Vector3.Zero;
// Step 1+2: animation root motion + Interp correction (combined via PositionManager).
float maxSpeed = rm.Motion.GetMaxSpeed();
System.Numerics.Vector3 offset = rm.Position.ComputeOffset(
dt: (double)dt,
@ -5806,6 +5815,28 @@ public sealed class GameWindow : IDisposable
interp: rm.Interp,
maxSpeed: maxSpeed);
rm.Body.Position += offset;
// Step 2.5: animation-driven rotation. Retail's sequencer bakes Omega
// for TurnRight/TurnLeft cycles; we apply it as a per-frame quaternion
// rotation. seqOmega is body-local angular velocity (axis-angle: axis
// is omega.Normalized, magnitude is rad/sec). For Z-axis turns it's
// (0, 0, ±π/2 × turnSpeed) typically.
if (seqOmega.LengthSquared() > 1e-9f)
{
float angleDelta = seqOmega.Length() * (float)dt;
System.Numerics.Vector3 axis = System.Numerics.Vector3.Normalize(seqOmega);
var rot = System.Numerics.Quaternion.CreateFromAxisAngle(axis, angleDelta);
rm.Body.Orientation = System.Numerics.Quaternion.Normalize(
System.Numerics.Quaternion.Concatenate(rm.Body.Orientation, rot));
}
// Step 3: calc_acceleration sets body.Acceleration from the Gravity flag
// (mirrors retail CPhysicsObj::calc_acceleration @ FUN_00511420, called
// per-frame in update_object). Without this, body.Acceleration stays stale
// or zero → gravity never decays jump velocity → endless rise on jumps.
rm.Body.calc_acceleration();
// Step 4: physics integration (Euler: pos += vel*dt + 0.5*accel*dt²).
rm.Body.UpdatePhysicsInternal(dt);
ae.Entity.Position = rm.Body.Position;