diff --git a/C-progress.md b/C-progress.md index 43970b1..e1c77b8 100644 --- a/C-progress.md +++ b/C-progress.md @@ -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). --- diff --git a/docs/LANES/LANE_C_GLB_VALIDATION.md b/docs/LANES/LANE_C_GLB_VALIDATION.md index cc84a0e..b994de5 100644 --- a/docs/LANES/LANE_C_GLB_VALIDATION.md +++ b/docs/LANES/LANE_C_GLB_VALIDATION.md @@ -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 diff --git a/docs/LANES/LANE_C_NOTES.md b/docs/LANES/LANE_C_NOTES.md new file mode 100644 index 0000000..191316d --- /dev/null +++ b/docs/LANES/LANE_C_NOTES.md @@ -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 diff --git a/docs/shots/laneC/glb_v1_1_r5.jpg b/docs/shots/laneC/glb_v1_1_r5.jpg new file mode 100644 index 0000000..20190a8 Binary files /dev/null and b/docs/shots/laneC/glb_v1_1_r5.jpg differ diff --git a/web/js/interiors/glb.js b/web/js/interiors/glb.js index c5660d7..9921f7e 100644 --- a/web/js/interiors/glb.js +++ b/web/js/interiors/glb.js @@ -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); }