PROCITY/docs/LANES/LANE_C_GLB_VALIDATION.md
m3ultra 5671a06aef Lane C R5 C3: re-measure interiors after Lane E's hero-prop bake
E baked the 6 heavy hero props to <=8k tris (c522464). Re-measured every
interior GLB-on (cache-busted to bypass the dev browser's stale HTTP cache;
production clients fetch baked directly):
- record 43.6k tris (C3 target, ~273k pre-bake), draws 322
- milkbar 38.9k tris (was 468k — the R4 over-budget flag is now CLOSED)
- all rooms <=107k tris; book is the draw-call outlier (1245 draws from ~9
  bookshelf GLBs + ~600 primitive spines) — tris fine, interiors render
  one-at-a-time so gate 3 unaffected; instancing spines is a future lever.
Full table in docs/LANES/LANE_C_GLB_VALIDATION.md. Round 5 complete for Lane C.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 21:16:15 +10:00

158 lines
11 KiB
Markdown
Raw Permalink 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 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.
### C3 — interior re-measure after Lane E's hero-prop bake (`c522464`)
E baked the 6 heavy hero props to ≤8k tris (listening_booth 53k→8k, drinks_fridge 77k→8k,
milkshake_mixer 73k→8k, arcade_cabinet 25k→8k, counter_till 32k→8k; record_crate/cash_register were
already ≤6k). **This closes the R4 milk-bar over-budget flag.** Per-room GLB-on triangles, single
interior, seed 1990 (measured with fitting URLs cache-busted to bypass this dev browser's stale HTTP
cache — production clients fetch the baked versions directly):
| room | R4 GLB-on | R5 post-bake | draws |
|---|---|---|---|
| **record** | 115,867 (record_crate was broken) | **43,621** | 322 |
| **milkbar** | **468,106** (the flagged room) | **38,901** | 573 |
| video | 61,669 | 21,361 | 458 |
| book | 102,998 | **106,950** | **1,245** |
| opshop | 58,230 | 62,182 | 702 |
| pawn / toy / dept / stall | ≤58k | 9k / 28k / 37k / 24k | ≤512 |
Every interior is now well within a sane single-room budget (≤107k tris). The record store — the C3
target, quoted as ~273k pre-bake — is **43.6k** post-bake. Remaining note: `book` is the draw-call
outlier (1,245 draws) from ~9 bookshelf GLBs + the ~600 primitive spine meshes; tris are fine (107k),
and interiors render one-at-a-time (Lane F swaps scenes, the street isn't drawn behind them), so this
does not affect gate 3. If draw calls ever matter for interiors, instancing the spines is the lever —
noting for a future round, not fixing now.
---
## 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).