docs+fix: sidequest close-out — T-1095 head tint, design doc, wardrobe docs, changelog (T-1088/T-1089)
T-1095 fixed: template heads now receive the body's skin tone (same treatment as seg_head) — _load_head passes tone.lit with a full-white fallback mask through _attach_to_bone; without a mask sidecar the tint was silently ignored, so untinted templates read grey against mid/dark tones. Verified by lookbook render (head_001 on child, tone 6 — face matches arms). Docs: the T-1088 locomotion design document committed to docs/design/ (the sandbox code's 'design §' references now resolve in-repo); character-asset- organization gains §11 Wardrobe Pipeline and sheds three stale claims (the never-built indexed-greyscale mask plan, single-mask-per-asset for clothing, the resolved multi-region open question); CHANGELOG carries the sidequest entry; D-250 amended with the shipped cutaway-modes verdict (mode 2 default, user-decided live). Tickets: T-1088, T-1089, T-1095 closed; T-1096 filed (S9 live feel-tuning, needs the user). The Fable-5 cascade exception is closed — the cascade resumes at Phase 4 (T-750). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -444,9 +444,14 @@ Convention: `<asset_name>_mask.png` as a sidecar file next to the GLB.
|
||||
- `hat_hardhat.glb` + `hat_hardhat_mask.png`
|
||||
- For clothing, the mask lives in the item directory: `coveralls_basic/reference_mask.png`
|
||||
|
||||
Masks are per-asset, not per-variant. The same mask applies to all 11 body-type variants of a
|
||||
clothing item because the mask regions (collar is region A, sleeves are region B) correspond
|
||||
to UV regions, and the UV layout is preserved through Surface Deform fitting.
|
||||
Masks are per-asset, not per-variant, for hair/facial hair/eyebrows/accessories/heads -- one
|
||||
mask applies across all 11 body-type variants because the mask is UV-space and the UV layout
|
||||
is preserved through fitting.
|
||||
|
||||
**Superseded for clothing (T-1089, see Section 11):** offset-shell garments now author a mask
|
||||
**per body type** (`<body_type>_mask.png`, that body's own UV0 layout), falling back to
|
||||
`reference_mask.png` only for the Surface-Deform-fit route (suit, uniform, Fantasy-derived
|
||||
cuts), which still shares one mask across variants as described above.
|
||||
|
||||
### Head Templates
|
||||
|
||||
@@ -567,13 +572,17 @@ For every GLB that supports recoloring, a `_mask.png` sidecar sits next to it:
|
||||
|
||||
- **Format:** PNG, same UV layout as the GLB's texture
|
||||
- **Channels:** Greyscale (R channel only). White = tintable region, black = preserve original
|
||||
- **For multi-region recoloring:** Use distinct greyscale values per region (e.g. 0.0, 0.33,
|
||||
0.66, 1.0 for up to 4 independent tint regions). The shader samples the mask and routes
|
||||
each region to a different `tint_color` uniform. This is not yet implemented in the shader
|
||||
but the mask format supports it.
|
||||
- **Default:** `hint_default_black` in the shader means "no mask loaded = no tinting."
|
||||
Assets without masks render with their original Trellis/authored texture.
|
||||
|
||||
**Multi-region resolved differently than proposed here (T-1089, see Section 11).** The
|
||||
distinct-greyscale-band idea above (0.0/0.33/0.66/1.0 in one channel) was never built --
|
||||
indexed greyscale bilinear-filters badly at region boundaries (a filtered sample between two
|
||||
index levels reads as a wrong third region, not a blend). Clothing multi-region instead
|
||||
channel-routes an **RGBA mask** (R/G/B/A -> 4 independent tints, `toon_garment.gdshader`),
|
||||
so bilinear filtering cross-fades neighboring regions correctly. This is clothing-only; the
|
||||
single-channel binary convention above is unchanged for hair/facial hair/eyebrows/accessories.
|
||||
|
||||
### Surface Deform Batch Output
|
||||
|
||||
The Blender batch pipeline takes one reference clothing mesh and produces 11 fitted variants.
|
||||
@@ -639,6 +648,7 @@ CharacterVisualDescriptor:
|
||||
skin_tone: int # index into the 9-tone palette
|
||||
clothing_slots: Dictionary # slot -> item_id (e.g. "torso" -> "coveralls_basic")
|
||||
clothing_tints: Dictionary # item_id -> Array[Color] for multi-region recolor
|
||||
clothing_logos: Dictionary # item_id -> brand id (logo_capable items only; T-1089, Section 11)
|
||||
accessory_slots: Dictionary # slot -> item_id (e.g. "hat" -> "hat_hardhat")
|
||||
accessory_tints: Dictionary # item_id -> Color
|
||||
```
|
||||
@@ -726,11 +736,11 @@ What does NOT carry forward:
|
||||
|
||||
## 10. Open Questions for Review
|
||||
|
||||
**Q: Multi-region recolor masks -- implement now or defer?**
|
||||
The current `toon_masked` shader supports single-region (binary mask). Multi-region requires
|
||||
a shader update to sample distinct greyscale bands and route them to separate tint uniforms.
|
||||
Recommendation: defer to when the first multi-region clothing item is authored. The mask PNG
|
||||
format supports it already (greyscale values), so no re-export is needed later.
|
||||
**Q: Multi-region recolor masks -- implement now or defer?** **RESOLVED (T-1089, Section 11).**
|
||||
Deferred until the first multi-region clothing item (the t-shirt) needed it, then implemented
|
||||
as an RGBA channel-routed mask + `toon_garment.gdshader` -- not the greyscale-band shader
|
||||
update this note anticipated (see the corrections in Sections 3 and 5 above). `toon_masked`
|
||||
is retained for single-region assets (hair, accessories).
|
||||
|
||||
**Q: Teen clothing variants -- separate catalogue or shared with adult?**
|
||||
Teen body types use the same clothing system (Surface Deform fitting) but teen proportions
|
||||
@@ -741,6 +751,94 @@ revisit if visual quality is insufficient.
|
||||
|
||||
---
|
||||
|
||||
## 11. Wardrobe Pipeline (T-1089)
|
||||
|
||||
The clothing engine that shipped the first real catalogue (2026-07-06/07). This section
|
||||
supersedes the single-region greyscale mask model assumed in Sections 3/5/10 above for
|
||||
**clothing specifically** -- hair/facial hair/eyebrows/accessories/heads keep the
|
||||
single-channel convention as originally written.
|
||||
|
||||
### Authoring: offset-shell from our own body segments
|
||||
|
||||
The preferred authoring route is **offset-shell**
|
||||
(`tooling/garment-fit/blender_author_offset_shell.py`, one per-garment companion script per
|
||||
item, e.g. `blender_author_hoodie.py`, `blender_author_boots.py`): extract the body segment(s)
|
||||
a garment covers, cut neckline/hem/sleeve boundaries via bone-plane thresholds, offset the
|
||||
surface outward along vertex normals, solidify for real cloth thickness. Because the shell
|
||||
*is* body topology, bone weights are inherited by construction -- no Surface Deform, no re-rig.
|
||||
|
||||
**PER-BODY mode (`--per-body`) is now the standard for offset-shell garments**, not
|
||||
single-reference-then-fit. Q-060 evidence: single-reference Surface-Deform fitting degraded
|
||||
with girth divergence (muscular_m hit an 859px worst clip at 24mm standoff on the
|
||||
reference-authored t-shirt); authoring per body guarantees clearance + exact weights by
|
||||
construction and lets the offset drop back to the ~12mm ideal. Cut/mask thresholds are
|
||||
derived from each body's own bone landmarks using the proportional ratios that reproduce the
|
||||
hand-calibrated reference constants -- recomputed per body, not copied.
|
||||
|
||||
Shared geometry practices proven across garments: **boundary weld** (`weld_boundaries` --
|
||||
merges coincident segment-boundary verts so the offset can't open cracks) and **open-rim
|
||||
flattening** (`flatten_open_rims` -- pulls the jagged segment-splitter boundary rings onto
|
||||
clean planes before the offset runs; first required by jeans and the track jacket).
|
||||
**`convex_toe_box`** (shared helper, used by boots/sneakers/shoes_formal) reshapes the
|
||||
forefoot into one smooth per-slice ellipse cap so individual-toe geometry doesn't poke the
|
||||
shoe shell -- residual toe hints are accepted for first release (see Catalogue below).
|
||||
|
||||
### Fitting: Surface-Deform batch route
|
||||
|
||||
`tooling/garment-fit/blender_batch_fit_skinned.py` remains the route for garments sharing one
|
||||
reference mesh + mask across all 11 bodies -- Fantasy-pack-derived cuts and hand-authored
|
||||
pieces on a single UV layout (the suit, the uniform). SD-bind to each target body, bake the
|
||||
deformed rest shape, transfer + normalize weights, retarget the armature, export with
|
||||
**`export_skins=True`** (the predecessor script's `export_skins=False` output could not
|
||||
animate on the shared skeleton -- why it was superseded, not merely extended).
|
||||
|
||||
### Runtime contract
|
||||
|
||||
- **`coverage.json`** per clothing item: `hides` (segment names to hide), `torso_variant`
|
||||
(`full`/`upper`), and the new **`multi_region`** flag (routes the item through
|
||||
`toon_garment.gdshader` instead of the single-tint `toon_masked.gdshader`).
|
||||
- **Per-body masks:** `<body_type>_mask.png` sits beside each body's fitted GLB; the
|
||||
compositor prefers it and falls back to `reference_mask.png` for the Surface-Deform-fit
|
||||
route. See the mask-convention correction in Section 3.
|
||||
- **`toon_garment.gdshader`** (`client/assets/characters/shaders/`): channel-routes FOUR tints
|
||||
through an RGBA region mask (R/G/B/A -> `tint_0`..`tint_3`), channel-blended rather than
|
||||
indexed-greyscale (see the correction in Section 5). A logo decal samples a dedicated UV2
|
||||
(TEXCOORD_1) chest channel, composited **after** region tinting (brand colors stay
|
||||
faithful) but **before** the toon shadow (the logo darkens with its fabric).
|
||||
- **`CharacterVisualDescriptor.clothing_logos`:** `item_id -> brand id`, per-character logo
|
||||
choice (Section 6), alongside `clothing_tints` (`item_id -> Array[Color]`, one per region).
|
||||
- **Manifest clothing schema** (`client/assets/characters/manifest.json`, `clothing` key):
|
||||
`slot`, `style` (`modern`/`formal`/`utility`/... -- the D-251 style-tag seam), `logo_capable`,
|
||||
and for multi-region items `regions` (named R/G/B/A) + `default_tints`.
|
||||
- **`outfits.json`:** named complete looks (`presets`) composing manifest items with per-item
|
||||
tints/logos -- 12 presets (casual/business/sport/beach/worker/sleep) for NPC dressing and
|
||||
shop display.
|
||||
|
||||
### QA: chromakey acceptance gate
|
||||
|
||||
`tooling/garment-qa` (`run-garment-qa <config>`) is the acceptance gate every garment in this
|
||||
pipeline runs before it ships: a two-pass depth-proximity chromakey renders the same frozen
|
||||
frame twice -- once keying claimed-covered body segments magenta, once with the garment
|
||||
nudged a few cm toward the camera and re-keyed cyan. A body-key pixel that flips from magenta
|
||||
to cyan is a genuine clip (`clip_through_pixels`, gates on connected-component size); a pixel
|
||||
that never flips despite being exposed is informational (`exposed_skin_pixels` -- collars,
|
||||
sleeveless armholes). Wave 1/2 garments were accepted at <=150px worst clip component.
|
||||
`client/tools/garment_preview/lookbook_scene` renders the full catalogue for visual review --
|
||||
the pass that caught the missing-albedo and manifest-nesting findings fixed in `e562418d0`.
|
||||
|
||||
### Current catalogue
|
||||
|
||||
24 entries: 21 modern (tops, bottoms, feet, full-body, swim -- wave 1 + wave 2 + the
|
||||
fill-in-the-blanks addendum) + 3 legacy peasant pieces (pre-dating this pipeline, still on
|
||||
the Surface-Deform-fit route). All 24 fitted across all 11 body types.
|
||||
|
||||
**Known artifact classes, accepted for first release:** sleeve/opening-hem epsilon blobs
|
||||
(offset-shell boundary artifacts, offset-insensitive); residual toe hints on closed footwear
|
||||
after `convex_toe_box` ("I still see some toes, but this will do" -- user review, 2026-07-07).
|
||||
Further smoothing is polish backlog, not open scope.
|
||||
|
||||
---
|
||||
|
||||
## Decisions Referenced
|
||||
|
||||
- **D-159:** 5 adult body types x 2 genders + 1 child = 11 base meshes (updated: added Teen)
|
||||
@@ -749,3 +847,4 @@ revisit if visual quality is insufficient.
|
||||
- **D-162:** Clothing is pre-baked per body type via Surface Deform
|
||||
- **D-163:** Trellis generates heads via BoneAttachment3D (offline asset production, curated into template library)
|
||||
- **D-164:** Fork Quaternius skeleton, replace all body meshes (Source tier .blends as starting point)
|
||||
- **D-251:** Character asset route reconfirmed 2026-07-06 -- Quaternius rig stays, wardrobe is in-house (offset-shell + Surface-Deform routes, Section 11), UAL1/UAL2 animation tiers purchased
|
||||
|
||||
Reference in New Issue
Block a user