From 89f295532a4d87e642433edb0a9cb37ae7a2bd41 Mon Sep 17 00:00:00 2001 From: Dejvino Date: Mon, 21 Sep 2026 18:27:45 +0200 Subject: [PATCH] Ride: full sportscar trim so it reads as a convertible, not a tractor Hood, trunk, bumpers, side skirts, torus-arch fenders, grille, headlamps, taillight bar, mirrors, rear spoiler, exhausts. Fixed layout in palette paint (painted panels join the viz reflection feed); dark trim stays matte. Verified front/side/rear: lamps on, viz pattern on the hood, arches over wheels. --- flow-state/src/venues/ride.js | 101 ++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/flow-state/src/venues/ride.js b/flow-state/src/venues/ride.js index 1cd7d02..d6c01b8 100644 --- a/flow-state/src/venues/ride.js +++ b/flow-state/src/venues/ride.js @@ -523,6 +523,107 @@ export const ride = { instance.car.add(cabin); instance.bodyMeshes.push(cabin); + // Sportscar trim: hood, trunk, bumpers, skirts, fenders, grille, + // lamps, mirrors, spoiler, exhausts. Fixed layout, palette paint + // — the chassis above varies per track, this is what makes every + // reading of it a convertible rather than a tractor. Painted + // panels join the viz reflection feed (more surface mirroring the + // video); dark trim stays matte. All constants: identical on + // every track, every run. + { + const paint = cc(0); + const paintMat = () => vizEnvMaterial(new T.MeshStandardMaterial({ + color: new T.Color(paint[0] * 0.85, paint[1] * 0.85, paint[2] * 0.85), + emissive: new T.Color(paint[0] * 0.1, paint[1] * 0.1, paint[2] * 0.1), + roughness: 0.32, metalness: 0.45, + transparent: opacity < 0.999, opacity, + }), instance, 0.45); + const trimMat = new T.MeshStandardMaterial({ + color: new T.Color(0.04, 0.04, 0.05), roughness: 0.6, metalness: 0.5, + transparent: opacity < 0.999, opacity, + }); + const add = (mesh) => { + instance.car.add(mesh); + instance.bodyMeshes.push(mesh); + return mesh; + }; + // Hood: long low panel ahead of the cabin, nose tilted down, + // thick enough to meet the cabin's lower edge with no gap. + 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; + // 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); + // Bumpers, low and wide, fore and aft. + const bumperF = add(new T.Mesh(new T.BoxGeometry(1.8, 0.22, 0.3), trimMat)); + bumperF.position.set(0, -0.35, -2.05); + const bumperR = add(new T.Mesh(new T.BoxGeometry(1.8, 0.22, 0.3), trimMat)); + bumperR.position.set(0, -0.35, 1.62); + // Side skirts between the wheels. + for (const side of [-1, 1]) { + 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); + 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; + fender.position.set(wx, -0.62, wz); + } + // Grille + headlamps proud of the nose so no face z-fights. + const grille = add(new T.Mesh(new T.BoxGeometry(0.9, 0.16, 0.06), trimMat)); + grille.position.set(0, -0.12, -2.21); + const lampMat = new T.MeshStandardMaterial({ + color: new T.Color(0.1, 0.1, 0.12), + emissive: new T.Color(0.75, 0.82, 1.0), + emissiveIntensity: 2.0, + roughness: 0.3, + }); + for (const side of [-1, 1]) { + const lamp = add(new T.Mesh(new T.CircleGeometry(0.09, 16), lampMat)); + lamp.position.set(side * 0.55, -0.05, -2.22); + lamp.rotation.y = Math.PI; + } + // Taillight bar set into the bumper's rear face. + const tail = add(new T.Mesh( + new T.BoxGeometry(1.2, 0.09, 0.05), + new T.MeshStandardMaterial({ + color: new T.Color(0.08, 0.01, 0.01), + emissive: new T.Color(1.0, 0.08, 0.08), + emissiveIntensity: 1.5, + roughness: 0.4, + }))); + tail.position.set(0, -0.28, 1.78); + // Mirrors on stalks at the cabin front corners. + for (const side of [-1, 1]) { + const stalk = add(new T.Mesh(new T.BoxGeometry(0.04, 0.12, 0.04), trimMat)); + stalk.position.set(side * 0.9, 0.72, -0.95); + const mirror = add(new T.Mesh(new T.BoxGeometry(0.14, 0.09, 0.07), paintMat())); + mirror.position.set(side * 0.94, 0.8, -0.95); + } + // Rear spoiler: blade on two mounts over the trunk. + for (const side of [-1, 1]) { + const mount = add(new T.Mesh(new T.BoxGeometry(0.08, 0.3, 0.12), trimMat)); + mount.position.set(side * 0.5, 0.28, 1.35); + } + const spoiler = add(new T.Mesh(new T.BoxGeometry(1.5, 0.06, 0.35), paintMat())); + spoiler.position.set(0, 0.46, 1.35); + // Exhausts under the rear bumper. + const pipeGeo = new T.CylinderGeometry(0.06, 0.06, 0.25, 12); + pipeGeo.rotateX(Math.PI / 2); + const pipeMat = new T.MeshStandardMaterial({ + color: new T.Color(0.08, 0.08, 0.09), roughness: 0.35, metalness: 0.8, + }); + for (const side of [-1, 1]) { + const pipe = add(new T.Mesh(pipeGeo, pipeMat)); + pipe.position.set(side * 0.35, -0.45, 1.7); + } + } + // Wheels: four dark torus + hub, spin with speed (see below). const wheelGeo = new T.TorusGeometry(0.34, 0.13, 8, 18); const hubGeo = new T.CylinderGeometry(0.2, 0.2, 0.1, 10);