4.4 KiB
4.4 KiB
ADR-0013: Blender-authored NPC characters and baked materials
- Status: Proposed
- Date: 2026-11-01
Context
NPCs are the procedural capsule-and-box rig from
ADR-0010: articulated Node3D limbs,
flat StandardMaterial3D colors, and sphere hair/eyes. ADR-0010 accepted this
as a placeholder and recorded that a later skinned-mesh pass would replace the
rig while keeping the Head contract. The props already moved to generated
.glb assets (ADR-0012), but
people remain the lowest-fidelity part of the scene: no UVs, no baked normal or
ambient-occlusion maps, and silhouettes built from capsules.
The project renders with the GL Compatibility backend (no SSAO/SSIL), so visual quality has to come from the assets and baked maps rather than a renderer change. CI must stay offline: the Blender step is a developer tool, not a build dependency.
Decision
- Author the NPC body in Blender and export a skinned, UV-mapped base mesh
as
assets/models/generated/npc_base.glb, driven by a newtools/npc_pipeline.py(build/audit) invoked through the Blender flatpak in headless mode. The.blendsource is committed underassets/models/npc/. - Bake material maps in Blender — neutral albedo, normal, and a packed ORM
(occlusion/roughness/metallic) map per material slot (
skin,cloth,pants,accessory,hair). Ambient occlusion is baked into the albedo or the ORM R channel, since Compatibility has no screen-space AO. - Keep a procedural fallback.
NpcAvatarloads the importedPackedScenewhen it exists and falls back to the current_build_body()rig otherwise, mirroringWorldBuilder._add_prop_model(). Committed assets mean CI never needs Blender, and the headless suite runs against the fallback when the import is absent. - Preserve the
Headcontract and seed determinism. The armature bones are named after the current nodes (Head,HipL/R,KneeL/R,ShoulderL/R,ElbowL/R,HandL/R);outfit_forand friends stay pure and drive the material slots. A thin_set_limb_rotation()wrapper lets_animate()/_idle_layer()target eitherNode3Dpivots orSkeleton3Dbones. - AI tooling supplies art direction only.
tools/asset_pipeline.pygenerates seamless clothing albedos,tools/model_pipeline.py's palette helpers (dominant_colors,assign_roles) derive per-role palettes from concept sheets, andtools/screen_designs.py judgecloses the loop. All AI outputs are committed; generation is dev-time and never a runtime or CI dependency.
Consequences
Positive
- NPCs gain real geometry, UVs, and baked normal/AO/roughness, so they read as people at cinematic distance without a renderer switch.
- Skinned meshes collapse ~33 draw calls per avatar into one, which can offset
the skinning cost at
MAX_LIVE_NPCS = 16. - The asset contract stays behind the same
.glb+ audit discipline that props already use; the procedural fallback keeps offline CI deterministic. - AO baked into the maps compensates for the absence of SSAO in Compatibility.
Negative
- A new toolchain (Blender flatpak +
npc_pipeline.py) and its path/colour-space gotchas enter the repo; contributors must runauditafter changing the.blend. - Skinning shifts cost from draw calls to CPU; the 16-NPC frame budget must be re-measured before raising the cap.
- Two avatar backends (skinned + fallback) must stay behaviourally identical, or tests that assume the fallback will silently pass against one and not the other.
Alternatives considered
- Keep the procedural rig and only add textures. Rejected: textures on capsules do not fix the silhouette; the rig is the fidelity ceiling.
- Use Mixamo / a downloaded skinned character. Rejected: licensing and
retargeting overhead exceed the value; a low-poly base authored in-house keeps
the art direction and the
Head/bone contract under our control. - Switch the renderer to Forward+ for SSAO/SSIL. Rejected as a prerequisite; it would break the Web export assumptions. A desktop-only Forward+ preset remains a follow-up, not a dependency of this decision.
- A 3D-model (image-to-mesh) API instead of Blender. Rejected for characters; output topology is not controllable enough to satisfy the bone/UV contract. It remains a possible prop-only follow-up (ADR-0012).