UNPKG

three-stdlib

Version:

stand-alone library of threejs examples

355 lines (354 loc) 13.9 kB
import { BufferGeometry, Vector3, Quaternion, BufferAttribute, Raycaster, Color } from "three"; class RollerCoasterGeometry extends BufferGeometry { constructor(curve, divisions) { super(); const vertices = []; const normals = []; const colors = []; const color1 = [1, 1, 1]; const color2 = [1, 1, 0]; const up = new Vector3(0, 1, 0); const forward = new Vector3(); const right = new Vector3(); const quaternion = new Quaternion(); const prevQuaternion = new Quaternion(); prevQuaternion.setFromAxisAngle(up, Math.PI / 2); const point = new Vector3(); const prevPoint = new Vector3(); prevPoint.copy(curve.getPointAt(0)); const step = [ new Vector3(-0.225, 0, 0), new Vector3(0, -0.05, 0), new Vector3(0, -0.175, 0), new Vector3(0, -0.05, 0), new Vector3(0.225, 0, 0), new Vector3(0, -0.175, 0) ]; const PI2 = Math.PI * 2; let sides = 5; const tube1 = []; for (let i = 0; i < sides; i++) { const angle = i / sides * PI2; tube1.push(new Vector3(Math.sin(angle) * 0.06, Math.cos(angle) * 0.06, 0)); } sides = 6; const tube2 = []; for (let i = 0; i < sides; i++) { const angle = i / sides * PI2; tube2.push(new Vector3(Math.sin(angle) * 0.025, Math.cos(angle) * 0.025, 0)); } const vector = new Vector3(); const normal = new Vector3(); function drawShape(shape, color) { normal.set(0, 0, -1).applyQuaternion(quaternion); for (let j = 0; j < shape.length; j++) { vector.copy(shape[j]); vector.applyQuaternion(quaternion); vector.add(point); vertices.push(vector.x, vector.y, vector.z); normals.push(normal.x, normal.y, normal.z); colors.push(color[0], color[1], color[2]); } normal.set(0, 0, 1).applyQuaternion(quaternion); for (let j = shape.length - 1; j >= 0; j--) { vector.copy(shape[j]); vector.applyQuaternion(quaternion); vector.add(point); vertices.push(vector.x, vector.y, vector.z); normals.push(normal.x, normal.y, normal.z); colors.push(color[0], color[1], color[2]); } } const vector1 = new Vector3(); const vector2 = new Vector3(); const vector3 = new Vector3(); const vector4 = new Vector3(); const normal1 = new Vector3(); const normal2 = new Vector3(); const normal3 = new Vector3(); const normal4 = new Vector3(); function extrudeShape(shape, offset2, color) { for (let j = 0, jl = shape.length; j < jl; j++) { const point1 = shape[j]; const point2 = shape[(j + 1) % jl]; vector1.copy(point1).add(offset2); vector1.applyQuaternion(quaternion); vector1.add(point); vector2.copy(point2).add(offset2); vector2.applyQuaternion(quaternion); vector2.add(point); vector3.copy(point2).add(offset2); vector3.applyQuaternion(prevQuaternion); vector3.add(prevPoint); vector4.copy(point1).add(offset2); vector4.applyQuaternion(prevQuaternion); vector4.add(prevPoint); vertices.push(vector1.x, vector1.y, vector1.z); vertices.push(vector2.x, vector2.y, vector2.z); vertices.push(vector4.x, vector4.y, vector4.z); vertices.push(vector2.x, vector2.y, vector2.z); vertices.push(vector3.x, vector3.y, vector3.z); vertices.push(vector4.x, vector4.y, vector4.z); normal1.copy(point1); normal1.applyQuaternion(quaternion); normal1.normalize(); normal2.copy(point2); normal2.applyQuaternion(quaternion); normal2.normalize(); normal3.copy(point2); normal3.applyQuaternion(prevQuaternion); normal3.normalize(); normal4.copy(point1); normal4.applyQuaternion(prevQuaternion); normal4.normalize(); normals.push(normal1.x, normal1.y, normal1.z); normals.push(normal2.x, normal2.y, normal2.z); normals.push(normal4.x, normal4.y, normal4.z); normals.push(normal2.x, normal2.y, normal2.z); normals.push(normal3.x, normal3.y, normal3.z); normals.push(normal4.x, normal4.y, normal4.z); colors.push(color[0], color[1], color[2]); colors.push(color[0], color[1], color[2]); colors.push(color[0], color[1], color[2]); colors.push(color[0], color[1], color[2]); colors.push(color[0], color[1], color[2]); colors.push(color[0], color[1], color[2]); } } const offset = new Vector3(); for (let i = 1; i <= divisions; i++) { point.copy(curve.getPointAt(i / divisions)); up.set(0, 1, 0); forward.subVectors(point, prevPoint).normalize(); right.crossVectors(up, forward).normalize(); up.crossVectors(forward, right); const angle = Math.atan2(forward.x, forward.z); quaternion.setFromAxisAngle(up, angle); if (i % 2 === 0) { drawShape(step, color2); } extrudeShape(tube1, offset.set(0, -0.125, 0), color2); extrudeShape(tube2, offset.set(0.2, 0, 0), color1); extrudeShape(tube2, offset.set(-0.2, 0, 0), color1); prevPoint.copy(point); prevQuaternion.copy(quaternion); } this.setAttribute("position", new BufferAttribute(new Float32Array(vertices), 3)); this.setAttribute("normal", new BufferAttribute(new Float32Array(normals), 3)); this.setAttribute("color", new BufferAttribute(new Float32Array(colors), 3)); } } class RollerCoasterLiftersGeometry extends BufferGeometry { constructor(curve, divisions) { super(); const vertices = []; const normals = []; const quaternion = new Quaternion(); const up = new Vector3(0, 1, 0); const point = new Vector3(); const tangent = new Vector3(); const tube1 = [new Vector3(0, 0.05, -0.05), new Vector3(0, 0.05, 0.05), new Vector3(0, -0.05, 0)]; const tube2 = [new Vector3(-0.05, 0, 0.05), new Vector3(-0.05, 0, -0.05), new Vector3(0.05, 0, 0)]; const tube3 = [new Vector3(0.05, 0, -0.05), new Vector3(0.05, 0, 0.05), new Vector3(-0.05, 0, 0)]; const vector1 = new Vector3(); const vector2 = new Vector3(); const vector3 = new Vector3(); const vector4 = new Vector3(); const normal1 = new Vector3(); const normal2 = new Vector3(); const normal3 = new Vector3(); const normal4 = new Vector3(); function extrudeShape(shape, fromPoint2, toPoint2) { for (let j = 0, jl = shape.length; j < jl; j++) { const point1 = shape[j]; const point2 = shape[(j + 1) % jl]; vector1.copy(point1); vector1.applyQuaternion(quaternion); vector1.add(fromPoint2); vector2.copy(point2); vector2.applyQuaternion(quaternion); vector2.add(fromPoint2); vector3.copy(point2); vector3.applyQuaternion(quaternion); vector3.add(toPoint2); vector4.copy(point1); vector4.applyQuaternion(quaternion); vector4.add(toPoint2); vertices.push(vector1.x, vector1.y, vector1.z); vertices.push(vector2.x, vector2.y, vector2.z); vertices.push(vector4.x, vector4.y, vector4.z); vertices.push(vector2.x, vector2.y, vector2.z); vertices.push(vector3.x, vector3.y, vector3.z); vertices.push(vector4.x, vector4.y, vector4.z); normal1.copy(point1); normal1.applyQuaternion(quaternion); normal1.normalize(); normal2.copy(point2); normal2.applyQuaternion(quaternion); normal2.normalize(); normal3.copy(point2); normal3.applyQuaternion(quaternion); normal3.normalize(); normal4.copy(point1); normal4.applyQuaternion(quaternion); normal4.normalize(); normals.push(normal1.x, normal1.y, normal1.z); normals.push(normal2.x, normal2.y, normal2.z); normals.push(normal4.x, normal4.y, normal4.z); normals.push(normal2.x, normal2.y, normal2.z); normals.push(normal3.x, normal3.y, normal3.z); normals.push(normal4.x, normal4.y, normal4.z); } } const fromPoint = new Vector3(); const toPoint = new Vector3(); for (let i = 1; i <= divisions; i++) { point.copy(curve.getPointAt(i / divisions)); tangent.copy(curve.getTangentAt(i / divisions)); const angle = Math.atan2(tangent.x, tangent.z); quaternion.setFromAxisAngle(up, angle); if (point.y > 10) { fromPoint.set(-0.75, -0.35, 0); fromPoint.applyQuaternion(quaternion); fromPoint.add(point); toPoint.set(0.75, -0.35, 0); toPoint.applyQuaternion(quaternion); toPoint.add(point); extrudeShape(tube1, fromPoint, toPoint); fromPoint.set(-0.7, -0.3, 0); fromPoint.applyQuaternion(quaternion); fromPoint.add(point); toPoint.set(-0.7, -point.y, 0); toPoint.applyQuaternion(quaternion); toPoint.add(point); extrudeShape(tube2, fromPoint, toPoint); fromPoint.set(0.7, -0.3, 0); fromPoint.applyQuaternion(quaternion); fromPoint.add(point); toPoint.set(0.7, -point.y, 0); toPoint.applyQuaternion(quaternion); toPoint.add(point); extrudeShape(tube3, fromPoint, toPoint); } else { fromPoint.set(0, -0.2, 0); fromPoint.applyQuaternion(quaternion); fromPoint.add(point); toPoint.set(0, -point.y, 0); toPoint.applyQuaternion(quaternion); toPoint.add(point); extrudeShape(tube3, fromPoint, toPoint); } } this.setAttribute("position", new BufferAttribute(new Float32Array(vertices), 3)); this.setAttribute("normal", new BufferAttribute(new Float32Array(normals), 3)); } } class RollerCoasterShadowGeometry extends BufferGeometry { constructor(curve, divisions) { super(); const vertices = []; const up = new Vector3(0, 1, 0); const forward = new Vector3(); const quaternion = new Quaternion(); const prevQuaternion = new Quaternion(); prevQuaternion.setFromAxisAngle(up, Math.PI / 2); const point = new Vector3(); const prevPoint = new Vector3(); prevPoint.copy(curve.getPointAt(0)); prevPoint.y = 0; const vector1 = new Vector3(); const vector2 = new Vector3(); const vector3 = new Vector3(); const vector4 = new Vector3(); for (let i = 1; i <= divisions; i++) { point.copy(curve.getPointAt(i / divisions)); point.y = 0; forward.subVectors(point, prevPoint); const angle = Math.atan2(forward.x, forward.z); quaternion.setFromAxisAngle(up, angle); vector1.set(-0.3, 0, 0); vector1.applyQuaternion(quaternion); vector1.add(point); vector2.set(0.3, 0, 0); vector2.applyQuaternion(quaternion); vector2.add(point); vector3.set(0.3, 0, 0); vector3.applyQuaternion(prevQuaternion); vector3.add(prevPoint); vector4.set(-0.3, 0, 0); vector4.applyQuaternion(prevQuaternion); vector4.add(prevPoint); vertices.push(vector1.x, vector1.y, vector1.z); vertices.push(vector2.x, vector2.y, vector2.z); vertices.push(vector4.x, vector4.y, vector4.z); vertices.push(vector2.x, vector2.y, vector2.z); vertices.push(vector3.x, vector3.y, vector3.z); vertices.push(vector4.x, vector4.y, vector4.z); prevPoint.copy(point); prevQuaternion.copy(quaternion); } this.setAttribute("position", new BufferAttribute(new Float32Array(vertices), 3)); } } class SkyGeometry extends BufferGeometry { constructor() { super(); const vertices = []; for (let i = 0; i < 100; i++) { const x = Math.random() * 800 - 400; const y = Math.random() * 50 + 50; const z = Math.random() * 800 - 400; const size = Math.random() * 40 + 20; vertices.push(x - size, y, z - size); vertices.push(x + size, y, z - size); vertices.push(x - size, y, z + size); vertices.push(x + size, y, z - size); vertices.push(x + size, y, z + size); vertices.push(x - size, y, z + size); } this.setAttribute("position", new BufferAttribute(new Float32Array(vertices), 3)); } } class TreesGeometry extends BufferGeometry { constructor(landscape) { super(); const vertices = []; const colors = []; const raycaster = new Raycaster(); raycaster.ray.direction.set(0, -1, 0); const _color = new Color(); for (let i = 0; i < 2e3; i++) { const x = Math.random() * 500 - 250; const z = Math.random() * 500 - 250; raycaster.ray.origin.set(x, 50, z); const intersections = raycaster.intersectObject(landscape); if (intersections.length === 0) continue; const y = intersections[0].point.y; const height = Math.random() * 5 + 0.5; let angle = Math.random() * Math.PI * 2; vertices.push(x + Math.sin(angle), y, z + Math.cos(angle)); vertices.push(x, y + height, z); vertices.push(x + Math.sin(angle + Math.PI), y, z + Math.cos(angle + Math.PI)); angle += Math.PI / 2; vertices.push(x + Math.sin(angle), y, z + Math.cos(angle)); vertices.push(x, y + height, z); vertices.push(x + Math.sin(angle + Math.PI), y, z + Math.cos(angle + Math.PI)); const random = Math.random() * 0.1; for (let j = 0; j < 6; j++) { _color.setRGB(0.2 + random, 0.4 + random, 0, "srgb"); colors.push(_color.r, _color.g, _color.b); } } this.setAttribute("position", new BufferAttribute(new Float32Array(vertices), 3)); this.setAttribute("color", new BufferAttribute(new Float32Array(colors), 3)); } } export { RollerCoasterGeometry, RollerCoasterLiftersGeometry, RollerCoasterShadowGeometry, SkyGeometry, TreesGeometry }; //# sourceMappingURL=RollerCoaster.js.map