docs(skill): the surface table, rig anchors, and asserting the consequence

The skill is this project's own instructions, and three commits made it wrong:
it described a runtime that re-guessed what every surface was, a lab that only
tuned weapon holds, and a suite that could not see any of the last five defects.
It also still listed a seventh character that is not in skins.json.

Records what is new — the surface table and how it is decided, the layered
tuning files and why a fixed rotation on the weapon mount is the wrong answer,
the rig lab — and what the work taught:

  * assert the CONSEQUENCE. Asking a model which clip it is playing reads a
    variable it set on itself, and says "Idle" just as happily when nothing is
    ticking. Asking whether an anchor saved says nothing about whether the gun
    moved.
  * four of the last five real defects came from looking at a PNG. Every one of
    them passed every assertion.
  * the line-work rule now exists twice, at build time and as the runtime
    fallback, and they must be changed together or a model with a surface table
    starts rendering differently from one without.

Also records momo's broken idle and the stray Icosphere under known-unsolved,
with what is worth suspecting first in each case, since neither is fixed.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Nicholas Butzke
2026-07-27 15:00:42 -04:00
co-authored by Claude Opus 5
parent 99a2ee131d
commit 1e6f3001ac
4 changed files with 199 additions and 21 deletions
@@ -19,6 +19,7 @@ the convention):
| Cloth/hair bones carry NO animation keys; physics owns them | `export_optimize_animation_keep_anim_armature=False` |
| Physics colliders are a small set of capsules: thighs, shins, and a big one at the waist acting as a lid | 5 capsules, measured from the mesh (`_leg_colliders`) |
| Neighbouring skirt panels are linked sideways | 278 cross-panel distance links from shared vertices |
| Each surface is TAGGED with what it is, so shading can differ per class | `tools/surface_map.py` writes it; `SkinSurfaces` reads it |
| Cel shading with a ramp, plus a separate outline pass | `LevelMaterials.apply_toon_recursive` + `apply_character_look` |
Where we differ: their collider capsules and cloth parameters are hand-authored
@@ -26,6 +27,38 @@ per character by a technical artist. We MEASURE them from the model's own
geometry at build time, because there is no artist in this loop. That is the
whole reason `<model>.rig.json` exists.
Where there IS an artist in the loop, there is now somewhere to put the answer:
`debug/rig_lab.tscn` and the layered files behind it (see the SKILL). Measuring
is the default and hand-authoring is the override, rather than the other way
round.
## Separation is only half of it — the parts have to be NAMED
Keeping the meshes apart is structural. Knowing which is which is what lets
anything act on the difference, and until the surface table existed nothing did:
every surface of every character took one set of shading numbers, calibrated on
skin, because there was no way to ask whether a surface was hair.
The table lives in the sidecar as `surfaces`, keyed on the MATERIAL name — mesh
node names are `Object_7` through `Object_32` on every character in this game and
carry no meaning, while material names survive the glTF round trip intact and are
what the artist actually chose. It is decided three ways, in descending order of
how much it trusts them:
1. **the material name.** On VRoid exports it is formal —
`N00_000_00_Body_00_SKIN_Instance` carries its own class infix, and every
VRoid character here uses SKIN / FACE / EYE / HAIR / CLOTH.
2. **the weights.** Decisive when the name says nothing: a surface pulled by the
skirt chain is a skirt whatever it is called. The threshold is deliberately
low (5%), because VRoid welds the whole cap of the hair to the head bone and
springs only the strands — kiyoko's hair mesh is 85% head, and a majority rule
would call it skin.
3. **the material flags.** These catch line-work, which is the one class that is
not a surface of the character at all.
It is built from the same chains the spring solver uses, so the two can never
disagree about which bones are a skirt.
## Why the separation is load-bearing
**Materials.** The body wants skin shading, hair wants an anisotropic-ish ramp