feat(pipeline): grow skirt and hair bone chains for a costume that has none

Miku shipped with 0 cloth chains against Taila's 35, so her twin tails
hung off her skull like a helmet. Nothing downstream could fix it: the
spring solver simulates cloth BONES, and a garment with none is welded to
whatever body bone it was weighted to. Every auto-rigged model is in that
state, and her source was an unrigged mesh.

tools/cloth_bones.py builds them, which is the job a technical artist does
by hand on a model like this. It finds the geometry by MATERIAL SLOT — the
artist already answered which surface is hair, and on a joined mesh (what
the auto-rig leaves behind) the slot is the only separation left. Hair is
split into connected islands, because a strand is a connected piece of
surface and clustering by position would merge two ponytails passing near
each other. A skirt is split into radial wedges instead, because a skirt
is ONE connected surface and islands would return the whole thing as a
single piece — the bell-shaped failure. Each clump gets a polyline fitted
down its middle by binning vertices by distance and taking centroids, so
the chain follows the piece's own curve rather than cutting the corner on
a bend, and vertices are re-weighted onto it while the first 22% keeps its
original body weight so the scalp stays on the skull.

On Miku: 19 chains, 57 bones from one `hair` slot. Sidecar 0 -> 19 chains.
Idle stability 0.007-0.018 deg/frame. Mesh intact, verified by render.

Opt-in, via `pipeline.py --grow-cloth`, and run before the retarget so
describe_rig() finds the chains by name exactly as it would an artist's.

Known limits, recorded in the skill: it cannot find a garment sharing a
material with the body (Miku's skirt is on her `body` slot, so she got
hair and no skirt), and grown chains are a fallback — an artist's chains
carry intent that no geometric fit recovers.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Nicholas Butzke
2026-07-26 13:19:44 -04:00
co-authored by Claude Opus 5
parent 4559a1adc3
commit 7892669319
9 changed files with 9533 additions and 136 deletions
Binary file not shown.
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -3,7 +3,7 @@
{
"id": "miku",
"name": "Miku",
"description": "Hatsune Miku \u2014 Virtual Idol",
"description": "",
"model": "res://assets/characters/skins/miku.glb",
"unlocked": true
},
Binary file not shown.
File diff suppressed because it is too large Load Diff