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>
11 KiB
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.
record_crateFIXED (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/recordBinnow upgrade to plastic crates with the procedural sleeves inside (was fail-soft to primitive). Removed the stale comment.counter_tillMAPPED (counter → 'counter_till'inKIND_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_tillcarries its own till, the separatecash_registercounter-top attach is dropped whenever the counter GLB is live (falls back to cash_register on the primitive counter ifcounter_tillis ever absent).milkshake_mixerstays 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 isrecord_crate+counter_tillcache-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.
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 mapcounterdirectly. 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_*.glblocally (web/assets/_local_glb/, copied frompipeline/_normalized/) and pointedglb.jsat them via a newmanifest.localBase(loads before/without the depot, per the round-3 instruction). Production still usesdepot:<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 measuredminY≈0;facing("Lane B/C rotate directional props") — Lane C now applies per-kind yaw inglb.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)
procity_fit_record_crate_01.glb— BROKEN, will not load.GLTFLoader.loadAsyncthrowsTypeError: Cannot read properties of undefined (reading 'uri'). The file has valid glTF magic and is 858 KB, but references an undefineduri(missing embedded image/buffer, or an extension the shipping barecore/loaders.jsGLTFLoader 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).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-addcountertoKIND_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.