Files
Papay-Shooter/.claude/skills/character-pipeline/references/stylization.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

3.4 KiB

Stylization — the cel-shaded look

Two passes, applied at load in SkinnedPlayerModel.load_model:

LevelMaterials.apply_toon_recursive(scene)   # world-wide toon shading
LevelMaterials.apply_character_look(scene)   # character-only corrections

The trap: imported models bring their own line-work

Anime models exported from MMD/VRoid/Blender toon setups very often ship the outline as geometry — an inverted-hull shell of the mesh with a flat black, UNTEXTURED material, plus separate flat cards for the eye whites, irises and the pupil highlight. The mesh you import is not just the character; part of it is already the drawing.

Toon-lighting that shell is what put a white rim on every hair strand. It is an inverted hull whose normals face away from you; a lighting model that adds a rim term lights it brightly exactly where it is supposed to read as ink.

apply_character_look therefore treats any untextured surface on a character as the model's own line-work and handles it flat and unshaded:

  • Outline hull → made fully transparent rather than deleted. Deleting a surface would renumber the rest and break the mesh's own skin bindings. The game draws its own outline.
  • Eye cards (resource_name starts with eyes) → flat ink, except anything with HL in the name, which is the glint in the pupil and really is white.

If a newly imported character comes out with a white halo, or with black eyes that should have irises, this function and its name-matching are where to look — the naming conventions vary by source and this is the one place they are read.

Materials on import: the unlit problem

Anime glTFs are very often exported "unlit": KHR_materials_unlit, a black baseColorFactor, and the real texture wired to emissiveTexture. Renderers honouring the unlit extension use base colour and ignore emission — so Blender reads black, never references the images, and imports with bpy.data.images empty. The character comes out a silhouette, and there is no node graph left to patch afterwards.

tools/gltf_fix.py rewrites the container before import: emissive becomes base colour, the unlit flag is dropped. It must run first — this is the first thing retarget.py::main does, before import_any.

fix_unlit_materials(meshes) then repairs anything left inside Blender.

What the toon pass does

apply_toon_recursive gives everything the game's banded ramp. apply_character_look then softens the banding on characters, because re-banding an already-shaded anime texture reads as gloss — the texture already contains its own shading and the second pass fights it.

Convention alignment

The Hoyoverse-class look is, broadly: a ramp texture indexed by NdotL for the body, a separate ramp and often a dedicated shader for the face, an inverted-hull outline whose width is vertex-colour-modulated, and specific handling for eyes and hair highlights. This project does the simplified version — one banded ramp plus a screen-space-ish ink treatment, and the model's own outline shell hidden in favour of the game's. The face is NOT specially shaded here; if a character comes out with harsh shadow shapes across the nose, that is the missing piece.

Outline thickness

Lives with the toon material in scenes/maps/level_materials.gd (CHARACTER_INK and the outline settings). This is the branch it was last touched on — feat/outline-thickness-and-tp-weapon-hold.