Lane B round 16 (v3.1 THE FLIP): town-wide streetlamp pools + default-boot budget re-baseline

Ledger #4. Verified fresh Chromium seed 20261990. No new rng, goldens untouched.

- Town-wide pools (venue.js): extended the R13 instanced-pool pass from venue frontages to EVERY
  streetlight + bus shelter. New townPoolSpecs(plan) replicates furniture.js's lamp cadence off the
  plan street graph (lamp every 18m from s=8, alternating sides, off=halfRoad+0.7) and reuses the
  exported busShelterStops(plan) -- read-only, furniture.js untouched (imported one function). All
  pools ride the ONE existing InstancedMesh -> still ~1 draw: 381 instances / 1 draw / ~760 tris.
  Night-driven; fog:true now so distant pools fade into night fog (pools aren't chunk-streamed) instead
  of floating as dots. Gig-gated -> ?classic=1 is pool-free = v2 parity by construction (no always-on
  furniture change, the v3.0 blocker). Deterministic cadence, no seeded draws (release law).

- Default-boot re-baseline: measured the default content (gigs + weather=rain + winmap + tram all
  live), settled. Busiest venue block (pub, NIGHT, rain) ~157 scene / ~170 total draws, 66k tris;
  venue_night 122, queue_night 121, district_posters/street_noon 185 (worst tour shot), market_square
  fresh 97/52k. ALL <=300 draws / <=200k tris. Flip adds ~3 draws (rain 1 + tram 2; winmap 0), pools
  add ~0 (instanced). Daytime dense-citizen ceiling (~275, Lane D, live-walk) unchanged (pools night-only).

- Bookmark spot-check: all 19 resolve, un-letterboxed, 0 errors under the default boot; rain+tram+winmap
  compose cleanly in old frames (street_noon rainy 177/114k, nothing blocked). No pose fixes.

Ran before F's flip landed (out of the stated order): pools are gig-gated so they work under ?gigs now;
re-baseline uses explicit all-flags URLs = post-flip default content. F re-confirms at close.

-> Lane F (B->F handshake): default-boot busiest block ~170 total draws / 66k tris; worst street view
   185 / 131k; densest daytime 97 / 52k. All under budget. Town-wide pools ~1 draw. Pool sprite is
   procedural (E has nothing to do).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-16 12:21:19 +10:00
parent 42b11e717d
commit 54a030b38b
2 changed files with 84 additions and 2 deletions

View File

@ -1,5 +1,55 @@
# LANE B — NOTES (measured)
## Round 16 (Fable's ROUND16 → Lane B) — v3.1 THE FLIP: town-wide pools + default-boot re-baseline
Two deliverables. Verified fresh Chromium, seed 20261990. (Ran before F's flip landed — my pools are
gig-gated so they work under `?gigs=1` now, and the re-baseline uses **explicit** all-four-flags URLs =
the post-flip default-boot content; F re-confirms against the real default at close.)
### 1. Town-wide streetlamp pools (ledger #4) — `venue.js`
Extended the R13 instanced-pool pass from venue frontages to **every streetlight + bus shelter**. New
`townPoolSpecs(plan)` replicates furniture.js's exact lamp cadence off the plan's street graph (a lamp
every 18 m from `s=8`, alternating sides, at `off = halfRoad+0.7`) and reuses the exported
`busShelterStops(plan)`**read-only, `furniture.js` untouched** (imported the one function). All pools
(venue + town) ride the **one existing InstancedMesh**, so town-wide is still **~1 draw**:
- **381 pool instances / 1 draw / ~760 tris** for Boolarra Heads (22 edges, 27 nodes). Night-driven via
the existing opacity ramp; `fog: true` now (was false) so distant pools fade into the night fog
instead of floating as dots over un-built chunks (pools aren't chunk-streamed). A/B'd `fog:false`
no dramatic difference and a dot-risk on bigger towns, so `fog:true` is the keeper.
- **Gig-gated → classic parity by construction:** venue.js only exists under the gig layer, so
`?classic=1` (gigs off) is pool-free = v2 parity. No always-on `furniture.js` change (the v3.0 blocker).
- Pure function of the plan (deterministic cadence, no rng) → **no new seeded draws** (release law).
### 2. Default-boot budget re-baseline (ledger #4) — the number the budget law now means
Measured on the **default boot content** (gigs + `weather=rain` worst-case + winmap + tram all live),
settled (shoot-twice / clean-load to defeat the orphan-chunk teleport inflation):
| view (default boot, all flags) | draws (composer total) | tris | gate |
|---|---|---|---|
| **busiest venue block** (pub, NIGHT, rain, 3/4) | **~157 scene / ~170 total** | **66k** | ≤300 / ≤200k ✅ |
| `venue_night` | 122 | 53k | ✅ |
| `queue_night` | 121 | 61k | ✅ |
| `district_posters` / `street_noon` (wide main spine) | **185** | 131k | ✅ (worst tour shot) |
| `market_square` (densest daytime, fresh) | 97 | 52k | ✅ |
**No budget bust.** The flip adds ~3 draws (rain Points 1 + tram 2; winmap 0 — it's a shader on
existing windows), and the **town-wide pools add ~0** (1 instanced mesh). Worst default-boot street
view measured is **185 total draws / 131k tris** — ~115 draw / ~70k tri margin. (Pools are night-only,
so the pre-existing daytime dense-citizen ceiling — Lane D territory, ~275 total per R13's live-walk —
is unchanged by this round; winmap/rain add ≤3 there. F's live week-soak confirms the full-density number.)
### 3. Bookmark spot-check on the default boot
All **19 bookmarks resolve, un-letterboxed, 0 page errors** with all four flags on. Rain particles +
tram + winmap now appear in the old opt-in frames and compose cleanly — `street_noon` rainy (177 draws
/ 114k tris) frames the strip with rain + storm sky + wet road, nothing blocked. No pose fixes needed.
**→ Lane F (B→F handshake — the default worst-case numbers):** default boot, busiest venue block =
**~170 total draws / 66k tris**; worst street view (wide main spine) = **185 / 131k**; densest daytime
= 97 / 52k. All ≤300 / ≤200k. Town-wide pools are ~1 draw. Re-confirm against the real default once
your flip lands; the pool sprite is procedural (no asset, E has nothing to do).
---
## Round 15 (Fable's ROUND15 → Lane B) — verify the hero after A's poster flip (v3.0 release)
Task: re-verify `queue_night` after A's ledger-#1 flip (`35da83c`: `buildPosters` item 1 → `+sin,+cos`

View File

@ -19,6 +19,7 @@
// ry under A's poster convention (front = local Z); posters are rendered at A's (x,z,ry) verbatim.
import * as THREE from 'three';
import { busShelterStops } from './furniture.js'; // read-only: shelter (tram-stop) positions for pools
const POSTER_W = 1.05, POSTER_H = 1.5; // ~A2 bill on a pole/wall
const POSTER_SKINS = ['grunge', 'retro', 'screenprint', 'xerox']; // E's poster pack (assets/gen/poster-*.jpg)
@ -169,11 +170,18 @@ export function createVenuePresentation(plan, skins, scene, { assetBase = 'asset
});
}
// ── one instanced streetlamp-pool mesh for the whole district (night-driven) ──────────────────────
// ── town-wide streetlamp pools (R16 ledger #4): a warm pool under every streetlight + bus shelter,
// not just venue frontages. Positions are replicated from the plan's street graph using furniture.js's
// exact lamp cadence (READ-ONLY — we don't touch furniture.js), so a pool lands under each real lamp.
// Gig-gated by construction (venue.js only exists under the gig layer ⇒ ?classic stays pool-free); one
// instanced mesh ⇒ still ~1 draw town-wide. Night-driven via the same opacity ramp as the venue pools.
for (const s of townPoolSpecs(plan)) poolSpecs.push(s);
// ── one instanced streetlamp-pool mesh for the whole town (night-driven) ──────────────────────────
let poolMesh = null, poolMat = null;
if (poolSpecs.length) {
const poolTex = poolTexture(); disposables.push(poolTex);
poolMat = new THREE.MeshBasicMaterial({ map: poolTex, transparent: true, opacity: 0, depthWrite: false, blending: THREE.AdditiveBlending, side: THREE.DoubleSide, fog: false, toneMapped: false });
poolMat = new THREE.MeshBasicMaterial({ map: poolTex, transparent: true, opacity: 0, depthWrite: false, blending: THREE.AdditiveBlending, side: THREE.DoubleSide, fog: true, toneMapped: false });
const pg = new THREE.PlaneGeometry(1, 1); pg.rotateX(-Math.PI / 2); // lies flat, normal +Y
disposables.push(poolMat, pg);
poolMesh = new THREE.InstancedMesh(pg, poolMat, poolSpecs.length);
@ -265,6 +273,30 @@ export function createVenuePresentation(plan, skins, scene, { assetBase = 'asset
const r2 = (v) => Math.round(v * 100) / 100;
const r4 = (v) => Math.round(v * 10000) / 10000;
// ── town-wide pool positions: one under every streetlight + bus shelter, derived from the plan's street
// graph. This MIRRORS furniture.js's streetlight rule (a lamp every 18 m from s=8, alternating sides, at
// off = halfRoad+0.7) so pools sit under the real lamps without importing furniture internals. Pure
// function of the plan (no rng — the cadence is deterministic), so it adds no seeded draws (release law).
function townPoolSpecs(plan) {
const specs = [];
const nodeById = new Map((plan.streets?.nodes || []).map((n) => [n.id, n]));
for (const e of (plan.streets?.edges || [])) {
const a = nodeById.get(e.a), b = nodeById.get(e.b);
if (!a || !b) continue;
const dx = b.x - a.x, dz = b.z - a.z, len = Math.hypot(dx, dz) || 1;
const ux = dx / len, uz = dz / len, px = -uz, pz = ux;
const halfRoad = (e.width || 8) / 2;
let side = 1;
for (let s = 8; s < len - 4; s += 18) {
const off = (halfRoad + 0.7) * side;
specs.push({ x: a.x + ux * s + px * off, z: a.z + uz * s + pz * off, diam: 3.4 });
side = -side;
}
}
for (const st of busShelterStops(plan)) specs.push({ x: st.x, z: st.z, diam: 3.6 });
return specs;
}
// ── a warm radial streetlamp-pool sprite (procedural, no fetch): bright warm centre → transparent edge.
function poolTexture() {
const S = 128;