Lane C R5 C1: v1.1 re-map — record_crate (fixed) + counter_till

Round-5 §Lane C task 1 (the v1.1 gate). Map Lane E's R4-late re-exports in
glb.js:

- record_crate FIXED (E stripped sourceless AO/roughness slots) → crate/
  recordBin upgrade to plastic crates with the procedural sleeves inside;
  removed the stale "fails to load" comment.
- counter → 'counter_till' (the real ~1.6m timber counter WITH a modelled
  till; NOT the 4m balcao). Footprint-fit + floor-plant + keeper-stand pose
  unchanged.
- Double-till resolved (documented): counter_till carries its own till, so
  the separate cash_register attach is dropped whenever the counter GLB is
  live (falls back to cash_register on the primitive counter otherwise);
  milkshake_mixer stays on the milk-bar bench, lifted +0.10m onto the taller
  GLB counter.

Validated (GLB on, via depot): sweep+soak {throws 0, path 0, det 0, worst
10.4ms}, 0 rooms with a visible primitive till, leak-free vs a saturated
baseline (the transient 2geo/4tex first-sight is record_crate + counter_till
cache-fill). Primitive path byte-unchanged (glb.js-only edit). qa.sh --strict
GREEN. Contact sheet docs/shots/laneC/glb_v1_1_r5.jpg. → F can tag v1.1
(LANE_C_NOTES.md).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-14 20:56:00 +10:00
parent 5485d67ae8
commit aa9c140eca
5 changed files with 100 additions and 16 deletions

View File

@ -3,7 +3,23 @@
*Status: **v1 complete & verified**. Standalone interiors library + test page. Every shop door opens
into a unique, seeded, themed interior, generated on demand in ~4ms, byte-identical every revisit.*
Last updated: 2026-07-14 (round 4) · owner: PROCITY-C · reviewer: Fable
Last updated: 2026-07-14 (round 5) · owner: PROCITY-C · reviewer: Fable
---
## Update 2026-07-14 (round 5) — C1 v1.1 re-map (record_crate + counter_till) · **F can tag v1.1**
Round-5 §Lane C task 1 (the v1.1 gate). Lane E's re-exports mapped + validated in `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). Keeper-stand pose unaffected.
- **Double-till resolved**: cash_register attach dropped when counter_till is live (it has its own till);
falls back on the primitive; milkshake_mixer stays on the milk-bar bench (lifted to the GLB height).
- Sweep + soak (GLB on): `0 throws / 0 path / 0 det / leak-free / worst 10.4ms`; **0 double tills**.
Primitive path unchanged (glb.js-only edit). Contact sheet: [glb_v1_1_r5.jpg](docs/shots/laneC/glb_v1_1_r5.jpg).
- Report + decision detail: [LANE_C_GLB_VALIDATION.md](docs/LANES/LANE_C_GLB_VALIDATION.md); F note in
[LANE_C_NOTES.md](docs/LANES/LANE_C_NOTES.md). **→ F: C1 committed, v1.1 can be tagged.**
Still in flight this round: C2 `?dig=1` crate-riffle (v2, behind flag), C3 record re-measure (after E's bake).
---

View File

@ -1,5 +1,30 @@
# 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

View File

@ -0,0 +1,34 @@
# LANE C — cross-lane notes (PROCITY-C)
## → 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`](../shots/laneC/glb_v1_1_r5.jpg),
report in [`LANE_C_GLB_VALIDATION.md`](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

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -13,18 +13,19 @@ import { loadGLB } from '../core/loaders.js';
import { clone as skeletonClone } from 'three/addons/utils/SkeletonUtils.js';
// my fitting kind → manifest.fittings id (only kinds with a depot GLB; others stay primitive).
// NOTE: `counter` is intentionally NOT mapped — the counter GLB (procity_fit_counter_01.glb) is a real
// 4.0 m-long × 1.1 m asset that squashes to ~0.6 m when fit to the ~2.2 m interior counter slot and has
// no till modelled; the primitive counter (correct height + till + keeper stand) reads better. Reported
// to Lane E (see C-progress.md / LANE_C_GLB_VALIDATION.md). Re-map here once a ~2 m counter asset lands.
// NOTE: `counter` → `counter_till` (R5): Lane E shipped a proper ~1.6 m timber counter WITH a modelled
// beige till (procity_fit_counter_till_01.glb, base-origin). The old `counter` id is the 4 m till-less
// balcao — do NOT use it here. Because counter_till carries its own till, the separate cash_register
// counter-top attach is dropped whenever the counter GLB is live (see attachCounterTop) — no double till.
const KIND_TO_GLB = {
crate: 'record_crate', recordBin: 'record_crate', // record_crate GLB currently fails to load → fail-soft to primitive (reported)
crate: 'record_crate', recordBin: 'record_crate', // R5: record_crate fixed by Lane E (sourceless-texture slots stripped) → bins upgrade now
metalShelf: 'wire_shelf', wallShelf: 'wire_shelf',
clothesRack: 'clothes_rack',
bookshelf: 'bookshelf',
cubeShelf: 'cube_shelf_wide',
trestleTable: 'work_table',
// Round-4 hero props (manifest now 14 fittings):
counter: 'counter_till', // R5: real counter-with-till (NOT the 4 m 'counter'/balcao)
// Round-4 hero props:
listeningCorner: 'listening_booth', // record store focal prop
fridge: 'drinks_fridge', // milk bar drinks fridge
arcadeCabinet: 'arcade_cabinet', // video store focal prop
@ -42,11 +43,12 @@ const KIND_TO_YAW = {
listeningCorner: 0,
fridge: 0,
arcadeCabinet: 0,
counter: 0, // R5 counter_till — tuned on-screen
};
// Counter-top GLB props: sit ON the primitive counter's benchtop (not a floor swap). Each targets a
// counterTop attach slot exposed by the counter fitting. cash_register replaces the primitive till on
// every counter; milkshake_mixer only on milk-bar counters.
// Counter-top GLB props: sit ON the counter's benchtop (not a floor swap). Each targets a counterTop
// attach slot exposed by the counter fitting. cash_register is the till stand-in on a PRIMITIVE counter
// (dropped when counter_till is live — that GLB has its own till); milkshake_mixer only on milk-bar counters.
const COUNTERTOP = [
{ id: 'cash_register', slot: 'till', hideTill: true, types: null, yaw: 0, maxFoot: 0.42 },
{ id: 'milkshake_mixer', slot: 'appliance', hideTill: false, types: ['milkbar'], yaw: 0, maxFoot: 0.42 },
@ -98,14 +100,19 @@ export function upgradeFitting(ctx, fitting, kind, manifest) {
}
// Place counter-top GLB props (cash_register, milkshake_mixer) onto a counter fitting's benchtop.
// Additive: does NOT hide the counter primitive (correct height + keeper stand stay); cash_register
// hides just the primitive till it replaces. Fail-soft: unreachable/missing GLB → primitive stays.
export function attachCounterTop(ctx, fitting, manifest, shopType) {
// Additive: does NOT hide the counter primitive frame (upgradeFitting does that when counter is GLB-live).
// `counterHasGlb`: when the counter itself upgrades to counter_till (which carries its OWN modelled till),
// cash_register is SKIPPED here to avoid a double till — it only stands in on the primitive counter. The
// benchtop surface is ~0.10 m higher on the taller GLB counter, so counter-top items are lifted to match.
// Fail-soft: unreachable/missing GLB → primitive stays.
export function attachCounterTop(ctx, fitting, manifest, shopType, counterHasGlb) {
const ct = fitting.counterTop;
if (!ct || !manifest || !manifest.fittings) return Promise.resolve();
const topY = ct.y + (counterHasGlb ? 0.10 : 0); // GLB counter_till (~1.13) sits above the primitive (~1.03)
const jobs = [];
for (const spec of COUNTERTOP) {
if (spec.types && !spec.types.includes(shopType)) continue;
if (spec.id === 'cash_register' && counterHasGlb) continue; // counter_till has its own till → no double
const entry = manifest.fittings[spec.id];
if (!entry || !entry.file) continue;
const ref = manifest.localBase ? `${manifest.localBase}${entry.file}` : `depot:${entry.file}`;
@ -120,8 +127,8 @@ export function attachCounterTop(ctx, fitting, manifest, shopType) {
const s = Math.min(1, spec.maxFoot / glbW); // clamp to the benchtop slot; never inflate
inst.scale.setScalar(s);
const at = ct[spec.slot];
inst.position.set(at.x, ct.y - bb.min.y * s, at.z); // plant base on the benchtop surface
if (spec.hideTill) for (const c of fitting.group.children) if (c.userData && c.userData.tillPrimitive) c.visible = false;
inst.position.set(at.x, topY - bb.min.y * s, at.z); // plant base on the benchtop surface
if (spec.hideTill && !counterHasGlb) for (const c of fitting.group.children) if (c.userData && c.userData.tillPrimitive) c.visible = false;
inst.userData = { glbUpgrade: true, kind: spec.id, counterTop: true };
fitting.group.add(inst);
}));
@ -134,11 +141,13 @@ export function attachCounterTop(ctx, fitting, manifest, shopType) {
// Returns a promise resolving once all in-room fittings' GLB swaps have settled.
export function upgradeRoom(ctx, placed, manifest, shopType) {
if (!manifest) return Promise.resolve();
// counter_till carries its own till → cash_register attach is dropped when the counter GLB is present.
const counterHasGlb = !!(manifest.fittings && manifest.fittings[KIND_TO_GLB.counter]);
const jobs = [];
for (const p of placed) {
if (p.removed) continue;
jobs.push(upgradeFitting(ctx, p.fitting, p.kind, manifest));
if (p.fitting.counterTop) jobs.push(attachCounterTop(ctx, p.fitting, manifest, shopType));
if (p.fitting.counterTop) jobs.push(attachCounterTop(ctx, p.fitting, manifest, shopType, counterHasGlb));
}
return Promise.all(jobs);
}