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>
109 lines
7.6 KiB
Markdown
109 lines
7.6 KiB
Markdown
# 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
|
||
~5–10k 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.4–2.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).
|