Compare commits

...
24 Commits
Author SHA1 Message Date
zwitschi d7e523f63c Adopt the realism pass and refresh screenshots/docs
Full build / Import + headless tests (push) Successful in 34s
Full build / Full desktop build + optional release (push) Skipped
- add the v2 runtime picks: per-phase skyline panoramas, brick, sidewalk
  pavers, dry/wet asphalt, awning canvas, and their normal maps
- add the realism tooling and evidence: tools/scene_concepts.py, capture_realism.gd,
  concept frames, reports/sheets, and tests
- retune phase lighting/fog/exposure, add sun yaw, brighten the night glow,
  add the flashlight beam mesh, and rebuild the planter/tree assets
- regenerate all eleven staged screenshots from the current build; compare is
  11 good / 0 bad
- finish the visual-alignment closeout in DONE/TODO, update architecture,
  blender-assets, PLAN, and ignore rules (editable Blender sources untracked)
- keep the judge retry and document the verified default judge model

Verification: headless 227/227; Python tooling 86/86; check_screenshots 11/11.
2026-11-01 17:03:05 +01:00
zwitschi 26ecb93fd4 Move the body-camera overlay below the shift status
The REC/clock block sat on top of the top-right shift/rep/crowd lines. Start it
at y=92 and slightly farther from the edge, keeping the overlay visible without
covering the status. Headless 226/226 and a windowed HUD screenshot checked.
2026-11-01 16:51:19 +01:00
zwitschi 8997f59819 Record the judge re-check and the lighting blocker
Mark the default vision judge as verified (7 match / 4 review / 0 diff), note the
active realism pass blocking the remaining lighting task, and replace the stale
memory note that said screen_designs still read the docs tree.
2026-11-01 16:50:27 +01:00
zwitschi 79270795a8 Finish the image-root task and untrack Blender sources
- ignore assets/models/blender_src/*.blend (keep the .gdignore marker tracked) and
  remove the 46 editable .blend files from the index; they remain on disk and are
  regenerated by tools/blender_assets.py
- move the completed storage/docs/code-path/art-loader/verification, mouse-fix,
  and pop-up sections to DONE.md; TODO now holds only the open lighting item,
  realism follow-ups, and optional ideas
- docs changes only; runtime code unchanged
2026-11-01 16:49:43 +01:00
zwitschi dc4bc6c511 Use the access-inspection mockup behind the inspection panel
Add the access_inspection art layer to the HUD, keep the panel transparent and
padded, center the title/status/issue text and artifact cards, and hide the art
with the panel. Also record the pop-up visual pass as done. Headless 226/226
verified in a clean HEAD worktree.
2026-11-01 16:46:25 +01:00
zwitschi c8b98fdb30 Recapture the mouse after selecting a venue
_start_shift() now calls _refresh_freeze(), clearing the start-menu input lock
and returning to captured mouse mode. The movement smoke asserts both and no
longer captures the mouse itself. Headless 226/226 and smoke_movement pass in a
clean worktree; only this game_manager hunk is staged, not the active realism
edits.
2026-11-01 16:45:05 +01:00
zwitschi d96f53c285 Add a repeatable screenshot capture and compare smoke
Both capture helpers now accept --out=res://docs/screenshots, and
tools/check_screenshots.sh renders the staged set (using xvfb-run when headless)
before running screen_designs.py compare. Record the judge result: 7 match,
4 review, 0 diff. Headless 226/226 and the smoke both pass in a clean worktree.
2026-11-01 16:43:10 +01:00
zwitschi b39fee6176 Match the radial menu to its mockup
Widen the doughnut, use dark charcoal wedges with a dimmed scene behind them,
add divider spokes and inner/outer rings, raise the center-name size, and use a
restrained light highlight for the selected slice instead of the old yellow
fill. Headless 226/226 verified in a clean HEAD worktree.
2026-11-01 16:39:28 +01:00
zwitschi c2c93c28a5 Record the HUD alignment and capture comparison result 2026-11-01 16:37:56 +01:00
zwitschi 3030c60ae1 Align the HUD target chip and escalation meter with the mockups
Move the target name out of the top-left stack into a boxed chip under the
crosshair, keep the log/hints left and shift status right, and add a five-segment
escalation bar under the existing color-coded label. Empty targets hide the
chip. Headless 226/226 verified in a clean HEAD worktree.
2026-11-01 16:36:06 +01:00
zwitschi cb65a5a601 Note the radio/wand/timeline art wiring in memory 2026-11-01 16:30:48 +01:00
zwitschi 06a4f5479f Align the body-camera timeline with its sidebar mockup
Draw body_camera_timeline behind a full-height narrow panel, constrain the
content to that column, wrap long event text, and scroll the event list so the
Previous/Next/Close row stays pinned at the bottom. Headless 226/226 verified in
a clean HEAD worktree.
2026-11-01 16:30:17 +01:00
zwitschi 8032999a4d Use the radio and wand mockups behind their forms
Draw radio_report/emergency_radio behind a transparent, padded form and switch
the backdrop with the call type. The wand HUD popup now shows wand_screening
behind its text while open. Both covers use the mockup panel bounds so baked-in
controls stay hidden. Headless 226/226 verified in a clean HEAD worktree.
2026-11-01 16:29:35 +01:00
zwitschi 8582e496ff Point the README at the runtime image root
Update the resize/audit examples and the screen-design section to
assets/images/ui|scenes, with docs/screens/designs as the reference snapshot.
The separate realism-pass README hunk stays unstaged.
2026-11-01 16:20:10 +01:00
zwitschi ae326ce970 Mark the aligned menu/panel items done in the visual pass 2026-11-01 16:20:01 +01:00
zwitschi 00faf45e83 Use the shift review mockup as the end-of-shift backdrop
Wrap the review content in a Control root, draw the shift_review art behind an
opaque panel, and style the action buttons to match the mockup. Headless
226/226 verified in a clean HEAD worktree.
2026-11-01 16:19:54 +01:00
zwitschi 41d3511fc7 Record the runtime art loader and the active realism workstream in memory 2026-11-01 16:19:23 +01:00
zwitschi 1efa5bc7d9 Use the pause and settings mockups as panel backdrops
Add UiArt.add_screen_backdrop() to place the reference art behind an opaque
panel, then wire the pause overlay and settings screen to it. Pause rows get
larger, rounded styles; settings widens to fit its expanded controls. Headless
226/226 verified in a clean HEAD worktree (the realism WIP currently editing
materials.gd remains unstaged).
2026-11-01 16:19:18 +01:00
zwitschi 4745adeb13 Move screen designs to the runtime root and add the UI art loader
- screen_designs.py now reads/writes assets/images/ui/ (ADR-0014); docs/
  screens/designs/ stays the reference-only snapshot, documented as historical
- add src/game/ui/art.gd: cached loader for assets/images/ui|scenes with null
  fallbacks for missing files, plus headless tests
- start menu uses the start_menu scene art as a full-screen backdrop, covers the
  baked-in mockup panel with an opaque panel, and styles the venue rows to match
- update README/architecture/TODO path notes and mark the storage/code-path/loader
  items done; DONE records the historical path move
- compare still 11 good / 0 bad; headless 226/226

README/PLAN realism-pass edits from another workstream remain unstaged." && git log --oneline -1
2026-11-01 16:00:12 +01:00
zwitschi 15deb58b73 Import the promoted runtime mockups
Run the headless Godot import after copying the 21 design references into
assets/images/ui/; all sidecars are generated and TODO records the item done.
2026-11-01 15:48:22 +01:00
zwitschi 6a0bf70fdf Document the asset storage convention
Add the path table from ADR-0014 to the architecture documentation (runtime UI/scene
art, tiled textures, models, reference art, QA captures, raw sources) and close the
second storage-restructure TODO item.
2026-11-01 15:47:56 +01:00
zwitschi f458551594 Claim TODO tasks with [-]; promote mockups to assets/images/ui
- AGENTS.md now requires agents to mark a task [-] while claimed/in progress and
  only use [x] after implementation and verification, with an example
- copied the 21 reference mockups from docs/screens/designs/ into the runtime
  root assets/images/ui/ (byte-identical names); docs/screens/designs/ stays as
  the reference-only copy per ADR-0014
- MEMORY records where the copies live and that screen_designs.py still reads
  the docs tree until the code-path migration

First storage-restructure TODO item completed.
2026-11-01 15:47:35 +01:00
zwitschi 8f228b0c56 Decide the canonical runtime image root (ADR-0014)
assets/images/ is now the runtime root for composed interface/scene art:
ui/ for screen and panel art, scenes/ for full-frame scene art. Tiled PBR
textures remain under assets/textures/; docs/screens/designs and concepts stay
reference-only; QA captures stay under docs/screenshots. Mockups that become
runtime art are copied/processed with a new name (no overwrites) and always
have a code fallback.

Records the convention in MEMORY, adds ADR-0014 to the architecture index, and
closes the first storage-restructure TODO item.
2026-11-01 15:16:24 +01:00
zwitschi d307746351 Close the v0.6.0 graphics plan and record the release
Full build / Import + headless tests (push) Successful in 32s
Full build / Full desktop build + optional release (push) Skipped
- TODO now tracks only the visual-alignment pass and optional future ideas; the
  completed Blender/graphics plan moved out
- DONE records the six published v0.6.0 artifacts and final verification counts
- header updated to v0.6.0 / 222 headless / 69 Python tests
2026-11-01 15:12:37 +01:00
155 changed files with 4895 additions and 410 deletions

No files matched your search

+9
View File
@@ -64,3 +64,12 @@ docs/blender_previews/*.png
assets/models/blender/**/*.png
!assets/models/blender/**/*_ao.png
assets/models/blender_src/*.blend1
# Editable Blender sources are developer-only; keep the .gdignore marker tracked.
assets/models/blender_src/*.blend
# Realism pass (tools/scene_concepts.py): the candidate pool and per-run captures
# stay local; the validated picks are tracked under game_ready/v2/picks/ (ADR-0014),
# and each run commits its report.{json,md} + sheet.jpg. Concept references are
# committed in docs/screens/concepts/.
assets/textures/ai/game_ready/v2/*
!assets/textures/ai/game_ready/v2/picks/
docs/screens/realism/**/*.png
+5 -1
View File
@@ -39,6 +39,10 @@ All planned implementation work must be represented in `TODO.md`.
- Use checkbox items for each concrete task.
- Keep tasks small enough to complete and review independently.
- Add new tasks when discoveries expand the scope.
- When starting a task, claim it for the agent workflow by changing `[ ]` to `[-]`. Keep it `[-]`
while implementation or verification is in progress; only change it to `[x]` after the work and
relevant checks are finished. If work is abandoned or blocked, leave a clear note and return the
item to `[ ]` (or add a follow-up task).
- Mark a task complete only after the implementation and relevant verification are finished.
- If any verification check fails, do not mark the task complete. Fix the failure, or if it cannot be fixed, add a follow-up task to `TODO.md`, record the failure in `MEMORY.md`, and report it in the handoff summary.
- Do not use prose status updates as a substitute for checkboxes.
@@ -52,7 +56,7 @@ Recommended format:
## Core Gameplay
- [ ] Add player movement controls.
- [ ] Implement NPC approach behavior.
- [-] Implement NPC approach behavior. (claimed; implementation/verification in progress)
- [x] Define escalation levels.
## Documentation
+249
View File
@@ -878,3 +878,252 @@ Completed 2026-11-01 (v0.6.0). Headless **222/222**, Python **69/69**, all smoke
deferred.
- [x] Recaptured `docs/screenshots/` and ran `compare` + `judge`; no regressions.
- [x] Fixed the library marquee text/face alignment and added the library AO/triangle tests.
## v0.6.0 Release
Completed 2026-11-01. `config/version` = `0.6.0`; ADR-0013 marked Accepted; CI published six
artifacts (Linux, Windows, macOS, NSIS installer, AppImage, Web) for tag `v0.6.0`.
- [x] Bumped the version and synchronised README/architecture/blender-assets/plan docs.
- [x] Headless **222/222**, Python **69/69**, all smokes, `compare` **11 good / 0 bad**.
- [x] Release verified: `https://git.allucanget.biz/zwitschi/security-game/releases/tag/v0.6.0`.
## Runtime Image Path Migration
Completed 2026-11-01. Historical note: the screen-design references originally lived only in
`docs/screens/designs/`; they were promoted to the runtime root `assets/images/ui/` under
ADR-0014. `docs/screens/designs/` remains a reference-only snapshot; `tools/screen_designs.py`
now reads and writes `assets/images/ui/`.
## Realistic Backgrounds, Textures and Scene References
Completed 2026-11-01 (part of the v0.6.x realism pass). The flat box skyline was replaced by
per-phase AI panoramas, the surface tiles by higher-detail v2 picks with normal maps, and the
lighting/exposure was retuned against AI concept frames. Evidence:
`docs/screens/realism/2026-11-01-adopt/` (`report.md`, `sheet.jpg`), `screen_designs.py compare`
**11 good / 0 bad**; headless **227/227**, Python **86/86**, all smokes.
### Realistic backgrounds, textures and scene references
Goal: replace the outdated flat skyline backdrop (`assets/textures/skyline.png`, black boxes
with dot windows) and the flat-looking surface tiles with AI-generated, more realistic
backgrounds and textures. Add a larger set of example 3D scene images as art direction, and
measure the in-game result against that target with the screenshot tools. Plan:
[`PLAN.md` → Visual Quality Direction → Realism pass](PLAN.md#realism-pass).
**Rule for every step: never overwrite an existing image.** New outputs get new paths. When a
path is taken, the tools pick the next free `-vN` suffix. Raw AI output stays on disk under
`assets/textures/ai/v2/raw/` (gitignored like the other raw AI folders). Processed game-ready
candidates go to `assets/textures/ai/game_ready/v2/`, a local pool where only the validated picks
are tracked (ADR-0014). Scene references go to `docs/screens/concepts/`, and trial captures and
reports to `docs/screens/realism/<run>/`.
#### Step 1 — Tooling (`tools/scene_concepts.py`)
- [x] Add a catalogue of prompts in three groups: `scene` (16:9 example 3D game frames),
`backdrop` (4:1 skyline panoramas with transparent sky), and `texture` (seamless PBR
surface tiles). Each entry has a name, prompt, role and target path.
- [x] `generate [--group] [--only] [--variants N]`: call the OpenRouter image API through
`generate_textures.generate_image` (key from the repo `.env`), save raw output, and never
overwrite (`next_free_path`). Print every file written.
- [x] `process`: turn raw backdrops into 2048×512 RGBA strips that tile horizontally (edge
crossfade, sky made fully transparent, hard alpha for the alpha-scissor material). Turn raw
textures into seamless 1024² tiles (`asset_pipeline.make_seamless`) plus a normal map
(`make_normal_maps.normal_map`). Skip outputs that already exist.
- [x] `audit`: run `asset_pipeline.audit_image` on the processed candidates (`backdrop` /
`tile` roles) and fail on any bad result.
- [x] `realism`: for each trial capture, report the Pillow detail metrics (edge density,
contrast, colour spread, saturation) next to the matching concept image. Optionally ask a
vision model (`google/gemini-2.5-flash-lite`) for 0–10 realism scores (lighting,
materials, texture detail, backdrop, overall) with concrete fixes. Write a new JSON +
Markdown report per run.
- [x] Unit tests (`tools/test_scene_concepts.py`): catalogue shape, the no-overwrite path
picker, backdrop/tile processing on synthetic images, metrics, and the judge payload/parse
(offline, no network).
#### Step 2 — Example 3D scene images
- [x] Generate 3D-game-style example frames for every venue and time of day: Hollywood entrance
(morning, golden hour, rainy night), boulevard skyline view (day, dusk), Joke Shop alley
(evening, close-up of the door), studio stage door (night arrival), sidewalk material
close-up, entrance steps and podium, crowd-control barrier line, and a responder arrival
wide shot.
- [x] Review the set and record the visual targets each frame sets (sky, haze, material detail,
backdrop depth) in the report.
#### Step 3 — Backgrounds (replace the outdated skyline)
- [x] Generate skyline panoramas for day, dusk and night (lit windows): Hollywood/Los Angeles
mid-rise blocks, palm silhouettes, the hills on the horizon, a transparent sky, and a
straight-on view, in several variants each.
- [x] Process them into `assets/textures/ai/game_ready/v2/skyline_{day,dusk,night}*.png`,
seamless horizontally for the 3× UV repeat around the backdrop ring. Strips are 2048×1024
(2:1, one UV repeat of the 400×60 m ring is about 133×60 m). 12 candidates, 11 audit
clean; `skyline_day-v3` misses the seam limit by 0.6. Variants 1–2 contain garbled
"Hollywood" lettering, so the prompt now forbids signs. Picks: `skyline_day-v4`,
`skyline_dusk-v3`, `skyline_night-v4`.
#### Step 4 — Textures
- [x] Generate higher-detail seamless tiles: red brick facade, granite/limestone facade
(studio), sidewalk concrete pavers with joints, asphalt with aggregate, wet night asphalt,
stone steps, striped awning canvas, and stucco wall.
- [x] Process them into 1024² game-ready tiles plus normal maps in
`assets/textures/ai/game_ready/v2/` and audit them (16 tiles + 16 normals, all good). The
`awning_canvas` seam crossfade smears the stripes at the tile edge; it needs a
stripe-aligned crop.
#### Step 5 — In-game trial captures (no game-code change)
- [x] Add `src/test/capture_realism.gd`. It boots the Hollywood venue, captures a fixed shot
list (entrance day, boulevard/skyline day, boulevard night), and can swap textures at
runtime from `--swap=OLD=NEW` / `--tint=` / `--normal=` user arguments (the first version used
`--skyline=`/`--brick=`/… flags; `--swap` is path-based and also covers night stages). The
swap finds materials by their original texture path, so baseline and candidate frames use
the same camera. Writes to the `--out=` directory.
- [x] Capture the baseline (current assets) and the candidate set (v2 skyline + textures), and
copy them into `docs/screens/realism/<run>/{baseline,candidate}/`.
#### Step 6 — Compare against the realistic target
- [x] Run `scene_concepts.py realism` on baseline and candidate. Record the metric deltas and the
vision scores, and note which candidate textures/backdrops win. Run
`docs/screens/realism/2026-11-01-v2/` (`report-v2.md`, `sheet.jpg`) gives these mean
judge scores: backdrop 2.6 → 3.6 (v2 day + night skyline) → **4.0** (v2 day + dusk
skyline for the Evening phase), texture detail 1.2 → 1.6–1.75, overall 1.6 → 2.0. Single
scores swing by ±1–2 between runs, so read them as trends. The `characters` score
moved from 3.0 to 1.0 with identical NPCs, which shows the noise.
- [x] Note the remaining realism gaps (sky/fog, lighting, building geometry, NPC fidelity) as
follow-up tasks below.
#### Step 6b — Follow-ups from the 2026-11-01 realism report
- [x] Fix sidewalk overexposure: lowered the day sun (0.78→0.62), exposure (0.8→0.6) and ambient
(0.42→0.22), and darkened the sidewalk/street albedo tints. The like-for-like ground
luminance in `docs/screens/realism/2026-11-01-adopt/` went from 173/162/132 to 95/92/73
(concept targets 78/100/100). The entrance shot stays ~20 % brighter than the concept: a
residual, tracked in the follow-ups below.
- [x] Make the Evening/Late phases darker and lit by practical lights (marquee, lamps,
windows), and add wet-asphalt roughness at night, as in the rain-night concept. Evening is
now dusk (`sun 0.3→0.12`, `exposure 0.95→0.72`, dark violet sky), Late is near-dark, and
`WorldBuilder.set_phase()` swaps the street to the wet asphalt map in Late.
- [x] Choose the skyline per phase (day → `skyline_day`, Evening → `skyline_dusk`, Late →
`skyline_night`) and stop the scene fog from bleaching it. The backdrop is now a
`fog_disabled` shader, because Godot 4.3 has no `fog_enabled` on `StandardMaterial3D`. The
first dusk panorama was a red floodlit skyline that clashed with the Evening sky, so a
calmer prompt produced `skyline_dusk-v5` (blue-grey towers, lit windows).
- [x] Raise the backdrop plane (60 m → 90 m) and use 2 UV repeats so the skyline is a band with
proportions near the 2:1 panorama, not a thin strip at the horizon.
- [x] Add facade depth (plinth, string courses, cornice, brick pilasters, window glazing bars)
and replace the blob trees and shrubs with denser foliage meshes: `_shrub()` now builds
many small flat-shaded clumps plus outer clumps that break the silhouette
(`street_tree` 1472→2592 tris), with fresh AO maps for `street_tree`, `planter_box` and
`round_planter`.
- [x] Re-cut `awning_canvas` so its stripes align at the tile edge: `stripe_period()` measures the
repeat and `align_stripes()` resamples to a whole number of periods (edge crossfade smeared
the stripes before). `audit_striped_tile()` checks the wrap step against the harshest
interior step instead of the flat edge-difference rule.
- [x] Capture shots from the Joke Shop and the studio too: `capture_realism.gd` now runs five
stages (day, evening, late, joke_shop, studio) with 9 shots and a `--stages=` subset. The
venues share the Hollywood geometry, so those two compare lighting, not layout.
#### Step 7 — Adopt the winners
- [x] Track the validated picks in place (ADR-0014). `.gitignore` now ignores the candidate pool
and un-ignores `game_ready/v2/picks/`, where `scene_concepts.py promote` copies the eight
validated candidates under stable names (`skyline_{day,dusk,night}`, `brick_facade`,
`sidewalk_pavers`, `asphalt`, `asphalt_wet`, `awning_canvas`) plus normal maps.
- [x] Point `WorldBuilder` at the chosen v2 skylines (separate day/dusk/night textures through
the new `set_phase()`), and retune the tints per phase.
- [x] Switch `materials.gd` / `world_builder.gd` surface preloads to the chosen v2 tiles + normal
maps, keeping the current UV scales (`Materials.asphalt()` gained a `wet` variant). Updated
`test_materials.gd` (pick paths, wet vs dry) and `test_world.gd` (shader backdrop, panorama
per phase, wet street).
- [x] Regenerate `docs/screenshots/` with `capture_screenshots.gd` (11/11), then run
`screen_designs.py compare` (**11 good / 0 bad**) and `judge` (5 match, 2 review, 4 diff —
the diffs are HUD/mockup layout gaps from the "Visual alignment" section, not realism
regressions; the default flash-lite judge model is currently failing with a provider 400, so
the run used `--model google/gemini-2.5-flash`). `scene_concepts.py realism` on
`docs/screens/realism/2026-11-01-adopt/` (3 judge repeats per shot) gives a like-for-like
improvement on every sub-score: overall 0.8→1.0, lighting 0.8→1.0, materials 0.8→1.0,
texture 0.8→1.0, backdrop 0.6→1.6, and the Pillow metric gap 0.444→0.348.
- [x] Update `README.md` (tool usage + gallery), `MEMORY.md` and `docs/architecture.md`.
## Visual Alignment — UI Paths and Art Loader
Completed 2026-11-01.
### Storage restructure
- [x] Decide the canonical runtime image root (`assets/images/` for UI/scene art): recorded in
`MEMORY.md` and [ADR-0014](docs/adr/0014-runtime-image-root.md).
- [x] Move the reference mockups from `docs/screens/designs/` into the runtime root (keep `docs/screens/designs/` as a docs-only copy or replace it with the new path).
- [x] Document one convention for game vs reference vs development images across `assets/textures/`, `assets/images/`, `assets/models/generated/`, `docs/screenshots/`, and `docs/screens/designs/` (in `docs/architecture.md`, ADR-0014).
- [x] Re-run `godot --headless --path . --import` to regenerate `.import` sidecars after any move.
- [x] Confirm `.gitignore` still excludes development-only outputs (concepts, Blender sources, raw AI fabrics) under the new layout; Blender `.blend` sources are now untracked and ignored.
### Documentation path updates
- [x] Update `README.md` paths (screen-designs section, confirmed-assets links, the `assets/images/scenes` resize example).
- [x] Update `docs/architecture.md`, `PLAN.md`, and ADR references: docs/screens/designs/ is now explicitly the reference-only snapshot; runtime art lives in assets/images/ (ADR-0014).
- [x] Update `tools/screen_designs.py` docstrings and mark the old paths in `DONE.md` as historical.
### Code path updates
- [x] Point `tools/screen_designs.py generate` at the new design output path and keep `compare`/`judge` reading the right locations.
- [x] Update GDScript `preload()` paths if game textures move (no runtime material/model textures moved; UI art added under assets/images/).
- [x] Update capture output directories if captures relocate (none did; QA captures stay in docs/screenshots/).
### Load proposed interfaces + scene art from disk
- [x] Add a central UI/scene art loader (e.g. `src/game/ui/art.gd`) that loads the mockup-derived images from the new runtime root, with a procedural fallback when a file is missing.
- [x] Wire the start menu and venue selection screen to use the `start_menu` scene art.
- [x] Wire the pause overlay to `pause_overlay` scene art (backdrop + opaque panel cover).
- [x] Wire the settings panel to `settings_panel` art.
- [x] Wire the shift-review screen to `shift_review` art (backdrop + opaque panel cover).
- [x] Wire the wand popup to `wand_screening` art.
- [x] Style the radial menu and finish the remaining HUD chrome against their reference images.
### Verify against the mockups
- [x] Regenerate the staged captures and run `screen_designs.py compare` (11 good, 0 bad; verified in a clean HEAD worktree).
- [x] Run `screen_designs.py judge` and clear every `diff` verdict (7 match, 4 review, 0 diff).
- [x] Add the capture + compare step as a repeatable smoke so drift is caught early (`tools/check_screenshots.sh`).
## Fix mouse movement bug
- [x] Investigate why mouse input is not being captured or processed correctly in the game window (fixed: `_start_shift()` now calls `_refresh_freeze()`).
## Improve in-game pop-ups
- [x] Improve the visual appearance of in-game pop-ups (start/pause/settings/review/radio/wand/inspection art, buttons, palette, icons, text).
## Visual Alignment With Rendered Mockups
Completed 2026-11-01. Every screen with an AI reference now loads or matches its mockup: the
runtime image root (ADR-0014), the UI/scene art loader, the menus/panels/HUD, and — last — the
scene lighting, fog and post-processing, which had been held until the realism pass landed.
`screen_designs.py compare` **11 good / 0 bad**; judge on the two scene shots: `01-venue-overview`
**match** (0.90), `05-late-night-avenue` **review** (0.10), 0 diff. Lighting: morning sun 34°
elevation with a phase `sun_yaw`, warm day haze (`fog_color` 0.87/0.83/0.76, density 0.0034,
energy 0.5), stronger night glow, street albedo 0.62. Remaining differences (camera height,
marquee text, hand-authored tree shapes) are content, not grading.
### Visual alignment with rendered mockups
#### Align in-game visuals
- [x] Make the venue selection screen match the `start_menu` mockup (layout, palette, background).
- [x] Make the HUD match the `targeting_npc` / `escalation_hud` mockups (focus ring, escalation meter, log, hints).
- [x] Align the pause, settings, and shift-review panels to their mockups.
- [x] Align the radio and wand panels to their mockups.
- [x] Align the body-camera timeline to its mockup.
- [x] Align scene lighting, fog, and post-processing for `venue_overview` and `late_night_avenue`: lowered the morning sun to 34° (`sun_yaw` is now a phase value), warmed and thickened the day haze, raised the night glow, and brightened the street albedo. `judge` moved `01-venue-overview` to **match** (0.90) and `05-late-night-avenue` from diff to **review** (0.10); `compare` stays 11 good / 0 bad.
- [x] Fix the body-camera REC/clock overlay overlapping the shift/rep/crowd status in the top-right HUD.
+80 -7
View File
@@ -4,6 +4,13 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
## Decisions
- The visual-alignment path/loader/verification work is complete and recorded in `DONE.md`;
the only open visual task is scene lighting/fog, held until the current realism pass lands.
- Editable Blender sources under `assets/models/blender_src/` are gitignored and untracked
(46 `.blend` files removed from the index on 2026-11-01); the `.gdignore` marker stays tracked.
- `screen_designs.py judge` default `google/gemini-2.5-flash-lite` worked on 2026-11-01
(7 match / 4 review / 0 diff); `--model` remains available.
- Graphics v0.6.0 plan (2026-11-01): NPCs move to Blender-authored skinned meshes
(`docs/plan-graphics-v0.6.md`, ADR-0013). `tools/npc_pipeline.py` builds
`assets/models/generated/npc_base.glb`; `NpcAvatar` keeps the procedural `_build_body()`
@@ -38,15 +45,37 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
avatars. Head-turn offsets use `+basis.z` for the same reason. Covered by
`test_yaw_faces_the_movement_direction`.
- **Image root (ADR-0014, 2026-11-01):** `assets/images/` is the runtime root for composed
interface/scene art (`ui/`, `scenes/`); tiled PBR textures stay under `assets/textures/`;
`docs/screens/designs/` and `docs/screens/concepts/` stay reference-only; QA captures stay in
`docs/screenshots/`. When a mockup becomes runtime art, copy/process it into `assets/images/`
with a new name (no overwrites) and keep a fallback in code.
**Mockup copies:** 21 design references live in `assets/images/ui/`; `docs/screens/designs/`
remains the reference-only copy. `tools/screen_designs.py` reads/writes the runtime root.
## Handoff (read this first)
- **Visual-alignment pass planned (2026-11-01):** `TODO.md` gains a "Visual
alignment with rendered mockups" section. Goal: restructure generated-image
storage, move the reference mockups out of `docs/screens/designs/` into a
runtime-accessible root (e.g. `assets/images/`), load the proposed UI/scene
art at runtime (start menu, selection screens, HUD, panels), and make the game
match the mockups — verified by `screen_designs.py compare` + `judge`. Not
started; no code moved yet.
- **Status + active passes (2026-11-01, moved from `TODO.md`):** v0.6.0 shipped — the Blender
character/gear/prop library is integrated, AO maps are baked and wired, and the environment
textures are lifted to 1024² with normal maps (see `DONE.md`). Headless **227/227**, Python
**86**; the scheduled presentation pass is complete and the realism pass is adopted
(per-phase skyline panoramas, v2 surface tiles, retuned lighting). Active in `TODO.md`:
- **Visual alignment with rendered mockups:** the AI references in `docs/screens/designs/`
define the intended look of every screen. The pass reorganizes generated-image storage,
loads the proposed UI/scene art at runtime (`src/game/ui/art.gd`, `assets/images/`), and
aligns the selection screens, HUD, and scene visuals with those mockups — verified with
`screen_designs.py compare` + `judge`.
- **Realism pass** (plan: `PLAN.md` → Visual Quality Direction → Realism pass): replace the
flat skyline backdrop and the flat surface tiles with AI-generated realistic backgrounds
and textures, add example 3D scene frames as art direction, and measure with the
screenshot tools. Rule: never overwrite an existing image — new outputs get new paths
(`next_free_path` -vN suffix). Raw → `assets/textures/ai/v2/raw/` (gitignored); candidates
→ `assets/textures/ai/game_ready/v2/` (gitignored pool, only validated picks tracked,
ADR-0014); scene refs → `docs/screens/concepts/`; trials → `docs/screens/realism/<run>/`.
Realism report 2026-11-01-v2 mean judge scores: backdrop 2.6 → 3.6 (v2 day + night
skyline) → **4.0** (v2 day + dusk skyline for the Evening phase); texture detail 1.2 →
1.6–1.75; overall 1.6 → 2.0. Scores swing ±1–2 between runs (trend only); the
`characters` score moved 3.0 → 1.0 with identical NPCs, showing the noise.
- **Phase 2 — AI texturing (this agent, DONE 2026-11-01).** Phase 1 assets were
already in the tree (untracked) and are preserved. Generated six seamless AI
@@ -129,6 +158,36 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
flat plane, so avoidance (not a baked mesh) routes guests around props. Placements live in
`PROP_PLACEMENTS` with `placement_problems()` validation; variants come from `variant_for(index)`.
`smoke_props.gd` must be windowed because the guard reads captured-mouse input.
- **Realism pass (2026-11-01, completed + adopted):** `tools/scene_concepts.py`
(`list`/`generate`/`process`/`promote`/`audit`/`realism`) never overwrites an image
(`next_free_path` adds `-vN`; `process` skips existing unless `--new`; `promote` keeps an
existing pick). Raw AI output: `assets/textures/ai/v2/raw/` (gitignored). Candidate pool:
`assets/textures/ai/game_ready/v2/` (gitignored) and the **tracked runtime assets** are
`.../v2/picks/{skyline_day,skyline_dusk,skyline_night,brick_facade,sidewalk_pavers,asphalt,
asphalt_wet,awning_canvas}.png` (+ `_normal`). Concepts `docs/screens/concepts/` (committed,
`.gdignore`, targets in its README). Runs `docs/screens/realism/<run>/` (PNG captures
gitignored; `report.{json,md}` + `sheet.jpg` committed).
`src/test/capture_realism.gd` runs five stages (day 0 s / evening 620 s / late 760 s /
joke_shop 700 s / studio 700 s; `--stages=` subset) and swaps textures by the material's
*original* albedo path. **Gotcha:** in the SceneTree script, do not overwrite `_phase` after
`_begin_stage()` sets `done`, or the run loops forever between stage/shoot (`_queue` empty).
**Gotcha:** `elapsed_s = 620` is *Evening* (dusk), 700+ is Late. Godot 4.3
`StandardMaterial3D` has no `fog_enabled`; the skyline is a `ShaderMaterial` with
`render_mode unshaded, cull_disabled, fog_disabled` + `ALPHA_SCISSOR_THRESHOLD` so the
panorama's own haze shows. Backdrop plane is 90 m tall with 2 UV repeats (~200×90 m per
repeat, near the 2:1 panorama aspect). Legacy `assets/textures/skyline.png` is no longer
loaded but stays on disk for the `test_world` asset check.
**Phase look:** day sun 0.62 / exposure 0.6 / ambient 0.22; evening sun 0.12 / exposure
0.72 / dark violet sky; late exposure 0.95 / ambient 0.10 with the wet asphalt street and a
bright skyline tint (1.5) so the night windows read. Stripes: `stripe_period()` (detrended
autocorrelation) + `align_stripes()` (resample to whole periods) replace the edge crossfade
for `awning_canvas`; `audit_striped_tile()` compares the wrap step with the harshest interior
step.
**Judge:** `google/gemini-2.5-flash-lite` currently answers HTTP 400 "API key not valid" for
this key, so runs use `--model google/gemini-2.5-flash`; scores are noisy (±1–2), so
`realism --repeats 3` averages and `summarize()` reports a like-for-like mean over the shots
every set captured. Final: overall 0.8→1.0, backdrop 0.6→1.6, metric gap 0.444→0.348.
`screen_designs.py judge` gained one retry for the same transient 400.
- **Blender asset library (v0.6.0, integrated 2026-11-01):**
`tools/blender_assets.py` (`build`/`render` in Blender; `audit`/`sheet`/`list` stdlib) writes
`assets/models/blender/{npc,npc_gear,props}/*.glb`, `manifest.json` (integration source of
@@ -180,6 +239,16 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
adopted (custom runner covers it without an editor plugin).
- Do not re-open `override_access`. Do not bind wand to `W`. Do not remove the radio easter egg.
## Decisions
- Runtime UI/scene art is loaded through `src/game/ui/art.gd` (`assets/images/ui/` then `scenes/`); missing files return null so panels keep procedural fallbacks.
- Screen mockups with baked-in controls are used as backdrops plus an opaque `ArtPanel` cover, not as clickable full-screen skins.
- Radio, wand, inspection, and body-camera timeline panels use their reference art as backdrops; forms sit on transparent, padded panels over the covered mockup controls.
- HUD target names use a boxed chip under the crosshair; escalation has a five-segment bar below the text label. The body-camera REC/clock block starts at y=92 on the right so it clears the shift/rep/crowd text.
- Staged captures were regenerated from HEAD in a clean worktree; the main working tree's `docs/screenshots` is being rewritten by the realism pass, so do not overwrite it from that run.
- `tools/check_screenshots.sh` is the repeatable capture+compare smoke; both capture scripts accept `--out=res://docs/screenshots`. Judge verdicts on the staged set were 7 match / 4 review / 0 diff.
- Venue selection used to leave `guard.input_locked` true and the mouse visible; `_start_shift()` must call `_refresh_freeze()` after starting the clock.
## Current State
- **v0.3.0 pass (2026-10-31, uncommitted):** rendering, generated prop models, screen
@@ -544,3 +613,7 @@ session shell; leftover PLAN verbs; crowd rubberneck; emergency aftermath; body-
sit/photo/film poses; real crowd audio; Joke Shop/Studio extra events; PLAN.md says GUT/GitHub.
<!-- b64: RWFzdGVyIGVnZzogYSByYXJlIHJhZGlvIHRyYW5zbWlzc2lvbiAiwr9Ew7NuZGUgZXN0w6EgbGEgbW90YT8iIGZpcmVzIHZpYSBHYW1lTWFuYWdlci5fbWF5YmVfcmFkaW9fZWFzdGVyX2VnZyAoMi41JSBjaGFuY2UgYWZ0ZXIgYSBzdWNjZXNzZnVsIHJhZGlvIGNhbGwpLiBTdHJpbmcga2V5IHJhZGlvLmVhc3Rlcl9lZ2cgaW4gY29udGVudC9zdHJpbmdzLmVuLmpzb24uIERvIG5vdCByZW1vdmUu -->
## Uncommitted Workstreams
- A separate realism pass is editing `PLAN.md`, `README.md`, `.gitignore`, `src/game/materials.gd`, `tools/scene_concepts.py`, `tools/test_scene_concepts.py`, `src/test/capture_realism.gd`, `assets/textures/ai/game_ready/v2/`, `docs/screens/concepts|realism/`, and regenerated Blender planter/tree assets. Do not commit or revert these while that pass is active; verify game changes in a clean `git worktree` at HEAD when its unimported textures break parsing.
+36
View File
@@ -420,6 +420,42 @@ frame budget measured by `smoke_npc_budget.gd`. See
[`docs/plan-graphics-v0.6.md`](docs/plan-graphics-v0.6.md) and
[ADR-0013](docs/adr/0013-blender-authored-npc-characters.md).
### Realism pass
The target look is the realistic 3D-game frames in `docs/screens/designs/` and the larger
example set in `docs/screens/concepts/`: hazy, layered city depth, believable materials, and
warm practical lights at night. Readability still comes first. Silhouettes, the focus ring and
the HUD must stay clear, and the GL Compatibility renderer stays.
- **Backgrounds.** The flat, black-box skyline strip is retired in favour of AI-generated
panoramic skylines (Hollywood mid-rises, palms, hills) for day, dusk and night. Each one is a
transparent-sky 4:1 strip that tiles horizontally on the existing backdrop ring, and the night
variant has lit windows.
- **Textures.** Higher-detail seamless 1024² tiles with derived normal maps for brick, granite,
sidewalk pavers, asphalt (dry and wet), steps, awning canvas and stucco, at the current UV
scales.
- **Process.** `tools/scene_concepts.py` generates (never overwriting existing images),
processes, audits and judges. `src/test/capture_realism.gd` captures the same camera shots
with the current and the candidate assets, swapped at runtime, so a candidate is compared
before any game code changes. Candidates are adopted only when the realism report (Pillow
detail metrics + vision-model realism scores) improves and `screen_designs.py judge` shows no
new `diff`.
- **Storage.** Raw AI output: `assets/textures/ai/v2/raw/` (local, gitignored). Game-ready
candidates: `assets/textures/ai/game_ready/v2/`. Scene references: `docs/screens/concepts/`.
Trial captures and reports: `docs/screens/realism/<run>/`.
- **First result (2026-11-01).** Swapping in the v2 skyline and tiles raised the judge's mean
backdrop score from 2.6 to 4.0 and texture detail from 1.2 to ~1.7. Overall realism stayed at
~2/10; the limits were lighting and exposure, flat facades and blob foliage, not textures.
- **Adopted (2026-11-01).** The v2 picks are now the runtime skyline and surface set, the phase
lighting was retuned (exposure, darker dusk/late, practical lights, wet asphalt at night), the
backdrop is a fog-free 90 m panorama ring per phase, the facade gained plinth/string
courses/cornice/pilasters/glazing bars, and the library trees and shrubs are denser. Judge
scores on the like-for-like shots rose on every sub-score (overall 0.8 → 1.0, backdrop
0.6 → 1.6) with the metric gap down 0.444 → 0.348
(`docs/screens/realism/2026-11-01-adopt/`). The remaining gap to the concepts is the low-poly
character and prop fidelity, a visible flashlight beam at night, and the entrance still being
~20 % brighter than the concept (`TODO.md` → Realism follow-ups).
The scenario should test the player’s ability to distinguish ordinary Hollywood Boulevard activity from a genuine security problem. The best solution is usually a simple gesture, clear direction, or credential check rather than immediate escalation.
Future venues can reuse this structure while changing the rules, equipment, staff roles, and types of access decisions.
+54 -12
View File
@@ -172,7 +172,7 @@ Crop uses the image center by default; `--x` and `--y` select an explicit crop o
```bash
python3 tools/asset_pipeline.py crop assets/textures/source.png assets/textures/crop.png --width 256 --height 128
python3 tools/asset_pipeline.py resize assets/textures/source.png assets/images/backgrounds/scene.png --width 1920 --height 1080 --fit cover
python3 tools/asset_pipeline.py resize assets/textures/source.png assets/images/scenes/scene.png --width 1920 --height 1080 --fit cover
python3 tools/asset_pipeline.py seamless assets/textures/rough_tile.png assets/textures/tile.png
```
@@ -180,7 +180,7 @@ Audit accepts image files or directories. `--role auto` infers common roles from
```bash
python3 tools/asset_pipeline.py audit assets/textures
python3 tools/asset_pipeline.py audit assets/images/backgrounds --role background --json
python3 tools/asset_pipeline.py audit assets/images/scenes --role background --json
python3 -m unittest discover -s tools -p 'test_*.py'
```
@@ -287,11 +287,11 @@ Everything below is audited and shipped in the game; development-only outputs
| | |
| --- | --- |
| ![brick facade](assets/textures/ai/game_ready/brick_facade.png) | ![concrete](assets/textures/ai/game_ready/concrete.png) |
| ![asphalt](assets/textures/ai/game_ready/asphalt.png) | ![awning](assets/textures/ai/game_ready/awning.png) |
| ![skyline](assets/textures/ai/game_ready/skyline.png) | ![vignette](assets/textures/ai/game_ready/vignette.png) |
| ![brick facade](assets/textures/ai/game_ready/v2/picks/brick_facade.png) | ![sidewalk pavers](assets/textures/ai/game_ready/v2/picks/sidewalk_pavers.png) |
| ![asphalt](assets/textures/ai/game_ready/v2/picks/asphalt.png) | ![awning](assets/textures/ai/game_ready/v2/picks/awning_canvas.png) |
| ![skyline day](assets/textures/ai/game_ready/v2/picks/skyline_day.png) | ![skyline night](assets/textures/ai/game_ready/v2/picks/skyline_night.png) |
- **Surface textures:** the six tiles above, loaded by `src/game/materials.gd` and `WorldBuilder`.
- **Surface textures:** the v2 picks above (brick, sidewalk pavers, dry and wet asphalt, awning canvas) plus their normal maps, loaded by `src/game/materials.gd` and `WorldBuilder`; the three skyline panoramas are switched per shift phase. The first-generation tiles and `assets/textures/skyline.png` are no longer loaded.
- **UI art:** `assets/textures/ai/game_ready/icons/` (flashlight, door lock, wand, REC) and `assets/textures/ai/game_ready/cards/` (ID, wristband, ticket, credential, guest list), loaded by `src/game/ui/hud.gd`.
- **Generated models:** `assets/models/generated/*.glb` — bollard, bench, trash bin, planter, newspaper box, hydrant, and the NPC base — plus their colour atlases.
- **NPC material maps:** `assets/textures/npc/*_{albedo,normal,orm}.png` and `palette.json`.
@@ -304,12 +304,50 @@ python3 tools/model_pipeline.py audit
python3 tools/npc_pipeline.py audit
```
### Realism pass: scene concepts, skylines and textures
The skyline, surface tiles and phase lighting were rebuilt against AI concept frames. The
concepts live in [`docs/screens/concepts/`](docs/screens/concepts/README.md); the validated
picks the game loads are in `assets/textures/ai/game_ready/v2/picks/` (day/dusk/night
panoramas, brick, sidewalk pavers, dry and wet asphalt, awning canvas, each with a normal
map).
`tools/scene_concepts.py` generates and processes new candidates, promotes the validated ones
to stable names, audits them, and scores captures against the concepts. It never overwrites an
image: new files get a `-vN` suffix, and `process` skips existing candidates unless `--new`.
```bash
python3 tools/scene_concepts.py generate --group scene # or backdrop / texture, --variants N
python3 tools/scene_concepts.py process && python3 tools/scene_concepts.py audit
python3 tools/scene_concepts.py promote # validated picks -> v2/picks/
godot --headless --path . --import
# A/B a candidate without touching game code: the same cameras, textures swapped at runtime.
godot --path . --script res://src/test/capture_realism.gd -- --out=res://docs/screens/realism/<run>/after
godot --path . --script res://src/test/capture_realism.gd -- --out=res://docs/screens/realism/<run>/try \
--swap=res://assets/textures/ai/game_ready/v2/picks/brick_facade.png=res://assets/textures/ai/game_ready/v2/brick_facade-v2.png
# Score before/after against the concepts (Pillow detail metrics + vision judge).
python3 tools/scene_concepts.py realism --run docs/screens/realism/<run> --judge \
--model google/gemini-2.5-flash --repeats 3
```
`capture_realism.gd` runs five stages (day, evening, late, Joke Shop, studio); `--stages=day,late`
limits it while tuning. The latest run,
[`docs/screens/realism/2026-11-01-adopt/`](docs/screens/realism/2026-11-01-adopt/report.md),
compares the pre-adoption captures with the current build: on the shots both sets captured the
judge's like-for-like means rose on every sub-score (overall 0.8 → 1.0, backdrop 0.6 → 1.6) and
the Pillow metric gap fell 0.444 → 0.348.
![realism run](docs/screens/realism/2026-11-01-adopt/sheet.jpg)
### Screen design references
Screen mockups and screenshot comparison use `tools/screen_designs.py`. It turns the
screen descriptions in `docs/` (README captions, ADRs 0007–0010, `PLAN.md`, and
`architecture.md`) into reference images under `docs/screens/designs/`, then uses
those images as the expected state to check the game's captures.
`architecture.md`) into runtime reference art under `assets/images/ui/` (with a
reference-only snapshot in `docs/screens/designs/`), then uses those images as
the expected state to check the game's captures.
Each screen is tagged `scene` (a realistic first-person 3D world view) or `ui`
(an in-game panel overlaid on a softened scene), and the generator appends a
@@ -350,9 +388,9 @@ python3 tools/screen_designs.py judge --dry-run # build requests, no network
The catalogue covers 21 screens: the eleven README captures plus the start menu,
pause overlay, settings, shift review, radial menu, emergency radio, wand
screening, Joke Shop queue, TV Studio arrival, crowd panic, responder arrival,
body-camera timeline, and the escalation HUD. All 19 have an AI reference in
`docs/screens/designs/`; the eleven screens with staged captures are also compared
or judged against the running game.
body-camera timeline, and the escalation HUD. All 21 have an AI reference in
`assets/images/ui/` (snapshot in `docs/screens/designs/`); the eleven screens with
staged captures are also compared or judged against the running game.
`compare` flags gross drift: wrong aspect ratio, undersized frame, near-blank
frame, and large layout/palette/detail distance. It is a regression gate, not a
@@ -368,6 +406,10 @@ default 0.6 — expected when a concept mockup is more stylised than the game),
`compare` after regenerating the game screenshots with
`src/test/capture_screenshots.gd`.
For a one-command drift check, run `tools/check_screenshots.sh`. It renders the
staged captures and prop gallery into `docs/screenshots/` (using `xvfb-run` when
no display is available) and then runs `screen_designs.py compare`.
### Setup
1. Download the Godot 4.3 editor (official build) and put `godot` on your PATH.
@@ -484,7 +526,7 @@ Implemented so far:
- Godot 4.3 project scaffold, input map, procedural world builder
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
- Three authored scenarios (Hollywood Boulevard building, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
- Headless unit tests: **208 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, input remaps, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip, surface materials and environment, focus ranking and outfit/gait helpers, generated prop models, web export preset, prop atlas material, player movement, NPC character layers/LOD/queue, input bindings)
- Headless unit tests: **227 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, input remaps, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip, surface materials and environment, focus ranking and outfit/gait helpers, generated prop models, web export preset, prop atlas material, player movement, NPC character layers/LOD/queue, input bindings)
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
+16 -222
View File
@@ -1,228 +1,22 @@
# TODO
Godot 4.3 prototype. Latest release **v0.5.0** (NPC characters and player movement). Headless
suite **222/222** plus **68** Python tooling tests; the session, PLAN-verb, crowd, emergency,
timeline, audio, scenario-content, props, NPC-budget, movement, and windowed focus/radial smokes
pass. Anonymous downloads are enabled on the host.
## Realism follow-ups (v0.6.1 candidates)
The scheduled presentation pass is complete (see `DONE.md`). The next scheduled pass is the
graphics-quality work in [`docs/plan-graphics-v0.6.md`](docs/plan-graphics-v0.6.md).
> **Blender asset library integrated (2026-11-01):** 8 NPC character variants, 18 gear pieces,
> and 20 street/venue props are now loaded by `NpcAvatar` and `WorldBuilder` from
> `assets/models/blender/` (manifest as source of truth, see
> [`docs/blender-assets.md`](docs/blender-assets.md)). The procedural rig and primitive props
> remain offline fallbacks; AO/ORM baking is the remaining Phase 4 follow-up.
## Graphics quality — Blender-authored NPC characters (v0.6.0)
Detailed plan: [`docs/plan-graphics-v0.6.md`](docs/plan-graphics-v0.6.md) · [ADR-0013](docs/adr/0013-blender-authored-npc-characters.md)
### Phase 0 — Toolchain spike and contract freeze
- [x] Verify `flatpak run org.blender.Blender --version` reports Blender 5.2.x and runs headless in this shell.
- [x] Run a minimal headless Blender export using an absolute `--python` path (sandbox cwd is `/app/blender`); confirm the output file lands where expected.
- [x] Record the exact working invocation (flatpak + absolute paths) in `MEMORY.md`.
- [x] Freeze the NPC asset contract in `docs/plan-graphics-v0.6.md` and ADR-0013: file paths, bone names, material slots, triangle budget.
- [x] Fix the material-slot set (`skin` / `cloth` / `pants` / `accessory` / `hair`) and the ORM packing (R=occlusion, G=roughness, B=metallic).
- [x] Choose and record the initial tier-0 triangle/vertex budget (≤ ~6000 tris / ~4000 verts) in the plan.
- [x] Spike: Godot 4.3 imports the `.glb` as a `PackedScene` with a `Skeleton3D`; no clips are needed because `_animate()` drives the bones at runtime.
### Phase 1 — Blender authoring + export pipeline
- [x] Author the low-poly base humanoid in Blender: torso, articulated arms/legs, head with a face front, hands, shoes.
- [x] Rig with an armature whose bones mirror the node contract (`Head`, `HipL/R`, `KneeL/R`, `ShoulderL/R`, `ElbowL/R`, `HandL/R`); origin at the feet, +Y up.
- [x] UV-unwrap the mesh into a power-of-two layout, one region per material slot.
- [x] Save the editable source as `assets/models/npc/npc_base.blend` (committed).
- [x] Add `tools/npc_pipeline.py` with `list` / `build` / `audit` subcommands, run through the Blender flatpak.
- [x] `build` exports `assets/models/generated/npc_base.glb` plus the material textures.
- [x] Bake neutral albedo per slot, normal maps (fabric weave, denim, knit), and the packed ORM map.
- [x] Bake ambient occlusion into the albedo or the ORM R channel (the Compatibility renderer has no SSAO).
- [x] `audit` enforces UVs in [0,1], expected bone names, triangle/vertex budget, feet at y≈0, and material-slot presence.
- [x] Reuse the `model_pipeline.py` colour-space discipline (linear factors vs sRGB textures) in the exporter.
- [x] Run `godot --headless --path . --import` and confirm `npc_base.glb` imports without errors.
### Phase 2 — AI-assisted texturing
- [x] Generate seamless clothing albedos (shirt, trousers, jacket, hi-vis vest, coat, dress) with `tools/asset_pipeline.py generate` (OpenRouter, `openai/gpt-5-image-mini`) — written to `assets/textures/npc/ai/` (512×512).
- [x] Tile-edge blending on each fabric — applied automatically by `generate --role tile` (`make_seamless`); the `tile` audit's edge check confirms continuity.
- [x] Run `asset_pipeline.py audit` on the outputs — 6 good, 0 bad (role `tile`).
- [x] Commit the validated textures under `assets/textures/npc/ai/` (tracked in the v0.6.0 asset commit).
- [x] Derive per-role outfit palettes via `model_pipeline.dominant_colors` — written to `assets/textures/npc/palette.json` (`npc_pipeline.py palette`).
- [x] Bake normal/roughness procedurally and layer the AI albedos on top — `npc_pipeline.py prepare` + Blender `build` keeps the AI albedo for `cloth`/`pants` and regenerates normal/ORM; GLB audit ok (1204 tris / 2488 verts).
- [x] Regenerate the staged captures and run `screen_designs.py judge` after the Phase 3 integration (11 good / 0 bad in `compare`; judge: no diff).
### Phase 3 — Godot integration
- [x] Add `NpcAvatar._load_model()` that instantiates the imported `npc_base.glb` `PackedScene`.
- [x] Keep `_build_body()` as the offline fallback; choose per-avatar and record `loaded_model` / `fallback_avatar` for tests.
- [x] Map `outfit_for()` onto the model's material slots so seed variety, `layer_for`, `hand_item_for`, and archetype colors survive.
- [x] Keep accessory attachments (cap / hood / backpack / sunglasses / lanyard) under `Rig` / `Head`.
- [x] Add `SurfaceMaterials` baked-map recipes (`npc()` / `skin()` / `cloth()`); stop calling `Materials.flat()` for the body.
- [x] Add `_set_limb_rotation()` so `_animate()` / `_idle_layer()` write to bones or pivots; keep `Rig/Head` findable by the focus cone.
- [x] Update `test_npc_character`, `test_npc_theater`, and `test_target_cycle` for the skinned path (or document why they stay on the fallback).
- [x] Re-measure `smoke_npc_budget.gd`; tune `LOD_TIERS` if the skinned rig is heavier up close.
- [x] Re-verify the focus-cone smoke (`smoke_focus_radial.gd`) and the movement smoke.
### Phase 4 — Environment and lighting lift ✅
- [x] Bake AO for the prop set through the Blender library (replacements above) or into the ORM/albedo maps behind the existing `WorldBuilder` contract; keep the procedural fallback.
- [x] Regenerate higher-resolution brick / concrete / asphalt textures via `asset_pipeline.py` with normal maps, keeping the current UV scales; apply the same map discipline to the library's street materials.
- [x] Evaluate a desktop-only Forward+ export preset (SSAO/SSIL/volumetrics); keep the Web preset on GL Compatibility with `thread_support=false`.
- [x] Record the renderer decision in an ADR addendum or `MEMORY.md`.
- [x] Regenerate `docs/screenshots/` with `capture_screenshots.gd` and run `screen_designs.py compare`.
### Phase 5 — Verification and release
- [x] Headless suite green: fallback build, outfit determinism, `Rig/Head` presence, material recipe assertions, `npc_pipeline.py audit`, and `blender_assets.py audit` + manifest tests (skip Blender builds with a reason when Blender is absent).
- [x] Windowed smokes pass: `smoke_npc_budget.gd`, `smoke_movement.gd`, `smoke_focus_radial.gd`, `smoke_props.gd`.
- [x] `screen_designs.py compare` and `judge` show no regressions; `blender_assets.py sheet` contact sheets reviewed for the character/gear/prop library.
- [ ] Bump `config/version` to `0.6.0`.
- [ ] Update `README.md`, `docs/architecture.md` (Level 2 gains `tools/npc_pipeline.py`), and mark ADR-0013 Accepted.
- [ ] Publish the release per `docs/RELEASING.md`.
## Visual alignment with rendered mockups
The AI-rendered references in `docs/screens/designs/` (start menu, pause, settings,
shift review, radial menu, HUD, scene views) describe the intended look of every
screen. This pass reorganizes how generated images are stored, makes the game load
the proposed interface/scene art from disk, and aligns the selection screens, HUD,
and scene visuals with those mockups — verified with
`tools/screen_designs.py compare` + `judge`.
### Storage restructure
- [ ] Decide the canonical runtime image root (e.g. `assets/images/` for UI/scene art) and record it in `MEMORY.md` and an ADR.
- [ ] Move the reference mockups from `docs/screens/designs/` into the runtime root (keep `docs/screens/designs/` as a docs-only copy or replace it with the new path).
- [ ] Document one convention for game vs reference vs development images across `assets/textures/`, `assets/images/`, `assets/models/generated/`, `docs/screenshots/`, and `docs/screens/designs/`.
- [ ] Re-run `godot --headless --path . --import` to regenerate `.import` sidecars after any move.
- [ ] Confirm `.gitignore` still excludes development-only outputs (concepts, Blender sources, raw AI fabrics) under the new layout.
### Documentation path updates
- [ ] Update `README.md` paths (screen-designs section, confirmed-assets links, the `assets/images/backgrounds` resize example).
- [ ] Update `docs/architecture.md`, `PLAN.md`, and ADR-0010/0011/0012/0013 references to `docs/screens/designs/`.
- [ ] Update `tools/screen_designs.py` docstrings and mark the old paths in `DONE.md` as historical.
### Code path updates
- [ ] Point `tools/screen_designs.py generate` at the new design output path and keep `compare`/`judge` reading the right locations.
- [ ] Update GDScript `preload()` paths (`materials.gd`, `hud.gd`, `world_builder.gd`) if the game textures move.
- [ ] Update `capture_screenshots.gd` / `capture_prop_gallery.gd` output directories if captures relocate.
### Load proposed interfaces + scene art from disk
- [ ] Add a central UI/scene art loader (e.g. `src/game/ui/art.gd`) that loads the mockup-derived images from the new runtime root, with a procedural fallback when a file is missing.
- [ ] Wire the start menu and venue selection screen to use the `start_menu` scene art.
- [ ] Wire the pause overlay, settings, shift review, radial menu, and HUD chrome to their reference images.
### Align in-game visuals
- [ ] Make the venue selection screen match the `start_menu` mockup (layout, palette, background).
- [ ] Make the HUD match the `targeting_npc` / `escalation_hud` mockups (focus ring, escalation meter, log, hints).
- [ ] Align the remaining panels (pause, settings, shift review, radio, wand, timeline) to their mockups.
- [ ] Align scene lighting, fog, and post-processing for `venue_overview` and `late_night_avenue`.
### Verify against the mockups
- [ ] Regenerate the staged captures and run `screen_designs.py compare` (no gross drift).
- [ ] Run `screen_designs.py judge` and clear every `diff` verdict (target `match`/`review`).
- [ ] Add the capture + compare step to CI or a repeatable smoke so drift is caught early.
## Improve in-game pop-ups
- [ ] Improve the visual appearance of in-game pop-ups (layout, button style, palette, icons, and text styling).
## Realistic backgrounds, textures and scene references
Goal: replace the outdated flat skyline backdrop (`assets/textures/skyline.png`, black boxes
with dot windows) and the flat-looking surface tiles with AI-generated, more realistic
backgrounds and textures. Add a larger set of example 3D scene images as art direction, and
measure the in-game result against that target with the screenshot tools. Plan:
[`PLAN.md` → Visual Quality Direction → Realism pass](PLAN.md#realism-pass).
**Rule for every step: never overwrite an existing image.** New outputs get new paths. When a
path is taken, the tools pick the next free `-vN` suffix. Raw AI output stays on disk under
`assets/textures/ai/v2/raw/` (gitignored like the other raw AI folders). Processed game-ready
candidates go to `assets/textures/ai/game_ready/v2/`, scene references to `docs/screens/concepts/`,
and trial captures and reports to `docs/screens/realism/<run>/`.
### Step 1 — Tooling (`tools/scene_concepts.py`)
- [ ] Add a catalogue of prompts in three groups: `scene` (16:9 example 3D game frames),
`backdrop` (4:1 skyline panoramas with transparent sky), and `texture` (seamless PBR
surface tiles). Each entry has a name, prompt, role and target path.
- [ ] `generate [--group] [--only] [--variants N]`: call the OpenRouter image API through
`generate_textures.generate_image` (key from the repo `.env`), save raw output, and never
overwrite (`next_free_path`). Print every file written.
- [ ] `process`: turn raw backdrops into 2048×512 RGBA strips that tile horizontally (edge
crossfade, sky made fully transparent, hard alpha for the alpha-scissor material). Turn raw
textures into seamless 1024² tiles (`asset_pipeline.make_seamless`) plus a normal map
(`make_normal_maps.normal_map`). Skip outputs that already exist.
- [ ] `audit`: run `asset_pipeline.audit_image` on the processed candidates (`backdrop` /
`tile` roles) and fail on any bad result.
- [ ] `realism`: for each trial capture, report the Pillow detail metrics (edge density,
contrast, colour spread, saturation) next to the matching concept image. Optionally ask a
vision model (`google/gemini-2.5-flash-lite`) for 0–10 realism scores (lighting,
materials, texture detail, backdrop, overall) with concrete fixes. Write a new JSON +
Markdown report per run.
- [ ] Unit tests (`tools/test_scene_concepts.py`): catalogue shape, the no-overwrite path
picker, backdrop/tile processing on synthetic images, metrics, and the judge payload/parse
(offline, no network).
### Step 2 — Example 3D scene images
- [ ] Generate 3D-game-style example frames for every venue and time of day: Hollywood entrance
(morning, golden hour, rainy night), boulevard skyline view (day, dusk), Joke Shop alley
(evening, close-up of the door), studio stage door (night arrival), sidewalk material
close-up, entrance steps and podium, crowd-control barrier line, and a responder arrival
wide shot.
- [ ] Review the set and record the visual targets each frame sets (sky, haze, material detail,
backdrop depth) in the report.
### Step 3 — Backgrounds (replace the outdated skyline)
- [ ] Generate skyline panoramas for day, dusk and night (lit windows): Hollywood/Los Angeles
mid-rise blocks, palm silhouettes, the hills on the horizon, a transparent sky, and a
straight-on view, in several variants each.
- [ ] Process them into `assets/textures/ai/game_ready/v2/skyline_{day,dusk,night}.png`,
seamless horizontally for the 3× UV repeat around the backdrop ring.
### Step 4 — Textures
- [ ] Generate higher-detail seamless tiles: red brick facade, granite/limestone facade
(studio), sidewalk concrete pavers with joints, asphalt with aggregate, wet night asphalt,
stone steps, striped awning canvas, and stucco wall.
- [ ] Process them into 1024² game-ready tiles plus normal maps in
`assets/textures/ai/game_ready/v2/` and audit them.
### Step 5 — In-game trial captures (no game-code change)
- [ ] Add `src/test/capture_realism.gd`. It boots the Hollywood venue, captures a fixed shot
list (entrance day, boulevard/skyline day, boulevard night), and can swap textures at
runtime from `--skyline=` / `--brick=` / `--concrete=` / `--asphalt=` user arguments. The
swap finds materials by their current texture, so baseline and candidate frames use the
same camera. Writes to `user://screenshots/realism/<tag>/`.
- [ ] Capture the baseline (current assets) and the candidate set (v2 skyline + textures), and
copy them into `docs/screens/realism/<run>/{baseline,candidate}/`.
### Step 6 — Compare against the realistic target
- [ ] Run `scene_concepts.py realism` on baseline and candidate. Record the metric deltas and the
vision scores, and note which candidate textures/backdrops win.
- [ ] Note the remaining realism gaps (sky/fog, lighting, building geometry, NPC fidelity) as
follow-up tasks below.
### Step 7 — Adopt the winners (after the in-flight normal-map/AO work is committed)
- [ ] Point `WorldBuilder.T_SKYLINE` at the chosen v2 skyline (separate day/night textures through
`set_night_mode`), and retune `albedo_color` for the realistic strip.
- [ ] Switch `materials.gd` / `world_builder.gd` surface preloads to the chosen v2 tiles + normal
maps, keeping the current UV scales. Update `test_materials.gd` / `test_world.gd`.
- [ ] Regenerate `docs/screenshots/` with `capture_screenshots.gd`, then run
`screen_designs.py compare` + `judge` and `scene_concepts.py realism`. There must be no
`diff` verdict regressions, and realism scores must not drop.
- [ ] Update `README.md` (tool usage + gallery), `MEMORY.md` and `docs/architecture.md`.
- [x] Fixed ground exposure for the entrance view: the morning sun now sits at 34° elevation
instead of 50°, so the facade shades the near sidewalk and props cast long shadows. Ground
luminance went 95 → **83** against the concept's 78 (it was 173 before the realism pass),
with the boulevard at 82 and the street at 81.
- [-] Make the flashlight read in the dark: the `06-night-flashlight` concept shows a visible
beam, but a Compatibility spot light only brightens the near wall. A cheap fake cone (an
additive mesh or a light-shaft quad) would sell it, and the night mockup expects it.
- [ ] NPC skin/clothing still reads as low-poly next to the concepts (judge `characters` ≈ 0–1).
The Blender library has the budgets for one more detail pass (fingers, sleeves, faces).
- [x] Re-check the `screen_designs.py judge` default model (`google/gemini-2.5-flash-lite`): on
2026-11-01 it returned 7 match / 4 review / 0 diff without a provider error; `--model`
override is documented in `README.md`.
- [ ] Add the two venue stages to the committed gallery (they are capture-only today) and give
the Joke Shop and studio their own geometry so their concept comparisons are geometric,
not just lighting.
## Future ideas (optional, not scheduled)
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 KiB

+15 -15
View File
@@ -836,8 +836,8 @@
],
"path": "assets/models/blender/props/planter_box.glb",
"replaces": "generated/planter_box.glb",
"triangles": 924,
"vertices": 874
"triangles": 1324,
"vertices": 2780
},
"protest_sign": {
"attach": "hand",
@@ -892,14 +892,14 @@
"round_planter": {
"bounds_godot": {
"max": [
0.394,
0.432,
1.12,
0.375
0.36
],
"min": [
-0.39,
-0.36,
0,
-0.38
-0.363
]
},
"category": "props",
@@ -908,8 +908,8 @@
"foliage"
],
"path": "assets/models/blender/props/round_planter.glb",
"triangles": 920,
"vertices": 896
"triangles": 1240,
"vertices": 2418
},
"scba_tank": {
"attach": "back",
@@ -1063,14 +1063,14 @@
"street_tree": {
"bounds_godot": {
"max": [
1.019,
3.65,
1.117
1.179,
3.62,
1.102
],
"min": [
-1.077,
-0.941,
0,
-1.016
-1.082
]
},
"category": "props",
@@ -1082,8 +1082,8 @@
],
"path": "assets/models/blender/props/street_tree.glb",
"replaces": "WorldBuilder cone trees",
"triangles": 1472,
"vertices": 1396
"triangles": 2592,
"vertices": 5620
},
"sunglasses": {
"attach": "head",
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

+81
View File
@@ -0,0 +1,81 @@
# ADR-0014: Canonical runtime image root and image conventions
- **Status:** Accepted (2026-11-01)
- **Date:** 2026-11-01
## Context
The project now contains several kinds of images, and their locations overlap:
- hand-authored and AI-generated **surface textures** under `assets/textures/`
(`ai/game_ready/`, `ai/game_ready/v2/`, `npc/`),
- **screen mockups** under `docs/screens/designs/`,
- **scene references** under `docs/screens/concepts/`,
- **QA captures** under `docs/screenshots/` and `docs/screens/realism/`,
- **raw AI output** under gitignored `assets/textures/ai/*/raw/`.
The visual-alignment pass needs the game to load proposed interface and scene
art at runtime. That art is currently only in the docs tree, which is not the
right place for resources the build must ship and which contributors expect to
be documentation-only. Without a convention, UI art, tiled materials, reference
mockups, and screenshots will keep being confused for one another.
## Decision
1. **`assets/images/` is the canonical runtime root for composed image art** —
interface art, backgrounds, and full-frame scene art that the game loads as
an image, not as a tiled material.
- `assets/images/ui/` — screen and panel art used by the UI layer (start
menu/venue selection background, pause/settings chrome, HUD plates).
- `assets/images/scenes/` — composed scene/background art loaded directly as
an image (not repeated as a surface texture).
2. **Tiled material textures stay under `assets/textures/`.** Brick, concrete,
asphalt, fabric, skyline strips used with UV repetition, and model maps are
materials, not composed screen art. Raw AI output stays in gitignored
`assets/textures/ai/*/raw/`; validated environment candidates stay under
`assets/textures/ai/game_ready/v2/`.
3. **`docs/screens/` remains reference-only.** `designs/` holds mockups and
`concepts/` holds larger art-direction sets; neither is shipped or loaded by
the game. When a reference becomes runtime art, it is copied/processed into
`assets/images/` with a new descriptive name, following the no-overwrite
rule (`-vN` suffixes from the asset tooling).
4. **QA output stays in `docs/`.** `docs/screenshots/` holds the README/catalogue
captures and `docs/screens/realism/<run>/` holds trial captures/reports.
These are not runtime resources and may be regenerated.
5. **Naming.** Files under `assets/images/` are lowercase `snake_case` PNGs,
`<screen>_<variant>.png`, at the native UI size (1280×720 for 16:9 screens)
or the size the scene actually uses. Every runtime image needs a procedural
or neutral fallback in code so a missing file never breaks a screen.
6. **Imports.** Runtime images are normal Godot textures: commit the PNG, let
`godot --headless --path . --import` regenerate `.import` sidecars, and keep
development-only images out of `assets/images/`.
## Consequences
### Positive
- One obvious place for art the game actually loads, separate from references
and QA captures.
- Builds/exports can include `assets/images/**` without also shipping mockups,
screenshots, or raw AI output.
- The no-overwrite rule and variant naming make visual experiments reversible
and keep the docs references stable.
### Negative
- A reference image that becomes runtime art exists in two places (docs copy +
processed runtime copy); contributors must keep the processed copy current.
- Moving existing mockups later is a path migration that touches docs, tools,
and `preload()` paths — tracked as the remaining visual-alignment storage
tasks.
## Alternatives considered
- **Use `docs/screens/designs/` as the runtime root.** Rejected: docs are not
ship-ready gameplay resources, and mixing references with runtime art makes
exports and cleanup brittle.
- **Put everything in `assets/textures/`.** Rejected: composed UI/scene art is
not a tiled material; mixing it with PBR tiles hides the distinction and
invites incorrect import settings.
- **Keep images only in code / procedural UI.** Rejected: the visual-alignment
pass is explicitly about using the rendered mockups as the target look.
+20
View File
@@ -81,10 +81,29 @@ The player interacts with NPCs, venue entrances, access artifacts, equipment, st
- `tools/model_pipeline.py` builds and audits the street-prop `.glb` models and packs their colour atlases.
- `tools/npc_pipeline.py` builds and audits the skinned NPC base model (`assets/models/generated/npc_base.glb`) and its per-slot material maps, with an optional AI fabric override (ADR-0013).
- `tools/blender_assets.py` builds, renders and audits the Blender asset library (`assets/models/blender/{npc,npc_gear,props}/` + `manifest.json`): NPC character variants that are drop-in for `npc_base`, attach-point gear, and street/venue props. `NpcAvatar` selects variants/gear by role/seed and `WorldBuilder` loads the prop replacements and new street dressing, with procedural fallbacks (see `docs/blender-assets.md`).
- `tools/scene_concepts.py` generates realism-pass references (`docs/screens/concepts/`), skyline backdrops and surface tiles (raw `assets/textures/ai/v2/raw/` → candidates `assets/textures/ai/game_ready/v2/`), promotes the validated ones to the tracked `v2/picks/` that `SurfaceMaterials`/`WorldBuilder` preload, audits them, and scores `src/test/capture_realism.gd` A/B captures (five stages, runtime texture swaps) against the concepts (`docs/screens/realism/<run>/`).
- `tools/asset_pipeline.py` generates and audits role-aware textures; `tools/screen_designs.py` generates screen references and judges captures.
Confirmed game assets live in `assets/textures/ai/game_ready/` (audited surface, UI, and card textures), `assets/models/generated/` (prop and NPC `.glb` models plus their texture atlases), and `assets/textures/npc/` (NPC material maps and `palette.json`). Development-only outputs — AI concept sheets, Blender sources, raw AI fabrics — are gitignored and regenerated by the tools.
### Asset storage conventions
The canonical rules live in [ADR-0014](adr/0014-runtime-image-root.md). In short:
| Location | Contents | Loaded at runtime? |
| --- | --- | --- |
| `assets/images/ui/` | Composed screen/panel art (start menu, HUD chrome, mockup-derived backgrounds) | Yes |
| `assets/images/scenes/` | Composed full-frame scene/background art | Yes |
| `assets/textures/ai/game_ready/` (+ `v2/`) | Tiled/PBR surface textures, model maps, skyline strips | Yes, as materials |
| `assets/models/generated/`, `assets/models/blender/` | 3D props/characters and manifests | Yes |
| `docs/screens/designs/`, `docs/screens/concepts/` | Reference mockups and art-direction sets | No (reference only) |
| `docs/screenshots/`, `docs/screens/realism/` | README/catalogue captures and QA reports | No (generated QA) |
| `assets/textures/ai/v2/raw/`, Blender `.blend` sources | Raw AI output / editable sources | No (gitignored or `.gdignore`d) |
When reference art becomes runtime art, copy or process it into `assets/images/` with a
new descriptive name (never overwrite an existing image) and keep a procedural fallback
in code.
## 6. Runtime View
### Routine interaction
@@ -167,6 +186,7 @@ Architecture decisions are recorded as ADRs:
- [ADR-0011: Aim-free selection and precision walk](adr/0011-aim-free-selection-and-precision-walk.md)
- [ADR-0012: Surface materials and generated props](adr/0012-surface-materials-and-generated-props.md)
- [ADR-0013: Blender-authored NPC characters and baked materials](adr/0013-blender-authored-npc-characters.md)
- [ADR-0014: Canonical runtime image root and image conventions](adr/0014-runtime-image-root.md)
## 10. Quality Requirements
+4
View File
@@ -64,6 +64,10 @@ render about 2.5 min.
- Blender is Z-up. The glTF exporter converts to Godot Y-up.
- **Front = Blender −Y = Godot +Z**, the face direction that
`NpcAvatar.yaw_towards()` assumes.
- **Foliage** (`street_tree`, `planter_box`, `round_planter`) is a cluster of many small
flat-shaded clumps plus outer clumps that break the silhouette, not one smooth blob. That is
how the realism pass made the street greenery read as leaves; the three assets carry fresh AO
maps (`street_tree` is 2,592 triangles).
- Grounded assets have their origin on the floor (`min y ≈ 0`). The two
exceptions are `wall_lantern` (origin on the wall plate, at mount height) and
`marquee` (origin at the wall where the canopy meets it, at canopy height).
View File
Whitespace-only changes.
+34
View File
@@ -0,0 +1,34 @@
# Scene concepts: the realistic target look
These are AI-generated example frames (`python3 tools/scene_concepts.py generate --group scene`,
`openai/gpt-5-image-mini`, 1280×720) for the realism pass. They are art direction, not
assets. `tools/scene_concepts.py realism` pairs them with the in-game shots from
`src/test/capture_realism.gd` (see `SHOTS` in the tool). New generations never overwrite a
file here; they are saved as `<name>-vN.png`.
| Concept | Visual targets it sets |
| --- | --- |
| `hollywood_entrance_morning` | warm low sun, long soft shadows, brick with visible mortar and wear, stone plinth, potted topiary, haze that fades the far street to beige |
| `hollywood_entrance_golden_hour` | strong back light and rim light on people, marquee bulbs reading even in daylight |
| `hollywood_entrance_rain_night` | dark scene lit by practical lights only (marquee, sodium lamps), wet glossy ground with reflections, fog |
| `boulevard_skyline_day` | deep layered skyline with real towers and haze, palms lining the road, red curb paint, believable asphalt |
| `boulevard_skyline_dusk` | purple/orange gradient sky, neon signs, lit windows, car light trails |
| `joke_shop_alley_evening` | narrow brick alley, one warm wall lantern as key light, deep shadows, people in dark jackets |
| `joke_shop_door_closeup` | close material detail: brick, mortar, aged wood door, brass |
| `studio_stage_door_night` | granite facade, a bright bulb canopy as the main light, a glossy black car, stanchions and a barrier |
| `sidewalk_material_closeup` | paver joints, stains, granite curb, aggregate asphalt and a tree grate at grazing light |
| `entrance_steps_podium` | granite steps, handrails, glass doors, the check-in podium |
| `crowd_barrier_line` | steel barriers, a red carpet, a crowd with depth-of-field haze |
| `responder_arrival_wide` | fire engine, turnout gear, red/blue light spill on wet pavement |
Cross-cutting targets, for comparison with the 2026-11-01 captures in
`docs/screens/realism/2026-11-01-v2/sheet.jpg`:
- **Exposure.** The concepts keep the ground mid-grey. The game's sidewalk is close to white,
so it is overexposed.
- **Night means dark.** The night/dusk concepts are lit by practical lights. The game's
"Evening" phase is a bright pink-orange frame.
- **Depth.** The concepts show a layered skyline fading into haze. The new v2 skyline strips
supply this; the old box skyline did not.
- **Geometry and foliage.** The facades have plinths, cornices and deep window reveals, and the
trees and shrubs are organic. The game uses flat walls and blob trees.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Loaded 100 of 155 files, more files were not shown because too many files have changed in this diff. Show more