SOURCE / PINNED RELEASE
Made of little things.
Pursuit PIT
- Release
- 565f4981bdc3…
- Author-recorded commit
- 197afdbed44b…
- License
- LICENSE
- Author’s source reference
- nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-951414e7fd7
Archive hash verified: 3b0485589942fd5c…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Bends the flat, straight road-space scene into the winding, rolling highway on the GPU.
// Everything is modelled as before (x across, y up, z = -(s - originS)); the vertex shader
// then looks up the road frame at that s and moves the vertex onto it. Cars, scenery, ramps
// and effects all follow bends and hills without knowing about them.
import * as THREE from 'three';
import type { RoadFrames } from './road.js';
const GLSL = /* glsl */ `
uniform sampler2D roadFrames;
uniform vec3 roadInfo; // first sample's s relative to the origin, step, count
vec3 bendRoad(vec3 p) {
float u = (-p.z - roadInfo.x) / roadInfo.y;
float uc = clamp(u, 0.0, roadInfo.z - 1.001);
float i = floor(uc);
vec4 a = texelFetch(roadFrames, ivec2(int(i), 0), 0);
vec4 b = texelFetch(roadFrames, ivec2(int(i) + 1, 0), 0);
vec4 fr = mix(a, b, uc - i);
float ext = (u - uc) * roadInfo.y;
float s = sin(fr.z), c = cos(fr.z);
return vec3(fr.x + s * ext + c * p.x, fr.w + p.y, fr.y - c * ext + s * p.x);
}
`;
const PROJECT = /* glsl */ `
vec4 mvPosition = vec4(transformed, 1.0);
#ifdef USE_INSTANCING
mvPosition = instanceMatrix * mvPosition;
#endif
mvPosition = viewMatrix * vec4(bendRoad((modelMatrix * mvPosition).xyz), 1.0);
gl_Position = projectionMatrix * mvPosition;
`;
export class Bender {
private texData: Float32Array;
private tex: THREE.DataTexture;
private uniforms: { roadFrames: { value: THREE.DataTexture }; roadInfo: { value: THREE.Vector3 } };
constructor(private frames: RoadFrames) {
this.texData = new Float32Array(frames.count * 4);
this.tex = new THREE.DataTexture(this.texData, frames.count, 1, THREE.RGBAFormat, THREE.FloatType);
this.tex.minFilter = this.tex.magFilter = THREE.NearestFilter;
this.uniforms = {
roadFrames: { value: this.tex },
roadInfo: { value: new THREE.Vector3(frames.from - frames.origin, frames.step, frames.count) },
};
}
/** Upload the frames after RoadFrames.update. */
sync(): void {
this.texData.set(this.frames.data);
this.tex.needsUpdate = true;
this.uniforms.roadInfo.value.set(this.frames.from - this.frames.origin, this.frames.step, this.frames.count);
}
/**
* Make every material in the scene bend, and stop three culling by unbent bounds.
* Cheap enough per frame; it also catches meshes added later (cars, effects).
*/
patch(scene: THREE.Scene): void {
scene.traverse((o) => {
const mesh = o as THREE.Mesh;
if (!mesh.isMesh || o.userData.flat) return;
o.frustumCulled = false;
const m = mesh.material as THREE.Material;
if (m.userData.bent) return;
m.userData.bent = true;
const uniforms = this.uniforms;
m.onBeforeCompile = (shader) => {
Object.assign(shader.uniforms, uniforms);
shader.vertexShader = shader.vertexShader
.replace('#include <common>', '#include <common>\n' + GLSL)
.replace('#include <project_vertex>', PROJECT);
};
m.customProgramCacheKey = () => 'bend';
m.needsUpdate = true;
});
}
}
