Elbow: read the hand-suggested angle nearest the current one (fixes side-switching when pointing the controller down: 45 -> 0 switches); point-down spike phase

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
FoxyzandClaude Opus 5.5 committed 2026-10-02 01:48:12 +03:00
1 parent 7bc6a866eb
commit 3f912367ff
2 files changed
+30 -3

No files matched your search

+22 -2
View File
@@ -53,6 +53,8 @@ public partial class ArmSpike : Node3D
private float _maxElbowStep; private float _maxElbowStep;
private Vector3 _lastElbow; private Vector3 _lastElbow;
private float _maxPinHandLag; private float _maxPinHandLag;
private float _downSide;
private int _downSideSwitches;
private float _restBodyPen; private float _restBodyPen;
private float _restDrawnPen; private float _restDrawnPen;
private float _restAnvilMove; private float _restAnvilMove;
@@ -178,13 +180,31 @@ public partial class ArmSpike : Node3D
Vector3 axis = (handPos - _arms.Shoulder(1)).Normalized(); Vector3 axis = (handPos - _arms.Shoulder(1)).Normalized();
Vector3 perp = axis.Cross(Vector3.Up).Normalized(); Vector3 perp = axis.Cross(Vector3.Up).Normalized();
Vector3 fwd = (axis * Mathf.Cos(1.22f) + perp.Rotated(axis, Mathf.Tau * _phaseT) * Mathf.Sin(1.22f)).Normalized(); Vector3 fwd = (axis * Mathf.Cos(1.22f) + perp.Rotated(axis, Mathf.Tau * _phaseT) * Mathf.Sin(1.22f)).Normalized();
_handTarget[1] = new Transform3D(Basis.LookingAt(fwd, Mathf.Abs(fwd.Y) > 0.95f ? Vector3.Forward : Vector3.Up), handPos); if (_phaseT <= 3.5f)
_handTarget[1] = new Transform3D(Basis.LookingAt(fwd, Mathf.Abs(fwd.Y) > 0.95f ? Vector3.Forward : Vector3.Up), handPos);
Vector3 elbow = _arms.ElbowTarget(1); Vector3 elbow = _arms.ElbowTarget(1);
if (_phaseT > 0.5f) if (_phaseT > 0.5f)
_maxElbowStep = Mathf.Max(_maxElbowStep, elbow.DistanceTo(_lastElbow)); _maxElbowStep = Mathf.Max(_maxElbowStep, elbow.DistanceTo(_lastElbow));
_lastElbow = elbow; _lastElbow = elbow;
if (_phaseT > 3.5f) if (_phaseT > 3.5f && _phaseT <= 5.5f)
{ {
// Controller pointing straight down with tracker-like wobble: the elbow
// must stay on one side.
Vector3 wobble = new Vector3(Mathf.Sin(5f * _phaseT), 0f, Mathf.Cos(3.7f * _phaseT)) * 0.5f;
Vector3 down = (Vector3.Down + wobble).Normalized();
_handTarget[1] = new Transform3D(Basis.LookingAt(down, Vector3.Forward), handPos);
float sideNow = Mathf.Sign((_arms.ElbowTarget(1) - _arms.Shoulder(1)).Dot(_torso.Body.GlobalTransform.Basis.X));
if (_phaseT > 4f)
{
if (_downSide == 0f)
_downSide = sideNow;
else if (sideNow != _downSide)
_downSideSwitches++;
}
}
if (_phaseT > 5.5f)
{
Record($"point-down: elbow keeps its side under wobble (switches {_downSideSwitches})", _downSideSwitches == 0);
Record($"flip: elbow never jumps (max step {_maxElbowStep:F4} m/tick, limit {FlipElbowStepLimit})", _maxElbowStep < FlipElbowStepLimit); Record($"flip: elbow never jumps (max step {_maxElbowStep:F4} m/tick, limit {FlipElbowStepLimit})", _maxElbowStep < FlipElbowStepLimit);
_handTarget[1] = new Transform3D(Basis.Identity, _handRest[1]); _handTarget[1] = new Transform3D(Basis.Identity, _handRest[1]);
_maxHoldPenetration = 0f; _maxHoldPenetration = 0f;
+8 -1
View File
@@ -331,7 +331,14 @@ public partial class PhysicsArms : Node
handElbow -= dir * handElbow.Dot(dir); handElbow -= dir * handElbow.Dot(dir);
float confidence = Mathf.Clamp(handElbow.Length() / (0.5f * radius + 1e-4f), 0f, 1f); float confidence = Mathf.Clamp(handElbow.Length() / (0.5f * radius + 1e-4f), 0f, 1f);
if (handElbow.LengthSquared() > 1e-8f) if (handElbow.LengthSquared() > 1e-8f)
targetAngle = Mathf.Atan2(handElbow.Dot(refB), handElbow.Dot(refN)) * PoleHandWeight * confidence; {
// Read the suggestion as the representation NEAREST the current elbow angle:
// pointing the controller down puts it at the ±180° seam, where jitter would
// otherwise flip the clamped elbow to the other side.
float raw = Mathf.Atan2(handElbow.Dot(refB), handElbow.Dot(refN));
raw = _poleAngle[index] + Mathf.Wrap(raw - _poleAngle[index], -Mathf.Pi, Mathf.Pi);
targetAngle = raw * PoleHandWeight * confidence;
}
} }
targetAngle = Mathf.Clamp(targetAngle, -PoleMaxAngle, PoleMaxAngle); targetAngle = Mathf.Clamp(targetAngle, -PoleMaxAngle, PoleMaxAngle);
float step = (targetAngle - _poleAngle[index]) * (1f - Mathf.Exp(-delta * PoleSmoothRate)); float step = (targetAngle - _poleAngle[index]) * (1f - Mathf.Exp(-delta * PoleSmoothRate));