Body step 1: one-time T-pose calibration (prefs.json, forced gate) + True Body solver + shared ArmSolver

- BodyProfile (Winter anthropometry, T-pose: arm = (span - shoulder width)/2 - 1.5 cm) and
  Prefs (gitignored prefs.json next to the executable / project folder in editor builds)
- Calibration node: loads prefs, else head-locked prompt + jitter-proof TPoseDetector; body
  systems wait for BodyProfile.Current; Recalibrate() in group "calibration"; recal rebuilds arms
- ArmSolver: PhysicsArms' elbow solve extracted (behaviour-identical) + min reach |U-F| fix
- TrueBody: derived real-body pose (neck, chest yaw + crouch lean at neck pivot, lagging pelvis,
  moving shoulder girdle with scapulohumeral shrug, arms) + debug skeleton
- PhysicsArms/PhysicsTorso/BodyRig now profile-driven (arm length no longer from head height)
- TrueBodySpike PASS x3; all spikes PASS x3 (arm spike thresholds re-based for correct 0.57 m arms;
  CollapseSpike uses a fixed standing profile)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
FoxyzandClaude Opus 5.5 committed 2026-10-02 05:47:20 +03:00
1 parent 3a990c6dbe
commit 81a9b1fca2
15 files changed
+994 -106

No files matched your search

+31 -2
View File
@@ -38,8 +38,8 @@ TERMINOLOGY (owner-corrected — use precisely):
collapse spike takes seconds instead of minutes). Spikes and their pass lines:
`physics_spike` "SPIKE: overall PASS", `weld_spike` "WELD: …", `grab_spike` "GRAB: …",
`body_spike` "BODY: …", `collapse_spike` "COLLAPSE: …", `arm_spike` "ARM: overall PASS",
`sixdof_spike` "6DOF: overall PASS" (run FIRST with `weld_spike` whenever the addon binary changes).
All seven PASS 3× as of this handoff. Headless asset import:
`sixdof_spike` "6DOF: overall PASS" (run FIRST with `weld_spike` whenever the addon binary changes),
`truebody_spike` "TRUEBODY: overall PASS" (no physics; plain `--headless`). All eight PASS 3× as of this handoff. Headless asset import:
`godot-mono --headless --import`.
- Engine logs: `~/.local/share/godot/app_userdata/VR Messs/logs/godot.log` — **read after user
playtests instead of guessing**.
@@ -182,6 +182,32 @@ TERMINOLOGY (owner-corrected — use precisely):
connected (an obstructed segment shows as a displaced elbow). Hidden until PhysicsArms
calibrates. IK, calibration and reach budget moved to PhysicsArms; torso mesh + yaw spring
live in PhysicsTorso.
- **Calibration.cs + BodyProfile.cs (body step 1, 2026-10-02)** — ONE-TIME T-POSE CALIBRATION (owner design:
VRChat-FBT-like, persists, forced on launch if missing). `BodyProfile` = the player's real metrics (eye height,
stature = eye/0.936, shoulder half-width = 0.1295·stature, shoulder drop below the eyes, upper arm/forearm in
the 0.186:0.146 Winter split, stored 1.5 cm SHORT so the real elbow never locks first). `Prefs` stores it in
**`prefs.json` next to the executable (editor builds incl. headless: the project folder) — gitignored,
portable (copy the file)**. `Calibration` (node in group "calibration", `Recalibrate()` for the menu) loads it;
if missing it shows a head-locked Label3D and runs `TPoseDetector`: arms level (±8 cm), at shoulder height,
symmetric about the head (±10 cm), to the sides (not forward), span 1.0–2.4 m, every point within 3 cm of where
the hold began (jitter-proof) for 1 s → measure: arm = (wrist span − shoulder width)/2 − margin. Body systems
(PhysicsTorso, PhysicsArms, BodyRig, TrueBody) WAIT for `BodyProfile.Current`; recalibration rebuilds them.
Flat preview (no headset) can't calibrate → no body there (expected). Headless spikes that inject a head use
`BodyProfile.Estimate(eye)`; CollapseSpike sets a fixed standing profile (posture never shrinks the body).
- **ArmSolver.cs** — the shared analytic 2-bone solve (moved verbatim from PhysicsArms: soft IK 0.98, pole as an
angle about the arm axis vs torso-frame down/back/out, 85% controller-forward suggestion read nearest the current
angle, ±110° clamp, 6 rad/s, elbow height cap). NEW: minimum reach |upper − fore| + 1 cm (unequal anatomical
segments made the law of cosines invalid for hands right at the shoulder).
- **TrueBody.cs (body step 1)** — `TrueBodySolver`: Nimso's TRUE BODY — the derived pose of the real body from
head + controllers + profile (trigonometry, not IK): neck pivot 9 cm below / 7 cm behind the eyes (head frame);
chest yaw = the HMD/hands yaw spring; chest pitch = crouch lean (crouch measured at the NECK PIVOT so nodding
isn't crouching; up to ~43°) + ¼ of head pitch-down; neck base along chest-up/head-up average; pelvis hangs
0.288·stature below the shoulder line through a critically damped 2.5 Hz spring (hip lag/sway, hips kick back in
a crouch); MOVING SHOULDER GIRDLE: past 98% reach the shoulder joint travels toward the hand within anatomical
limits (out +3/in −2, up +7/down −2, forward +8/back −3 cm) plus a scapulohumeral shrug up to 4 cm overhead;
arms via ArmSolver. `TrueBody` node solves every physics tick and draws a translucent cyan DEBUG SKELETON
(`TrueBody.ShowDebug`) for in-headset judgement. Step 2 (muscle ragdoll) will drive its joints toward this pose.
Spike `scenes/truebody_spike.tscn`.
- **BodyMath.cs** — shared servo math. `RotationError(target, current)` = shortest-arc
axis·angle via `2·atan2(|xyz|, W)` — **the only allowed way to compute servo rotation
errors** (`Quaternion.GetAngle()` NaNs at W = 1.0000001, `GetAxis()` is un-normalized near
@@ -402,6 +428,9 @@ Floor StaticBody mask 1|3|4|5 (collision_mask 29).
## 7. Next steps (in order)
0. **Owner playtest of body step 1** — T-pose calibration (forced on first launch; delete `prefs.json` to redo,
or the menu's Recalibrate), arm length should now match; judge the cyan True Body skeleton (shoulders that
shrug/reach, crouch lean, hip sway, elbows). Then body step 2 = the muscle ragdoll on the 6-DOF joints.
1. **Owner re-playtest of the body (chest fix + Stage 2 arms)** — the Stage-1 collapse is
fixed (`collapse_spike` 49/49); feel is unvalidated in-headset. Read the log after.
2. **Locomotion decision** — owner report 2026-10-01: two-hand anchor turning broken (not