Files
security-game/docs/3d-model-review.md
T
zwitschi 1d4f718376
Full build / Import + headless tests (push) Successful in 45s
Full build / Full desktop build + optional release (push) Skipped
Narrow the pose-capture discrepancy to the harness and lock bone posing
2026-11-03 11:24:44 +01:00

14 KiB
Raw Blame History

3D model review (2026-11-03)

Status: baseline measured; scorecard open. This is the working document for the unversioned 3D model re-evaluation track in TODO.md. It records what was measured, what the Blender skill guidance says the targets are, the ranked candidate gaps, and the decisions that still need the project owner. Nothing here re-opens the accepted 0.9.4 limits by itself.

Related documents: npc-visual-review.md (accepted 0.9.4 limits and before/after sheets), npc-proportion-audit.md (proportion guide), blender-assets.md (asset contract), asset-tooling.md (commands).

Method and skill routing

The review is screenshot- and measurement-backed, following qa-review (screenshot set, defect severity, explicit verdict) rather than an opinion pass:

Family Skills loaded Criteria taken from them
Routing and pipeline blender-director Plan → blockout → detail → validate → export; MCP steps translated to headless flatpak runs
Characters character-artist, retopology, rigging Eye/mouth/jaw/shoulder loops, quad-dominant deformation surfaces, 3-loop joints, no triangles on primary deformers
UVs and textures uv-workflow, texture-workflow, materials Texel-density tiers (hero 512–1024, standard 256–512, background 128–256 px/m), consistent density, no overlapping UVs
Budgets and structure asset-optimization, lod-pipeline Triangle/vertex budgets, naming, LOD tiers
Props, gear, foliage prop-artist, hard-surface, vegetation-artist Silhouette read, bevel/chamfer language, collision proxies, carded foliage
Look and verdict lookdev, qa-review Day/night beauty captures, ship / ship-with-notes / no-ship verdict

Project contract and gates stay authoritative; see AGENTS.md §Local Blender skills. The skills assume Blender MCP, which this project does not use: everything below came from the headless tools/blender_assets.py / tools/npc_pipeline.py pipelines plus the Godot capture harnesses.

What was captured and measured (2026-11-03)

Evidence Command Result
Contract audit python3 tools/blender_assets.py audit --json 46/46 assets clean; library 59,012 tris
Imported-GLB inspection blender_assets.py inspect (all 46) All clean; transforms applied, helpers ignored
Gear fit python3 tools/blender_assets.py fit --json Head width 0.315 m on all eight; clothing radius 0.190–0.260 m
Proportions python3 tools/npc_proportions.py report --json All eight inside the stylized guide
Texel density python3 tools/blender_assets.py texels --json New measurement, table below
Neutral baseline capture_npc_baseline.gd → tmp/model-baseline (48 frames) + npc_baseline.py sheet 48 frames, none blank; sheet in tmp/ only
Final context capture_npc_review.gd → tmp/model-review (96 frames) 96 frames, none blank; raw frames stay local
Performance smoke_npc_budget.gd 24 avatars, 168 meshes, 131,616 tris, 6.94 ms of 50 ms

Raw captures and JSON stay in the gitignored tmp/; committed review images remain the docs/blender_previews/sheet_*.jpg set from 0.9.4/0.9.5 until a fix actually lands.

Character measurements

Contract numbers from audit + inspect; density from texels (256² slot maps, authored and with the runtime 4× fabric repeat).

Character Tris / verts Height / feet Materials Authored td (px/m) Fabric effective (px/m) Verdict
npc_casual 4,482 / 4,260 1.824 / +0.000 7 106.8–122.2 429–440 Contract pass; visual accepted 0.9.4
npc_jacket 4,872 / 4,662 1.824 / +0.000 7 100.3–113.7 409–413 Contract pass; visual accepted 0.9.4
npc_coat 5,474 / 5,265 1.824 / +0.000 7 75.3–81.4 307–310 Contract pass; visual accepted 0.9.4
npc_vest 4,980 / 4,879 1.819 / +0.000 9 85.5–99.3 362–366 Contract pass; visual accepted 0.9.4
npc_dress 4,900 / 4,719 1.825 / +0.000 7 81.9–89.0 338–339 Contract pass; visual accepted 0.9.4
npc_hoodie 5,304 / 4,950 1.856 / +0.000 7 89.5–98.6 359–364 Contract pass; visual accepted 0.9.4
npc_firefighter 5,344 / 5,494 1.819 / +0.000 9 64.0–75.1 287–290 Contract pass; lowest density
npc_guard 5,046 / 5,096 1.819 / +0.000 11 90.0–100.8 374–374 Contract pass; visual accepted 0.9.4
npc_base (fallback) 1,204 / 2,488 1.836 / +0.000 5 70.1–72.9 4× on fabric Contract pass; far below its 6,000/4,000 cap

Blender-side inspect counts can differ by ±1 vertex from the glTF accessor count in audit (normal-split rounding); both are recorded and neither changes a verdict.

Readings:

  • Every character is inside 8,000/8,000 with the farthest at 5,474 tris (68% of budget) and 5,494 verts (69%), so there is headroom for targeted topology detail without a cap change.
  • Surface area tracks clothing volume: the firefighter coat is the largest body (4.28 m²) and has the lowest authored density; npc_casual is the smallest (2.44 m²) and the highest.
  • Authored density (64–122 px/m) sits below the skill's background tier (128–256 px/m). The runtime tint controls most of the look: skin, hair, accessory and the fixed detail slots are neutral maps tinted at runtime, not painted skin, so density mostly controls subtle weave/noise. Fabric is effective 287–440 px/m (standard tier) because of the 4× repeat. This is a recorded note, not a defect, until a portrait shows texture starvation.
  • Material counts 7–11 per character are fixed detail slots (eyes, rubber, hivis, reflective, leather, metal), not extra tint slots.

Scorecard skeleton

Verdicts use qa-review semantics: P0 (blocker: wrong scale, broken normals, budget fail), P1 (major: silhouette/readability), P2 (minor), accept (recorded limit). — means not yet reviewed; this document is the place to fill it.

Characters

Character Silhouette Face @1.85 m Hands/grip Weights/pose Cloth/hair LOD Verdict
(pose evidence blocked: see "Pose evidence is blocked" below)
npc_casual accept (0.9.4) P1 (faceted planes) accept (open palm) blocked accept accept (two-quad impostor) pending owner
npc_jacket accept P1 accept blocked accept accept pending owner
npc_coat accept P1 accept blocked accept (hem 0.76 stride) accept pending owner
npc_vest accept P1 accept blocked accept accept pending owner
npc_dress accept; performer props dominate P1 accept blocked accept accept pending owner
npc_hoodie accept (hooded criterion) accept (lower face only) accept blocked accept accept pending owner
npc_firefighter accept P1 accept blocked accept accept pending owner
npc_guard accept P1 accept blocked accept accept pending owner

Gear (18)

Gear Attach Tris Verdict
cap head 328 — (fit measured, visual pending)
beanie head 460 — (unused by any character)
hood head 804 —
sunglasses head 164 accept (0.9.4 clearance)
firefighter_helmet head 672 accept (0.9.4 + 3 cm runtime offset)
backpack back 380 accept (0.9.4 strap refit)
shoulder_bag back 188 —
scba_tank back 362 —
lanyard chest 100 accept (0.9.4 + 4 cm runtime offset)
phone hand 96 accept (forward offset)
cup hand 188 accept
clipboard hand 84 accept
umbrella hand 134 accept
camera hand 318 accept
protest_sign hand 92 accept (performer/protester read)
flashlight hand 168 accept
wand hand 244 accept
radio hand 220 accept

Gear total: 5,002 tris across 18 pieces (all far below the 1,500-per-piece cap).

Props (20 library + 6 generated fallbacks)

Library total 13,608 tris; marquee (2,804) and street_tree (2,592) are the two heaviest and are the first candidates for a vegetation-artist/lookdev pass.

Prop Tris Verdict
bollard 138 —
bench 560 —
trash_bin 228 —
planter_box 1,324 —
newspaper_box 244 —
fire_hydrant 466 —
street_lamp 428 —
street_tree 2,592 P1 candidate (flat-shaded clumps)
palm_tree 400 P1 candidate (same foliage language)
round_planter 1,240 —
a_frame_sign 212 —
wall_lantern 180 —
traffic_cone 210 —
crowd_barrier 388 —
stanchion 434 —
handrail 268 —
security_podium 184 —
double_door 380 —
marquee 2,804 — (emissive check pending)
sedan 928 —

Generated fallbacks (assets/models/generated/): bench, bollard, fire_hydrant, newspaper_box, planter_box, trash_bin. They are only used when the library prop is missing, so they are audited for presence, not re-reviewed for art quality.

Venues

Venue Measured (map-budget test) Status
Hollywood Boulevard 58,884 tris / 409 meshes shape-language review pending
The Joke Shop 22,680 tris / 454 meshes shape-language review pending
TV Studio — shape-language review pending

Pose evidence is blocked (2026-11-03)

The weights/pose column cannot be filled yet. The pose logic is verified; the capture is not. src/test/capture_npc_pose.gd stages idle / walk-contact / walk-pass / seated for all eight variants (full-body + shoulder views) and its output does not match the state it reports.

Check Result
Pose reaches proxies Seated pose logs rig_y=-0.339, leg0=-1.357
Pose reaches skeleton bones Skeleton3D.get_bone_pose_rotation(HipL/KneeL) differs from rest; new headless test test_pose_reaches_the_skeleton_bones locks it (suite 273/273)
Idle restores rest Same test asserts the hip returns to its rest orientation
Skinning bound One Skeleton3D, mesh bound to it (same=true); hiding npc_base removes the body
Bone rotations deform the mesh Direct writes change rendered pixels: Spine 1,719 / HipL 1,052 / KneeL 810 / ElbowL 745 / Head 671 / ShoulderL 640 sampled pixels
Bone translation deforms the mesh A +0.5 m Head offset moves the head in frame
Standalone probes render the pose Three variants (avatar under root, under a Node3D stage, created mid-_process) all render the seated crouch (posed vs rest diff ≈ 2,200–2,360 samples)
The harness renders the pose No. All 8 variants × 4 poses save the rest pose while the logged state and bone poses are posed

The cause is therefore in the harness's capture path, not in the rig or the animation math. Two leads are left and both are cheap: reproduce the stage inside a harness that is known to render correctly (capture_npc_review.gd already produces 96 valid frames), or capture through a SubViewport with RenderingServer.force_draw() so the frame cannot lag the state. Until a scene test asserts that a posed avatar renders differently from its rest frame, do not treat pose sheets as evidence and do not claim rig deformation in the review.

Ranked candidate gaps

Ordered by expected player-visible gain per unit of work. None is approved for fixing yet; the owner review decides.

  1. Face plane structure at portrait distance (P1, all eight). The neutral sheet shows flat, faceted cheek/brow planes and slit-like eyes at 1.85 m. character-artist/retopology guidance asks for concentric eye loops and a 3-loop mouth. This is the largest readability gap and also the most expensive (head rebuild + re-UV + re-AO per variant). Needs a targeted single-variant prototype before committing to eight.
  2. No pose/weight evidence (unknown). Idle, walk, sit, block and turn deformation has never been captured as a review set, so shoulder/hip/neck collapse is unmeasured. Cheap to add and it gates any claim that the rig is acceptable.
  3. Foliage language (P1, street_tree, palm_tree, planter_box, round_planter). Flat-shaded clumps against an otherwise readable set, already deferred once. vegetation-artist carded clusters would fix it inside the existing triangle budget.
  4. Texel density below the background tier (note). 64–122 px/m authored vs the skill's 128–256 background tier. Fabric is already standard-tier at runtime. Decide: accept because the maps are neutral tinted detail, or raise only skin/hair to 512² for portrait distance.
  5. npc_firefighter density floor (P2). 64 px/m with the largest coat area; split the coat UV shell or raise the map size before the next character pass, whichever the density decision says.
  6. Hands at conversation distance (accept, revisit only if face work opens the budget). Open palm with HAND_ITEM_FORWARD; no finger bones exist, so a curl rig is a contract decision, not a modelling tidy-up.
  7. Gear never reviewed as silhouette language (P2). 18 pieces pass fit/contract but have no prop-artist verdict; beanie is unused and could be retired or reassigned.
  8. Venue shape language (P2). Hollywood and the Joke Shop have budget data but no environment-artist/archviz review; the TV Studio has no recorded map-budget number in this document yet.

Decisions needed from the owner

  • Accept the measured texel density as a stylized limitation, or fund a skin/hair resolution pass.
  • Approve or reject a single-variant face/eye-loop prototype (and pick which variant to prototype on) before any eight-character rebuild.
  • Decide whether the pose/weight capture is required before the crowd-variant roster.
  • Confirm foliage rebuild priority versus the map track (both touch the same street trees).

Next actions

Tracked in TODO.md 3D model re-evaluation: the remaining A. Baseline… item is this owner review; B/C/D carry the per-family work, and E adds the pose/weight, collider-coverage and scorecard-coverage gates this document found missing.