# TODO Godot 4.3 prototype. Latest release **v0.5.0** (NPC characters and player movement). Headless suite **212/212** plus **60** 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). > **Asset build in progress:** `tools/blender_assets.py` is being run separately (flatpak > Blender) to generate a larger library: 8 NPC character variants, 18 gear pieces, and 22 > street/venue props under `assets/models/blender/`, with a manifest and preview renders. > Do not edit the generator while that process is running. Treat > `assets/models/blender/manifest.json` as the integration source of truth, and audit the > finished outputs before wiring anything into the game. ## 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 3.5 — Blender asset library integration (generator running) Contract and outputs: [`assets/models/blender/manifest.json`](assets/models/blender/manifest.json), generator `tools/blender_assets.py`, previews `docs/blender_previews/` (the generator's integration notes are being written to `docs/blender-assets.md`). The library currently targets 8 `npc` variants, 18 `npc_gear` pieces, and 22 `props`; each entry records path, triangle/vertex count, bounds, material slots, and (for gear) its attach point. - [ ] Wait for the running build to finish, then run `python3 tools/blender_assets.py audit` and reconcile every failure. The first `npc_casual` build is over the 6,000-triangle contract (7,756 tris / 5,616 verts): reduce the mesh or raise/annotate the character budget in the plan before integrating. - [ ] Run `python3 tools/blender_assets.py sheet` and review the contact sheets; reject assets whose silhouette, grounded origin, or face direction (+Z in Godot) is wrong. - [ ] Commit the finished library with the generator, `.blend` sources, manifest, previews, and `docs/blender-assets.md`; add generated/preview folders to `.gitignore` only where CI can rebuild them offline. Keep the audit runnable without Blender. - [ ] Add `tools/test_blender_assets.py` for the manifest contract (all catalogue entries present, audit clean, attach points known, paths inside `assets/models/blender/`). - [ ] Let `NpcAvatar` choose a character model from the manifest by the profile's role/layer (`npc_casual` / `npc_jacket` / `npc_coat` / `npc_vest` / `npc_dress` / `npc_hoodie` / `npc_firefighter` / `npc_guard`), with the existing `npc_base.glb` and procedural rig as fallbacks; record the chosen path for tests. - [ ] Keep `outfit_for()` tinting the `skin`/`cloth`/`pants`/`accessory`/`hair` slots on every variant and keep `Rig/Head` as the focus/nametag anchor. - [ ] Replace the procedural cap/hood/backpack/sunglasses/lanyard and hand items with the matching `npc_gear` assets at the documented `head` / `back` / `chest` / `hand` attach points; fall back to the current primitives when gear is missing. - [ ] Load the replacement props (`bollard`, `bench`, `trash_bin`, `planter_box`, `newspaper_box`, `fire_hydrant`, `street_lamp`, `street_tree`, `traffic_cone`) from the Blender library behind the existing `WorldBuilder` model/fallback path. - [ ] Place the new street/venue props (`palm_tree`, `round_planter`, `a_frame_sign`, `wall_lantern`, `crowd_barrier`, `stanchion`, `handrail`, `security_podium`, `double_door`, `marquee`, `sedan`) through `PROP_PLACEMENTS`/dedicated venue slots, with collider + `NavigationObstacle3D` footprints and no overlap with the door/approach lane. - [ ] Wire the emissive parts (`street_lamp` bulb, `wall_lantern`, `double_door` lock LED, `marquee` bulbs/sign face, `sedan` headlights) to the day/night phase lighting instead of leaving them always on. - [ ] Use the library `street_tree`/`palm_tree` instead of the current cone trees, and the Blender `marquee`/`double_door` where the procedural versions currently sit; compare silhouettes with `screen_designs.py`. - [ ] Re-measure `smoke_npc_budget.gd` and add a prop draw/triangle budget check; keep `MAX_LIVE_NPCS` unchanged until the new characters are measured. - [ ] Regenerate all staged captures and run `screen_designs.py compare` + `judge`; add a character close-up capture (the 02 target shot) to the catalogue if needed. ### Phase 4 — Environment and lighting lift - [ ] 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. - [ ] 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. - [ ] Evaluate a desktop-only Forward+ export preset (SSAO/SSIL/volumetrics); keep the Web preset on GL Compatibility with `thread_support=false`. - [ ] Record the renderer decision in an ADR addendum or `MEMORY.md`. - [ ] Regenerate `docs/screenshots/` with `capture_screenshots.gd` and run `screen_designs.py compare`. ### Phase 5 — Verification and release - [ ] 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). - [ ] Windowed smokes pass: `smoke_npc_budget.gd`, `smoke_movement.gd`, `smoke_focus_radial.gd`, `smoke_props.gd`. - [ ] `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. ## 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/.glb` contract.