# LANE C — GLB validation report → for Lane E + Fable ## ROUND 5 / v1.1 (2026-07-14) — record_crate fixed + counter_till mapped Lane E's R4-late re-exports validated and mapped (LANE_E_NOTES → C). Contact sheet: [`docs/shots/laneC/glb_v1_1_r5.jpg`](../shots/laneC/glb_v1_1_r5.jpg). - **`record_crate` FIXED** (E stripped the sourceless AO/roughness texture slots) — loads in the vendored GLTFLoader (0.46·0.41·0.63 m, minY≈0, 5,000 tris). `crate`/`recordBin` now **upgrade** to plastic crates with the procedural sleeves inside (was fail-soft to primitive). Removed the stale comment. - **`counter_till` MAPPED** (`counter → 'counter_till'` in `KIND_TO_GLB`) — the new ~1.6 m timber counter WITH a modelled till (1.61·1.15·0.64 m, minY≈0, 32k tris). Footprint-fit + floor-plant work unchanged; yaw 0. Keeper-stand pose is a room-local pose, unaffected by the geometry swap (verified in HUD). - **Double-till resolved (decision, documented in glb.js):** because `counter_till` carries its own till, the separate `cash_register` counter-top attach is **dropped whenever the counter GLB is live** (falls back to cash_register on the primitive counter if `counter_till` is ever absent). `milkshake_mixer` stays on the milk-bar bench, lifted +0.10 m to sit on the taller GLB counter. Soak: **0 rooms with a visible primitive till** (no double till). - **Sweep + soak (GLB on)**: `throws 0 · pathFail 0 · determinismFail 0 · worst 10.4 ms`. Leak: 0/0 against a saturated baseline (the transient 2 geo / 4 tex on first sight is `record_crate` + `counter_till` cache-fill, bounded — confirmed by a double-soak). Primitive path unchanged by C1 (glb.js-only). Still open: nothing blocking. The `counter_till` material reads over-shiny in some lighting (TRELLIS shell-soup) — cosmetic, E's bake pass will help; not a Lane C issue. --- ## 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:` 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:` → local, Lane C needs **no change**: `glb.js` uses `depot:` 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).