Files
security-game/TODO.md
T
zwitschi 1b319c48b7 Guard gear fit with headless and Blender regression checks
The Python fit test now caps the glasses at 0.70x head width and requires a
positive bag clearance on both the coat and dress. The Godot character suite
loads head/back gear on six roles and checks attach parents, head width and
brow bounds, and that back gear reaches beyond the 0.26 m body silhouette.
Rebuilt and re-baked all 18 gear assets; no-op GLB export churn was reverted.
Headless 229/229, Python 87/87.
2026-11-01 18:04:51 +01:00

7.6 KiB
Raw Blame History

TODO

Work is grouped by theme. Each item should be small enough to complete and verify on its own; see AGENTS.md for the workflow and MEMORY.md for the decisions behind these plans.

NPC gear fit (v0.6.1)

The 2026-11-01 detail pass fixed the bodies but not the props worn on them. In the portrait capture (--stages=day,portrait, see the report in docs/screens/realism/2026-11-01-npc/) the sunglasses span the whole upper face and the shoulder bag cuts through npc_coat's skirt. Blender sources for gear live in tools/blender_assets.py (_build_gear()), the contract in docs/blender-assets.md (gear budget 1500 tris, GEAR_ATTACH points in NpcAvatar).

  • Measure the fit in Blender, not by eye: python3 tools/blender_assets.py fit prints each character's measured head width and hip/skirt radius, then every head/back/chest gear bounding box with head-band status or skirt clearance. Current measurements: head width 0.315 m; coat skirt 0.225 m, dress 0.260 m; sunglasses 0.282 m (0.90x head); shoulder-bag x-clearance -0.025 m on the coat (radial -0.031 m on the dress).
  • Narrow sunglasses from 0.28 m to ~0.20 m. The hinges moved to ±0.075 (0.035 box), the arms to ±0.098, and the lenses are 0.024 m radius at the same y=-0.152, z=0.035 plane. Rebuilt GLB/AO: measured width 0.202 m (0.64x head), still inside the head band. Also fixed capture_realism.gd so --portrait-type=N is actually parsed (it was documented but ignored).
  • Fix shoulder_bag clipping through npc_coat/npc_dress: narrowed the bag to 0.06 m and moved it outboard (centre x=0.292); the body's inner face is now 0.262 m, clearing the coat (0.225) by 0.037 m and the dress (0.260) by 0.002 m. The strap's lower attach points sit on the same outboard line. fit now measures the bag body separately from the torso strap.
  • Same pass for cap/beanie/hood/firefighter_helmet: rendered the cap (vest), hood (hoodie), and firefighter helmet on their characters. Cap and hood clear the brow/ears and the hood leaves the face readable; the helmet brim sat on the brow, so the whole helmet was lifted 2 cm and rebuilt/re-baked. Beanie uses the same capped-shell sizing as the cap and is currently unused; it is covered by the upcoming headgear regression check.
  • Rebuild the gear (build --only <18 gear names>) and re-bake AO (tools/bake_ao.py --only <same>), then godot --headless --path . --import (clean). Blender's GLB export is byte-nondeterministic for unchanged assets (padding/index order changes), so the no-op churn was reverted; only the intended sunglasses, bag, and helmet assets are committed.
  • Add a regression test: tools/test_blender_assets.py limits the glasses to 0.70x head width and requires positive bag clearance on both coat and dress; test_npc_character.test_head_and_back_gear_clear_the_body_silhouette loads the gear on six roles and checks head-parent/width/brow bounds plus back-parent/0.26 m outer radius.
  • Re-capture the portrait (--stages=day,portrait) and the gallery shots; record the before/after in docs/screens/realism/<date>-gear/.
  • Consider a second character close-up for a hoodie/winter NPC so headgear is covered too (--portrait-type=5 already forces a hooded role for comparison).

Today all three venues share the Hollywood WorldBuilder geometry, so the Joke Shop and studio concepts are only compared on lighting (noted in tools/scene_concepts.py SHOTS). They also have no stage in the committed docs/screenshots/ gallery, only in capture_realism.gd.

  • Extract a geometry spec per venue instead of branching inline: a scenario. (or WorldBuilder) table of facade width/height, door style, window rows, awning/marquee flags, street furniture set, palette and props, defaulting to the Hollywood values so nothing regresses.
  • Joke Shop: narrow brick alley (10–12 m wide), one warm wall lantern as the key light, recessed door with a step, shuttered window, dumpster/AC clutter, no skyline ring — the alley concept has depth, not a horizon.
  • TV Studio: granite/stone facade, bulb canopy instead of the marquee, glossy black car on the curb, stanchions and barrier line at the door, no palm trees.
  • Keep navigation working: regenerate/verify the NavigationRegion3D bake per venue, update sidewalk/steps/door waypoints (Joke Shop and Studio already have sidewalk and steps_end), and confirm NPCs still reach the door (smoke + test_venue_*).
  • Split the concept mapping so each venue is judged on its own frame (joke_shop_*, studio_*) and drop the "shared geometry" caveat in tools/scene_concepts.py.
  • Add both venues to the committed gallery: two new stages in src/test/capture_screenshots.gd (12-joke-shop.png, 13-studio-arrival.png) reusing the existing joke_shop_queue / studio_arrival designs, then check_screenshots.sh and a fresh judge run.
  • Update docs/architecture.md (venue geometry), README.md (gallery) and the screenshot table when the stages land.

Forward+ desktop preset and a volumetric flashlight (v0.7.0)

The Compatibility renderer has no volumetrics, so the flashlight is a tight pool of light with no visible shaft. A second desktop build can render it properly without touching the CI/default path.

  • Write the preset as an override.cfg (Godot reads it beside the binary/project) that sets rendering/renderer/rendering_method="forward_plus", enables volumetric_fog_* and SDFGI, and keeps the Compatibility build as the default (project.godot unchanged).
  • Gate the effect in code: only give the flashlight a volumetric component when the renderer is Forward+ (RenderingServer.get_rendering_device() != null), so Web/CI builds are untouched and the existing headless tests keep passing.
  • Tune light_volumetric_fog_energy/fog_density for the LATE phase and re-check the entrance so the shaft does not wash the facade again (the reason the wide cone was cut).
  • A/B the two renderers with capture_realism.gd (same stages, two --out dirs) and record the diff + judge scores in docs/screens/realism/<date>-forward-plus/.
  • Add the preset to tools/build_releases.sh and docs/RELEASING.md as an optional "desktop HQ" artifact, and note the runtime requirement (Vulkan driver) in README.md.
  • Test: a headless run must still boot when Forward+ is unavailable (the preset must degrade to Compatibility instead of failing to start).

Realism follow-ups (deferred)

  • Human-authored tree/hedge silhouettes: the whole scene now reads well except the foliage, which is still flat-shaded clumps (_shrub()/_tree() in tools/blender_assets.py). Only worth doing if a higher-poly organic builder is added to the library.
  • Camera height: the concept frames are shot at ~1.55 m with a slightly wider lens; the game camera sits at 1.7 m. Decide whether the first-person height should match the art direction or stay at the current gameplay value.

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.