Group the top-level docs into docs/project, docs/design, docs/assets and
docs/process, each with its own README index. Delete the superseded
docs/screens/head-review page and sheets. Rewrite every markdown link,
absolute docs path and tool/test reference to the new locations, fix the
pre-existing broken anchors and remove the dead Joke Shop photo-board link.
check_docs OK, Python tooling 167/167.
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.
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 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.
- ignore assets/models/blender_src/*.blend (keep the .gdignore marker tracked) and
remove the 46 editable .blend files from the index; they remain on disk and are
regenerated by tools/blender_assets.py
- move the completed storage/docs/code-path/art-loader/verification, mouse-fix,
and pop-up sections to DONE.md; TODO now holds only the open lighting item,
realism follow-ups, and optional ideas
- docs changes only; runtime code unchanged
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.
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.