PROCITY/docs/LANES/LANE_C_NOTES.md
m3ultra 35981ade5f Lane C R6 C1: interior draw batching (<=350 draws/room law)
Review debt: book room was 1,245 draws GLB-on. Diagnosed two drivers —
multi-material stock boxes (one draw per face, 6x) and one mesh per stock
item — then batched:

- stock.js: stock boxes are single-material now; every stock mesh tagged
  userData.isStock.
- batch.js (new): batchRoom() two-tier merge, called from interiors.js before
  the GLB upgrade. Room-level merge of all isStock across every fitting
  (grouped by material, world transforms baked) + per-fitting merge of fixture
  frames (kept in-group so record bins stay raycast targets for ?dig=1 and the
  GLB upgrade can hide the fixture). Uses vendored BufferGeometryUtils
  mergeGeometries.
- glb.js: frame-hide is now by isStock tag (not frame-count), so batched
  fixtures hide while the room-level stock rides on the GLB.
- interior_test.html: drawSweep() asserts <=350 draws across all type x
  archetype (GLB off and on), folded into the soak; exposed as
  window.PROCITY_C.drawSweep for F's harness (hook in LANE_C_NOTES).

Result (drawSweep, seed 1990): GLB-off worst 152, GLB-on worst 313
(opshop/hall) — book 1,245 -> 189. Determinism 0 fails / 810, leak-free, dig
still riffles batched bins, worst warm build 38ms (<50), visually identical.
qa.sh --strict GREEN (5 gates).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 23:01:04 +10:00

3.2 KiB
Raw Blame History

LANE C — cross-lane notes (PROCITY-C)

→ Lane F: interior ≤350-draw assertion for the harness (round-6 C1)

C1 batching landed — worst room is now 313 draws GLB-on / 152 GLB-off (was 1,245). The test page exposes the assertion for your harness:

  • window.PROCITY_C.drawSweep({ glb: true|false }){ worst, worstAt, pass, law: 350, perType }. Builds every shop type × archetype at seed 1990, renders each, returns the worst renderer.info.render.calls. It's already folded into the interior soak's PASS/FAIL. For your smoke/soak gate, call it with glb:true (the tighter case) and assert pass (worst <= 350). Headless-safe, ~1s.
  • The draw law is a Lane-C constant (DRAW_LAW = 350 in interior_test.html) so it's one number to bump if the budget ever changes. Ping me if you'd rather it read from a shared config.

→ Fable / Lane F: v1.1 is ready (round-5 C1 done)

C1 committed. Lane E's re-exports are mapped + validated in web/js/interiors/glb.js:

  • record_crate (fixed) → record bins/crates upgrade to plastic crates with the procedural sleeves.
  • counter → counter_till → the real ~1.6 m counter-with-till drops in; footprint-fit + floor-plant unchanged; keeper-stand pose still valid (it's a room-local pose, not tied to the counter geometry).
  • No double till: my separate cash_register counter-top attach is dropped whenever counter_till is live (that GLB has its own till). milkshake_mixer still on the milk-bar bench.
  • GLB soak: 0 throws / 0 path / 0 determinism / leak-free / worst 10.4 ms, 0 rooms with a visible primitive till. Primitive path byte-unchanged (C1 touched only glb.js). qa.sh --strict green.

→ You can tag v1.1 per ROUND5 §Lane C acceptance ("bins + counter GLB-live, no double till, soak green → F tags v1.1"). Evidence: docs/shots/laneC/glb_v1_1_r5.jpg, report in LANE_C_GLB_VALIDATION.md.

→ Lane F: input/mode hook I need for ?dig=1 (round-5 C2)

The crate-riffle (dig) is interior-mode-only and needs the shell to tell an interior when the player interacts with a fitting. Proposed seam (you own the shell/interior_mode.js):

  • On the player pressing the interact key while aimed at a fitting whose userData.kind === 'bin' (record bins expose places:[group] with userData = { kind:'bin', interactable:true }), call a hook the interior optionally provides: room.onInteract?.(hitObject) — where room is the object returned by buildInterior(...). If it returns truthy, the shell should suppress normal movement / pointer-lock until the interior calls back that the dig closed (I'll expose room.digActive + fire a procity:digClosed event, or a callback you pass in — your call on the exact contract).
  • Gate the whole thing on ?dig=1 (I read the flag inside the interiors lib; when off, onInteract is absent and nothing changes — flag-off boot stays byte-identical).

I'm building the dig against my own test page (web/interior_test.html) with a direct click handler in the meantime, so it's testable without the shell. Tell me if you'd prefer a different hook shape and I'll match it. — PROCITY-C