Files
Papay-Shooter/.claude/skills/character-pipeline/SKILL.md
T
Nicholas ButzkeandClaude Opus 5 daf9627ece 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]>
2026-07-26 12:46:50 -04:00

7.0 KiB

name, description
name description
character-pipeline 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.pyautorig.pymerge_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

# 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:

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

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.