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]>
Character pipeline (tools/):
- sketchfab_import.py: search/download via Sketchfab Download API with
license/attribution tracking (SKETCHFAB_API_TOKEN)
- autorig.py: headless Blender auto-rig — fits a Mixamo-named skeleton
to unrigged humanoids, binds automatic weights
- merge_animations.py: merges the shared Mixamo-skeleton animation
library onto any rigged character, strips root motion, canonical names
- pipeline.py: one command chains download -> rig -> animate -> register
In-game skin system:
- SkinManager autoload reads skins.json (auto-written by the pipeline)
- SkinnedPlayerModel rewritten: canonical clips with fallback chains,
blend times, speed-scaled locomotion, weapon bone attachment
- First-person: full animated body for the owner, head hidden via
SkeletonModifier3D; third-person: full model for other players
- Skin selector in main menu; skin id synced in multiplayer
- Fixed GLBLoader crash (GLTFDocument.get_animation_count doesn't exist)
Multiplayer sync overhaul:
- Movement is now client-authoritative: the owning peer simulates
locally (no input round-trip), server keeps health/kills/death
- Remote players interpolate synced_position/velocity with
extrapolation and snap-on-teleport
- Knockback/impulses routed to the simulating peer
Audio:
- AudioManager autoload: SFX/Weapons/Footsteps/UI/Music buses, pooled
3D players, variation + pitch randomization, auto-registration from
assets/sounds
Docs: 3D_ASSET_PIPELINE.md rewritten end-to-end, new ASSET_SOURCES.md
(non-procedural animation/map/sound sources) and SOUND_DESIGN.md.
Verified with debug/spawn_smoke_test.gd (headless: 24/24 checks pass).
Co-Authored-By: Claude Fable 5 <[email protected]>