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 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 art check. Owner picks full swap / crowd-only / extra head detail. Do not lower character
fidelity silently. fidelity silently.
- **Pose composition is rest-relative (resolved 2026-11-05):** Godot's `set_bone_pose_rotation` - **Pose composition is rest-relative (resolved 2026-11-05):** see `_swing_bone()` in the gotchas —
offsets from the bone rest, so the correct swing around a world axis is the rest-relative `set_bone_pose_rotation` form was abandoned; the global-pose write replaced it.
`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`).
- **Full roster swap (2026-11-05, owner):** at the `0.9.9` face gate the owner chose option (a), - **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 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 (`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 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 (`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. 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 - **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 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. head stay tested until the Quaternius base passes its gate.
## Gotchas / Known Issues ## 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 - **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 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** 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 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. 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 **Full roster (0.9.9, commits `a6ccd86` … `9fd4824`):** all twelve characters run on the
the Quaternius bases (4,458–6,424 tris, 62 joints, 73–74 px/m texel density), built by the Quaternius bases (62 joints, 73–74 px/m texel density), built by the shipped
shipped `tools/blender_assets.py` two-pass pipeline (`_quaternius_tpose_glb` → `tools/blender_assets.py` two-pass pipeline (`_quaternius_tpose_glb` → `_bake_quaternius_rest`).
`_bake_quaternius_rest`). Gates: `audit` 12/12, `inspect` clean, `fit` OK, 24-NPC budget The owner signed the roster off **with follow-ups** and rejected three review rows; the Worker
160,608 tris / 0.84 ms, Godot 278/278, Python 167/167. The owner visual signoff page is body defects (holes, unibrow, missing eyes) and the head-gear seating are fixed, every hat except
`docs/npc-visual-review.md`. Release packaging was also fixed the same day: excluding the firefighter helmet is removed, and every head has an authored mouth. Gates: `audit` 12/12,
`tmp/`, `build/`, `docs/`, `tools/`, `src/test/` and `.agents/` from the export presets cut the `inspect` clean, `fit` OK, 24-NPC budget 174,976 tris / 0.85 ms, Godot 278/278, Python 167/167.
Linux binary from 426 MB to 273 MB, and `tools/build_releases.sh` now writes a SHA256SUMS file. 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 **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 the Quaternius base. It renders arm-down, carries the five tint slots, plays the Idle/Walk/Sit pose