Hide the tourist photo-pose bag outside camera poses, flatten the backpack straps onto the chest, rebuild the gear GLB/AO and publish the before/after NPC sheet.
Add a neutral 48-frame character contact sheet, acceptance criteria and ranked baseline gaps, plus repeatable sheet/comparison tooling. No character GLB or runtime mesh changes.
Add deterministic captures 14-21 for the remaining screen-design states, fix the panel/button layout issues they exposed, and record 21/21 compare and judge results. Bump the source to 0.9.3 and move the completed milestone to DONE.md.
- Replace the alley with the 27 m Comedy Store-inspired hall, signature canopy, circular blade sign, red curtain bays, west entrance, low-rise neighbours, dry hillside and opposite frontage.
- Add day/night/across-street concept frames and real-site mapping notes.
- Expose the Joke Shop day stage in the realism captures and refresh the gallery shots.
- Render multiline HUD/wand strings correctly and list carried items in the wand panel.
- Add the Joke Shop layout/budget tests and update docs, roadmap and changelog.
Replace the generic boulevard facade with a stylized 6840 Hollywood Blvd block: six Ionic columns, eight stone steps, Masonic frieze/medallions, event banners, side doors, El Capitan Theatre and retail neighbours, Ovation Hollywood arcade, Walk of Fame and four-lane street markings.
Add the Day and Night Hollywood venue-menu rows. Night uses a phase-time offset so a full-length shift stays in LATE while the clock starts at zero; late routine traffic keeps the street populated and the offset round-trips through checkpoint restore.
Retune local-light falloff to linear attenuation, add a cool north sky fill, and use the smooth limestone facade texture. Refresh the staged gallery and map references; add geometry-budget, menu, night-offset and late-traffic tests.
Add tools/check_docs.py to require READMEs only in tracked docs directories,
validate local links and verify every tracked image is linked. Gitignored
generated files such as realism runs are exempt. Document the command and add
unit tests. Python 97/97.
Update project features and CI installs/templates to Godot 4.7.2, remove the
old version matrix, and cache the Godot binary and export templates with Gitea
Actions cache. Update release/docs/version references accordingly. Project
import and headless tests pass locally under 4.7.2.
Delete docs/plan-graphics-v0.6.md and update README, architecture, asset
tooling, Blender docs and MEMORY references. Completed work stays summarized
in ADR-0013, the asset-pipeline architecture and the changelog; the v1.0.0
roadmap owns future graphics work.
Add openrouter_judge.py to rank live image+JSON models by advertised price,
with ordered fallbacks, auth-error handling and auto selection. Wire both judges
to --model auto, record the model used, and add fake-catalogue/mocked-call
tests. Re-run the judges with the valid key: 13/13 screen designs match, 0 diff;
the Forward+ A/B report records the selected model and ~$0.006 spend. Headless
231/231, Python 94/94.
Package a Forward+ preset with volumetric fog, SDFGI and a volumetric flashlight
for a separate Linux/Windows Desktop HQ zip. Gate the effects on a real
RenderingDevice, tune LATE fog, and adapt local light falloff for Godot 4.7.
Record the Compatibility/Forward+ A/B captures and metrics, add the artifact to
the build/publish scripts, and keep the default Compatibility/Web path
unchanged. Headless 231/231, Python 87/87; exported HQ binary boots with Vulkan.
Add the alley walls, warm lantern, shutters, dumpster, AC clutter and door
step for the Joke Shop. Build the studio stone facade, bulb canopy, glossy
black car, stanchions and barriers. Rebuild only the world container when the
venue changes, so the guard and UI survive; add per-venue navigation checks.
Capture gallery shots 12/13, update the concept mapping and docs. Headless
231/231, Python 87/87, screenshot gate 13 good/0 bad. The vision-judge retry is
tracked separately because the API key is invalid.
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.
The face pass added a brow ridge; lift the helmet mesh 2 cm so the brim sits
above the eyes. Cap and hood were rendered on their characters and leave the
brow/ears and face readable. Rebuild and re-bake the helmet; Python 87/87.
Narrow the bag to 0.06 m and move it outboard to centre x=0.292. The body inner
face is now 0.262 m, clearing the coat skirt (0.225) and dress (0.260); the
strap lower points start on the same line. The fit report measures the bag body
separately from the torso strap and uses a signed inner-face clearance. Run
Blender build/bake and Godot import. Python tooling 87/87.
Move the hinges to +/-0.075 (0.035 box), arms to +/-0.098, and lenses to a
0.024 m radius while keeping the lens plane in front of the eyes. Rebuild the
GLB and AO; fit now measures 0.202 m total width (0.64x head). capture_realism
now parses the documented --portrait-type option instead of ignoring it.
Python tooling 87/87; headless 228/228.
`blender_assets.py fit` measures each character head width (skin vertices
weighted to Head in y 1.55-1.81) and clothing radius (cloth skirt in y 0.30-0.52,
pants hip fallback), then prints every attached head/back/chest gear bounding box
with head-band status and x/radial skirt clearance. The report confirms the
glasses width and shoulder-bag coat clip, giving the next fit fixes numbers.
Python tooling 87/87; asset audit clean.
Blender library
- Rebuild the face: sclera, iris and upper lid per eye, brow ridge, cheekbones,
nose bridge and tip, ears with lobes, split upper/lower lips.
- Give each hand four fingers plus a thumb and end every sleeve in a cuff ring.
- Raise CHAR_MAX_VERTS 4000 -> 5000 (16 avatars still cost ~6.9 ms of a 50 ms
frame), decimate the body to 26 %; characters are now 3,974-5,026 tris and
3,744-4,807 verts. Rebuild all 8 variants and re-bake their AO maps.
Capture tooling
- Add a `portrait` stage to capture_realism.gd: freeze one NPC, park the crowd
out of frame, stand the subject on open sidewalk, turn it into the camera and
put the guard on the sunlit side. New --portrait-type=N picks the role.
- Add the npc_portrait_day concept and map the shot to it, so avatar detail is
scored against a portrait instead of a wide street frame.
Flashlight
- Drop the fake additive beam (Materials.light_beam + the cone mesh): on GL
Compatibility a first-person camera inside the cone renders it as a filled
disc with a bright far ring. Keep the tight spot (14 deg, 20 m, energy 4.2)
and its headless test; the visible shaft is now planned behind a Forward+
preset.
Docs and task tracking
- Move the finished realism follow-ups (ground exposure, flashlight, NPC detail,
judge re-check) to DONE.md and expand TODO.md with step-by-step plans for NPC
gear fit, venue geometry plus the committed gallery, and the Forward+ preset.
- Record the decisions, gotchas and portrait recipe in MEMORY.md; update the
Blender contract, the concept README and the capture section of README.md.
- Refresh the gallery captures affected by the avatar rebuild.
Checks: headless 228/228, Python 86/86, blender_assets audit exit 0,
scene_concepts audit all GOOD, screen_designs compare 11 good / 0 bad,
smoke_npc_budget OK, boot clean (only the known headless mesh warning).
- add the v2 runtime picks: per-phase skyline panoramas, brick, sidewalk
pavers, dry/wet asphalt, awning canvas, and their normal maps
- add the realism tooling and evidence: tools/scene_concepts.py, capture_realism.gd,
concept frames, reports/sheets, and tests
- retune phase lighting/fog/exposure, add sun yaw, brighten the night glow,
add the flashlight beam mesh, and rebuild the planter/tree assets
- regenerate all eleven staged screenshots from the current build; compare is
11 good / 0 bad
- finish the visual-alignment closeout in DONE/TODO, update architecture,
blender-assets, PLAN, and ignore rules (editable Blender sources untracked)
- keep the judge retry and document the verified default judge model
Verification: headless 227/227; Python tooling 86/86; check_screenshots 11/11.
Both capture helpers now accept --out=res://docs/screenshots, and
tools/check_screenshots.sh renders the staged set (using xvfb-run when headless)
before running screen_designs.py compare. Record the judge result: 7 match,
4 review, 0 diff. Headless 226/226 and the smoke both pass in a clean worktree.
- screen_designs.py now reads/writes assets/images/ui/ (ADR-0014); docs/
screens/designs/ stays the reference-only snapshot, documented as historical
- add src/game/ui/art.gd: cached loader for assets/images/ui|scenes with null
fallbacks for missing files, plus headless tests
- start menu uses the start_menu scene art as a full-screen backdrop, covers the
baked-in mockup panel with an opaque panel, and styles the venue rows to match
- update README/architecture/TODO path notes and mark the storage/code-path/loader
items done; DONE records the historical path move
- compare still 11 good / 0 bad; headless 226/226
README/PLAN realism-pass edits from another workstream remain unstaged." && git log --oneline -1
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.
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.
Wire the skinned model from ADR-0013 into NpcAvatar:
- _build_skinned_body() instantiates the imported npc_base.glb, binds the 14
armature bones, and keeps the procedural _build_body() as the offline
fallback (use_skinned_model can force it in tests)
- outfit_for()/layer_for() tint the five material slots through the new
SurfaceMaterials.npc_slot(); accessories and hand items attach under Rig/Head
- the existing limb pivots (Hip/Knee/Shoulder/Elbow/Hand + Head marker) keep the
old animation math; _sync_skeleton() copies their rotations to bones. Bone
pose rotation is absolute local orientation, so it is composed with the rest
basis (otherwise the whole body snaps into a broken pose)
- skinned rigs scale uniformly (non-uniform parent scale shears the skin) and
keep Rig/Head for the focus cone, nametag, and reaction tag
- commit the editable npc_base.blend plus a .gdignore: Godot's editor otherwise
tries to import the blend and hangs during --import
- npc_pipeline audit tests (contract, textures, clean GLB, committed source)
Verification: headless 212/212, Python tooling 60/60, NPC budget 16 avatars =
144 meshes at ~6.9 ms, movement/focus/props smokes pass, compare 11 good / 0 bad.
TODO Phases 0-3 checked; environment lift and release remain.
Update the game name in project.godot, export presets, UI/marquee/title strings, packaging (AppImage/NSIS/desktop/appdata), tools, docs, and the generated prop GLBs. Packaging assets and README screenshots are regenerated. Headless suite 209/209 and Python tooling tests pass.
asset_pipeline generate gains --variants N (saved as <stem>-1..N.png). .gitignore now excludes assets/models/concepts, assets/models/npc, assets/textures/npc/ai, and raw assets/textures/ai outputs; only game_ready and the game-facing NPC maps/models stay tracked.