Body Stage 1 failed first playtest (NaN solver blowup: seated chest-in-table + two-handed anvil weld); failure brief for external agent; docs marked, body track blocked pending diagnosis
This commit is contained in:
1 parent
4fdfcfd928
commit
ce1e0e3b0c
3 files changed
+167
-17
No files matched your search
@@ -228,18 +228,25 @@ device only; the editor has an XR simulator for dev. Trackers/action map: see `x
|
||||
(arm IK, IK stability/deep, grabbing, two-handed grab/Box3D internals, addon weld patch,
|
||||
body physics hugs, body physics Godot facts) — READ before re-researching or changing
|
||||
grab/IK/body. Stink-O's box3d-godot (custom components) = documented plan-B.
|
||||
8. **Body physics Stage 1 — PHYSICAL CHEST, implemented 2026-10-01.** `PhysicsTorso.cs`:
|
||||
8. **Body physics Stage 1 — PHYSICAL CHEST, implemented 2026-10-01, FAILED first playtest.**
|
||||
`PhysicsTorso.cs`:
|
||||
one chase-driven RigidBody3D (30 kg box, gravity off, CCD) toward an HMD-derived target —
|
||||
the PhysicsHands PD pattern at body mass (2400 N budget), orientation = slewed
|
||||
angular-velocity servo (self-rights, holds yaw), 2 m snap safety, torso-yaw spring moved
|
||||
here from BodyRig. Camera stays KINEMATIC — torso chases the head, never the reverse
|
||||
(Boneworks sickness lesson). No self-collision with own hands (layer scheme above).
|
||||
`BodyRig.cs` is VISUALS-only now (head cube + 2-bone analytic arms, soft IK, elbow pole,
|
||||
**First in-headset session NaN'd the solver** (owner seated: chest overlapped the table;
|
||||
two-handed 50 kg anvil weld pressed into it → `!v.is_finite()` transform flood, "head
|
||||
detaching from the body"). **DO NOT extend the body track until diagnosed** — analysis,
|
||||
hypotheses and the headless-repro plan live in `research/body-stage1-failure-brief.md`
|
||||
(self-contained, forwardable to the owner's external agent). `BodyRig.cs` is
|
||||
VISUALS-only (head cube + 2-bone analytic arms, soft IK, elbow pole,
|
||||
reach budget feeding `PhysicsTorso.ReachOffset`). Headless regression:
|
||||
`scenes/body_spike.tscn` must print "BODY: overall PASS" (also proves the Box3D pair
|
||||
rules: one-way contact, hand-through-chest exclusion, prop contact). Stage 2 (physical
|
||||
arm chains + hierarchical per-joint budgets, Nimso/Boneworks tier) awaits the owner's
|
||||
Stage-1 feel verdict; design + prior art in `research/vr-body-physics-hugs.md`,
|
||||
rules: one-way contact, hand-through-chest exclusion, prop contact) — note its repro gap:
|
||||
no static-table overlap, no welded heavy props. Stage 2 (physical
|
||||
arm chains + hierarchical per-joint budgets, Nimso/Boneworks tier) is BLOCKED on the
|
||||
Stage-1 fix; design + prior art in `research/vr-body-physics-hugs.md`,
|
||||
stack facts in `research/vr-body-physics-godot.md`.
|
||||
9. **Getting a body (rig) — placeholder v1.3 (cube character, agent-built).** `BodyRig.cs`:
|
||||
all-BOX primitives (owner preference) — head 0.15³ behind the eyes, slim torso retracted,
|
||||
|
||||
+26
-12
@@ -120,7 +120,10 @@ TERMINOLOGY (owner-corrected — use precisely):
|
||||
grab/teleport; `IsPanelCollider(node)` identifies interactive colliders for lasers.
|
||||
- **TeleportMarker.cs** — `scenes/teleport_marker.tscn`: disc + beam + Label3D, group
|
||||
"teleport_marker", StaticBody pick box. `SetNear` hides visuals, `SetHighlighted` swaps colors.
|
||||
- **PhysicsTorso.cs** — the player's PHYSICAL CHEST (body-physics Stage 1, implemented 2026-10-01).
|
||||
- **PhysicsTorso.cs** — the player's PHYSICAL CHEST (body-physics Stage 1). **Implemented
|
||||
2026-10-01; FAILED first in-headset playtest (seated chest-vs-table overlap + two-handed
|
||||
50 kg anvil weld → NaN transform flood) — see §5 known issues +**
|
||||
**`research/body-stage1-failure-brief.md` before touching anything here.**
|
||||
One RigidBody3D box (0.22×0.5×0.13, 30 kg, gravity off, CCD, contact monitor), chase-driven
|
||||
toward an HMD-derived target: target = head − 6 cm back + torso-yaw basis·(0, −0.42, +0.06)
|
||||
+ `ReachOffset` (fed by BodyRig's reach budget). Linear drive = the PhysicsHands pattern at
|
||||
@@ -213,6 +216,19 @@ Floor StaticBody scans layer 3 (collision_mask 13).
|
||||
|
||||
## 5. Known issues / open questions
|
||||
|
||||
- **Body Stage 1 (chest) FAILED in-headset on first playtest (2026-10-01) — DO NOT extend the
|
||||
body track until diagnosed.** Owner report: "it quickly collapsed with my head detaching
|
||||
from the body"; screenshot = prop table chaos. Log evidence: session calibrated at head
|
||||
height **1.01 m (seated)** → chest box overlaps the table; the NaN flood
|
||||
(`!v.is_finite()` at `instance_set_transform`, 22.8k lines) starts right after
|
||||
**"grabbed 'Anvil · 50 kg' (50 kg, left, two-handed)"** — i.e. chest-vs-statics overlap +
|
||||
50 kg prop rigid-welded to both 90 N hands pressed into that mess. Headless body spike
|
||||
passes (it lacks static-table overlap + welded heavy props — that's the repro gap).
|
||||
Full self-contained analysis for the owner's external agent:
|
||||
**`research/body-stage1-failure-brief.md`** (evidence chain, hypotheses H1–H3, mitigation
|
||||
candidates M1–M5, headless-repro plan). Working suspicion: the documented
|
||||
"script velocity SETs + reaction forces = limit cycle" lesson (grab v5.0) resurfacing at
|
||||
body scale — 30 kg velocity-write servo braced against statics.
|
||||
- **AnchorMoveMode NEEDS FIXING (owner playtest report, 2026-10-01)** — two-hand turning
|
||||
is NOT working as intended (not 2-anchor based) and simultaneous two-hand movement acts
|
||||
weird. NOTE before fixing: owner is considering replacing grab-locomotion with physics
|
||||
@@ -295,17 +311,15 @@ Floor StaticBody scans layer 3 (collision_mask 13).
|
||||
|
||||
## 7. Next steps (in order)
|
||||
|
||||
1. **Full physics body track — Stage 1 (chest body) IMPLEMENTED, validate in-headset** —
|
||||
`PhysicsTorso.cs` is live: chase chest (30 kg, 2400 N), yaw spring moved in, collision
|
||||
layers reworked (3 = body, 4 = hands, no self-collision). Owner playtests: chest contact
|
||||
with props (lean a crate on it, catch the bowling ball against it), shove response
|
||||
(props push the chest, it recovers — camera never moves), turn following (torso lags
|
||||
head naturally), weight feel. Hug testing on props as agreed. Headless: `body_spike`
|
||||
PASS 3×; all other spikes still PASS 3×. **Stage 2 (arm chains, hierarchical budgets,
|
||||
Nimso/Boneworks tier) awaits Stage-1 feel verdict** — see
|
||||
`research/vr-body-physics-hugs.md` §5/§7 + `research/vr-body-physics-godot.md`.
|
||||
Tuning knobs live in HANDOFF §6 (PhysicsTorso rows); first levers if feel is off:
|
||||
`MaxForce` (gives more/less), `OrientKp` (torso yaw stiffness).
|
||||
1. **Diagnose + fix the body Stage-1 collapse (BLOCKS the body track)** — first playtest of
|
||||
the physical chest NaN'd the solver (seated chest-in-table overlap + two-handed 50 kg
|
||||
anvil weld pressed into it; "head detaching from the body"). All context, hypotheses,
|
||||
mitigation candidates and the headless-repro plan are in
|
||||
`research/body-stage1-failure-brief.md` — written self-contained so the owner can forward
|
||||
it to their external agent verbatim. First move per the brief: extend `body_spike` with
|
||||
the exact cocktail (chest target overlapping a static slab + two welded 90 N hands
|
||||
dragging a 50 kg box into the chest) and get a deterministic NaN repro before touching
|
||||
PhysicsTorso.cs. Owner re-playtests feel only after the fix.
|
||||
2. **Locomotion decision** — owner report 2026-10-01: two-hand anchor turning broken (not
|
||||
2-anchor based), simultaneous two-hand movement acts weird, teleport + lasers broken
|
||||
(experiments, not current issues). Owner's candidate replacement: physics thumbstick
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# Body Stage 1 failure brief — chest collapse in-headset (2026-10-01)
|
||||
|
||||
SELF-CONTAINED brief for an external agent (zero project context assumed). Godot 4.7.2 mono,
|
||||
C#, PCVR at 90 Hz (physics tick = 90), physics engine = **Box3D** (bearlikelion's
|
||||
`godot-box3d` GDExtension — a full PhysicsServer3D replacement; locally patched so Godot's
|
||||
ConeTwistJoint3D maps to Box3D's `b3WeldJoint` with rigid stiffness at bias 0). Everything
|
||||
below is verified code or log evidence unless marked [H] (hypothesis).
|
||||
|
||||
## What was just built (body-physics Stage 1)
|
||||
|
||||
The player's virtual chest became a physical body. `PhysicsTorso.cs` (Node) spawns:
|
||||
|
||||
- One RigidBody3D, box 0.22×0.5×0.13 m, **Mass 30**, GravityScale 0, CCD on, contact monitor,
|
||||
CollisionLayer = layer 3 (player body), CollisionMask = layers 1|3 (world+props | body).
|
||||
- Every physics tick (90 Hz), target = head position (from the HMD camera node, kinematic)
|
||||
minus 6 cm along flattened camera forward, then offset by the torso-yaw basis (a yaw-only
|
||||
basis smoothed toward a blend of HMD yaw and hands-midpoint yaw) by (0, −0.42, +0.06),
|
||||
plus a small "reach offset" fed by the visual rig (≤ 0.25 m horizontal clamp).
|
||||
- Linear drive — force-clamped PD, the same pattern the hand bodies use:
|
||||
```csharp
|
||||
Vector3 desiredVelocity = (target - _body.GlobalPosition) / dt; // dt = 1/90
|
||||
if (desiredVelocity.Length() > MaxSpeed) desiredVelocity = desiredVelocity.Normalized() * MaxSpeed; // 6
|
||||
Vector3 dv = desiredVelocity - _body.LinearVelocity;
|
||||
Vector3 force = dv / dt * TorsoMass; // 30 kg
|
||||
if (force.Length() > MaxForce) force = force.Normalized() * MaxForce; // 2400 N
|
||||
_body.LinearVelocity += force / TorsoMass * dt; // DIRECT velocity write
|
||||
```
|
||||
- Orientation drive — slewed angular velocity servo toward the yaw target:
|
||||
```csharp
|
||||
Quaternion qErr = torsoBasisQ * _body.GlobalTransform.Basis.GetRotationQuaternion().Inverse();
|
||||
if (qErr.W < 0f) qErr = -qErr; // shortest arc
|
||||
Vector3 spinDesired = axis(qErr) * min(angle(qErr) * OrientKp /*20*/, OrientMaxSpin /*10*/);
|
||||
Vector3 wErr = spinDesired - _body.AngularVelocity;
|
||||
if (wErr.Length() > OrientMaxAlpha * dt /*150/90*/) wErr = wErr.Normalized() * OrientMaxAlpha * dt;
|
||||
_body.AngularVelocity += wErr; // DIRECT angular velocity write
|
||||
```
|
||||
- Snap safety: if body-to-target distance > 2 m → teleport body to target, zero velocities.
|
||||
- Head/camera stays kinematic; the torso chases the head, never the reverse.
|
||||
|
||||
Context bodies:
|
||||
- **Hands**: two RigidBody3D palms (0.4 kg, layer 4, mask 1|4), same velocity-write chase at
|
||||
90 N, rotation hard-locked via angular velocity sets (cap 120 rad/s). They chase XR
|
||||
controllers. Stuck-snap and teleport-snap safeties are SUSPENDED for a hand while it grips.
|
||||
- **Grab**: grabbing a prop creates a ConeTwistJoint3D (= b3WeldJoint, bias 0 = RIGID,
|
||||
locks relative pose) between hand body and prop. Two-handed grab = prop welded to BOTH
|
||||
hands rigidly. Props: layer 1, mask 1|3|4. Statics (floor/table): layer 1.
|
||||
- **No self-collision**: hands (4) and chest (3) exclude each other both ways. Chest DOES
|
||||
collide with props (layer 1) and static world (layer 1) — deliberate, chest is the
|
||||
contact surface for hugs/leaning.
|
||||
- Headless regression (body spike: track/yaw/5kg-impact/teleport/self-right) PASSES 3× —
|
||||
but the spike has NO static table and NO welded heavy prop touching the chest.
|
||||
|
||||
## The failure (owner playtest, first session with the chest)
|
||||
|
||||
Owner report: "it quickly collapsed with my head detaching from the body". Screenshot shows
|
||||
the prop table in chaos (props piled/thrown, panel knobs displaced). Log
|
||||
(`godot.log`, ~45.7k lines, 22.8k of them the error below):
|
||||
|
||||
- Calibration line: **head height 1.01 m** → the player was SEATED/crouching (standing
|
||||
calibration would be ~1.6-1.7 m). Chest target therefore ≈ 0.59 m height; chest box spans
|
||||
≈ 0.34-0.84 m; the prop table top is at 0.70-0.75 m → **the seated chest overlaps the
|
||||
table geometry** (chest mask includes statics). [H] constant penetration + 2400 N servo.
|
||||
- Normal play: grabbed/released bowling ball, brick, apple — all fine.
|
||||
- Then: `grabbed 'Anvil · 50 kg' (50 kg, left)`, released, then
|
||||
**`grabbed 'Anvil · 50 kg' (50 kg, left, two-handed)`** — and within ticks the log floods:
|
||||
```
|
||||
ERROR: Condition "!v.is_finite()" is true.
|
||||
at: instance_set_transform (servers/rendering/renderer_scene_cull.cpp:1000)
|
||||
```
|
||||
×22,829 — some node's transform went NaN/Inf (rendering rejects it). The visual result
|
||||
matches the chest body (and/or anvil) transforming to garbage → the torso mesh vanishes/
|
||||
teleports → "head detached from the body" (head cube rides the camera, body went away).
|
||||
- Owner kept grabbing/releasing the anvil afterwards (releases logged at 0.00-0.01 m/s —
|
||||
the anvil was braced/nearly immobile), then removed the headset (focus lost).
|
||||
|
||||
## Hypotheses (ranked)
|
||||
|
||||
- **H1 — solver blowup from stacked authorities [most likely]**: the chest (velocity-write
|
||||
servo, 2400 N) pressed against the STATIC table (seated overlap) while the 50 kg anvil
|
||||
rigid-welded to both 90 N velocity-write hand bodies was grabbed/moved into that mess.
|
||||
Three script-side velocity servos + rigid welds + persistent static contact in one
|
||||
constraint island. Box3D's solver gets conflicting infinite-stiffness demands → energy
|
||||
injection → NaN. This project has PRIOR ART for exactly this class: documented in
|
||||
`research/vr-two-handed-grab.md` — "script velocity SETs + reaction forces = limit
|
||||
cycle" (grab v5.0-era instability). The hands survive the same pattern only because
|
||||
they're 0.4 kg, rarely braced, and have stuck-snap escape hatches. The chest is 30 kg,
|
||||
overlaps the table whenever the owner sits at it, and has no escape.
|
||||
- **H2 — chest vs table penetration alone**: even without the anvil, seated chest-in-table
|
||||
= constant deep penetration vs a static body; the velocity servo never yields (position
|
||||
error persists → max force every tick) → pressure + friction against statics pumps the
|
||||
solver. The anvil grab was just the straw.
|
||||
- **H3 — CCD tunneling/spike**: 30 kg body with CCD in deep contact + welded 50 kg chain —
|
||||
CCD contact generation bug under stress. [H] least likely but cheap to test.
|
||||
|
||||
## Candidate mitigations (evaluate, don't just pick one)
|
||||
|
||||
- **M1 — ApplyForce instead of velocity writes for the chest** (solver-owned integration;
|
||||
keeps the 2400 N clamp). The hands keep velocity writes (they have escape hatches).
|
||||
Boneworks' design rule "no infinite forces" supports solver-integrated forces.
|
||||
- **M2 — collision-aware target or compliance**: if the chest is in persistent contact
|
||||
(contact monitor reports statics) with saturated force, soften (reduce effective budget)
|
||||
or stop pushing (target clamps out of penetration). Generic "yield when braced".
|
||||
- **M3 — seated reality**: the chest target height derives purely from HMD (−0.42). When
|
||||
seated at a table the body legitimately overlaps world geometry. Options: chest collision
|
||||
disabled vs statics (mask 3 only — loses leaning on tables), or per-body contact disabling
|
||||
when penetration is self-inflicted (chase target inside a static), or pushing the TABLE
|
||||
(chest becomes heavy enough to matter? 30 kg vs static = never).
|
||||
- **M4 — saturation watchdog**: like the hands' stuck-snap: if force saturated AND position
|
||||
error persistent AND in contact for > N ms → log + temporarily reduce budget / exclude the
|
||||
offending pair. Keeps M1's solver honesty while adding the escape hatch.
|
||||
- **M5 — reproduce headless first**: extend the body spike with the exact cocktail — chest
|
||||
target set so the box overlaps a static table slab; a 50 kg box welded rigid to two
|
||||
0.4 kg "hand" bodies (90 N chases) pressed into the chest; then sweep. A deterministic
|
||||
NaN repro beats every hypothesis debate. The full grab spike already emulates hands+welds
|
||||
headless (GrabSpike.cs pattern) — compose it with the chest.
|
||||
|
||||
## Questions to answer
|
||||
|
||||
1. Can a deterministic headless repro be built (M5)? If yes → does M1 alone stop the NaN?
|
||||
2. Is the velocity-write chase on a contact-rich 30 kg body categorically unsafe on Box3D,
|
||||
or only under simultaneous weld+static stacking? (Box3D source available in .vendor/.)
|
||||
3. What does the solver do with direct LinearVelocity writes on a body that is ALSO in an
|
||||
active contact manifold — are the writes applied pre- or post-solve, and can the contact
|
||||
impulse + next-tick write loop escalate?
|
||||
4. Seated-at-table is a first-class scenario for this product (scene viewer). What's the
|
||||
right long-term chest-vs-world story (M2/M3) that still allows leaning on tables?
|
||||
|
||||
Owner directive: painlessness bar = no multi-iteration fundamental sagas. Deliver the
|
||||
analysis, the repro, and the smallest honest fix — the owner playtests feel afterwards.
|
||||
Reference in new issue
Block a user