Record the roster fixes, material-list traps and head-gear offset in MEMORY.md
Full build / Import + headless tests (push) Successful in 41s
Full build / Full desktop build + optional release (push) Skipped

This commit is contained in:
zwitschi committed 2026-11-06 18:05:57 +01:00
1 parent 9fd48243ad
commit cba00facf5
1 file changed
+51 -12
+51 -12
View File
@@ -119,22 +119,54 @@ canonical implemented-features list in `docs/development-status.md`.
library with a sculpted realistic face, so the 0.9.9 gate is a product decision, not just an
art check. Owner picks full swap / crowd-only / extra head detail. Do not lower character
fidelity silently.
- **Pose composition is rest-relative (resolved 2026-11-05):** Godot's `set_bone_pose_rotation`
offsets from the bone rest, so the correct swing around a world axis is
`rest * Quaternion(rest.inverse() * axis, angle)`. For the old 180° rest this reduces to the
legacy `rest * Quaternion(axis, angle)`, so the 13-core fallback is unchanged (commit `8b1bf73`).
- **Pose composition is rest-relative (resolved 2026-11-05):** see `_swing_bone()` in the gotchas —
the rest-relative `set_bone_pose_rotation` form was abandoned; the global-pose write replaced it.
- **Full roster swap (2026-11-05, owner):** at the `0.9.9` face gate the owner chose option (a),
full swap. All twelve public character names are baked from the three CC0 Quaternius sources
(`casual`, `hoodie`, `worker`); variety comes from the five tint slots, the outfit rules and the
gear library. The procedural library + MakeHuman head stay in the tree as the tested fallback
(`build --source procedural`), not in the shipped roster. Silhouette variety is the known
regression — three meshes for twelve roles — and the modular part library is the `0.9.10` fix.
- **No hats except the firefighter helmet (2026-11-06, owner):** the iconography is not worth the
cost — a cap or hood on this head hides the face, and every hat needed a lift+scale hack to clear
the 0.228 m hair shell. `ACCESSORIES` maps no role to `cap`/`hood`, the guard/vest per-character
caps are gone, the performer cone was removed, and the primitive cap/hood fallbacks were deleted
with their `GEAR_OFFSETS`/`GEAR_SCALE` compensation. `firefighter_helmet` is the only head item.
Do not re-add head gear without an owner decision and a fit measurement.
- **Mouths are authored, not upstream (2026-11-06):** none of the three Quaternius heads has a mouth
material, so the faces read as brow-and-eye masks. `_add_quaternius_mouth()` places a small lip
block on the measured skin surface (front-most skin vertex in the 1.600–1.632 m band, ±35 mm of
centre) at 1.616 m. One rule covers all three bases; it runs before the slot map, so match the
material as `_base_name(m.name).lower() == "skin"`.
- **One character source, one fallback:** no new NPC source without an ADR. MPFB body work stays
closed, the Stage-1 proportion warp is not reopened, and the procedural `npc_base` + MakeHuman
head stay tested until the Quaternius base passes its gate.
## Gotchas / Known Issues
- **Blender material-list edits corrupt polygons silently.** Two separate bugs, same root:
`obj.data.materials.clear()` resets **every polygon to slot 0**, so the per-face indices must be
captured *before* the clear and reassigned after; and `IDMaterials.pop(index=)` repoints the
polygons that used the slot, which once painted a whole face with the eye material. The safe
pattern is `_purge_unused_materials()`: read the per-face material **names**, rebuild the slot
list from the names, then write the indices back by name.
- **A zero-user material still owns its name.** Removing a slot leaves the datablock behind, so a
donor material renamed to the freed name lands as `Eye.001` and every `m.name == "Eye"` rule
silently misses it. Purge the datablocks (`bpy.data.materials.remove(m)` when `m.users == 0`) or
match on `_base_name()`.
- **Blender node colours are linear; Godot tonemaps them.** A mouth lip at `0.16` linear rendered as
salmon/mauve. Divide by roughly 3–4 versus the intended sRGB value: the hair uses 0.027 linear,
the lip 0.055/0.018/0.02.
- **The modular Quaternius Worker has no torso under its vest and no crown under its hard hat.**
Deleting either material leaves real holes (that is what shipped in the first 0.9.9 build: guard
and firefighter showed the inside of the head and torso). Remap instead of deleting; the only
geometry the build removes is the Worker brow bar, which is replaced by a donor part.
- **Head gear authored for the 0.315 m procedural head is wrong on the 0.20 m Quaternius head.** Gear
is authored around the head *centre* (1.62) while the attach node is the Head *bone* (1.587), and
the Quaternius centre is 1.69 — a 7 cm error put every cap brim across the brows, which I spent
several passes misreading as a mesh hole (the band was pure black and no material had faces
there). `HEAD_GEAR_LIFT = 0.103` fixes the common case. With the hats removed this only affects
the firefighter helmet.
- **Two character builders — do not confuse them.** `tools/blender_assets.py` is the real builder
for the **eight library characters, 18 gear pieces and props** (it has the 0.9.4 face/hand
sculpting in `_face()`/`_hands_and_shoes()`); `tools/npc_pipeline.py` builds **only**
@@ -281,14 +313,21 @@ patches (`0.9.9`–`0.9.12`) with `0.10.0` as the public-readiness minor. The ga
three release roadmaps now live in `PLAN.md`; `TODO.md` is the executable task list. ADR-0017
Stage 1 (proportion warp) is dead; ADR-0018 option 2 (62-joint runtime) is accepted.
**Full roster (0.9.9, commits `a6ccd86`, `04d211b`, `b3acf37`):** all twelve characters run on
the Quaternius bases (4,458–6,424 tris, 62 joints, 73–74 px/m texel density), built by the
shipped `tools/blender_assets.py` two-pass pipeline (`_quaternius_tpose_glb` →
`_bake_quaternius_rest`). Gates: `audit` 12/12, `inspect` clean, `fit` OK, 24-NPC budget
160,608 tris / 0.84 ms, Godot 278/278, Python 167/167. The owner visual signoff page is
`docs/npc-visual-review.md`. Release packaging was also fixed the same day: excluding
`tmp/`, `build/`, `docs/`, `tools/`, `src/test/` and `.agents/` from the export presets cut the
Linux binary from 426 MB to 273 MB, and `tools/build_releases.sh` now writes a SHA256SUMS file.
**Full roster (0.9.9, commits `a6ccd86` … `9fd4824`):** all twelve characters run on the
Quaternius bases (62 joints, 73–74 px/m texel density), built by the shipped
`tools/blender_assets.py` two-pass pipeline (`_quaternius_tpose_glb` → `_bake_quaternius_rest`).
The owner signed the roster off **with follow-ups** and rejected three review rows; the Worker
body defects (holes, unibrow, missing eyes) and the head-gear seating are fixed, every hat except
the firefighter helmet is removed, and every head has an authored mouth. Gates: `audit` 12/12,
`inspect` clean, `fit` OK, 24-NPC budget 174,976 tris / 0.85 ms, Godot 278/278, Python 167/167.
Owner review page: `docs/npc-visual-review.md`. Release packaging was fixed the same day:
excluding `tmp/`, `build/`, `docs/`, `tools/`, `src/test/` and `.agents/` from the export presets
cut the Linux binary from 426 MB to 273 MB, and `tools/build_releases.sh` writes a SHA256SUMS file.
**Open after the swap:** hand-held gear and role props are still authored for the old body (wrist
y 0.78 → 1.017, arm shorter), so the clipboard and umbrella are oversized; the eyes have no whites;
the arms graze the torso on the `worker` base; roster variety is three meshes for twelve roles. The
prop/gear fit pass is queued in `TODO.md` under 0.9.10.
**Quaternius pilot (2026-11-05, commits `cd285b3`, `fb3e75a`, `90f189a`):** `npc_casual` ships as
the Quaternius base. It renders arm-down, carries the five tint slots, plays the Idle/Walk/Sit pose