Files
security-game/TODO.md
T
zwitschi d872034388
Full build / Full desktop build + optional release (push) Successful in 3m43s
Full build / Import + headless tests (push) Successful in 29s
Bake library AO and lift environment textures
Phase 4 of the v0.6.0 graphics plan:

- tools/bake_ao.py bakes 256² AO maps for every Blender character/gear/prop
  (Cycles, existing UVs) next to each GLB; Materials.apply_ao() wires the red
  channel into character slots, gear, and props. Compatibility has no SSAO.
- Regenerated brick/concrete/asphalt at 1024² with OpenRouter, derived tangent
  normal maps via tools/make_normal_maps.py, and wired them through
  SurfaceMaterials.textured() without changing UV scales.
- Evaluated Forward+: boots on desktop Vulkan, but the renderer is project-wide
  and Web must stay GL Compatibility; v0.6.0 keeps Compatibility (decision in
  MEMORY/plan). A desktop-only override is deferred.
- Recaptured screenshots; compare 11 good / 0 bad. Fixed marquee text alignment.
- Tests: baked AO maps exist for all 46 assets; prop/character AO material
  checks; headless 222/222, Python 69/69, all smokes pass.
- Version 0.6.0; ADR-0013 accepted; README/architecture/blender-assets/plan and
  TODO/DONE updated.
2026-11-01 15:07:27 +01:00

234 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
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`.
## Future ideas (optional, not scheduled)
- [ ] Live 3D body-camera replay (the timeline is text-only today).
- [ ] Additional venues or seasonal events authored from the new `.tres` files.
- [ ] Real voice/dialogue audio (the crowd bed and radio crackle are procedural).
- [ ] Swap the primitive prop builder for an image-to-mesh model API behind the same
`assets/models/generated/<prop>.glb` contract.