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:
co-authored by
Claude Opus 5
parent
4559a1adc3
commit
7892669319
@@ -0,0 +1,76 @@
|
||||
# Growing cloth bones on a model that has none
|
||||
|
||||
`tools/cloth_bones.py`, opt-in via `pipeline.py --grow-cloth`.
|
||||
|
||||
## When you need it
|
||||
|
||||
The spring solver simulates cloth **bones**. A garment with none is welded to
|
||||
whatever body bone it was weighted to, and no runtime setting changes that. Every
|
||||
auto-rigged model is in this state, and so is many a "rigged" download whose
|
||||
skeleton is body-only.
|
||||
|
||||
Check before importing (no Blender needed — see `separation.md`): if the joint
|
||||
list has nothing matching `hair|skirt|tail|ribbon`, the costume will not move.
|
||||
|
||||
## What it does
|
||||
|
||||
Runs on the **rigged** model, before the retarget.
|
||||
|
||||
1. **Finds the geometry by material slot.** A slot called `hair` is hair. The
|
||||
artist already answered the question, and on a joined mesh — which is what the
|
||||
auto-rig leaves behind — the material slot is the only separation left.
|
||||
2. **Splits it into clumps.**
|
||||
- *Hair*: connected islands over the mesh's own edges. A strand is a connected
|
||||
piece of surface; clustering by position would merge two ponytails passing
|
||||
near each other and split one that bends.
|
||||
- *Skirt*: radial wedges around the body's up axis. A skirt is ONE connected
|
||||
surface, so islands would return the whole thing as a single piece — which
|
||||
is the bell-shaped failure. Wedges are the ZZZ-convention panel grid.
|
||||
3. **Fits a polyline down each clump** by binning vertices by distance from the
|
||||
anchored end and taking each bin's centroid, so the chain follows the piece's
|
||||
own curve. A straight root-to-tip line cuts the corner on a bent ponytail and
|
||||
every vertex on the outside of that bend ends up on a bone travelling the
|
||||
wrong way.
|
||||
4. **Builds a bone chain along the polyline**, parented to the body bone that was
|
||||
already holding that geometry.
|
||||
5. **Re-weights** each vertex onto the two bones either side of where it projects,
|
||||
blended by how far between them it lands — while keeping the ORIGINAL body
|
||||
weight over the first `ROOT_BLEND` (22%) of the chain, so the scalp stays on
|
||||
the skull and the waistband stays on the hips.
|
||||
|
||||
Then `retarget.py::describe_rig` finds the chains by name exactly as it would an
|
||||
artist's, and writes tips, hulls and neighbours to the sidecar.
|
||||
|
||||
## Tuning
|
||||
|
||||
```
|
||||
--hair-segments 3 bones per hair strand
|
||||
--skirt-segments 4 bones per skirt panel
|
||||
--skirt-panels 12 radial panels; more = opens around a leg more smoothly
|
||||
--classes hair,skirt which material names to look for
|
||||
```
|
||||
|
||||
`MIN_STRAND_LENGTH` (60 mm) and `MIN_STRAND_VERTS` (12) drop fringe and
|
||||
ornaments. Simulating those costs the same as a ponytail and only ever produces
|
||||
jitter around the face.
|
||||
|
||||
## Worked result: Miku
|
||||
|
||||
```
|
||||
19 chains, 57 bones grown from one `hair` material slot
|
||||
sidecar: 0 cloth chains -> 19
|
||||
idle stability: 0.007-0.018 deg/frame (quiet)
|
||||
mesh intact, no tearing (debug/character_look_capture.gd)
|
||||
```
|
||||
|
||||
## Limits
|
||||
|
||||
- **It cannot find a garment that shares a material with the body.** Miku's skirt
|
||||
is on her `body` slot, so she got hair chains and no skirt. Splitting by
|
||||
geometry rather than by material would be the next step.
|
||||
- **It does not set `weights_authored`.** An auto-rigged model still gets the
|
||||
destructive load-time `SkinLegRepair`. That repair only touches cross-leg
|
||||
vertices, so it leaves hair alone, but it is worth knowing.
|
||||
- **Grown chains are a fallback, not a substitute for a rigged source.** They
|
||||
follow the geometry, but an artist's chains carry intent — where a panel should
|
||||
split, which strands move together — that no fit recovers.
|
||||
Reference in New Issue
Block a user