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.
234 lines
16 KiB
Markdown
234 lines
16 KiB
Markdown
# 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.
|