diff --git a/MEMORY.md b/MEMORY.md index 7dbda17..510a0c4 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -123,6 +123,12 @@ canonical implemented-features list in `docs/development-status.md`. 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`). +- **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. - **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. @@ -275,6 +281,15 @@ 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. + **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 code on the 62-joint rig, and the procedural rigs are visually unchanged. Verified: `inspect` diff --git a/TODO.md b/TODO.md index b1212a6..c691330 100644 --- a/TODO.md +++ b/TODO.md @@ -820,15 +820,21 @@ blocked rig can no longer block the whole release. `npc_casual`, front / three-quarter / profile. ADR-0018 removed the proportion warp, so source proportions are accepted by definition (head 13.4 % of height, measured by `npc_proportions`). +- [x] Owner review page with the visual signoff sheets and checklist: + [`docs/npc-visual-review.md`](docs/npc-visual-review.md) (the procedural-era review text + that used to live there is retired; its baseline sheets stay in `blender_previews/`). - [x] Face-fidelity comparison sheet: [`docs/screens/npc-face-gate/`](../../docs/screens/npc-face-gate/README.md) captures the Quaternius base, the procedural + MakeHuman head and a procedural crowd variant at front / three-quarter / profile / 8 m. The page records the three options (full swap, crowd only, add head detail) and recommends the full swap. -- [ ] **Owner decision:** pick (a) full swap, (b) crowd only or (c) add head detail from - `docs/screens/npc-face-gate/README.md`, then apply it within 24,000 tris / 24,000 glTF - vertices and re-run the gate. -- [ ] Record the decision and its consequence in `MEMORY.md`, `docs/npc-model-sourcing.md` and - (if it changes the contract) a new ADR. Do not silently lower character fidelity. +- [x] **Owner decision:** option (a) full swap (2026-11-05). Applied: all twelve characters are + baked from the three Quaternius sources inside 24,000 tris / 24,000 glTF vertices + (`a6ccd86`), and the gate was re-run. +- [x] Record the decision and its consequence in `MEMORY.md`, + `docs/npc-model-sourcing.md#shipped-roster-099-owner-chose-the-full-swap` and the + follow-up decision section of + [`docs/adr/0018-quaternius-stage2-runtime-rig.md`](docs/adr/0018-quaternius-stage2-runtime-rig.md). +- [ ] Owner visual signoff on the shipped roster: [`docs/npc-visual-review.md`](docs/npc-visual-review.md). ### Pilot rollout @@ -879,7 +885,12 @@ regressing gear fit, budget or readability. import) before judging any face or body change. - [ ] Run `inspect`/`audit`/`fit`/`texels` and `npc_proportions.py report`; record the 12-variant measurement table. -- [ ] Capture the 12 × 6 baseline and get owner approval before replacing the committed GLBs. +- [-] Capture the 12 × 6 baseline and get owner approval before replacing the committed GLBs. + **Owner review page: [`docs/npc-visual-review.md`](docs/npc-visual-review.md)** — the + full-body roster, front/profile per role, the idle/sit/walk pose sheet and the 8 m crowd + strip, with an eight-row signoff checklist and the six issues found while capturing. The + GLBs are already committed (0.9.9); this signoff either accepts them or turns each rejected + row into a fix task. - [ ] Re-run the 24-NPC budget smoke and the Web/Compatibility capture; record frame time, mesh/triangle counts and Web load before/after. - [ ] Retire the procedural variants that are no longer used, keeping one tested fallback; update diff --git a/docs/blender_previews/sheet_npc_roster_0.9.9.jpg b/docs/blender_previews/sheet_npc_roster_0.9.9.jpg new file mode 100644 index 0000000..1e51f21 Binary files /dev/null and b/docs/blender_previews/sheet_npc_roster_0.9.9.jpg differ diff --git a/docs/blender_previews/sheet_npc_roster_crowd_0.9.9.jpg b/docs/blender_previews/sheet_npc_roster_crowd_0.9.9.jpg new file mode 100644 index 0000000..a25bc40 Binary files /dev/null and b/docs/blender_previews/sheet_npc_roster_crowd_0.9.9.jpg differ diff --git a/docs/blender_previews/sheet_npc_roster_poses_0.9.9.jpg b/docs/blender_previews/sheet_npc_roster_poses_0.9.9.jpg new file mode 100644 index 0000000..5016d59 Binary files /dev/null and b/docs/blender_previews/sheet_npc_roster_poses_0.9.9.jpg differ diff --git a/docs/blender_previews/sheet_npc_roster_views_0.9.9.jpg b/docs/blender_previews/sheet_npc_roster_views_0.9.9.jpg new file mode 100644 index 0000000..816a014 Binary files /dev/null and b/docs/blender_previews/sheet_npc_roster_views_0.9.9.jpg differ diff --git a/docs/npc-visual-review.md b/docs/npc-visual-review.md index 26eaf5c..2d5e657 100644 --- a/docs/npc-visual-review.md +++ b/docs/npc-visual-review.md @@ -1,317 +1,109 @@ -# NPC visual baseline and acceptance criteria +# NPC roster visual review — 0.9.9 signoff -Status: **accepted 2026-11-02 for the 0.9.4 stylized target.** The P1 fixes -(tourist photo-prop visibility, backpack straps), neck/shoulder and torso/hip -transitions, face depth/detail, hand and gear placement, headgear clearance and -shoe/boot silhouettes are applied. The baseline remains the comparison reference; -the accepted limitations are listed below. Comparison sheets: -[`sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg), -[`sheet_npc_baseline_compare-body.jpg`](blender_previews/sheet_npc_baseline_compare-body.jpg), -the latest [`sheet_npc_baseline_compare-torso.jpg`](blender_previews/sheet_npc_baseline_compare-torso.jpg), -the face-detail pass [`sheet_npc_baseline_compare-face-detail.jpg`](blender_previews/sheet_npc_baseline_compare-face-detail.jpg), -the conversation-distance hand pass -[`sheet_npc_hands_compare.jpg`](blender_previews/sheet_npc_hands_compare.jpg), and the -forward-grip pass [`sheet_npc_hands_grip_compare.jpg`](blender_previews/sheet_npc_hands_grip_compare.jpg). +**Status: awaiting owner visual signoff.** This page is the single review point for the +Quaternius roster swap that shipped in 0.9.9 +([ADR-0017](adr/0017-quaternius-npc-base.md), [ADR-0018](adr/0018-quaternius-stage2-runtime-rig.md)). +Every character name the game ships is captured here at gameplay distance, in three studio views, +and in the three animation poses the runtime drives. -The 2026-11-03 skill-guided re-evaluation, its measurements (texel density, budgets, imported -bounds) and the open scorecard live in [`3d-model-review.md`](3d-model-review.md). It does not -change the accepted limits below. +Until this page is signed off, the roster swap is reversible: the procedural library and the +MakeHuman head are still in the tree and still pass `audit`, `inspect`, `npc_proportions` and the +tests (`build --source procedural`). -A single-variant face prototype (`npc_casual`) landed 2026-11-03/04: `seg=24/rings=16` head, -subtle brow ridge, fuller lower lip and both eyelids. It reads cleaner than the 0.9.4 face and is -a candidate for the remaining seven once the owner approves (see the review doc). +## 1. Full-body roster (12 roles) -## What was captured +One tile per shipped character, neutral studio, camera at 4.2 m. -`src/test/capture_npc_baseline.gd` stages all eight library characters in a -neutral studio and captures six views per character: +![NPC roster full body](blender_previews/sheet_npc_roster_0.9.9.jpg) -| View | Camera | Purpose | +## 2. Front and profile per role + +Front row set, then profile. Use this to judge the silhouette, the head size and the gear placement +without perspective hiding anything. + +![NPC roster front and profile](blender_previews/sheet_npc_roster_views_0.9.9.jpg) + +## 3. Animation poses (idle / sit / walk contact) + +Six representative roles × three poses, driven by the real `_animate()` path with the FSM frozen. +Check: arms hang instead of holding an A-pose, the seated pose puts the forearms on the thighs and +keeps the shins down, the walk contact keeps limbs in contralateral opposition, and no limb +pinches or detaches at the shoulder, elbow, hip or knee. + +![NPC roster poses](blender_previews/sheet_npc_roster_poses_0.9.9.jpg) + +## 4. Crowd distance (8 m) + +The distance the player actually sees a passer-by from the sidewalk. + +![NPC roster at crowd distance](blender_previews/sheet_npc_roster_crowd_0.9.9.jpg) + +## Signoff checklist + +Tick each row. Anything you reject is a concrete task rather than an opinion, so it can go +straight into `TODO.md`. + +| # | Question | Verdict | | --- | --- | --- | -| `front` | 1.85 m, 32° FOV, head/upper body | face, hair, collar, shoulders | -| `three_quarter` | 1.85 m, yaw 35° | cheek/jaw, ear, profile transition | -| `profile` | 1.85 m, yaw 90° | head depth, nose, ear, chest silhouette | -| `back` | 1.85 m, yaw 180° | hair cap, neck, coat/backpack fit | -| `full_body` | 4.2 m, 38° FOV | proportions, hands, shoes, hem | -| `crowd` | 8.0 m, 45° FOV | silhouette read at normal crowd distance | +| 1 | Do the twelve characters read as distinct people at 8 m? | ☐ yes ☐ no | +| 2 | Is the head size right for the body (no bobblehead, no pinhead)? | ☐ yes ☐ no | +| 3 | Do the faces hold up at portrait distance? | ☐ yes ☐ no | +| 4 | Do the hands and the hand-held gear (camera, cup, clipboard, sign) read correctly? | ☐ yes ☐ no | +| 5 | Does the head gear (cap, hood, sunglasses, firefighter helmet) sit on the skull? | ☐ yes ☐ no | +| 6 | Are the role outfits believable for a Hollywood-boulevard shift (tourist, delivery, guard, responder)? | ☐ yes ☐ no | +| 7 | Are the seeded tint palettes acceptable, or too pastel/saturated? | ☐ yes ☐ no | +| 8 | Do the seated and walking poses hold up? | ☐ yes ☐ no | -The stage is deliberately separate from the venue review harness: a mid-grey -solid background, ground plane, one key light and one cool fill light, no HUD, -no crowd, no flashlight, no phase lighting and no animation. Front is Godot +Z, -matching `NpcAvatar.yaw_towards()`. The camera orbits at the recorded yaw rather -than rotating the avatar, so the pose and foot contact stay fixed between views. -The full command is: +**Signoff:** ☐ approved as shipped ☐ approved with the follow-ups below ☐ rejected -```bash -godot --path . --script res://src/test/capture_npc_baseline.gd -- \ - --out=res://tmp/npc-baseline -python3 tools/npc_baseline.py sheet --dir tmp/npc-baseline +## Issues visible in the captures + +Found while building this page; none blocks the swap, all are queued in `TODO.md`: + +1. **`npc_dress` wears the performer cone hat plus the protest sign** because the performer props + are procedural and scaled for the old body. The dress itself has no geometry anymore — the role + is the `casual` base with a dress tint. +2. **`npc_coat` and `npc_hoodie`** are the hoodie base with a coat/hood tint; the raised hood and + the long coat silhouette are gone. +3. **The `npc_vest` clipboard** reads oversized at 0.23 × 0.31 m on the shorter Quaternius arm. +4. **The firefighter helmet brim** is 1.55× the head width — realistic for the helmet, but it reads + as a wide-brim hat next to the small head. +5. **Skin and cloth read pale** on the `hoodie` and `worker` bases; the seeded palette was tuned + against the procedural library's darker cloth. +6. **Roster variety is three meshes for twelve roles.** Tint and gear carry the difference; the + modular part library is the fix. + +## Measured facts for this build + +| Metric | Value | +| --- | --- | +| Characters | 12, all on the Quaternius 62-joint rig | +| Triangles per character | 4,458 (`worker` minus vest) … 6,424 (`casual`) | +| glTF vertices per character | 9,370 … 13,440 | +| Budget | 24,000 tris / 24,000 verts per character | +| Tint slots | `skin`, `cloth`, `pants`, `hair`, `accessory`, plus fixed detail | +| Texel density | 73–74 px/m on the 256² maps | +| Height | 1.817–1.881 m | +| 24-NPC frame budget | 160,608 tris, 0.84 ms average frame (budget 50 ms) | +| Gates | `inspect` clean, `audit` 12/12, `fit` OK, Godot 278/278, Python 167/167 | + +## Reproducing the captures + +```sh +godot --path . --script res://src/test/capture_npc_baseline.gd -- --out=res://tmp/npc-signoff +godot --path . --script res://src/test/capture_npc_pose.gd -- --out=res://tmp/npc-signoff-pose \ + --pose=idle # repeat for sit and walk_contact ``` -The 48 raw PNGs are local QA under `tmp/` (gitignored). The committed contact -sheet is [`blender_previews/sheet_npc_baseline.jpg`](blender_previews/sheet_npc_baseline.jpg). -For the P2 hand/grip check there is an optional eighth view, captured separately -so the 48-frame baseline stays stable: +## Superseded -```bash -godot --path . --script res://src/test/capture_npc_baseline.gd -- \ - --out=res://tmp/npc-hands --view=hand -``` +This page previously carried the **0.9.4 procedural library** baseline (eight hand-built +characters, the face/hand/neck fixes, the 13-core proportion audit). That mesh no longer ships, so +the text is retired; the comparison sheets stay in `blender_previews/` as history: +`sheet_npc_baseline_compare-p1.jpg`, `-body.jpg`, `-torso.jpg`, `-face-detail.jpg`, +`sheet_npc_hands_compare.jpg`, `sheet_npc_hands_grip_compare.jpg`. -That writes eight `*__hand.png` frames at 0.80 m from the right hand; the -before/after review image is -[`sheet_npc_hands_compare.jpg`](blender_previews/sheet_npc_hands_compare.jpg). -The capture is deterministic apart from engine rasterisation; re-running into -the same directory keeps the earlier files and writes `-vN` copies rather than -overwriting them. - -## Acceptance criteria - -The criteria below are for the stylized target, not photorealism. A change must -pass every applicable item before a library rebuild is accepted. - -1. **Asset contract:** `npc_base` mesh node, the required bones, five tint slots, - front +Z, feet within 3 cm of y=0, 1.6–2.1 m height, at most 24,000 triangles - and 24,000 glTF vertices per character. The required 13 core bones stay fixed; - optional extra bones are allowed. Gear remains at most 1,500 triangles. -2. **Proportion and silhouette:** head, neck, shoulders, torso, hips, limbs and - feet read as one body; front/three-quarter/profile silhouettes agree with the - rest of the eight-character library; broad/slim/average build variation is - preserved. -3. **Face:** at 1.85 m the eyes, iris, brows, nose, mouth and ears are distinct; - the head volume has a readable jaw/cheek/chin transition; no face-plane seams, - inverted normals or z-fighting. The face direction must remain unambiguous. -4. **Hair and headgear:** hair covers the scalp without gaps or scalp-through - seams; fringes, buns and beards keep their silhouette. Caps, hoods, - sunglasses and the firefighter helmet clear the brow, eyes and ears. Headgear - may sit outside the 1.55–1.81 m face band, but its brim must not cover the - eyes. -5. **Hands and footwear:** at conversation distance the fingers and thumb read - as a hand, not a mitten; hand items contact the palm rather than floating; - shoes and boots are grounded with a readable toe/heel silhouette and preserve - the responder/firefighter footwear language. -6. **Clothing:** collars, cuffs, plackets, waistbands, pockets, badges and - reflective bands transition cleanly; no z-fighting or floating detail; coat, - dress and skirt hems do not show legs through the fabric during idle, walk, - block, sit or turn; the five tint slots and fixed detail colours remain - unchanged. -7. **Gear:** head gear clears the refined face/hair; the lanyard badge remains - readable in front of the jacket; backpack, shoulder bag and SCBA tank straps - lie outside the torso and do not read as a second bag; hand items use the - authored grip points. -8. **Crowd and performance:** the silhouette remains readable at the 8 m crowd - frame and the LOD/impostor tiers match the full mesh; the 24-NPC budget stays - within the existing 6.94 ms baseline (no regression beyond the measured - variance), and no new per-frame material or mesh churn is introduced. - -Asset-space `fit` values are measurements of the untouched GLBs, not a pass/fail -result. A negative `x_clearance` means the gear's narrowest face is inside the -body reference; a negative `radial_clearance` means it extends beyond the body -silhouette (normal for a backpack, suspicious when it is a strap). Runtime -`NpcAvatar.GEAR_OFFSETS` is applied after attachment and is not part of the GLB -measurement. - -## Proportion audit - -Status: audited 2026-11-02. All eight library variants are inside the stylized -proportion guide; no geometry correction was required in this pass. The report -is repeatable from the exported GLBs: - -```bash -python3 tools/npc_proportions.py report -python3 tools/npc_proportions.py report --json -``` - -### Method - -`tools/npc_proportions.py` reads the GLB meshes and rest-pose joint hierarchy: - -- height from the full mesh bounds; -- head height, width and aspect from `Head`/`skin` vertices (hair excluded so - long hairstyles do not change the head measurement); -- shoulder silhouette from vertices in the upper-chest band around the - shoulder joint; -- hip width from `HipL`/`HipR` pants vertices; -- torso length from the Spine joint to the shoulder joint; -- leg length from the Spine joint to the lowest vertex; -- arm length from the shoulder joint to the Hand joint; -- foot length from the forward extent of the KneeL/KneeR shoe vertices. - -All ratios are divided by total standing height except `head_aspect` -(width / head height). - -### Guide - -These are stylized-but-credible bands for the game's low-poly adults, not -photoreal anatomy. The broad build is allowed a larger shoulder/hip silhouette -than the slim build. - -| Ratio | Target | Notes | -| --- | ---: | --- | -| `head_height` | 0.16–0.22 | deliberately larger than a realistic 0.13–0.15 | -| `head_aspect` | 0.70–1.00 | width / height; rounded heads are acceptable | -| `shoulder_width` | 0.28–0.38 | silhouette includes sleeves and build scaling | -| `hip_width` | 0.17–0.23 | pants-only width, not skirt/coat flare | -| `torso_length` | 0.28–0.38 | Spine joint to shoulder | -| `leg_length` | 0.40–0.48 | Spine joint to ground | -| `arm_length` | 0.28–0.38 | shoulder to hand | -| `foot_length` | 0.12–0.18 | shoe forward extent | - -### Result - -| Character | Height | Head | Aspect | Shoulder | Hip | Torso | Leg | Arm | Foot | Result | -| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | -| `npc_casual` | 1.824 m | 0.190 | 0.907 | 0.321 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS | -| `npc_jacket` | 1.824 m | 0.190 | 0.907 | 0.333 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS | -| `npc_coat` | 1.824 m | 0.190 | 0.907 | 0.328 | 0.209 | 0.307 | 0.449 | 0.329 | 0.144 | PASS | -| `npc_vest` | 1.819 m | 0.191 | 0.907 | 0.322 | 0.209 | 0.308 | 0.451 | 0.330 | 0.144 | PASS | -| `npc_dress` | 1.825 m | 0.190 | 0.907 | 0.289 | 0.191 | 0.307 | 0.449 | 0.329 | 0.144 | PASS | -| `npc_hoodie` | 1.856 m | 0.187 | 0.907 | 0.328 | 0.206 | 0.302 | 0.442 | 0.323 | 0.141 | PASS | -| `npc_firefighter` | 1.819 m | 0.191 | 0.907 | 0.358 | 0.223 | 0.308 | 0.451 | 0.330 | 0.148 | PASS | -| `npc_guard` | 1.819 m | 0.191 | 0.907 | 0.369 | 0.219 | 0.308 | 0.451 | 0.330 | 0.144 | PASS | - -No variant is outside a band, so the body pass does not need a global skeleton -or mesh rescale. The values closest to a boundary are: - -- **head height 0.190** against the 0.22 upper bound, and **head aspect 0.907** - against 1.00 — the head is deliberately large. Any head-size change must be - coordinated with the face/headgear pass and its gear-fit measurements. -- **guard shoulder 0.369** and **firefighter hip 0.223** against the broad-build - upper bounds — these are the intended uniform/turnout silhouettes, not - outliers. -- **leg length 0.449** and **torso 0.307** sit inside the guide for all variants; - the earlier short-leg impression came from the hip joint position, not the - visible pelvis line. - -### Follow-up - -The proportion audit does not replace the visual criteria in -[`npc-visual-review.md`](npc-visual-review.md): neck/shoulder/hip transitions, -face depth, hand grips, clothing hems and shoes still need the appearance pass. -Re-run the audit after any body mesh or skeleton change and record the new -ratios here. - -## Baseline measurements - -From `python3 tools/blender_assets.py fit` and the manifest: - -| Character | Tris | Verts | Head width | Clothing radius | Notable gear clearances | -| --- | ---: | ---: | ---: | ---: | --- | -| `npc_casual` | 4,482 | 4,260 | 0.315 m | 0.191 m (hip) | backpack x −0.041, radial −0.162 | -| `npc_jacket` | 4,872 | 4,661 | 0.315 m | 0.191 m (hip) | lanyard x −0.116, radial +0.023 (runtime +4 cm forward) | -| `npc_coat` | 5,474 | 5,264 | 0.315 m | 0.225 m (skirt) | sunglasses width ratio 0.641; bag x +0.037, radial −0.130 | -| `npc_vest` | 4,980 | 4,879 | 0.315 m | 0.191 m (hip) | cap width ratio 1.067 | -| `npc_dress` | 4,900 | 4,719 | 0.315 m | 0.260 m (skirt) | bag x +0.002, radial −0.095 | -| `npc_hoodie` | 5,304 | 4,950 | 0.315 m | 0.190 m (hip) | — | -| `npc_firefighter` | 5,344 | 5,494 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 | -| `npc_guard` | 5,046 | 5,096 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 | - -`npc_coat` is widest at 5,474 triangles (well below the 16,000 cap); -`npc_firefighter` is closest on vertices at 5,494 (well below the 16,000 cap). -The refit backpack measures 380 triangles / 406 vertices. - -## Baseline findings, ranked - -### P1 — fix in the first model pass - -1. **The tourist carried two bag silhouettes. Fixed 2026-11-02.** `_add_props()` - created a 0.30 × 0.36 × 0.14 box at `(-0.34, 1.15, -0.18)` for the tourist - photo pose while `_add_accessory("backpack")` also attached the library - backpack. From the three-quarter/back view the two read as separate bags. The - photo prop is now hidden outside `photo`/`film` poses - (`photo_prop_visible_for_pose()`), so it only appears when the character is - actually taking a picture; the after frame confirms one bag at rest. -2. **Face depth and detail. Fixed 2026-11-02.** The sclera was recessed into the - socket and wrapped with a thin upper lid; the separate jaw/cheek spheres were - replaced by a chin plate and cheekbones sculpted into the head mesh. The nose - now has ala wings and a smaller bridge/tip, the mouth is one rounded slot - instead of a flat bar, and the ears have a darker concha. That removes the - dark intersection rings and the moustache-like read at portrait distance; the - low-poly brows remain stylized. -3. **Backpack straps read as rigid black bars. Refit 2026-11-02.** The straps - were thin round tubes whose chest/waist points sat inside the torso, so only - fragments showed from the front. They are now flattened bands whose front path - sits outside the chest; the after sheet shows a continuous strap instead of - two detached rods. The backpack asset is now 380 triangles / 406 vertices - (still far below the 1,500-triangle gear budget). - -### P2 — fix after the first P1 pass - -4. **Hands: geometry and forward offset fixed 2026-11-02; finger curl still - open.** The palm is wider and shallower, and each finger is now a - two-segment bent tube with a rounded tip and a separate bent thumb. Hand gear - is authored around the wrist proxy, so the runtime now moves every hand item - `HAND_ITEM_FORWARD` (4 cm) into the open palm; the phone, flashlight, camera, - cup and clipboard are no longer buried inside the hand. The fingers are still - open, so a true wrap/thumb contact and the umbrella/sign grips remain to be - judged. -5. **Clothing detail is tile-driven and transitional seams are visible.** The - fabric pattern repeats strongly on the casual, coat and dress variants, and - collar/cuff/waist edges are abrupt. This is more visible than the shoe/hem - details in the current portraits. -6. **Shoes: fixed 2026-11-02.** Casual shoes gained rounded toe and heel caps; - responder/firefighter boots gained matching toe/heel volumes while keeping the - taller black rubber boot language. Both stay above the sole contact plane, and - the full-body comparison is `sheet_npc_baseline_compare-shoes.jpg`. -7. **Headgear scale and clearance. Checked 2026-11-02.** The cap and helmet - width ratios are 1.067 and 1.651; those are expected for headgear. Final-context - portraits confirm the cap, firefighter helmet, hood and sunglasses clear the - refined brow/eyes, and the helmet brim still reads as deliberate turnout gear. - No gear rebuild was needed. -8. **Performer props dominate the silhouette.** The red cone hat and the - primitive sign are intentionally large stage props, but they should be - checked during the pose pass so they do not read as head/body geometry. - -### Accepted limits for the 0.9.4 release - -- The hooded variants intentionally cover part of the face; face readability is - judged on the visible lower face and silhouette, not on hidden eyes. -- The low-poly style is the target. The acceptance bar is readability and clean - construction, not photoreal skin or hair. -- Held items use the forward-offset open palm (`HAND_ITEM_FORWARD`) rather than a - finger-curl rig. There are no finger bones in the contract; phone, cup, - clipboard, camera, flashlight, wand and radio were judged in the hand capture. -- Fabric folds are not separate geometry; the current torso, elbow and knee - shaping is the accepted stylized construction. -- The >26 m impostor remains a two-quad billboard; the LOD tier/hysteresis and - near/far swap are covered by `test_avatar_mesh_budget_and_lod_swap`. -- NPC fabric textures are 256² with a 4× UV repeat; cloth/pants skip the baked - per-character AO to avoid large portrait blotches. Skin, hair and gear keep AO. -- Evening readability is represented by the day/night/LATE final-context set; - LATE is the venue night variant. - -## Before/after workflow - -Keep the baseline directory, rebuild the same capture into a new directory after -the model changes, then produce a comparison sheet. Do not overwrite the -committed baseline image: - -```bash -godot --path . --script res://src/test/capture_npc_baseline.gd -- \ - --out=res://tmp/npc-baseline-after -python3 tools/npc_baseline.py compare \ - --before tmp/npc-baseline --after tmp/npc-baseline-after \ - --out tmp/npc-baseline-compare.jpg -``` - -The comparison sheet places the before/after pair for each view side by side. -The committed runs are -[`sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg), -[`sheet_npc_baseline_compare-body.jpg`](blender_previews/sheet_npc_baseline_compare-body.jpg) -and [`sheet_npc_baseline_compare-face.jpg`](blender_previews/sheet_npc_baseline_compare-face.jpg). -The transition pass is [`sheet_npc_baseline_compare-torso.jpg`](blender_previews/sheet_npc_baseline_compare-torso.jpg). -The face-detail pass is [`sheet_npc_baseline_compare-face-detail.jpg`](blender_previews/sheet_npc_baseline_compare-face-detail.jpg). -The hand pass is [`sheet_npc_hands_compare.jpg`](blender_previews/sheet_npc_hands_compare.jpg); -the forward-grip pass is -[`sheet_npc_hands_grip_compare.jpg`](blender_previews/sheet_npc_hands_grip_compare.jpg). -The 0.9.5 texture pass adds -[`sheet_texture_refinement_0.9.5.jpg`](blender_previews/sheet_texture_refinement_0.9.5.jpg), -which pairs the close-range surface and cloth comparisons. Keep the baseline -sheet as the before artifact and add a new comparison sheet rather than -replacing it. - -## Next implementation order - -0.9.4 and the 0.9.5 texture pass are closed. The next visual work belongs to the -0.10.0 acceptance review; keep the NPC baseline/final-context commands as the -regression gate for any future character edit. +The current source evaluation and per-character build table live in +[`npc-model-sourcing.md#shipped-roster-099-owner-chose-the-full-swap`](npc-model-sourcing.md#shipped-roster-099-owner-chose-the-full-swap); +the face options that led to the full swap are in +[`screens/npc-face-gate/`](screens/npc-face-gate/README.md).