diff --git a/flow-state/src/venues/ride.js b/flow-state/src/venues/ride.js index e3a65f1..0a8a6d8 100644 --- a/flow-state/src/venues/ride.js +++ b/flow-state/src/venues/ride.js @@ -45,9 +45,9 @@ function vizEnvMaterial(mat, instance, strength) { vec3 vDir = normalize( vViewPosition ); vec3 rDir = reflect( vDir, normalize( normal ) ); // Mirrored azimuth shared with the sky dome (see sky.js): - // folds ±π onto the same texel so neither surface shows a seam. - float sU = atan( rDir.z, rDir.x ) * 0.15915494 + 0.5; - sU = 1.0 - abs( 1.0 - 2.0 * sU ); + // quarter-turn offset puts the fold lines fore and aft. + float sU = atan( rDir.z, rDir.x ) * 0.15915494 + 0.5 + 0.25; + sU = 1.0 - abs( 1.0 - 2.0 * fract( sU ) ); vec2 sUv = vec2( sU, asin( clamp( rDir.y, -1.0, 1.0 ) ) * 0.31830988 + 0.5 ); @@ -578,11 +578,27 @@ export const ride = { 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. + // Side skirts between the wheels, tall enough to meet the + // door bottoms so no daylight shows under the doors. 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); + const skirt = add(new T.Mesh(new T.BoxGeometry(0.12, 0.5, 2.0), trimMat)); + skirt.position.set(side * 0.93, -0.32, -0.15); } + // Front side panels: fill between hood edge and wheels, where + // the engine bay showed straight through to the ground. + for (const side of [-1, 1]) { + const panel = add(new T.Mesh(new T.BoxGeometry(0.25, 0.35, 1.0), paintMat())); + panel.position.set(side * 0.825, -0.08, -1.5); + } + // Rear quarter panels between doors and rear arches. + for (const side of [-1, 1]) { + const quarter = add(new T.Mesh(new T.BoxGeometry(0.25, 0.35, 0.9), paintMat())); + quarter.position.set(side * 0.825, -0.08, 1.1); + } + // Floor pan: closes the see-through under the cabin between + // the wheels. Overlaps skirts, bumpers and doors all round. + const floorPan = add(new T.Mesh(new T.BoxGeometry(1.78, 0.16, 3.5), trimMat)); + floorPan.position.set(0, -0.52, -0.25); // 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. @@ -592,9 +608,10 @@ export const ride = { fender.rotation.y = Math.PI / 2; fender.position.set(wx, -0.62, wz); } - // Grille + headlamps proud of the nose so no face z-fights. + // Grille + headlamps seated INTO the bumper face (not floating + // above it): both overlap the bumper top edge. const grille = add(new T.Mesh(new T.BoxGeometry(0.9, 0.16, 0.06), trimMat)); - grille.position.set(0, -0.12, -2.21); + grille.position.set(0, -0.2, -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), @@ -603,7 +620,7 @@ export const ride = { }); 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.position.set(side * 0.55, -0.16, -2.22); lamp.rotation.y = Math.PI; } // Taillight bar set into the bumper's rear face. @@ -616,12 +633,13 @@ export const ride = { roughness: 0.4, }))); tail.position.set(0, -0.28, 1.78); - // Mirrors on stalks at the cabin front corners. + // Mirrors on stalks at the cabin front corners, stalks rooted + // in the cabin wall (not floating beside it). 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 stalk = add(new T.Mesh(new T.BoxGeometry(0.06, 0.12, 0.04), trimMat)); + stalk.position.set(side * 0.88, 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); + mirror.position.set(side * 0.92, 0.8, -0.95); } // Rear spoiler: blade on two mounts over the trunk. for (const side of [-1, 1]) { diff --git a/flow-state/src/venues/sky.js b/flow-state/src/venues/sky.js index eb3f249..3222d27 100644 --- a/flow-state/src/venues/sky.js +++ b/flow-state/src/venues/sky.js @@ -56,10 +56,13 @@ export function buildSky(THREE, { radius = 90 } = {}) { // Mirrored spherical projection shared with the ride's paint // (see vizEnvMaterial): direction to sphere UVs, with the azimuth // folded into a triangle so the ±π wrap lands on the same texel - // instead of a visible seam. + // instead of a visible seam. Offset a quarter turn (shared + // constant with ride.js): the fold lines sit fore and aft, where + // a mirrored join never faces the chase camera head-on — on the + // flanks it mirrored the world at the viewer. vec2 skyVizUv(vec3 dir) { - float u = atan(dir.z, dir.x) * 0.15915494 + 0.5; - u = 1.0 - abs(1.0 - 2.0 * u); + float u = atan(dir.z, dir.x) * 0.15915494 + 0.5 + 0.25; + u = 1.0 - abs(1.0 - 2.0 * fract(u)); float v = asin(clamp(dir.y, -1.0, 1.0)) * 0.31830988 + 0.5; return vec2(u, v); }