tools: measure rest poses, and diagnose momo

momo's idle plays with her arms overhead and her waist pinched, and every
assertion in the suite passes on her.

The first suspicion was the retarget's rest-relative delta: it applies "what the
clip does to the LIBRARY's rest" to THIS rig's rest, which quietly assumes the
two rests are alike. rest_pose_check.gd tests that, and disproves it — miku's
arms rest 41° off the library's and taila's 32°, and both animate correctly. The
delta retarget handles a rest-pose difference, which is what it is for. Recorded
in the reference so nobody spends that hour again.

Writing the tool reproduced this project's own recurring mistake in miniature.
Measuring "the direction from a bone to its first child" reported kiyoko's and
aria's legs 71° off the library — because a thigh's first child is as likely to
be a skirt bone as a shin, and it was measuring the hang of a skirt panel.
Pointing it at the next limb BY ROLE dropped both to 1°.

What momo actually has: `Root_001` through `Root_007` are in her driven_bones,
and they are her HAIR roots — Hair_A is dominated by Root_001_001, Root_007 and
Root_005. The animation is keying bones the spring solver is supposed to own,
which is non-negotiable #2 broken by the role resolver rather than by a clip.
The surface table corroborates it: her hair surfaces report 1.8% and 9.4% chain
share, because most of their vertices belong to bones in no chain at all.

Not fixed here. `Root_00N` matches no COSMETIC stem, and adding "root" to the
stems would cost a rig whose actual root is called `Root` its hips. The
structural fix is that a bone whose geometry is dominated by a mesh classified
`hair` is a hair bone whatever it is called — which the surface table makes
answerable at build time, and did not when momo was imported. It needs a Blender
re-run and re-verification of all six characters.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Nicholas Butzke
2026-07-27 15:03:33 -04:00
co-authored by Claude Opus 5
parent 1e6f3001ac
commit 700d0925d7
3 changed files with 148 additions and 5 deletions
+23 -5
View File
@@ -250,11 +250,29 @@ The six in `skins.json`, with what `surface_class_check` reports:
still runs its legs ~40° off the direction of travel.
- **momo's idle pose is wrong** — arms overhead and a pinched waist. Every
assertion passes on her: she loads, animates, classifies and mounts a weapon.
It shows up only in `roster_capture`. Her source rig is the reason to suspect —
its bones are auto-named (`Item_O_Sphere_005`, `Unused_Noname_004`,
`Root_001_001`), so the role resolver has almost nothing to go on, and a limb
role claimed by the wrong bone would look exactly like this. Start by dumping
her resolved roles against her skeleton.
It shows up only in `roster_capture`.
**Diagnosed, not fixed.** Her `driven_bones` contains `Root_001` through
`Root_007` — and those are her HAIR roots. The Godot surface dump shows
`Hair_A` dominated by `Root_001_001:3203`, `Root_007:3203`, `Root_005:2642`.
So the animation is keying bones that physics is supposed to own, which is
non-negotiable #2 being violated by the role resolver rather than by a clip.
The corroboration is in the surface table: her hair surfaces report only 1.8%
and 9.4% chain share, because most of their vertices belong to `Root_00N`,
which is in no chain at all.
`Root_00N` matches no COSMETIC stem, so `is_cosmetic` does not catch it and
nothing keeps it out of the driven set. Fixing it by adding "root" to the
stems would be wrong — a rig whose actual root is called `Root` would lose its
hips. The fix is structural: a bone whose geometry is dominated by a mesh
classified `hair` is a hair bone, whatever it is called. The surface table
now makes that answerable at build time, which it was not when this rig was
imported. Not attempted here — it needs a Blender re-run and re-verification
of all six characters.
Her `head` role is also wrong (`Unused_Noname_010`, when a real `Head` bone
exists and is in her spine chain), and her spine chain runs two junk bones
PAST the head. Probably the same import; worth fixing in the same pass.
- **A stray `Icosphere` ships inside every skin GLB** — 42 vertices, no parent,
no vertex groups. It rides in from the animation library. Harmless, and now
skipped by construction rather than by name in `surface_map`, but the export
@@ -47,6 +47,22 @@ Two related traps:
| `anim_capture.gd` / `orbit_capture.gd` | renders, for looking | — |
| `roster_capture.gd` | one photo of every character, from the picker | — |
| `ui_capture.gd` | one photo of every menu screen | — |
| `rest_pose_check.gd` | each rig's bind-pose limb directions vs. the library's | see below |
## What `rest_pose_check` actually established
It was written to test a suspicion — that the rest-relative retarget silently
assumes both rigs rest alike — and it disproved it. Miku's arms rest **41°** off
the animation library's and Taila's **32°**, and both animate correctly. The
delta retarget handles a rest-pose difference, which is what it is for. Do not
go looking there again.
It also demonstrates the measurement trap in miniature. Written as "the direction
from a bone to its FIRST CHILD", it reported kiyoko's and aria's legs 71° off —
because a thigh's first child is as likely to be a skirt bone as a shin, and it
was measuring the hang of a skirt panel. Pointing it at the next limb BY ROLE
dropped both to 1°. The same rule as everywhere else in this pipeline: resolve
roles, never take whatever the rig happens to hand you.
## Assert the consequence, not the plumbing