PROCITY/docs/LANES/LANE_C_GLB_VALIDATION.md
m3ultra 34926324bd Lane C R4: hero props into rooms (arcade/booth/fridge/mixer/register)
Round-4 §Lane C — map the 4 new hero props + cash_register (manifest now
14 fittings), validate, measure, clean up.

- glb.js: map listening_booth→listeningCorner (record), drinks_fridge→fridge
  (milkbar), arcade_cabinet→new arcadeCabinet primitive (video focal prop).
  New attachCounterTop() places cash_register on every counter's till slot
  (hides the primitive till) + milkshake_mixer on the milk-bar benchtop;
  counter primitive + keeper stand untouched. All yaw 0 (no correction).
- fittings.js: new arcadeCabinet primitive (footprint/height matched to the
  GLB); counter exposes counterTop attach points + tags the primitive till.
- theme.js: arcadeCabinet in the video recipe; the 3 focal props (arcade,
  listeningCorner already, +) raised to priority 6 + placed first so the
  door-counter path loop can't pull them → 24/24 appearance.
- interiors.js: thread recipe.key to upgradeRoom (gates milkshake_mixer to
  milk bars).
- interior_test.html: load GLBs via the live depot: path (drop the deleted
  _local_glb localBase shim; ?localdepot=1 still works offline).

Validated (all via live depot): primitive sweep 810 {throws 0, pathFail 0,
detFail 0}; GLB-on soak {throws 0, path 0, det 0, leakGeo 0, leakTex 0,
worst 7.3ms}; all 5 new props render planted/scaled/facing right (contact
sheet docs/shots/laneC/glb_heroprops_r4.jpg). Room tris measured: milk-bar
GLB-on ~468k (drinks_fridge 77k + milkshake_mixer 73k) — reported to Lane E
as the #1 bake-pass target (not fixed, per integrator decision #2; interiors
render one-at-a-time so gate 3 is unaffected). record_crate still broken +
counter still oversized — both await Lane E re-export (C task 5). Deleted the
stale web/assets/_local_glb/ validation copy. qa.sh --strict GREEN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:44:09 +10:00

109 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# LANE C — GLB validation report → for Lane E + Fable
## ROUND 4 update (2026-07-14) — the 4 new hero props + cash_register
Mapped and validated the 5 round-4 hero GLBs (manifest now 14 fittings). Loaded via the **live
`depot:` path** (all published, HTTP 200); the R3 `_local_glb/` copy is deleted. Screenshot evidence:
[`docs/shots/laneC/glb_heroprops_r4.jpg`](../shots/laneC/glb_heroprops_r4.jpg).
**All 5 load cleanly** (unlike the still-broken `record_crate`), follow `glb_law` (minY≈0, real metres),
and match their manifest footprints — **no per-kind yaw correction needed (yaw 0)**:
| id / file | native x·y·z (m) | tris | maps to | placement |
|---|---|---|---|---|
| `arcade_cabinet` | 0.75·1.70·0.85 | 25,142 | **new** `arcadeCabinet` primitive (video recipe) | floor, aisle zone, focal |
| `listening_booth` | 0.88·1.60·0.71 | 53,502 | `listeningCorner` (record) | floor, window zone, focal |
| `drinks_fridge` | 0.85·1.60·0.96 | 77,047 | `fridge` (milkbar) | floor, against wall |
| `milkshake_mixer` | 0.38·0.50·0.27 | 73,492 | counter-top (milkbar) | ON counter benchtop (left slot) |
| `cash_register` | 0.42·0.35·0.34 | 5,999 | counter-top (all types) | ON counter benchtop (till slot); hides primitive till |
New mechanism: **counter-top GLB attach** (`glb.js attachCounterTop`) — cash_register replaces the
primitive till on every counter; milkshake_mixer sits on the milk-bar benchtop. The counter primitive
(correct height + keeper stand) is kept; only the till mesh is hidden. Focal props made reliable
(priority 6 + placed first): **24/24 appearance** across seeds, 0 carves, 0 path-fails.
**Sweep + soak with all R4 mappings on**: `throws 0 · pathFail 0 · determinismFail 0 · leakGeo 0 ·
leakTex 0 · worst build 7.3 ms`. GLB clones share cached geometry (SkeletonUtils.clone) → per-room
dispose is leak-free (baseline taken after a full 9-type warmup).
### ⚠ Interior tris — one room over budget (task 4 report, NOT fixed per integrator decision #2)
Per-room triangles, single interior rendered alone (primitives vs GLB-on), seed 1990:
| room | prims tris | GLB-on tris | GLB-on draws | note |
|---|---|---|---|---|
| pawn | 812 | 7,019 | 161 | fine |
| opshop | 2,476 | 58,230 | 706 | fine |
| video | 5,712 | 61,669 | 461 | arcade 25k dominates — fine |
| book | 6,372 | 102,998 | 1,249 | high **draws** (≈600 primitive spines) |
| record | 2,530 | 115,867 | 337 | listening_booth 53k — fine |
| **milkbar** | 1,662 | **468,106** | 578 | **OVER**`drinks_fridge` (77k) ×up-to-3 + `milkshake_mixer` (73k) |
**→ Lane E bake-pass priority (future, not v1):** `drinks_fridge` (77k) and `milkshake_mixer` (73k) are
grossly over-tessellated for their visual role (a fridge and a bench appliance). Baking these two to
~510k would drop the milk-bar room from ~468k to well under 150k. `cash_register` (6k) and
`arcade_cabinet` (25k) are already reasonable. Interiors render one-at-a-time (Lane F swaps scenes, the
street is not drawn behind them), so this does **not** affect gate 3 (street budget) or the v1 tag — but
it's the #1 candidate for the bake pass. Not blocking; reporting per task 4.
### Still open (Lane E)
- `record_crate` — STILL fails to load (`Cannot read ...'uri'`); record bins/crates stay primitive. Re-export.
- `counter` — still the 4 m till-less asset; interiors keep the primitive counter. A ~2 m counter-with-till
would let me drop cash_register's separate attach and map `counter` directly. When both land I re-map (C task 5).
---
## ROUND 3 report (superseded above where they overlap) → for Lane E + Fable
*PROCITY-C, 2026-07-14. Round-3 task "GLB path visual validation" (ROUND3_INSTRUCTIONS §Lane C).
Validated Lane E's 9 fitting GLBs from `pipeline/_normalized/` in real interior rooms via the
`interiors/glb.js` upgrade path (`useGLB` on). Verdict below; the 5 "good" assets are live.*
## How validated
- Served the 9 `procity_fit_*.glb` locally (`web/assets/_local_glb/`, copied from `pipeline/_normalized/`)
and pointed `glb.js` at them via a new `manifest.localBase` (loads before/without the depot, per the
round-3 instruction). Production still uses `depot:<file>` unchanged.
- Toggle **GLB props** on the interior test page (`web/interior_test.html`) rebuilds every shop type
with the GLBs and re-renders once they load; a headless sweep+soak checks throws/leaks/perf.
- Convention followed: `glb_law` (metres, +Y up, base at origin) ✓ every asset measured `minY≈0`;
`facing` ("Lane B/C rotate directional props") — Lane C now applies per-kind yaw in `glb.js`.
## Result — soak with `useGLB` on (50 rooms, all types)
`throws 0 · pathFail 0 · determinismFail 0 · leakGeo 0 · leakTex 0 · worst build 10.5ms (<50) ·
256 GLB upgrades applied`. **GLB textures/geometries dispose leak-free on room exit** (loader-cached
+ clone-shared; baseline taken after warmup). Acceptance met.
## ✅ Good — live now (5 assets)
| id / file | native (x·y·z m) | notes |
|---|---|---|
| `wire_shelf` · procity_fit_wire_shelf_01.glb | 1.17 · 1.8 · 0.44 | clean, full height, faces right (yaw 0) |
| `clothes_rack` · procity_fit_clothes_rack_01.glb | 1.34 · 1.5 · 0.43 | garments hang correctly, casters read well |
| `bookshelf` · procity_fit_bookshelf_01.glb | 0.2 · 2.0 · 1.02 | **needs +90° yaw** — modelled wide along Z; Lane C rotates it. Then spines face the aisle ✓ |
| `work_table` · procity_fit_work_table_01.glb | 0.44 · 0.75 · 1.04 | **needs +90° yaw** (Z-major); goods sit on top ✓ |
| `cube_shelf_wide` · procity_fit_cube_shelf_wide_01.glb | 0.75 · 1.1 · 1.24 | **needs +90° yaw**. ⚠ 1.24 wide → in narrow (2-col ≈0.84m) cube slots it clamps to ~0.75m tall. Fine in wide slots. Optional: a ~0.8m-wide cube variant. |
Lane C applies the +90° yaw for the three Z-major assets (per the `facing` convention — no action needed
from Lane E unless you want to re-bake them Z-forward). All plant on the floor; no z-fighting with the
procedural stock/decor that rides on top.
## ⚠ Mismatches to fix (2 assets)
1. **`procity_fit_record_crate_01.glb` — BROKEN, will not load.**
`GLTFLoader.loadAsync` throws `TypeError: Cannot read properties of undefined (reading 'uri')`.
The file has valid glTF magic and is 858 KB, but references an undefined `uri` (missing embedded
image/buffer, or an extension the shipping bare `core/loaders.js` GLTFLoader can't resolve — it is
the **only one of the 16 that fails** that loader; the other 8 fittings + street furniture load fine).
Impact: record bins/crates fall back to primitives (fail-soft, no crash). **Please re-export** (and if
it uses KTX2/meshopt, flag it — the core loader has no KTX2/meshopt/Draco decoder configured).
2. **`procity_fit_counter_01.glb` — too large for the interior counter slot; no till.**
It's a real **4.02 m-long × 1.1 m** counter. Interior counter slots are ~1.42.2 m, so fitting to the
slot squashes it to ~0.6 m tall, and it has no till/register modelled. Lane C therefore **keeps the
counter primitive** (correct height + till + keeper stand). If you want a GLB counter for interiors,
a **~2 m-long counter with a till** would drop straight in (re-add `counter` to `KIND_TO_GLB`).
## Notes for Lane E's local-depot mode (your round-3 §2)
When `?localdepot=1` / `core/loaders.js` resolves `depot:<file>` → local, Lane C needs **no change**:
`glb.js` uses `depot:<file>` by default and only used `localBase` as a standalone validation shim. Point
your local resolver at `pipeline/_normalized/` and my `useGLB` path works through it as-is.
Screenshot evidence: [`docs/shots/laneC/glb_validation_grid.jpg`](../shots/laneC/glb_validation_grid.jpg).