From ac6654b0ddfd6700b8ce4281d6563f5874744930 Mon Sep 17 00:00:00 2001 From: Dejvino Date: Mon, 21 Sep 2026 18:55:09 +0200 Subject: [PATCH] Ride: proper 2/4 seating plus doors, dash, console, wheel Two rows of two like a real car (2 riders: 2-seater; more: 4-seater with one seat left empty); riders sit on cushions against backrests with headrests, driver ahead of the wheel. Long painted doors with handles, full-width dashboard with dial strip, center console with shifter glow, torus steering wheel on a column. Fender arches fattened. Rng cadence preserved per rider so downstream draws don't move. --- flow-state/src/venues/ride.js | 91 ++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 7 deletions(-) diff --git a/flow-state/src/venues/ride.js b/flow-state/src/venues/ride.js index d6c01b8..9f2f634 100644 --- a/flow-state/src/venues/ride.js +++ b/flow-state/src/venues/ride.js @@ -552,6 +552,10 @@ export const ride = { const hood = add(new T.Mesh(new T.BoxGeometry(1.5, 0.24, 1.15), paintMat())); hood.position.set(0, 0.18, -1.6); hood.rotation.x = -0.07; + // Hood spine: breaks the flat table read. + const spine = add(new T.Mesh(new T.BoxGeometry(0.28, 0.07, 1.0), paintMat())); + spine.position.set(0, 0.3, -1.6); + spine.rotation.x = -0.07; // Trunk deck behind the cabin, meeting its lower edge. const trunk = add(new T.Mesh(new T.BoxGeometry(1.5, 0.2, 0.8), paintMat())); trunk.position.set(0, 0.2, 1.15); @@ -565,10 +569,10 @@ export const ride = { const skirt = add(new T.Mesh(new T.BoxGeometry(0.12, 0.14, 2.0), trimMat)); skirt.position.set(side * 0.93, -0.5, -0.15); } - // Fenders: half-torus arches over each wheel. Torus arc 0..π - // spans the upper half in XY; yawed 90° it arches across the - // wheel in the rolling plane. - const fenderGeo = new T.TorusGeometry(0.5, 0.1, 8, 12, Math.PI); + // Fenders: half-torus arches over each wheel. Torus arc 0..π + // spans the upper half in XY; yawed 90° it arches across the + // wheel in the rolling plane. + const fenderGeo = new T.TorusGeometry(0.5, 0.12, 8, 12, Math.PI); for (const [wx, wz] of [[-0.95, 1.05], [0.95, 1.05], [-0.95, -1.35], [0.95, -1.35]]) { const fender = add(new T.Mesh(fenderGeo, paintMat())); fender.rotation.y = Math.PI / 2; @@ -622,6 +626,48 @@ export const ride = { const pipe = add(new T.Mesh(pipeGeo, pipeMat)); pipe.position.set(side * 0.35, -0.45, 1.7); } + // Doors: long painted panels with handles. One pair, front + // and rear rows alike on a convertible — they flank the cabin + // and clear the arches vertically (door bottoms sit above the + // wheel tops). + for (const side of [-1, 1]) { + const door = add(new T.Mesh(new T.BoxGeometry(0.08, 0.5, 1.9), paintMat())); + door.position.set(side * 0.89, 0.15, -0.05); + const handle = add(new T.Mesh(new T.BoxGeometry(0.04, 0.05, 0.22), trimMat)); + handle.position.set(side * 0.945, 0.28, 0.35); + } + // Dashboard across the cabin front with a dial strip on top, + // where high chase angles can see it. + const dashPanel = add(new T.Mesh(new T.BoxGeometry(1.5, 0.12, 0.4), trimMat)); + dashPanel.position.set(0, 0.6, -0.85); + const dials = add(new T.Mesh( + new T.BoxGeometry(0.7, 0.02, 0.06), + new T.MeshStandardMaterial({ + color: new T.Color(0.02, 0.03, 0.04), + emissive: new T.Color(0.5, 0.7, 0.85), + emissiveIntensity: 0.8, + roughness: 0.5, + }))); + dials.position.set(0, 0.665, -0.85); + // Center console between the seats with a shifter-line glow. + const console_ = add(new T.Mesh(new T.BoxGeometry(0.16, 0.16, 1.1), trimMat)); + console_.position.set(0, 0.64, 0.15); + const consoleGlow = add(new T.Mesh( + new T.BoxGeometry(0.05, 0.02, 0.8), + new T.MeshStandardMaterial({ + color: new T.Color(0.02, 0.03, 0.04), + emissive: new T.Color(0.5, 0.7, 0.85), + emissiveIntensity: 0.6, + roughness: 0.5, + }))); + consoleGlow.position.set(0, 0.725, 0.15); + // Steering wheel ahead of the front-left seat. + const column = add(new T.Mesh(new T.BoxGeometry(0.05, 0.05, 0.3), trimMat)); + column.position.set(-0.33, 0.7, -0.42); + column.rotation.x = 0.2; + const wheel = add(new T.Mesh(new T.TorusGeometry(0.11, 0.025, 8, 18), trimMat)); + wheel.position.set(-0.33, 0.76, -0.55); + wheel.rotation.x = -0.5; } // Wheels: four dark torus + hub, spin with speed (see below). @@ -717,6 +763,33 @@ export const ride = { bodyParts = body.children.slice(); } catch { bodyParts = null; } const count = vs && vs.population ? Math.max(2, Math.min(4, vs.population.count | 0)) : 2; + // Two rows of two, like a real car: a 2-rider track gets a + // 2-seater, anything more a 4-seater (3 riders leave one seat + // empty, which is natural). Riders take seats in order, driver + // front-left where the wheel is. + const SEATS = count > 2 + ? [{ x: -0.33, z: -0.15 }, { x: 0.33, z: -0.15 }, + { x: -0.33, z: 0.45 }, { x: 0.33, z: 0.45 }] + : [{ x: -0.33, z: -0.15 }, { x: 0.33, z: -0.15 }]; + const leatherMat = new T.MeshStandardMaterial({ + color: new T.Color(0.06, 0.06, 0.07), roughness: 0.8, metalness: 0.05, + transparent: opacity < 0.999, opacity, + }); + for (const seat of SEATS) { + const cushion = new T.Mesh(new T.BoxGeometry(0.42, 0.12, 0.42), leatherMat); + cushion.position.set(seat.x, 0.62, seat.z); + instance.car.add(cushion); + instance.bodyMeshes.push(cushion); + const back = new T.Mesh(new T.BoxGeometry(0.4, 0.5, 0.12), leatherMat); + back.position.set(seat.x, 0.85, seat.z + 0.24); + back.rotation.x = -0.12; + instance.car.add(back); + instance.bodyMeshes.push(back); + const headrest = new T.Mesh(new T.BoxGeometry(0.2, 0.14, 0.1), leatherMat); + headrest.position.set(seat.x, 1.12, seat.z + 0.28); + instance.car.add(headrest); + instance.bodyMeshes.push(headrest); + } for (let i = 0; i < count; i++) { let m; if (bodyParts && bodyParts.length) { @@ -742,10 +815,14 @@ export const ride = { } const g = new T.Group(); g.add(m); - // Seat row: spread across the cabin width, heads above the glass. - const fx = (i - (count - 1) / 2) * 0.62 + (rng() - 0.5) * 0.1; + // Seated in order: butt on the cushion, back against the + // backrest. Same rng cadence as the old scatter row (four + // draws per rider, same order) so downstream draws don't move. + const seat = SEATS[i]; + const jx = (rng() - 0.5) * 0.08; + const jz = (rng() - 0.5) * 0.06; g.scale.setScalar(0.5); - g.position.set(fx, 0.78, -0.25 + (rng() - 0.5) * 0.3); + g.position.set(seat.x + jx, 0.78, seat.z + jz); g.rotation.y = Math.PI + (rng() - 0.5) * 0.5; g.userData.baseY = g.position.y; g.userData.index = i;