Lane C R14 (v3.0 release): venue audit + docs freeze + guitar_amp ruling
Light release round — C interior code unchanged since R13 (venue geometry stable by construction); this is the audit, the amp ruling, and the v3.0-FROZEN docs. - Re-verified vs the current tree (D's continuity + RY-fix + real instrument GLBs landed): contract byte-identical (caps 8/8/12, deckY/riserY, gigKeys, determinism), drawSweep <=350 (worst 161), gig-night draws pub 60 / band_room 52 / rsl 71 <=350. Release-law confirmed: D's rng namespace is disjoint from C's ctx.stream; no golden can move. - guitar_amp ruling (LANE_C_PUB §3): NOT blessed as-is. D's pose (stage.z+0.15, down-stage) interpenetrates the bass rig by 0.29 m; move it up-stage-right ~ (stage.w*0.36, deckY, stage.z - stage.d*0.22), clear of the bass + C's primitive ampStacks. Full amp unification = v3.1. Cosmetic + ?gigs opt-in -> not a tag blocker. C->D handshake. - Docs freeze: LANE_C_PUB.md + LANE_C_AUDIO.md marked v3.0-FROZEN; RY §0 reframed past-tense (the +Z fleet was fixed at source in D's buildFigure, R13 - no per-consumer flips); budgets updated to R14; in-flight handoffs removed. - Comment-only code (zero runtime/golden/rng change): fixed stale R12 inline return-shape comments (watchPoints 6..8 -> 8/8/12; stage bandPoses[3] -> [4] + riserY) in interiors.js + layout.js; documented the standalone gigKey hand-build. theme.js untouched. Note for E/F: settled gig-night interior is ~182k tris (E instruments 14k each x5); F's smoke reports ~34k measuring before the async GLBs load. Interior draw gate passes; v3.1 LOD candidate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a3911a4590
commit
62e38c5df8
@ -3,7 +3,54 @@
|
||||
*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-15 (round 13) · owner: PROCITY-C · reviewer: Fable
|
||||
Last updated: 2026-07-16 (round 14) · owner: PROCITY-C · reviewer: Fable
|
||||
|
||||
---
|
||||
|
||||
## Update 2026-07-16 (round 14, v3.0: the release) — venue audit + docs freeze (light round)
|
||||
|
||||
R14 §Lane C: interior release audit, the `guitar_amp` ruling, and the docs freeze. C's interior code was
|
||||
**unchanged** since R13, so venue geometry is stable by construction; the audit re-verified it against the
|
||||
current tree (D's R13 continuity + RY-fix + real instrument GLBs landed) and froze the contracts. Ran an
|
||||
adversarial audit **workflow** (10 agents: conformance + D-integration/amp + docs-freeze edit list, then a
|
||||
verify pass) — it earned its keep (caught the amp defect I'd first waved through + two stale comments).
|
||||
|
||||
**Empirical re-verify (fresh context, port-isolated server):**
|
||||
- Venue contract **byte-identical to R13**: caps 8/8/12, deckY 0.32/0.20/0.40, riserY 0.48/0.36/0.56, gigKeys
|
||||
gig-pubrock/grunge/covers, quiet-night = no gigKey, determinism true.
|
||||
- `drawSweep` glb-off: band_room 124 · pub 126 · rsl 135 (worst 161 opshop/hall) ≤350 — unchanged.
|
||||
- **Gig-night draws with D's real `GigCrew` + GLB instruments** (localdepot): pub **60** · band_room **52** ·
|
||||
rsl **71** ≤350. Asset-free worst RSL 237. All 4 instruments load (guitar/bass/mic/drums).
|
||||
- **Release-law confirmed** (workflow, CONFIRMED): D's continuity + the RY fix ride a disjoint rng namespace
|
||||
(`rng(citySeed,kind,id)`) from C's `ctx.stream(salt)`; the RY fix is a constant Y-rotation in `buildFigure`
|
||||
(no rng). Cannot perturb C's placement or any golden.
|
||||
|
||||
**`guitar_amp` ruling (debt for C) → Lane D:** **NOT blessed as-is — reposition up-stage.** D plants the GLB
|
||||
at `(stage.x+stage.w·0.32, deckY, stage.z+0.15)` — that `+0.15` is *down-stage*, landing the 0.5 m amp 0.29 m
|
||||
from the bass rig (**interpenetrates the bassist**, confirmed by render + 3 audit agents), and it's redundant
|
||||
with C's own primitive ampStacks (`placeStage` plants 1–2 up-stage at `(±stage.w·0.28, deckY, stage.z −
|
||||
stage.d·0.18)`). Corrected pose in [LANE_C_PUB.md](docs/LANES/LANE_C_PUB.md) §3: move up-stage-right ≈
|
||||
`(stage.w·0.36, deckY, stage.z − stage.d·0.22)`, clear of the bass and offset from C's primitives → a clean
|
||||
flanking backline. Full amp unification (C exposes `stage.backline[]`, GLB replaces primitive) = v3.1.
|
||||
Cosmetic + `?gigs=1` opt-in → does not block the tag.
|
||||
|
||||
**Tri observation → E/F (not a C gate):** E's decimated instrument GLBs are **14k tris each** (5 on a stage =
|
||||
70k; peds ~2.7k each), so a settled RSL gig-night interior is ~**182k tris**. The interior gate is *draws*
|
||||
(passes at ≤71); the 200k tris is the *street* budget, so no violation. But F's gig-night smoke reports ~34k
|
||||
because it measures **before** the async instrument GLBs load — the real settled scene is ~5× heavier. A
|
||||
camera-distance instrument LOD is a v3.1 candidate.
|
||||
|
||||
**Docs freeze (v3.0-FROZEN markers added):**
|
||||
- `LANE_C_PUB.md` — retitled + frozen marker; §0 ry convention reframed past-tense (the +Z fleet was fixed at
|
||||
the source in D's `rigs.js buildFigure`, R13 — no per-consumer flips); 4-piece stated as shipped (veto
|
||||
language removed); the amp ruling added (§3); budgets updated to R14; in-flight handoffs removed.
|
||||
- `LANE_C_AUDIO.md` — frozen marker + a scope note (this covers base interior audio; gig audio `gigKey`
|
||||
lives in LANE_C_PUB); the deferred `audioEmitter` idea re-targeted to v3.1.
|
||||
|
||||
**Code (comment-only, zero runtime/golden/rng impact):** fixed two stale R12 inline return-shape comments in
|
||||
`interiors.js` (`watchPoints` 6..8 → 8/8/12 caps; `stage` `bandPoses[3]` → `[4]` + `riserY`) + one in
|
||||
`layout.js`; added a note at the gigKey hand-build explaining it's deliberately standalone (byte-identical to
|
||||
citygen's `gigKeyFor`, not imported — keeps the interiors lib citygen-free). `theme.js` untouched.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,8 +1,16 @@
|
||||
# LANE C — audio contract (round 11) → for Lane F + Lane B
|
||||
# LANE C — interior audio contract (v3.0-FROZEN) → for Lane F + Lane B
|
||||
|
||||
*PROCITY-C, 2026-07-15. Answers the R11 §Lane C question: "where should the interior bed + room-tone
|
||||
attach?" **Confirmed** — `buildInterior` now returns `room.audio = { musicKey, toneKey }`, seeded per
|
||||
shop. Lane F's interior_mode just plays it; Lane B's audio.js resolves the keys → files.*
|
||||
> **v3.0-FROZEN** · 2026-07-16 · durable contract for Lane F + Lane B. Describes the shipped state; changes
|
||||
> require a version bump.
|
||||
|
||||
*`buildInterior` returns `room.audio = { musicKey, toneKey }`, seeded per shop. Lane F's interior_mode just
|
||||
plays it; Lane B's audio.js resolves the keys → files.*
|
||||
|
||||
> **Scope:** this doc covers the **base interior audio** (the seeded music bed + room-tone every shop
|
||||
> returns). The **gig audio** — `room.audio.gigKey`, the live-band bed, canonical form `gig-<genreKey>`
|
||||
> (`gig-pubrock` / `gig-grunge` / `gig-covers`), present only when `opts.gig` is on — is specified in
|
||||
> [LANE_C_PUB.md](LANE_C_PUB.md) §1. `room.audio` carries `gigKey?` as a third, optional key alongside the
|
||||
> two below.
|
||||
|
||||
## The contract
|
||||
```js
|
||||
@ -42,9 +50,9 @@ on exit / dispose(room):
|
||||
Nothing before the first gesture; `?mute=1`/`?noassets=1` short-circuit before any fetch. `room.audio`
|
||||
is plain data on the return — no new lifecycle, no dispose hook needed from Lane C's side.
|
||||
|
||||
## Optional (deferred to v2.2, per the round scope)
|
||||
## Deferred idea (v3.1+, not in the frozen contract)
|
||||
A `places`-tagged visual source ("the music comes from *there*") — e.g. a radio/hifi mesh with
|
||||
`userData.audioEmitter = musicKey` so Lane B can position/spatialise the bed. Only the record shop has a
|
||||
fitting for it today (`listeningCorner`); milk-bar/video would need a new radio prop. Not in this round —
|
||||
proposing the `userData.audioEmitter` hook + the two props as a tidy v2.2 item. v2.1 plays the bed
|
||||
non-spatially (room-filling), which is the right default for an interior anyway.
|
||||
fitting for it today (`listeningCorner`); milk-bar/video would need a new radio prop. A tidy v3.1 item: the
|
||||
`userData.audioEmitter` hook + the two props. v3.0 plays the bed non-spatially (room-filling), which is the
|
||||
right default for an interior anyway.
|
||||
|
||||
@ -1,66 +1,48 @@
|
||||
# LANE C — venue interfaces (round 13, v3.0-beta: the district) → Lane D + Lane F
|
||||
# LANE C — venue interface contract (v3.0-FROZEN: the district) → Lane D + Lane F
|
||||
|
||||
*PROCITY-C, 2026-07-15. R13 §Lane C. The district ships **three** venue archetypes behind `?gigs=1` —
|
||||
`pub` (R12), and new this round **`band_room`** and **`rsl`**. A converts a chosen shop to a venue kind in
|
||||
place (`shop.type = kind`); C keys the interior recipe off `shop.type`. All three return the SAME shape
|
||||
(below), so Lane D drops rigs in and Lane F reads state with no per-kind branching. Non-venue rooms are
|
||||
byte-identical (venue code only runs for `recipe.venue`, on its own seed sub-streams).*
|
||||
> **v3.0-FROZEN** · 2026-07-16 · durable contract for Lane D + Lane F. Describes the *shipped* state;
|
||||
> changes require a version bump (CITY_SPEC amendment law). Verified R13 (build) + R14 (release audit).
|
||||
|
||||
*Verified (fresh context, seeds 20261990/1990/7/42/101 + 90-build venue soak): 0 throws · 0 carves ·
|
||||
0 path-fails · 0 determinism-fails · leak geo0/tex0 · worst build 8.3 ms. Draws below.*
|
||||
*The district ships **three** venue archetypes behind `?gigs=1` — `pub`, `band_room`, `rsl`. Lane A
|
||||
converts a chosen shop to a venue kind in place (`shop.type = kind`); C keys the interior recipe off
|
||||
`shop.type`. All three return the SAME shape (below), so Lane D drops rigs in and Lane F reads state with
|
||||
no per-kind branching. Non-venue rooms are byte-identical (venue code runs only for `recipe.venue`, on its
|
||||
own seed sub-streams). Verified fresh (seeds 20261990/1990/7/42/101 + 90-build soak): 0 throws · 0 carves ·
|
||||
0 path-fails · 0 determinism-fails · leak geo0/tex0 · worst build 8.3 ms. Budgets in §3.*
|
||||
|
||||
---
|
||||
|
||||
## 0. The pinned pose `ry` convention (ROUND13 debt #4) — READ THIS FIRST
|
||||
## 0. The pose `ry` convention (pinned) — READ THIS FIRST
|
||||
|
||||
**Canonical convention (all of C's poses use it):** a pose's `ry` is the yaw of a thing whose **front /
|
||||
**Canonical convention (every C pose uses it):** a pose's `ry` is the yaw of a thing whose **front /
|
||||
outward normal is local −Z at ry=0**, i.e. world-facing `(−sin ry, −cos ry)`. `counter.stand`,
|
||||
`browsePoints`, `watchPoints` and `bandPoses` ALL follow it, and the box placeholder (`placeholder.js`) is
|
||||
built to it (toes at −Z). This is the long-standing convention `keepers.js` greet math already assumes.
|
||||
built to it (toes at −Z). `keepers.js` greet math assumes it. **Drop a rig in at a pose's `ry` and it faces
|
||||
the right way — no per-consumer flip.**
|
||||
|
||||
**The GLB fleet mesh is the outlier — it faces local +Z at ry=0.** Verified two ways in a fresh context:
|
||||
- toe-bone probe (`toe − foot`, the anatomically-anterior direction) on 5 peds → `(0.05..0.14, +0.99)` = **+Z**.
|
||||
- planting a fleet rig at C's `counter.stand.ry` via `spawnRig` (no flip) and dotting its forward against
|
||||
the intended customer-facing direction → **−0.99 (180° backward)**; at `ry+π` → +0.99.
|
||||
- for contrast the box placeholder's foot mesh sits at z −0.076 → **−Z** (matches the convention).
|
||||
|
||||
**Finding: with the GLB fleet loaded, keepers AND browsers have been facing 180° backward since the fleet
|
||||
was wired in.** `keepers.js` spawns rigs at `stand.ry` / `browsePoint.ry` with **no flip**, and the mesh
|
||||
front is +Z, so the keeper faces the bar wall and the browser faces the aisle instead of the goods. The R12
|
||||
band reads correctly only because `band.js` added `RY_FLIP = π`. The two actor kinds are **opposite**
|
||||
(placeholder −Z, GLB rig +Z), so the current per-consumer state is inconsistent both ways:
|
||||
|
||||
| consumer | applies | placeholder (−Z front) | GLB rig (+Z front) |
|
||||
|---|---|---|---|
|
||||
| `keepers.js` (keepers, browsers) | no flip | ✅ correct | ❌ **backward** |
|
||||
| `band.js` (band, crowd) | `+RY_FLIP` (π) | ❌ backward | ✅ correct |
|
||||
|
||||
**Recommended canonical fix (Lane D — visual only, NO golden moves, no C poses change):** normalise the
|
||||
fleet at the source. In `rigs.js buildFigure`, rotate the cloned `inner` by π about Y so every GLB rig's
|
||||
front becomes **−Z**, matching the placeholder and every C pose. Then:
|
||||
- `keepers.js` stays flip-free and is now correct for GLB too (fixes keepers + browsers);
|
||||
- `band.js` **drops** `RY_FLIP` (set to 0) — the poses already face right under the −Z convention;
|
||||
- placeholder and GLB modes become consistent (no more mode-dependent facing).
|
||||
|
||||
One rotation at the source, delete every consumer flip. C's data does not move (pure poses, all −Z already),
|
||||
so no golden is touched — it's the visual fix debt #4 anticipated. Repro handed to D in `C-progress.md`.
|
||||
|
||||
*(If D prefers to keep the flip per-consumer instead, the equivalent is: ADD `RY_FLIP` to `keepers.js`
|
||||
spawns — but that leaves placeholder-mode keepers backward, so the source-normalise is the clean one.)*
|
||||
**Why no flip (the R13 fix, for the record):** the GLB fleet meshes ship facing local **+Z** at ry=0 — the
|
||||
opposite of the −Z convention (verified: the anterior toe-bone vector `toe − foot` reads `(≈0, +0.99)`).
|
||||
Before R13 this was compensated per-consumer, inconsistently: `band.js` flipped by π (band/crowd correct,
|
||||
placeholders backward) while `keepers.js` did not (placeholders correct, **keepers + browsers 180° backward
|
||||
whenever the GLB fleet was loaded** — they'd been shipping back-to-front). **D fixed it at the source in
|
||||
`rigs.js buildFigure`: the cloned `inner` is rotated π about Y so every GLB rig faces −Z**, matching the
|
||||
placeholder and every C pose. All consumer flips were then deleted. Net: keepers, browsers, band and crowd
|
||||
all face correctly, GLB and placeholder modes are consistent, and C's pure-data poses never moved (no golden
|
||||
touched). If you add a new rig consumer, plant it at the pose `ry` with no flip.
|
||||
|
||||
---
|
||||
|
||||
## 1. What `buildInterior` returns for a venue (`shop.type` ∈ `pub` | `band_room` | `rsl`)
|
||||
|
||||
Everything below is **pure data on the return** (room-local coords, the ry convention pinned in §0). Lane D
|
||||
drops rigs straight in; Lane F reads state — no new lifecycle vs. the R9 keeper/browser seam.
|
||||
Everything below is **pure data on the return** (room-local coords, the ry convention in §0). Lane D drops
|
||||
rigs straight in; Lane F reads state — no new lifecycle vs. the R9 keeper/browser seam.
|
||||
|
||||
### `room.stage` — the band stage, now a **4-PIECE** (ROUND13 debt #3, null for non-venues)
|
||||
### `room.stage` — the band stage, a **4-piece** (null for non-venues)
|
||||
```js
|
||||
room.stage = {
|
||||
x, z, // deck centre (against the back wall)
|
||||
w, d, // deck size (m) — varies by kind (table below)
|
||||
deckY, // deck top surface height (m) — front line stands here
|
||||
deckY, // deck top surface height (m) — the front line stands here
|
||||
riserY, // drum riser top height (m) = deckY + 0.16 — the drummer sits here
|
||||
frontZ, // z of the deck lip (the crowd stands in front of this, +Z)
|
||||
bandPoses: [ { x, z, ry, role, y, seated? } ×4 ],
|
||||
@ -68,14 +50,13 @@ room.stage = {
|
||||
```
|
||||
- `bandPoses[0..2]` = the **front line**: `role` ∈ `guitar` / `vocal` / `bass`, across the deck lip, `y = deckY`.
|
||||
- `bandPoses[3]` = the **drummer**: `role: 'drums'`, `seated: true`, up-stage centre on the drum riser,
|
||||
`y = riserY` (= `deckY + 0.16`, the `fittings.js` stage riser). The riser mesh already exists in the stage
|
||||
fitting — the pose just seats the kit + drummer on it.
|
||||
- **Lane D:** lift each member to **its own `pose.y`** (was `stage.deckY`; front line is unchanged, the
|
||||
drummer now sits 0.16 m higher on the riser). `role` hints the instrument — E's GLBs via `opts.instrumentFor(role)`
|
||||
(electric_guitar/bass_guitar/mic_stand/drum_kit), primitives otherwise (asset law). `seated:true` → seated bob.
|
||||
- `ry = π` faces the audience (+Z) **under the pinned −Z convention** → apply your fleet flip per §0.
|
||||
- **John may veto to a front-line trio** (charter): say so before you start and C ships `bandPoses` length 3
|
||||
(drop `[3]`), and F amends its `band==` smoke back to 3.
|
||||
`y = riserY` (= `deckY + 0.16`, the `fittings.js` stage riser box).
|
||||
- **Lane D:** lift each member to its own `pose.y` (front line = `deckY`, drummer = `riserY`). `role` hints
|
||||
the instrument — E's GLBs via `opts.instrumentFor(role)` (electric_guitar/bass_guitar/mic_stand/drum_kit),
|
||||
primitives otherwise (asset law). `seated:true` → seated bob. `ry = π` already faces the audience (+Z) — no
|
||||
flip (§0).
|
||||
- The band is a **4-piece** for v3.0 (front-line trio + drummer). Reverting to a trio would be a version bump
|
||||
(drop `bandPoses[3]`), not a runtime toggle.
|
||||
|
||||
### `room.watchPoints` — the audience, **cap by kind** (empty `[]` for non-venues)
|
||||
```js
|
||||
@ -88,10 +69,10 @@ room.watchPoints = [ { x, z, ry, dance, slotIndex } ] // seeded floor poses, a
|
||||
stand + browse points + stage/PA. Deterministic per `shop.seed`.
|
||||
|
||||
### `room.counter` / keeper — unchanged
|
||||
The bar is a normal counter; the barkeep is just a keeper at `room.counter.stand` (R9 seam). *(Subject to the
|
||||
§0 facing fix — the keeper is one of the actors currently backward with the GLB fleet.)*
|
||||
The bar is a normal counter; the barkeep is just a keeper at `room.counter.stand` (R9 seam). Faces the
|
||||
customer correctly under §0.
|
||||
|
||||
### `room.audio.gigKey` — the live bed (ROUND13 debt #1)
|
||||
### `room.audio.gigKey` — the live bed
|
||||
`room.audio = { musicKey, toneKey, gigKey? }`. The manifest key **is** the gigKey, canonical form
|
||||
**`gig-<genreKey>`** — no mapping table. F passes gig state via `opts.gig`:
|
||||
```js
|
||||
@ -99,10 +80,12 @@ buildInterior(rslShop, THREE, { gig: { on: true } }) // → room.audio.gigKey
|
||||
```
|
||||
- Genre resolves `opts.gig.genreKey` → the venue's own `shop.genreKey` (A sets it) → a kind default
|
||||
(`pub`→pubrock, `band_room`→grunge, `rsl`→covers). Present **only when a gig is on**.
|
||||
- **Prefer `gigKey` over `musicKey`** while the gig plays. **Lane E** names the beds `gig-pubrock`,
|
||||
`gig-grunge`, `gig-covers`; a missing key → silence (audio law), band still visible.
|
||||
- **Prefer `gigKey` over `musicKey`** while the gig plays. E's beds are `gig-pubrock` / `gig-grunge` /
|
||||
`gig-covers`; a missing key → silence (audio law), band still visible.
|
||||
- C hand-builds `'gig-'+genreKey` (byte-identical to citygen's `gigKeyFor`, selfcheck-asserted) rather than
|
||||
importing it — deliberately, to keep the interiors lib standalone (it never imports citygen).
|
||||
|
||||
### The venue table (C-owned tuning, FYI)
|
||||
### The venue table (C-owned tuning)
|
||||
| kind | archetype bias | ceiling | deck `w × d`, `deckY` | crowd cap | genre → gigKey | dressing |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `pub` | wide / hall | 3.6 m | `0.58·W × 2.0`, 0.32 | 8 | pubrock → `gig-pubrock` | pub tables, sticky carpet, bar |
|
||||
@ -114,19 +97,39 @@ No `opts.gig` → a normal interior: `stage` + `watchPoints` are still returned
|
||||
`gigKey`** (room-tone + maybe the seeded radio `musicKey`), and D spawns no crowd. A seeded dark night reads
|
||||
true — the venue is just a quiet pub/club.
|
||||
|
||||
## 3. Budgets (measured, seed 20261990, fresh context)
|
||||
- **Empty shells** (`drawSweep` glb-off): band_room **124** · pub **126** · rsl **135** — town-wide worst
|
||||
161 (opshop/hall). ≤ 350. ✓
|
||||
- **Gig-night incl. FULL crowd + 4-piece** (worst-case framing from the door): RSL **237 draws** asset-free
|
||||
(16 rigs: 12 crowd + 4 band) / **77** GLB, **40.9k tris**. pub 176/56. band_room 173/53. ≤ 350 draws /
|
||||
200k tris. ✓ *(Measured with all 12 RSL watchPoints filled — band.js still caps crowd at 8 in R12; raising
|
||||
it to the watchPoint cap is D's R13 item, so this is the forward-looking worst case.)*
|
||||
- **90-build venue soak** (pub/band_room/rsl mixed): 0 determinism · 0 carve · 0 path-fail · worst 8.3 ms;
|
||||
leak geo 0 / tex 0.
|
||||
## 3. `guitar_amp` placement ruling (v3.0)
|
||||
|
||||
## 4. The archetypes (C-owned, FYI)
|
||||
D plants E's `guitar_amp` GLB (`band.js _addBackline`) at `(stage.x + stage.w·0.32, deckY, stage.z + 0.15)`.
|
||||
**Ruling: reposition up-stage — this pose is a v3.1 fix for D, not blessed as-is.** That `z = stage.z + 0.15`
|
||||
is **down-stage** (audience side): the 0.5 m amp lands 0.29 m from the bass rig, so it **interpenetrates the
|
||||
bassist** and reads as clutter, not a backline. Move it **up-stage** behind the front line and clear of both
|
||||
the bass rig *and* C's own primitive ampStacks. C's `placeStage` plants **1–2 primitive `ampStack` fittings**
|
||||
at `(±stage.w·0.28, deckY, stage.z − stage.d·0.18)` (up-stage, always rendered, the asset-free backline
|
||||
fallback — no GLB slot, so they stay primitive). A clean spot for D's GLB: **`(stage.w·0.36, deckY, stage.z −
|
||||
stage.d·0.22)`** (up-stage-right, clears the bass and offsets from the primitives). With the GLB up-stage it
|
||||
reads as a flanking backline with C's primitive. *Redundancy note:* the primitive + GLB coexist (2–3 amps);
|
||||
fully unifying them — C exposes a `stage.backline[]` slot list that D's GLB fills, replacing the primitive —
|
||||
is a **v3.1** item (no new contract fields in the frozen v3.0). Since the interpenetration is cosmetic and
|
||||
`?gigs=1` is opt-in, it does not block the v3.0 tag; D corrects the pose when convenient.
|
||||
|
||||
## 4. Budgets (measured R14, seed 20261990, fresh context)
|
||||
- **Empty shells** (`drawSweep` glb-off): band_room **124** · pub **126** · rsl **135** — town-wide worst
|
||||
161 (opshop/hall). ≤ 350. ✓ (identical to R13 — C geometry unchanged).
|
||||
- **Gig-night interior draws**, D's `GigCrew` with the real instrument GLBs + amp (localdepot): pub **60** ·
|
||||
band_room **52** · rsl **71**. Asset-free (placeholder crowd, no GLBs) worst RSL **237** (16 rigs). ≤ 350. ✓
|
||||
- **Tris**: fleet peds ~2.7k each; E's instrument GLBs **14k each** (decimated to the ≤15k target), 5 on a
|
||||
stage = 70k — the instruments dominate. RSL gig-night ≈ **182k tris** once the async GLBs settle. The
|
||||
interior gate is **draws** (passes); tris are the *street* budget (200k), so this is not an interior gate.
|
||||
*Note for E/F:* F's gig-night smoke reports ~34k because it measures **before** the async instrument GLBs
|
||||
load; the settled scene is ~180k. A camera-distance LOD on instruments is a v3.1 candidate.
|
||||
- **Determinism / release-law**: C's interior code is unchanged since R13; D's continuity + RY-fix ride
|
||||
disjoint rng namespaces (`ctx.stream(salt)` vs `rng(citySeed,kind,id)`) and cannot perturb C's placement
|
||||
or any golden. 90-build venue soak: 0 determinism · 0 carve · 0 path-fail · worst 8.3 ms; leak geo0/tex0.
|
||||
|
||||
## 5. The archetypes (C-owned)
|
||||
`band_room` = a tin shed: small (cosy/pokey), low ceiling, barely-a-riser deck, near-empty standing floor,
|
||||
bar in the corner — the PA is louder than the room. `rsl` = carpet-and-flock club: the biggest floor in
|
||||
town (hall/wide), a proper raised stage at one end, bistro tables round the edge, members' bar, the biggest
|
||||
crowd cap. Both reuse the `pub` venue machinery (`theme.js venueSpec` + `layout.js` placeStage/placeWatchPoints);
|
||||
only the dressing + `venueSpec` differ. Stage/PA/amp keep their GLB slots for Lane E (primitive fallback holds).
|
||||
bar in the corner — the PA louder than the room. `rsl` = carpet-and-flock club: the biggest floor in town
|
||||
(hall/wide), a proper raised stage at one end, bistro tables round the edge, members' bar, the biggest crowd
|
||||
cap. Both reuse the `pub` venue machinery (`theme.js venueSpec` + `layout.js` placeStage/placeWatchPoints);
|
||||
only the dressing + `venueSpec` differ. Stage/PA keep GLB slots for Lane E (primitive fallback holds); the
|
||||
primitive ampStacks are the backline fallback (see §3).
|
||||
|
||||
@ -111,7 +111,10 @@ function audioFor(type, ctx, opts, shop) {
|
||||
// Venue gig (R12/13): Lane F passes the live gig state via opts.gig; F should prefer gigKey over musicKey
|
||||
// while the gig is on. A quiet-night venue has no opts.gig → normal interior (room-tone + maybe radio).
|
||||
// The genre resolves gig.genreKey → the venue's own shop.genreKey (A sets it) → the kind default. The
|
||||
// manifest key IS the gigKey, canonical form `gig-<genreKey>` — no mapping table (ROUND13 debt #1).
|
||||
// manifest key IS the gigKey, canonical form `gig-<genreKey>` — no mapping table (ROUND13 debt #1). This
|
||||
// string is byte-identical to citygen's `gigKeyFor(genreKey)` (selfcheck asserts it); it is hand-built,
|
||||
// NOT imported, on purpose — the interiors lib stays standalone (never imports citygen). Keep the form,
|
||||
// don't privately rename it (the R12 trap) and don't import citygen (breaks standalone).
|
||||
const gig = opts && opts.gig;
|
||||
if (gig && (gig.on || gig.genreKey)) {
|
||||
const genreKey = gig.genreKey || (shop && shop.genreKey) || GENRE_BY_TYPE[type] || 'pubrock';
|
||||
@ -172,8 +175,8 @@ export function buildInterior(shop, THREE, opts) {
|
||||
places: lay.places,
|
||||
counter: lay.counter, // { mesh, pose:{x,z,ry}, stand:{x,z,ry} } — Lane D keeper spawn pose
|
||||
browsePoints: lay.browsePoints, // [ {x,z,ry,atKind,slotIndex} ] 0..3 — Lane D browser rig poses (R9)
|
||||
watchPoints: lay.watchPoints || [], // venue (R12): [ {x,z,ry,dance,slotIndex} ] 6..8 audience poses (else [])
|
||||
stage: lay.stage || null, // venue (R12): { x,z,w,d,deckY,frontZ,bandPoses[3] } (else null)
|
||||
watchPoints: lay.watchPoints || [], // venue: [ {x,z,ry,dance,slotIndex} ] audience poses, cap by kind (pub/band_room 8, rsl 12); else []
|
||||
stage: lay.stage || null, // venue: { x,z,w,d,deckY,riserY,frontZ,bandPoses[4] } (4-piece: trio + drummer on the riser); else null
|
||||
audio: audioFor(recipe.key, ctx, opts, norm.raw), // { musicKey, toneKey, gigKey? } — Lane E manifest.audio keys (R11/R12)
|
||||
dims: { ...dims, archetype, type: recipe.key },
|
||||
recipe: { key: recipe.key, label: recipe.label, counterPos: recipe.counterPos, clutter: recipe.clutter },
|
||||
|
||||
@ -432,7 +432,7 @@ export function layout(ctx, { recipe, dims, shell, adapter, shop }) {
|
||||
placed: placed.filter(p => !p.removed),
|
||||
places, decor: decor.meshes, grid, pathOK, carved,
|
||||
spawnCell, targetCell, placement, browsePoints,
|
||||
watchPoints, // venue: 6–8 audience poses {x,z,ry,dance} facing the stage (else [])
|
||||
watchPoints, // venue: audience poses {x,z,ry,dance} facing the stage, cap by kind (pub/band_room 8, rsl 12); else []
|
||||
stage: stageInfo, // venue: { x,z,w,d,deckY,frontZ,bandPoses } (else null)
|
||||
counter: {
|
||||
mesh: counter.fitting.group,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user