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.
9.8 KiB
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.
Graphics quality — Blender-authored NPC characters (v0.6.0)
Detailed plan: docs/plan-graphics-v0.6.md · ADR-0013
Phase 0 — Toolchain spike and contract freeze
- Verify
flatpak run org.blender.Blender --versionreports Blender 5.2.x and runs headless in this shell. - Run a minimal headless Blender export using an absolute
--pythonpath (sandbox cwd is/app/blender); confirm the output file lands where expected. - Record the exact working invocation (flatpak + absolute paths) in
MEMORY.md. - Freeze the NPC asset contract in
docs/plan-graphics-v0.6.mdand ADR-0013: file paths, bone names, material slots, triangle budget. - Fix the material-slot set (
skin/cloth/pants/accessory/hair) and the ORM packing (R=occlusion, G=roughness, B=metallic). - Choose and record the initial tier-0 triangle/vertex budget (≤ ~6000 tris / ~4000 verts) in the plan.
- Spike: Godot 4.3 imports the
.glbas aPackedScenewith aSkeleton3D; no clips are needed because_animate()drives the bones at runtime.
Phase 1 — Blender authoring + export pipeline
- Author the low-poly base humanoid in Blender: torso, articulated arms/legs, head with a face front, hands, shoes.
- 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. - UV-unwrap the mesh into a power-of-two layout, one region per material slot.
- Save the editable source as
assets/models/npc/npc_base.blend(committed). - Add
tools/npc_pipeline.pywithlist/build/auditsubcommands, run through the Blender flatpak. buildexportsassets/models/generated/npc_base.glbplus the material textures.- Bake neutral albedo per slot, normal maps (fabric weave, denim, knit), and the packed ORM map.
- Bake ambient occlusion into the albedo or the ORM R channel (the Compatibility renderer has no SSAO).
auditenforces UVs in [0,1], expected bone names, triangle/vertex budget, feet at y≈0, and material-slot presence.- Reuse the
model_pipeline.pycolour-space discipline (linear factors vs sRGB textures) in the exporter. - Run
godot --headless --path . --importand confirmnpc_base.glbimports without errors.
Phase 2 — AI-assisted texturing
- 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 toassets/textures/npc/ai/(512×512). - Tile-edge blending on each fabric — applied automatically by
generate --role tile(make_seamless); thetileaudit's edge check confirms continuity. - Run
asset_pipeline.py auditon the outputs — 6 good, 0 bad (roletile). - Commit the validated textures under
assets/textures/npc/ai/(tracked in the v0.6.0 asset commit). - Derive per-role outfit palettes via
model_pipeline.dominant_colors— written toassets/textures/npc/palette.json(npc_pipeline.py palette). - Bake normal/roughness procedurally and layer the AI albedos on top —
npc_pipeline.py prepare+ Blenderbuildkeeps the AI albedo forcloth/pantsand regenerates normal/ORM; GLB audit ok (1204 tris / 2488 verts). - Regenerate the staged captures and run
screen_designs.py judgeafter the Phase 3 integration (11 good / 0 bad incompare; judge: no diff).
Phase 3 — Godot integration
- Add
NpcAvatar._load_model()that instantiates the importednpc_base.glbPackedScene. - Keep
_build_body()as the offline fallback; choose per-avatar and recordloaded_model/fallback_avatarfor tests. - Map
outfit_for()onto the model's material slots so seed variety,layer_for,hand_item_for, and archetype colors survive. - Keep accessory attachments (cap / hood / backpack / sunglasses / lanyard) under
Rig/Head. - Add
SurfaceMaterialsbaked-map recipes (npc()/skin()/cloth()); stop callingMaterials.flat()for the body. - Add
_set_limb_rotation()so_animate()/_idle_layer()write to bones or pivots; keepRig/Headfindable by the focus cone. - Update
test_npc_character,test_npc_theater, andtest_target_cyclefor the skinned path (or document why they stay on the fallback). - Re-measure
smoke_npc_budget.gd; tuneLOD_TIERSif the skinned rig is heavier up close. - Re-verify the focus-cone smoke (
smoke_focus_radial.gd) and the movement smoke.
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>.glbcontract. - Regenerate higher-resolution brick / concrete / asphalt textures via
asset_pipeline.pywith normal maps, keeping the current UV scales. - 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/withcapture_screenshots.gdand runscreen_designs.py compare.
Phase 5 — Verification and release
- Headless suite green: fallback build, outfit determinism,
Rig/Headpresence, material recipe assertions,npc_pipeline.py audit(skip with 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 compareandjudgeshow no regressions.- Bump
config/versionto0.6.0. - Update
README.md,docs/architecture.md(Level 2 gainstools/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 inMEMORY.mdand an ADR. - Move the reference mockups from
docs/screens/designs/into the runtime root (keepdocs/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/, anddocs/screens/designs/. - Re-run
godot --headless --path . --importto regenerate.importsidecars after any move. - Confirm
.gitignorestill excludes development-only outputs (concepts, Blender sources, raw AI fabrics) under the new layout.
Documentation path updates
- Update
README.mdpaths (screen-designs section, confirmed-assets links, theassets/images/backgroundsresize example). - Update
docs/architecture.md,PLAN.md, and ADR-0010/0011/0012/0013 references todocs/screens/designs/. - Update
tools/screen_designs.pydocstrings and mark the old paths inDONE.mdas historical.
Code path updates
- Point
tools/screen_designs.py generateat the new design output path and keepcompare/judgereading 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.gdoutput 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_menuscene 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_menumockup (layout, palette, background). - Make the HUD match the
targeting_npc/escalation_hudmockups (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_overviewandlate_night_avenue.
Verify against the mockups
- Regenerate the staged captures and run
screen_designs.py compare(no gross drift). - Run
screen_designs.py judgeand clear everydiffverdict (targetmatch/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
.tresfiles. - 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>.glbcontract.