Files
brandling-safari/docs/CHARACTER_ART.md
Gabe 238cd9c6c3 Brandling Safari: LAN safari-zone creature catching on the HD-2D engine
- Authoritative 30 Hz server (ENet, SQLite): accounts, characters, wild AI, tall grass /
  water / wandering spawns, fishing, captures, follower chains, clock + weather, LAN beacon
- Client: title / join / character creator / play screens, prediction + reconciliation,
  interpolated players and parties, HD-2D UI (dialogue with Professor Henry, HUD, collection)
- Shared deterministic zone generator, movement, protocol, roster, appearance
- Paper-doll character art pipeline (PixelLab base walk + per-item animation edits)
- Brandling roster from the company list (logo colors, jev types), Blender safari props
- Headless test bot
2026-10-02 15:20:27 -05:00

92 lines
4.4 KiB
Markdown

# Character art: one animation, any outfit
Player sprites are paper dolls. PixelLab is called a fixed number of times to build a
**layer library**; every look a player can create is composed from it on the CPU with no
further API calls.
```mermaid
flowchart LR
A[create-character-with-4-directions<br/>bald, white tee, grey shorts, barefoot] --> B[walking-4-frames template<br/>south, east, north]
B --> C[15 base frames<br/>3 dirs x stand + 4 walk]
C --> D[edit-animation-v2 per item<br/>all 15 frames in one call,<br/>item drawn in pure green]
C --> E[split base: skin / tee / shorts<br/>shade layers + fixed pixels]
D --> F[extract green hue -> shade layer<br/>outline + details -> fixed pixels]
E --> G[assets/characters/&lt;body&gt;/]
F --> G
G --> H[client composes any look:<br/>skin tone, item per slot, color per slot]
```
## Why it works
- The base walk is generated once per body. `edit-animation-v2` edits all 15 frames in one
request and keeps the motion, so an item's frames line up pixel-for-pixel with the base
frames. No per-outfit animation calls.
- Items are drawn in a stand-in color (bright pure green) that no base pixel uses. The
extractor (`tools/character_art.py build`) keeps the green pixels as a **shade layer**: each
pixel stores a shade index 1..4 (luminance relative to the frame's median, so frames the
model rendered a bit brighter or darker still band the same). The dark outline and small
details right next to the green become **fixed pixels**.
- The base body is split the same way: skin, tee and shorts become shade layers, everything
else (outline, eyes, mouth) stays fixed. Skin tone is therefore just a color, like a shirt.
- Tops are clipped two rows below the shorts' waistline (the edit model sometimes turns a
sweater into a robe).
- West is east mirrored at runtime, so only three directions are stored.
## Cost
| Step | Calls | Generations |
| --- | --- | --- |
| Base body (x2) | `create-character-with-4-directions` | ~1 each |
| Walk (x2) | `characters/animations` template mode, 3 directions | ~0 (template) |
| Item (x18 per body) | `edit-animation-v2`, 15 frames | 20 each |
| Professor Henry | character + walk + `create-image-pixen` portrait | ~2 |
18 items x 2 bodies = 720 generations for every combination of 5 hair styles, 3 hats,
4 tops (+ the base tee), 3 bottoms (+ the base shorts) and 3 shoes, in any color and any of
6 skin tones. A new item is one more call per body.
## Files
```
art_src/characters/<body>/ raw PixelLab output (rotations, walk4/, items/<id>/frame_N.png)
assets/characters/manifest.json slots, z order, skin tones, palette, items, bodies, NPCs
assets/characters/<body>/body.png fixed pixels of the base body (sheet)
assets/characters/<body>/skin_shade.png, top_tee_shade.png, bottom_shorts_shade.png
assets/characters/<body>/<item>.png + <item>_shade.png
assets/characters/henry/henry.png + portrait.png
```
Sheets are 5 columns (standing, walk 0..3) x 3 rows (south, east, north) of 64 px cells.
Shade layers store the shade index in red (`index * 60`) with alpha 255.
## Composition
`client/CharacterSprite.cpp` (C++) and `tools/character_compose.py` (Python, for previews)
implement the same steps:
1. skin shade layer in the skin tone,
2. tee and shorts shade layers (always under everything; recolored when they are the chosen
top / bottom),
3. body fixed pixels,
4. every chosen item by slot z order (`shoes < bottom < top < hair < hat`): shade layer in the
chosen color, then its fixed pixels.
A color becomes 4 shades multiplicatively (`colorRamp` / `ramp`): x0.50 with a cool tint,
x0.73, the color itself, and a warm highlight 34% toward cream. Keep both implementations in
sync.
## Commands
```sh
python3 tools/character_art.py bases # base bodies + walk (cached)
python3 tools/character_art.py items [ids..] # missing item edits (resumable)
python3 tools/character_art.py henry # Professor Henry + portrait
python3 tools/character_art.py build # extract layers, write the manifest (free)
python3 tools/character_art.py preview # contact sheets in art_src/preview/
python3 tools/pixellab_client.py # ledger spend + account balance
```
Paid jobs are recorded in `art_src/.pending/` before waiting, so an interrupted run resumes
the same jobs instead of paying again. To add an item, add it to `ITEMS` in
`tools/character_art.py`, run `items <id>` and `build`.