Record the verified pose-capture recipe and the harness defect
Full build / Import + headless tests (push) Successful in 41s
Full build / Full desktop build + optional release (push) Skipped

This commit is contained in:
zwitschi committed 2026-11-03 11:44:55 +01:00
1 parent 1d4f718376
commit 88a0793bba
3 files changed
+32 -21

No files matched your search

+8 -6
View File
@@ -11,12 +11,14 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
direct `set_bone_pose_rotation` on HipL/KneeL/Spine/ShoulderL/ElbowL/Head changes rendered direct `set_bone_pose_rotation` on HipL/KneeL/Spine/ShoulderL/ElbowL/Head changes rendered
pixels; a bone *position* offset moves the head; a standalone probe running the same pixels; a bone *position* offset moves the head; a standalone probe running the same
`_animate()` sequence renders the crouch. A staged in-game `film` frame also showed the arms `_animate()` sequence renders the crouch. A staged in-game `film` frame also showed the arms
Standalone SceneTree probes (avatar under `root`, under a `Node3D` stage, and created Standalone SceneTree probes (avatar under `root`, under a `Node3D` stage, created
mid-`_process`) all render the seated crouch, so the rig and animation math are fine and the mid-`_process`) all render the seated crouch, and a `SubViewport` + `RenderingServer.force_draw()`
harness capture path is the outlier. A headless test now locks the proxy→bone contract probe does too (window probes diff ~2,200-2,360; offscreen 602 at +2/+6/+12 frames, including a
(`test_pose_reaches_the_skeleton_bones`, suite 273). Next leads: reproduce the stage inside re-created avatar). So the rig, the animation math and the offscreen path are sound; only
`capture_npc_review.gd` (known-good captures) or capture via `SubViewport` + `capture_npc_pose.gd` still saves rest frames, which makes its phase/camera/force-draw ordering
`RenderingServer.force_draw()`. the defect. A headless test locks the proxy→bone contract
(`test_pose_reaches_the_skeleton_bones`, suite 273). Rebuild the harness from the verified
recipe in `docs/3d-model-review.md` instead of iterating on the current phase machine.
- **3D review baseline (2026-11-03):** `docs/3d-model-review.md` records the first skill-guided - **3D review baseline (2026-11-03):** `docs/3d-model-review.md` records the first skill-guided
measurements: 46/46 assets clean, library 59,012 tris, characters 4,482–5,474 tris (68–69% of measurements: 46/46 assets clean, library 59,012 tris, characters 4,482–5,474 tris (68–69% of
the 8,000 caps), `npc_base` only 1,204/2,488, 24-NPC 6.94 ms, and authored texel density the 8,000 caps), `npc_base` only 1,204/2,488, 24-NPC 6.94 ms, and authored texel density
+7 -6
View File
@@ -64,12 +64,13 @@ except the rebuilt assets themselves.
frames still show the rest pose; direct bone writes deform the mesh, so this is a staging/ frames still show the rest pose; direct bone writes deform the mesh, so this is a staging/
capture discrepancy that may also affect live venue frames. See capture discrepancy that may also affect live venue frames. See
`docs/3d-model-review.md` §"Pose evidence is blocked". `docs/3d-model-review.md` §"Pose evidence is blocked".
- [ ] Finish the pose-capture diagnosis. Done so far: the pose reaches proxies and skeleton - [ ] Rebuild the pose harness from the verified recipe. Established: the pose reaches proxies
bones (new headless test, 273/273), direct bone rotations deform the rendered mesh, and and bones (headless test, 273/273), direct bone rotations deform the mesh, and both window
three standalone probes render the seated crouch — only `capture_npc_pose.gd` saves the probes and a `SubViewport` + `RenderingServer.force_draw()` probe render the crouch (offscreen
rest pose. Remaining leads: reproduce the stage inside `capture_npc_review.gd` (known to diff 602 at +2/+6/+12 frames, including a re-created avatar). The current harness still
render) or capture via `SubViewport` + `RenderingServer.force_draw()`, then add a scene saves rest frames, so its phase/camera/force-draw ordering is the remaining defect. Rebuild
test that a posed avatar renders differently from its rest frame. one pose at a time using the recipe in `docs/3d-model-review.md`, verify each pose visually,
then wire the sheets and fill the weights/pose column.
- [ ] UV/texel density: measure the 256² slot maps; decide whether atlas, UDIM or a resolution - [ ] UV/texel density: measure the 256² slot maps; decide whether atlas, UDIM or a resolution
change is justified. Do not change `CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` without re-running the change is justified. Do not change `CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` without re-running the
budget smoke and the Web/Compatibility path. budget smoke and the Web/Compatibility path.
+17 -9
View File
@@ -172,20 +172,28 @@ variants (full-body + shoulder views) and its output does not match the state it
| Check | Result | | Check | Result |
| --- | --- | | --- | --- |
| Pose reaches proxies | Seated pose logs `rig_y=-0.339`, `leg0=-1.357` | | Pose reaches proxies | Seated pose logs `rig_y=-0.339`, `leg0=-1.357` |
| Pose reaches skeleton bones | `Skeleton3D.get_bone_pose_rotation(HipL/KneeL)` differs from rest; new headless test `test_pose_reaches_the_skeleton_bones` locks it (suite 273/273) | | Pose reaches skeleton bones | `HipL`/`KneeL` bone rotations differ from rest; headless test `test_pose_reaches_the_skeleton_bones` locks it (suite 273/273) |
| Idle restores rest | Same test asserts the hip returns to its rest orientation | | Idle restores rest | Same test asserts the hip returns to its rest orientation |
| Skinning bound | One `Skeleton3D`, mesh bound to it (`same=true`); hiding `npc_base` removes the body | | Skinning bound | One `Skeleton3D`, mesh bound to it (`same=true`); hiding `npc_base` removes the body |
| Bone rotations deform the mesh | Direct writes change rendered pixels: Spine 1,719 / HipL 1,052 / KneeL 810 / ElbowL 745 / Head 671 / ShoulderL 640 sampled pixels | | Bone rotations deform the mesh | Direct writes change rendered pixels: Spine 1,719 / HipL 1,052 / KneeL 810 / ElbowL 745 / Head 671 / ShoulderL 640 sampled pixels |
| Bone translation deforms the mesh | A +0.5 m Head offset moves the head in frame | | Bone translation deforms the mesh | A +0.5 m Head offset moves the head in frame |
| Standalone probes render the pose | Three variants (avatar under `root`, under a `Node3D` stage, created mid-`_process`) all render the seated crouch (`posed` vs rest diff ≈ 2,200–2,360 samples) | | Standalone window probes render the pose | Three variants (avatar under `root`, under a `Node3D` stage, created mid-`_process`) render the seated crouch (diff ~2,200-2,360 samples) |
| The harness renders the pose | **No.** All 8 variants × 4 poses save the rest pose while the logged state and bone poses are posed | | Offscreen capture renders the pose | A `SubViewport` + `RenderingServer.force_draw()` probe renders the crouch reliably (diff 602 at +2/+6/+12 frames), including a re-created second avatar |
| The harness renders the pose | **No** in its current form: same stage, same pose, rest frames. One ordering (`_frame_view()` immediately before `force_draw()`) produced posed walk frames once, then regressed to rest/garbage on the next run |
The cause is therefore in the harness's capture path, not in the rig or the animation math. Two The rig, the animation math and the offscreen render path are all sound. The remaining failure is
leads are left and both are cheap: reproduce the stage inside a harness that is known to render specific to the harness's frame/phase ordering around staging, camera framing and `force_draw()`.
correctly (`capture_npc_review.gd` already produces 96 valid frames), or capture through a Do not keep guessing: rebuild the harness from the working recipe below, one pose at a time, and
`SubViewport` with `RenderingServer.force_draw()` so the frame cannot lag the state. Until a verify each pose before wiring up the sheets.
scene test asserts that a posed avatar renders differently from its rest frame, do not treat pose
sheets as evidence and do not claim rig deformation in the review. Working recipe (verified by the probes; keep it when rebuilding):
1. Create a `SubViewport` (1280x720, `own_world_3d = true`, `UPDATE_ALWAYS`) in `_initialize`.
2. Build the studio (environment, key/fill light, ground, camera) and one avatar inside it.
3. Wait 2-3 frames so the skinned mesh binds.
4. Apply the pose with `_animate()` ticks, with the avatar's process/physics disabled.
5. Call `RenderingServer.force_draw()`, then `viewport.get_texture().get_image()`.
6. Free the avatar and repeat for the next pose; a re-created avatar also rendered correctly in a probe.
## Ranked candidate gaps ## Ranked candidate gaps