Everything about getting an anime-styled character into the game — import, stylization, the body/garment/hair separation, rigging, retargeting, cloth and hair physics, and how to measure any of it — collected into .claude/skills/character-pipeline/. Organised around the principle the Hoyoverse-class pipelines are built on and that every failure in this project traced back to: a character is not one object. It is a body, a set of garments and hair, authored and rigged separately and moved by different systems. The body is skinned and animated; the garments and hair are bone chains the animation never touches and physics moves. The skill's non-negotiables are the four ways that separation has been destroyed here before — joining meshes, keying cosmetic bones, skinning cloth to a leg, and running the auto-rig repair on authored weights. references/verification.md leads with the trap that invalidated every cloth measurement ever taken in this repo: Godot restores bone poses after the modifier pass, so a tool that reads them afterwards measures the animation and never sees what any modifier did. Also records what is known-unsolved, with numbers: peak cloth clipping in a run/slide/dash, no foot IK, no strafe clips. Co-Authored-By: Claude Opus 5 <[email protected]>
4.3 KiB
Verification — and the trap that invalidated all of it
READ THIS FIRST
Godot restores every bone's local pose after the SkeletonModifier3D pass.
So calling force_update_all_bone_transforms() and reading
get_bone_global_pose() from a SceneTree script, from _process, or anywhere
outside that pass recomputes the globals from the animation alone. The
shooter pose layer and the cloth solver are simply not in what you measure.
debug/cloth_clip_check.gd did exactly this. It reported the same ~95 mm of
leg-inside-skirt with collision fully enabled and with the collision call
commented out. Every number ever taken from that tool before 2026-07-26 is
void, and several rounds of "tuning did nothing" in the history were reading a
pose the solver never touched.
To measure a pose layer, add your own SkeletonModifier3D as a child of the
Skeleton3D AFTER the one you care about, and snapshot inside its
_process_modification(). The PoseProbe class in cloth_clip_check.gd and
travel_dir_check.gd is the pattern.
Two related traps:
- Headless runs uncapped, so the engine delta is sub-millisecond and anything
integrated barely moves. Set
SpringBones.fixed_delta = 1.0/60.0. - A single frame of a locomotion clip measures the clip. A run cycle twists the torso against the hips by tens of degrees twice per stride, swamping anything a pose layer does. Average over a stride.
The tools
| Tool | Measures | Good |
|---|---|---|
spawn_smoke_test.gd |
spawn, skins, anim tree, camera, state cycling | 29 OK, 0 failures |
cloth_clip_check.gd |
leg-inside-cloth per movement state, per vertex | idle < 25 mm |
cloth_settle_check.gd |
deg/frame at a dead idle, contacts/frame | skirt < 0.1, hair < 0.01 |
cloth_perf_check.gd |
ms per character per frame | ~2.6 ms |
cloth_allow_check.gd |
how much of each limb the rest-clearance cap makes the solver blind to | 17–35 mm on Taila |
cloth_stretch_check.gd |
mesh tearing between panels | no 3× edges |
travel_dir_check.gd |
stride direction vs. travel direction | < 10° except a capped sidestep |
limb_deform_check.gd |
joint collapse | knee ~0.99 |
verify_character.py |
meshes, bones, weights of a SOURCE model | several meshes, cloth bones present |
anim_capture.gd / orbit_capture.gd |
renders, for looking | — |
Run them:
godot --headless --path . -s res://debug/<tool>.gd
godot --headless --path . -s res://debug/<tool>.gd -- res://assets/characters/skins/<name>.glb
Scripts run with -s MUST extend SceneTree. A Node script never quits and
hangs forever.
Measure the right quantity
cloth_clip_check.gd used to report "how much CLOSER the leg got than the artist
modelled it". A hem 200 mm clear of a shin legitimately comes 180 mm closer when
the leg kicks out in a slide, and counting that as a failure buried the real
clipping under motion the character is supposed to have. It now reports how far
INSIDE a capsule a cloth vertex is, over and above however far inside it was
modelled — only cloth actually within the capsule can be showing a leg through.
It also applies the collider's from offset, so it tests the same band of thigh
the solver is defending. Measuring the full bone tests the hip cap the solver
deliberately excludes and reports it as clipping no tuning can fix.
Diagnosing "the solver isn't working"
In order:
- Is the measurement inside the modifier pass? (Above. Do this first.)
- Does the solver SEE the contact?
debug_hit_report()— bone → deepest overlap it found. If ~0 while the mesh is deep inside a leg, the collision hull does not cover the geometry that is clipping. - Does it CONVERGE?
debug_residual_report()— overlap left after the relaxation. Seen 93 mm, left 95 mm is a standing fight, not slow convergence; quadrupling the iterations will buy nothing. Find what is pulling back. - Only then, tune.
That order was learned the hard way: the drape, the bend limits, the backstop, the iteration count and the hull sampling were each suspected and tested, and the answer was in step 1.
Also run
godot --headless --path . -s res://movement/tests/run_fsm_tests.gd # 11 tests
godot --headless --path . --check-only --script res://<file>.gd # syntax
Autoload identifiers report false "not found" errors under --check-only —
ignore those.