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]>
72 lines
3.4 KiB
Markdown
72 lines
3.4 KiB
Markdown
# Stylization — the cel-shaded look
|
|
|
|
Two passes, applied at load in `SkinnedPlayerModel.load_model`:
|
|
|
|
```gdscript
|
|
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`.
|