Venues: persistent room, motivated camera, ride; drop Pylon Grid

- Venue as one continuous place: persistent per-song layer, interpolated
  venueState, story jumps (one re-hang max + recap) with camera cuts
- Room camera shot plan (venueShots.js): per-section size/target, monotonic
  look sweep, traverse glide body, room-side alternation
- New Ride venue: generated vehicle + actor passengers, radio RTT screen,
  chase/stationary modes, ride archetype across generator/camera/staging
- Flora cost trim (5x(12+6+6) -> 4x(8+4+4) SDF evals, 204ms -> 140ms worst)
- Delete Pylon Grid shader (2.5s/frame SwiftShader); drop its metadata row
- tools/venue-check.js headless gate harness + venue-gallery explorer
This commit is contained in:
Dejvino
2026-09-17 19:35:26 +02:00
parent b1b6431eeb
commit 46fab512df
31 changed files with 3684 additions and 358 deletions
+47
View File
@@ -145,6 +145,7 @@ export class Layer {
this.blend = blend;
this.palette = [];
this.personality = null;
this.venueState = null;
}
/**
@@ -374,6 +375,7 @@ export class ModelLayer extends Layer {
this.camera = new THREE.PerspectiveCamera(60, 16 / 9, 0.1, 200);
this.camera.position.set(0, 0, 5);
this.actorSpec = options.actorSpec || null;
this.venueSpec = options.venueSpec || null;
// Shared rig injected by Compositor at render time when available.
this.sharedCamera = null;
this.instance = this.module.build({
@@ -382,10 +384,48 @@ export class ModelLayer extends Layer {
seed: this.seed,
params: this.baseParams,
actorSpec: this.actorSpec,
venueSpec: this.venueSpec,
THREE,
});
}
/** Allow the look to swap the venue without rebuilding the layer. */
setVenue(venueSpec) {
this.venueSpec = venueSpec || null;
return this;
}
/**
* Bind the composited viz texture onto this layer's screen (venue RTT).
* No-op unless the module implements bindVizTexture(instance, texture).
* Delegating keeps venue materials owned by the venue module — the
* compositor never touches venue meshes directly.
*/
bindVizTexture(texture) {
if (this.module && typeof this.module.bindVizTexture === 'function' && this.instance) {
this.module.bindVizTexture(this.instance, texture);
}
return this;
}
/**
* Base wash for the viz composite. The shot is keyed over this colour
* instead of over black: a shot floating on black is the composition
* failure the phase 12 rules forbid, miniaturised onto the venue screen —
* and it measured ~5x the no-venue section distance (sparse bright things
* on nothing maximise layout/region jumps). Defaults to a dark neutral;
* venue modules override via washColor(instance) to match their room.
*/
washColor() {
try {
if (this.module && typeof this.module.washColor === 'function' && this.instance) {
const c = this.module.washColor(this.instance);
if (Array.isArray(c) && c.length >= 3) return [c[0], c[1], c[2]];
}
} catch { /* fall through to default */ }
return [0.03, 0.03, 0.04];
}
/** Allow the look to swap the actor without rebuilding the layer. */
setActor(actorSpec) {
this.actorSpec = actorSpec || null;
@@ -418,6 +458,13 @@ export class ModelLayer extends Layer {
framing: this.framing,
opacity: this.opacity,
actorSpec: this.actorSpec,
venueSpec: this.venueSpec,
venueState: this.venueState,
// Song-level arrangement salt (see ArcDriver.venueJumpAt): which
// dressing of the room is live. Null most of the time; the module
// re-hangs only when it changes. Plain field, not on Layer base —
// only venue modules read it.
venueArrangement: this.venueArrangement ?? null,
THREE,
});
renderer.renderScene(this.scene, cam, target, true);