docs(skill): compact the character pipeline into a reusable skill
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]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
040b595397
commit
daf9627ece
@@ -0,0 +1,139 @@
|
||||
---
|
||||
name: character-pipeline
|
||||
description: Import, rig, stylize and animate an anime-styled character into Papaya-Shooter as a selectable skin — keeping the model's own skeleton, artist weights and separate body/cloth/hair meshes, with cloth and hair driven by the spring solver. Use when adding a new playable character, re-importing an existing one, debugging skinning/cloth/hair/animation problems on a character, or changing the cel-shaded look. Triggers on "add a character", "import a skin", "new playable model", "skirt clipping", "hair flailing", "T-posing", "character looks squashed".
|
||||
---
|
||||
|
||||
# Character pipeline
|
||||
|
||||
Turns a source model into a playable, cel-shaded, cloth-simulated character skin.
|
||||
|
||||
The whole design follows one principle, which is also what the Hoyoverse-class
|
||||
anime pipelines (Genshin / Star Rail / Zenless Zone Zero) are built on:
|
||||
|
||||
> **The character is not one object. It is a body, a set of garments, and hair —
|
||||
> authored separately, rigged separately, and moved by different systems.**
|
||||
> The body is skinned and animated. The garments and hair are bone chains that
|
||||
> the animation never touches; physics moves them. Keeping those separate is
|
||||
> what makes the result read as an anime character instead of a mannequin in a
|
||||
> painted-on costume.
|
||||
|
||||
Everything below exists to protect that separation.
|
||||
|
||||
## The one rule
|
||||
|
||||
**If a model arrives with a skeleton, that skeleton ships.** Its bones, its
|
||||
artist-painted weights, its per-part meshes and its skirt/hair chains all
|
||||
survive. Only the ANIMATION is moved onto it.
|
||||
|
||||
The old route (`strip_rig.py` → `autorig.py` → `merge_animations.py`) solved a
|
||||
bone-*naming* problem by destroying the asset — 18 meshes became 1, 21 skirt
|
||||
bones and ~50 hair bones became 0, and 16% of vertices ended up pulled by both
|
||||
legs. `tools/rig_map.py` solves naming properly now. **Never reach for
|
||||
`strip_rig.py` or `--rebind`** unless the model genuinely has no skeleton at all.
|
||||
|
||||
## Doing it
|
||||
|
||||
```bash
|
||||
# From an already-rigged local model (the normal case)
|
||||
python tools/pipeline.py --input assets/characters/incoming/<name>.glb --name <name> --rigged
|
||||
|
||||
# From a Sketchfab UID (needs SKETCHFAB_API_TOKEN)
|
||||
python tools/pipeline.py --uid <uid> --name <name>
|
||||
|
||||
# From an unrigged mesh — auto-rigs, and accepts the quality loss
|
||||
python tools/pipeline.py --input <mesh.glb> --name <name>
|
||||
```
|
||||
|
||||
Then, once, so Godot sees the new files:
|
||||
|
||||
```bash
|
||||
godot --headless --path . --import
|
||||
```
|
||||
|
||||
The result is `assets/characters/skins/<name>.glb` + `<name>.rig.json`, and a
|
||||
registry entry in `skins.json` that `SkinManager` picks up with no code change.
|
||||
|
||||
Blender is required (`BLENDER_PATH`, or auto-found under
|
||||
`C:\Program Files\Blender Foundation`). Godot lives at
|
||||
`C:\Program Files\Godot\Godot_v4.7-stable_win64_console.exe`.
|
||||
|
||||
## The stages, and what each one protects
|
||||
|
||||
| Stage | Where | Protects |
|
||||
|---|---|---|
|
||||
| Fix unlit/emissive materials | `tools/gltf_fix.py` | Textures surviving import at all |
|
||||
| Resolve bone ROLES, not names | `tools/rig_map.py` | The model's own skeleton |
|
||||
| Rebuild parenting | `retarget.py::rebuild_hierarchy` | Limbs/cloth following the hips |
|
||||
| Subdivide cloth panels | `retarget.py::subdivide_cloth_panels` | A skirt being able to bend at all |
|
||||
| Retarget clips as rest-relative deltas | `retarget.py::retarget_clip` | Limbs not being twisted by foreign bone roll |
|
||||
| Leave cosmetic bones unkeyed | `export_optimize_animation_keep_anim_armature=False` | Physics owning the cloth |
|
||||
| Write the rig sidecar | `retarget.py::describe_rig` | The runtime never re-guessing anatomy |
|
||||
| Cel look | `LevelMaterials.apply_character_look` | The model's own line-work not being re-lit |
|
||||
| Cloth + hair | `characters/spring_bones.gd` | Clothes reading as clothes |
|
||||
|
||||
## Read before you touch anything
|
||||
|
||||
Load the reference that matches what you are doing. They are short and each one
|
||||
is a list of things that cost a debugging cycle to learn.
|
||||
|
||||
- **`references/separation.md`** — body vs. garments vs. hair: what must stay
|
||||
separate, how cloth chains are detected and classed, why cloth is never
|
||||
skinned to a leg, and the ZZZ-convention mapping.
|
||||
- **`references/rigging.md`** — role resolution, hierarchy rebuild, cloth panel
|
||||
subdivision, twist bones, joint helpers, the retarget maths.
|
||||
- **`references/cloth-and-hair.md`** — the position-based spring solver, its
|
||||
colliders, per-class tuning, collision hulls, LOD and cost.
|
||||
- **`references/stylization.md`** — cel shading, outlines, the imported
|
||||
line-work trap, eyes, materials.
|
||||
- **`references/verification.md`** — every measuring tool, what each one
|
||||
actually measures, and the pose-reading trap that invalidated all of them
|
||||
once. **Read this before trusting any measurement.**
|
||||
|
||||
## Non-negotiables
|
||||
|
||||
1. **Never join meshes.** Per-part meshes are how body, cloth and hair stay
|
||||
separable — for materials, for the outline pass, and for the cloth solver's
|
||||
hull extraction.
|
||||
2. **Never key cosmetic bones.** If a clip has tracks on skirt/hair bones, the
|
||||
AnimationPlayer overwrites the solver every frame and the cloth goes rigid.
|
||||
3. **Never skin cloth to a leg.** A vertex weighted 0.9 to a thigh cannot be
|
||||
moved by its own cloth bone, so the solver loses the authority to push it out
|
||||
of that thigh — and the leg still overtakes it. There was a
|
||||
`bind_cloth_to_legs()`; it is deleted, and the note above its grave in
|
||||
`retarget.py` says why.
|
||||
4. **Never run `SkinLegRepair` on authored weights.** It snaps weights and
|
||||
deletes triangles. It exists only to undo auto-rigging. It is gated on
|
||||
`weights_authored`, which is MEASURED, not assumed.
|
||||
5. **Measure from inside the modifier pass.** See `references/verification.md`.
|
||||
|
||||
## After importing
|
||||
|
||||
```bash
|
||||
godot --headless --path . -s res://debug/spawn_smoke_test.gd # 29 checks
|
||||
godot --headless --path . -s res://debug/cloth_clip_check.gd # leg-through-cloth
|
||||
godot --headless --path . -s res://debug/cloth_settle_check.gd # idle stability
|
||||
godot --headless --path . -s res://debug/cloth_perf_check.gd # ms per character
|
||||
godot --headless --path . -s res://debug/travel_dir_check.gd # legs face travel
|
||||
```
|
||||
|
||||
What "good" looks like on Taila, for calibration:
|
||||
|
||||
| Measure | Good | Bad |
|
||||
|---|---|---|
|
||||
| Idle skirt movement | < 0.1 deg/frame | 0.5+, or never decaying |
|
||||
| Leg inside cloth, idle/walk | < 25 mm | 100 mm |
|
||||
| Leg inside cloth, run/slide/dash | ~95 mm *(current, unsolved)* | — |
|
||||
| Solver cost | ~2.6 ms/character | 10 ms |
|
||||
| Stride vs. travel direction | < 10° (except a capped sidestep) | 90° |
|
||||
|
||||
## Known-unsolved
|
||||
|
||||
- **Peak cloth clipping** in a run, slide and dash sits at ~95 mm of thigh
|
||||
inside the skirt. Idle, walk and fall are clean. The solver sees the contact
|
||||
and pushes on it every iteration; the remaining gap is a standing fight
|
||||
between the collision and the garment's own shape constraints.
|
||||
- **No foot IK.** Feet do not plant on ground height, so stairs and uneven
|
||||
ground read as sliding.
|
||||
- **No strafe or backpedal clips.** Direction is conveyed by yawing the hips
|
||||
(`SkinnedPlayerModel._update_travel`), which is capped, so a pure sidestep
|
||||
still runs its legs ~40° off the direction of travel.
|
||||
Reference in New Issue
Block a user