Body Stage 1 collapse root-caused + fixed: NaN in torso servo math (GetAngle = 2*acos(W) at W=1.0000001), not solver stacking

- BodyMath.RotationError: atan2-based shortest-arc error (no NaN, no GetAxis gain collapse
  inside 0.16 deg); used by PhysicsTorso orientation servo and PhysicsHands rotation lock
- BodyMath.ResetIfNonFinite escape hatch on torso + hands
- PhysicsHands.Drive extracted (static) so spikes run the real hand chase
- CollapseSpike: deterministic repro (slow yaw drift across a parked chest, 7/40 NaN on old
  code) + solver-stacking scenarios (seated chest in table, two-hand-welded 50 kg anvil,
  real PropShop) — 49/49 clean after fix, x3; all other spikes PASS x3
- Failure brief RESOLUTION section; HANDOFF/AGENTS updated

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
FoxyzandClaude Opus 5.5 committed 2026-10-01 23:55:46 +03:00
1 parent ce1e0e3b0c
commit cb518999e5
9 files changed
+466 -47

No files matched your search

+12 -7
View File
@@ -88,6 +88,8 @@ public partial class PhysicsHands : Node
{
if (body == null)
return;
if (BodyMath.ResetIfNonFinite(body, controller.GlobalTransform, "PhysicsHands"))
return;
int hand = _left == controller ? 0 : 1;
Vector3 target = controller.GlobalPosition;
@@ -121,18 +123,21 @@ public partial class PhysicsHands : Node
return;
}
}
Vector3 desiredVelocity = (target - body.GlobalPosition) / delta;
Drive(body, controller.GlobalTransform, delta);
}
// The hand chase itself — public so headless spikes drive their stand-ins with the real code.
public static void Drive(RigidBody3D body, Transform3D target, float delta)
{
Vector3 desiredVelocity = (target.Origin - body.GlobalPosition) / delta;
Vector3 dv = desiredVelocity - body.LinearVelocity;
Vector3 force = dv / delta * body.Mass;
if (force.Length() > HandMaxForce)
force = force.Normalized() * HandMaxForce;
body.LinearVelocity += force / body.Mass * delta;
Basis deltaBasis = controller.GlobalTransform.Basis * body.GlobalTransform.Basis.Inverse();
Quaternion q = deltaBasis.GetRotationQuaternion();
Vector3 spin = q.GetAxis() * (q.GetAngle() / delta);
if (spin.Length() > HandMaxSpin)
spin = spin.Normalized() * HandMaxSpin;
body.AngularVelocity = spin;
Vector3 rotErr = BodyMath.RotationError(target.Basis.GetRotationQuaternion(),
body.GlobalTransform.Basis.GetRotationQuaternion());
body.AngularVelocity = (rotErr / delta).LimitLength(HandMaxSpin);
}
}