UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

192 lines (130 loc) 5.15 kB
import { Matrix4, Vector3 } from "three"; import { max2 } from "../../../../../../core/math/max2.js"; import { min2 } from "../../../../../../core/math/min2.js"; import { clamp } from "../../../../../../core/math/clamp.js"; import { vec3 } from "gl-matrix"; import { array_copy } from "../../../../../../core/collection/array/array_copy.js"; /** * Compute a normal vector for Frenet frame based on a tangent (direction in which vertex points) * @param {Vector3} result * @param {Vector3} tangent */ function computeNormalHintFromTangent(result, tangent) { let min = Number.MAX_VALUE; const tx = Math.abs(tangent.x); const ty = Math.abs(tangent.y); const tz = Math.abs(tangent.z); if (ty <= min) { min = ty; result.set(0, 1, 0); } if (tz < min) { min = tz; result.set(0, 0, 1); } if (tx < min) { min = tx; result.set(1, 0, 0); } } /** * @see https://github.com/mrdoob/three.js/blob/c12c9a166a1369cdd58622fff2aff7e3a84305d7/src/extras/core/Curve.js#L260 * @param {Float32Array|number[]} points * @param {boolean} [closed] * @param {Vector3} [normal_hint] * @returns {{normals:Vector3[], binormals:Vector3[], tangents:Vector3[]}} */ export function computeFrenetFrames(points, closed = false, normal_hint) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); const point_count = points.length / 3; const segments = point_count - 1; const v3_0 = []; const v3_1 = []; const p0_a = []; const pR_a = []; const p1_a = []; // compute the tangent vectors for each segment on the curve for (let i = 0; i <= segments; i++) { // get points on either side const i0 = max2(i - 1, 0); const i1 = min2(i + 1, segments); array_copy(points, i0 * 3, p0_a, 0, 3); array_copy(points, i * 3, pR_a, 0, 3); array_copy(points, i1 * 3, p1_a, 0, 3); // get points along the lines close to the reference vec3.sub(v3_0, p0_a, pR_a); vec3.normalize(v3_0, v3_0); vec3.sub(v3_1, p1_a, pR_a); vec3.normalize(v3_1, v3_1); vec3.sub(v3_1, v3_1, v3_0); vec3.normalize(v3_1, v3_1); // write out const tangent_x = v3_1[0]; const tangent_y = v3_1[1]; const tangent_z = v3_1[2]; if (tangent_x === 0 && tangent_y === 0 && tangent_z === 0) { // no tangent, copy previous one if (i > 0) { tangents[i] = tangents[i - 1]; } else { // very first tangent is undefined, set something arbitrary // TODO take normal_hint into account tangents[i] = new Vector3(0, 0, 1); } continue; } tangents[i] = new Vector3( tangent_x, tangent_y, tangent_z ); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[0] = new Vector3(); binormals[0] = new Vector3(); if (normal_hint !== undefined) { normal.copy(normal_hint); } else { computeNormalHintFromTangent(normal, tangents[0]); } vec.crossVectors(tangents[0], normal).normalize(); normals[0].crossVectors(tangents[0], vec); binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve for (let i = 1; i <= segments; i++) { normals[i] = normals[i - 1].clone(); binormals[i] = binormals[i - 1].clone(); vec.crossVectors(tangents[i - 1], tangents[i]); if (vec.length() > Number.EPSILON) { vec.normalize(); const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta)); } binormals[i].crossVectors(tangents[i], normals[i]); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if (closed === true) { let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1)); theta /= segments; if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) { theta = -theta; } for (let i = 1; i <= segments; i++) { // twist a little... normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i)); binormals[i].crossVectors(tangents[i], normals[i]); } } return { tangents: tangents, normals: normals, binormals: binormals }; }