Establish the 0.9.4 NPC visual baseline
Add a neutral 48-frame character contact sheet, acceptance criteria and ranked baseline gaps, plus repeatable sheet/comparison tooling. No character GLB or runtime mesh changes.
This commit is contained in:
1 parent
c1cd4898e8
commit
e7ee177c31
13 files changed
+628
-11
No files matched your search
@@ -11,6 +11,7 @@ This folder holds the project documentation, visual references and generated QA
|
||||
- [`architecture.md`](architecture.md) — arc42 system architecture
|
||||
- [`getting-started.md`](getting-started.md) — setup, run/test commands and controls
|
||||
- [`asset-tooling.md`](asset-tooling.md) — image/model pipelines and screenshot gates
|
||||
- [`npc-visual-review.md`](npc-visual-review.md) — NPC visual baseline, acceptance criteria and ranked gaps
|
||||
- [`development-status.md`](development-status.md) — implemented features and current gaps
|
||||
- [`smoke-checklist.md`](smoke-checklist.md) — manual verification
|
||||
- [`ui-copy.md`](ui-copy.md) — screen, copy and button inventory
|
||||
|
||||
@@ -213,6 +213,20 @@ and firefighter helmet so the badge clears the jacket and the brim does not sit
|
||||
over the eyes; these are `NpcAvatar.GEAR_OFFSETS`, not changes to the gear
|
||||
contract.
|
||||
|
||||
`capture_npc_baseline.gd` is the 0.9.4 neutral baseline: eight characters ×
|
||||
front/three-quarter/profile/back/full-body/crowd = 48 frames with a fixed
|
||||
camera, studio lighting and no venue/HUD/animation. The raw PNGs stay in
|
||||
`tmp/`; `tools/npc_baseline.py sheet` builds the committed contact sheet, and
|
||||
`tools/npc_baseline.py compare --before DIR --after DIR` builds the review
|
||||
comparison after a model change. See
|
||||
[`npc-visual-review.md`](npc-visual-review.md).
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
`capture_realism.gd` runs seven stages (day, portrait, evening, late, Joke Shop day, Joke Shop night, studio);
|
||||
`--stages=day,late` limits it while tuning; `--flashlight` turns on the guard lamp for
|
||||
night stages (used by the Desktop HQ A/B). The `portrait` shot freezes one NPC on open sidewalk
|
||||
|
||||
@@ -5,7 +5,10 @@ Architecture context: the asset contracts and fallback path are summarized in
|
||||
|
||||
Status: built, audited, and integrated (2026-11-01). `NpcAvatar` selects the
|
||||
character variants and attaches the gear; `WorldBuilder` loads the prop library
|
||||
with colliders and phase-driven emissives.
|
||||
with colliders and phase-driven emissives. The 0.9.4 neutral baseline and its
|
||||
acceptance criteria are in [`npc-visual-review.md`](npc-visual-review.md);
|
||||
the committed 8 × 6 contact sheet is
|
||||
[`blender_previews/sheet_npc_baseline.jpg`](blender_previews/sheet_npc_baseline.jpg).
|
||||
|
||||
Generator: [`tools/blender_assets.py`](../tools/blender_assets.py). Manifest (the
|
||||
source of truth for integration):
|
||||
@@ -197,3 +200,7 @@ carry baked AO and the shared neutral material maps described above.
|
||||
the legs through the hem.
|
||||
- Preview colours (`PREVIEW_TINTS`) only stand in for `outfit_for()`. The GLBs
|
||||
stay neutral.
|
||||
- The 0.9.4 baseline found three P1 portrait issues: the tourist's photo prop
|
||||
duplicates the backpack silhouette, the face is shallow/faceted at portrait
|
||||
range, and the backpack straps read as rigid bars. The full ranked list and
|
||||
accepted/deferred items are in [`npc-visual-review.md`](npc-visual-review.md).
|
||||
@@ -43,6 +43,7 @@ Committed contact sheets and local Blender renders for the character, gear and p
|
||||
| <img src="security_podium.png" width="280" alt="security_podium.png"> | [`security_podium.png`](security_podium.png) |
|
||||
| <img src="sedan.png" width="280" alt="sedan.png"> | [`sedan.png`](sedan.png) |
|
||||
| <img src="sheet_npc.jpg" width="280" alt="sheet_npc.jpg"> | [`sheet_npc.jpg`](sheet_npc.jpg) |
|
||||
| <img src="sheet_npc_baseline.jpg" width="280" alt="sheet_npc_baseline.jpg"> | [`sheet_npc_baseline.jpg`](sheet_npc_baseline.jpg) |
|
||||
| <img src="sheet_npc_gear.jpg" width="280" alt="sheet_npc_gear.jpg"> | [`sheet_npc_gear.jpg`](sheet_npc_gear.jpg) |
|
||||
| <img src="sheet_props.jpg" width="280" alt="sheet_props.jpg"> | [`sheet_props.jpg`](sheet_props.jpg) |
|
||||
| <img src="shoulder_bag.png" width="280" alt="shoulder_bag.png"> | [`shoulder_bag.png`](shoulder_bag.png) |
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 268 KiB |
@@ -39,6 +39,7 @@ Implemented so far:
|
||||
- Aim-free selection pass (v0.3.0 work, [ADR-0011](adr/0011-aim-free-selection-and-precision-walk.md)): the focus cone ranks candidates by angle then distance, `Tab` and the mouse wheel cycle nearby people nearest-first and pin the selection, right click clears focus with a short re-acquire grace, the HUD reports how many people are in the ring, and holding `Shift` walks at half speed for close work
|
||||
|
||||
- Visual alignment: start/venue menu, pause, settings, shift review, radio, wand, inspection, timeline, HUD target chip/escalation bar, and radial menu now use or match their AI references in `assets/images/ui/`. The runtime art loader (`src/game/ui/art.gd`) falls back cleanly when a file is missing. The wand panel lists the guest's carried items, and multiline HUD/wand strings render real line breaks.
|
||||
- NPC visual baseline (0.9.4 work): `src/test/capture_npc_baseline.gd` captures all eight library characters from front, three-quarter, profile, back, full-body and crowd views in a neutral studio (48 frames). The criteria, measurements and ranked gaps are in [`npc-visual-review.md`](npc-visual-review.md); the committed sheet is [`blender_previews/sheet_npc_baseline.jpg`](blender_previews/sheet_npc_baseline.jpg). No model changes were made in this baseline step.
|
||||
- UI-state capture pass (0.9.3 work): `src/test/capture_ui_states.gd` adds committed captures 14–21 for the start menu, pause, settings, shift review, emergency radio, action wheel, crowd panic and responder arrival. Every one of the 21 catalogue entries now has a current-build capture; `tools/check_screenshots.sh` reports 21 good / 0 bad and the automatic judge reports 21/21 match, 0 diff. Crowd panic and responder arrival are staged presentation frames, not gameplay changes.
|
||||
- Realism adoption (post-v0.6.0): v2 per-phase skylines and surface tiles with normal maps, retuned sun/exposure/fog, wet asphalt, denser foliage, a visible additive flashlight beam, and refreshed staging captures. `tools/check_screenshots.sh` is the repeatable capture/compare gate; judge is clean of `diff` verdicts.
|
||||
- Input/reliability fixes: selecting a venue releases the start-menu input lock and recaptures the mouse; the body-camera overlay no longer covers the shift status. Mouse look now reads at `_input`, restores capture on window focus/click-back, and the HUD is mouse-transparent. Normal radio reports submit on click; emergency calls still require the hold-to-confirm path.
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
# NPC visual baseline and acceptance criteria
|
||||
|
||||
Status: baseline captured 2026-11-02. No character GLB, texture, gear or runtime
|
||||
mesh was changed in this step. The baseline is the reference for the 0.9.4 NPC
|
||||
visual optimization.
|
||||
|
||||
## What was captured
|
||||
|
||||
`src/test/capture_npc_baseline.gd` stages all eight library characters in a
|
||||
neutral studio and captures six views per character:
|
||||
|
||||
| View | Camera | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `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 |
|
||||
|
||||
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:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
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).
|
||||
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 6,000 triangles
|
||||
and 5,000 glTF vertices per character. 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, beanies, 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.93 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.
|
||||
|
||||
## Baseline measurements
|
||||
|
||||
From `python3 tools/blender_assets.py fit` and the manifest:
|
||||
|
||||
| Character | Tris | Verts | Head width | Clothing radius | Notable gear clearances |
|
||||
| --- | ---: | ---: | ---: | ---: | --- |
|
||||
| `npc_casual` | 3,974 | 3,744 | 0.315 m | 0.191 m (hip) | backpack x −0.041, radial −0.162 |
|
||||
| `npc_jacket` | 4,394 | 4,175 | 0.315 m | 0.191 m (hip) | lanyard x −0.116, radial +0.023 (runtime +4 cm forward) |
|
||||
| `npc_coat` | 5,026 | 4,807 | 0.315 m | 0.225 m (skirt) | sunglasses width ratio 0.641; bag x +0.037, radial −0.130 |
|
||||
| `npc_vest` | 4,474 | 4,340 | 0.315 m | 0.191 m (hip) | cap width ratio 1.067 |
|
||||
| `npc_dress` | 4,428 | 4,026 | 0.315 m | 0.260 m (skirt) | bag x +0.002, radial −0.095 |
|
||||
| `npc_hoodie` | 4,826 | 4,504 | 0.315 m | 0.190 m (hip) | — |
|
||||
| `npc_firefighter` | 4,848 | 4,735 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 |
|
||||
| `npc_guard` | 4,560 | 4,529 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 |
|
||||
|
||||
The widest character is `npc_coat` at 5,026 triangles and 4,807 vertices, so
|
||||
the current headroom is 974 triangles and 193 vertices per character. Any face
|
||||
or hand refinement must come out of that budget or replace existing detail.
|
||||
|
||||
## Baseline findings, ranked
|
||||
|
||||
### P1 — fix in the first model pass
|
||||
|
||||
1. **The tourist carries two bag silhouettes.** `_add_props()` creates 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 attaches the library backpack. From
|
||||
the three-quarter/back view the two read as separate bags. The photo prop
|
||||
should be hidden outside the photo pose, merged into the backpack, or moved to
|
||||
a clearly held camera.
|
||||
2. **The face is the largest portrait gap.** The eyes, nose and lips are
|
||||
readable but sit on a shallow, faceted head: the eye whites protrude, the
|
||||
eyelid/cheek transition is missing, and the mouth is a dark slot. This is
|
||||
intentionally stylized, but the head volume and eye socket are the first
|
||||
things a player sees at conversation range.
|
||||
3. **Backpack straps read as rigid black bars.** The back gear's straps cross
|
||||
the chest as thin, straight geometry with visible gaps from the shoulders,
|
||||
not as fabric sitting on the torso. A player sees this on the most common
|
||||
tourist silhouette in every crowd.
|
||||
|
||||
### P2 — fix after the first P1 pass
|
||||
|
||||
4. **Hands and grips are not proven at conversation distance.** The full-body
|
||||
frame shows small block hands; the camera, phone and clipboard need a
|
||||
close-up grip check before the hand mesh is accepted.
|
||||
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 are simple blocks.** They are grounded, but the toe/heel silhouette
|
||||
is minimal; the responder and firefighter footwear should remain distinct
|
||||
after any refinement.
|
||||
7. **Headgear scale needs a visual, not just numeric, check.** The cap and
|
||||
helmet width ratios are 1.067 and 1.651; those are expected for headgear, but
|
||||
the brim/ear clearance and the helmet's relationship to the head still need a
|
||||
face-on check.
|
||||
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 or deferred for this baseline
|
||||
|
||||
- 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.
|
||||
- Hem-through-leg clipping during a walk cannot be judged from a rest pose; it
|
||||
stays in the body/clothing workstream.
|
||||
- The 8 m `crowd` view is still LOD 0. The separate LOD/impostor check remains
|
||||
required; this baseline does not claim to validate it.
|
||||
|
||||
## 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.
|
||||
If the change is accepted, copy the after sheet to a new committed name
|
||||
(`sheet_npc_baseline_after.jpg`) and link both from the preview index. The
|
||||
baseline is not replaced.
|
||||
|
||||
## Next implementation order
|
||||
|
||||
1. Remove the duplicate tourist bag silhouette and refit the backpack straps.
|
||||
2. Refine the head/eye socket/jaw volume without changing the face direction or
|
||||
the five tint slots.
|
||||
3. Add a conversation-distance hand/grip capture and fix hand geometry before
|
||||
judging hand items.
|
||||
4. Audit hems and footwear in walk/sit/block poses.
|
||||
5. Re-run the 48-frame baseline, the 96-frame final-context review, the 24-NPC
|
||||
budget and `blender_assets.py audit/fit`; publish the before/after sheet.
|
||||
Reference in new issue
Block a user