Extend the v0.6.0 plan with the Blender asset library

Document the larger asset set produced by the running Blender generator
(tools/blender_assets.py): 8 NPC character variants, 18 gear pieces, and 22
street/venue props with a manifest, editable sources, and preview sheets.

- TODO gains Phase 3.5 with the audit/budget reconciliation (npc_casual is
  currently 7,756 tris / 5,616 verts, above the 6,000/4,000 contract), character
  selection by role, gear attach points, prop replacement/placement, emissive
  wiring, budgets, and screenshot gates
- PLAN's Visual Quality Direction describes the library, runtime selection, and
  offline fallbacks
- docs/plan-graphics-v0.6.md adds the library contract/output table and
  integration steps, and points Phase 4 at the Blender props
- MEMORY records the manifest-as-source-of-truth rule and the generator caveat

Generated library files stay untracked until the running build finishes and
passes the audit; the generator itself is committed so the plan is reproducible.
This commit is contained in:
zwitschi committed 2026-11-01 13:39:03 +01:00
1 parent a231f7588a
commit 7efb5024ec
5 files changed
+1766 -20

No files matched your search

+9
View File
@@ -129,6 +129,15 @@ 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.
- **Blender asset library (v0.6.0, in progress):** `tools/blender_assets.py` generates
`assets/models/blender/{npc,npc_gear,props}/` plus `manifest.json`, `docs/blender_previews/`,
and (when finished) `docs/blender-assets.md`. Contract: 8 character variants (same bones/slots
as `npc_base`), 18 gear pieces in attach-point space (head/hand/back/chest), 22 props; the first
nine replace current furniture/lamps/trees/cone, the rest are new dressing. Treat the manifest
as the source of truth and run `python3 tools/blender_assets.py audit`/`sheet` before wiring.
At the time of writing only `npc_casual` existed (7,756 tris / 5,616 verts, above the current
6,000/4,000 character budget) — reconcile the budget before integration. Don't edit the
generator while its Blender process is running.
- **Prop pipeline (done):** audit enforces 400 tris / 600 verts, ground contact, UV range, no
degenerate tris, one material. `capture_prop_gallery.gd` writes `10-prop-gallery.png` (3x2 studio
line-up, back row on a platform) and the `prop_gallery` catalogue entry compares it.
+13 -3
View File
@@ -404,9 +404,19 @@ The studio also has authorized guests and staff. They must either be recognized
## Visual Quality Direction
The game prioritizes readable, well-lit, low-poly presentation over realism (GL Compatibility
renderer). The next visual pass, v0.6.0, replaces the procedural NPC rig with Blender-authored,
UV-mapped, skinned characters carrying baked normal/AO/roughness maps, using the existing AI tools
for textures, palettes, and screen judging. See
renderer). The v0.6.0 visual pass moves presentation to a **Blender-authored asset library**:
skinned NPC character variants, head/back/chest/hand gear, and street/venue props, all carrying
baked albedo/normal/AO/ORM maps. A generator (`tools/blender_assets.py`) and manifest
(`assets/models/blender/manifest.json`) keep the assets reproducible; the game selects variants
by NPC role/outfit and keeps the procedural rig and primitive props as offline fallbacks. The
existing AI tools remain the source of art direction for fabric textures, palettes, and the
screen-design compare/judge gate.
The library targets 8 character variants (casual, jacket, coat, vest, dress, hoodie, firefighter,
guard), 18 gear pieces, and 22 props — including replacements for the current generated street
furniture, lamps, trees, doors, marquee, and a venue car. Integration must preserve the
`Rig/Head` focus anchor, seed-driven outfit determinism, collider/navigation footprints, and the
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).
+57 -4
View File
@@ -8,6 +8,13 @@ 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)
@@ -58,19 +65,65 @@ Detailed plan: [`docs/plan-graphics-v0.6.md`](docs/plan-graphics-v0.6.md) · [AD
- [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 (or rebuild the props in Blender) behind the same `assets/models/generated/<prop>.glb` contract.
- [ ] Regenerate higher-resolution brick / concrete / asphalt textures via `asset_pipeline.py` with normal maps, keeping the current UV scales.
- [ ] 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` (skip with reason when Blender is absent).
- [ ] 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.
- [ ] `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`.
+87 -13
View File
@@ -164,20 +164,89 @@ Outputs land under `assets/textures/ai/game_ready/` (or a new
6. Re-measure `smoke_npc_budget.gd`; adjust `LOD_TIERS` if the skinned rig is
heavier up close. The impostor path is unchanged.
### Phase 3.5 — Blender asset library (characters, gear, props)
The library generator is `tools/blender_assets.py` (flatpak Blender, headless).
It stages outputs for integration; nothing is wired into the game until the
audit and manifest are clean. While the generator is running, do not edit it.
**Outputs and contract**
| Kind | Path | Notes |
| --- | --- | --- |
| Characters | `assets/models/blender/npc/<name>.glb` | 8 variants: `npc_casual`, `npc_jacket`, `npc_coat`, `npc_vest`, `npc_dress`, `npc_hoodie`, `npc_firefighter`, `npc_guard` |
| Gear | `assets/models/blender/npc_gear/<name>.glb` | 18 pieces; authored in attach-point space (`head`, `hand`, `back`, `chest`) |
| Props | `assets/models/blender/props/<name>.glb` | 22 pieces; the first nine replace current generated/primitive street furniture, the rest are new venue dressing |
| Manifest | `assets/models/blender/manifest.json` | path, category, triangles, vertices, bounds, materials, roles, tint slots, attach points |
| Sources | `assets/models/blender_src/<name>.blend` | committed, editable source |
| Previews | `docs/blender_previews/*.png` + `sheet_<category>.png` | `render` / `sheet` for visual review |
All characters reuse the `npc_base` contract: mesh node `npc_base`, bones
`Head/HipL/R/KneeL/R/ShoulderL/R/ElbowL/R/HandL/R` (+ Hips/Spine/Neck),
material slots `skin/cloth/pants/accessory/hair`, front toward Blender `-Y`
(Godot `+Z`), feet at `y ~= 0`. Gear origins are the attach points (head at
`Rig/Head`, hand at the hand proxy, back at the spine, chest at the sternum).
Props are grounded except `wall_lantern` and `marquee`; both may be emissive.
**Integration steps**
1. **Audit the finished build.** `python3 tools/blender_assets.py audit` and
`sheet`; fix assets or reconcile the budgets. The first `npc_casual` result
is 7,756 tris / 5,616 verts, above the current 6,000 / 4,000 character
contract — either simplify the mesh or explicitly raise and document the
character budget before wiring variants in.
2. **Commit the library and sources.** Add `assets/models/blender/**`,
`assets/models/blender_src/**`, the generator, manifest, previews, and
`docs/blender-assets.md`; generated folders that CI can rebuild offline may
stay ignored. `blender_assets.py audit` must work without Blender.
3. **Select characters by role.** Extend `NpcAvatar` to pick a variant from the
manifest using the profile's role and `outfit_for`/`layer_for` (casual,
jacket, coat, vest, dress, hoodie, firefighter, guard). Fall back to
`npc_base.glb`, then to the procedural rig, and record the chosen path.
4. **Tint and anchor.** Apply `outfit_for` to every variant's five slots via
`SurfaceMaterials.npc_slot()`; keep `Rig/Head` for the focus circle, name tag,
reaction tag, and accessories. Keep the walk/idle/escalation bone-driving
wrapper.
5. **Replace procedural gear.** Use the `npc_gear` catalogue for caps, hoods,
helmets, backpacks, lanyards, hand items (phone, cup, clipboard, umbrella,
camera, sign, flashlight, wand, radio) and the SCBA tank; attach at the
documented points and fall back to the current primitives when an asset is
missing.
6. **Replace and extend props.** Load the replacement set through
`WorldBuilder` (`bollard`, `bench`, `trash_bin`, `planter_box`,
`newspaper_box`, `fire_hydrant`, `street_lamp`, `street_tree`,
`traffic_cone`), then place the new dressing (`palm_tree`, `round_planter`,
`a_frame_sign`, `wall_lantern`, `crowd_barrier`, `stanchion`, `handrail`,
`security_podium`, `double_door`, `marquee`, `sedan`) using the existing
placement table/collider/avoidance contract. No prop may enter the
door/steps/guest lane.
7. **Wire emissives to the phase.** Street-lamp bulbs, wall lanterns, the door
lock LED, marquee bulbs/sign face, and sedan headlights should follow the
existing day/night lighting rather than staying always on.
8. **Measure and gate.** Re-run `smoke_npc_budget.gd` with the new characters
before raising `MAX_LIVE_NPCS`; add a prop triangle/draw budget check. Then
recapture `docs/screenshots/` and run `screen_designs.py compare` + `judge`
plus a close-up target shot for character readability.
9. **Fallbacks stay offline.** A fresh checkout with no Blender and no network
must boot on the committed library (or `npc_base.glb`/procedural rig when an
asset is absent); CI still runs the Godot import and headless suite.
### Phase 4 — Environment and lighting lift (after NPCs land)
1. **Baked AO on props:** export the existing prop set through Blender (or bake
AO in `npc_pipeline`-style tooling) so contact shadows read without SSAO.
This replaces the pure box/cylinder builder with Blender-authored props
behind the same `assets/models/generated/<prop>.glb` contract (ADR-0012's
follow-up).
1. **Baked AO on props:** use the Phase 3.5 Blender prop replacements
(`street_lamp`, `street_tree`, `bench`, …) and bake AO into their
albedo/ORM maps so contact shadows read without SSAO. Keep the same
`WorldBuilder` load/fallback contract (ADR-0012's follow-up).
2. **Higher-resolution environment textures:** regenerate brick/concrete/asphalt
through `asset_pipeline.py` with normal maps, keeping the current UV scales.
through `asset_pipeline.py` with normal maps, keeping the current UV scales;
use the same sRGB/albedo + packed ORM discipline as the library assets.
3. **Evaluate a desktop-only Forward+ preset** (SSAO/SSIL/volumetrics) while the
Web export preset keeps `thread_support=false` and GL Compatibility. Decide
explicitly in an ADR addendum; do not silently change the renderer.
4. Regenerate `docs/screenshots/` with `capture_screenshots.gd`, then run
`screen_designs.py compare` and `judge` as the regression gate.
`screen_designs.py compare` and `judge` as the regression gate, plus
`blender_assets.py sheet` for the character/gear/prop library.
### Phase 5 — Verification and release
@@ -204,15 +273,20 @@ Outputs land under `assets/textures/ai/game_ready/` (or a new
## Acceptance criteria (definition of done)
1. NPCs are Blender-authored skinned meshes with baked maps; they read as people
at conversation distance and survive a cinematic close-up.
1. NPCs and street furniture are Blender-authored meshes with baked maps; the
character variants, gear, and props read as people/objects at conversation
distance and survive a cinematic close-up.
2. `Rig/Head`, the focus cone, nametag, theater poses, crowd reactions, and LOD
all still work; all existing NPC tests pass unchanged or are updated with
clear reasons.
3. The game boots and plays from a fresh checkout with **no** Blender and no
network (committed assets + procedural fallback).
4. `npc_pipeline.py build` is deterministic and `audit` enforces the contract.
5. 208 headless + 56 Python tests stay green (Python audit tests skip when
Blender is absent); NPC-budget and movement smokes pass; screenshot
`compare`/`judge` show no regressions.
6. Version bumped to v0.6.0, docs and ADR-0013 updated, release published.
5. 212 headless + 60 Python tests stay green (Blender-dependent Python tests
skip with a reason when Blender is absent); `npc_pipeline.py audit` and
`blender_assets.py audit` pass; NPC-budget and movement smokes pass;
screenshot `compare`/`judge` and the library contact sheets show no
regressions.
6. The library is committed with its generator/manifest/previews, runtime
selection and fallbacks are covered by tests, version bumped to v0.6.0,
docs and ADR-0013 updated, release published.
File diff suppressed because it is too large. Load diff