UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

303 lines (239 loc) • 6.78 kB
import { vec3 } from "gl-matrix"; import { EPSILON } from "../../../../../math/EPSILON.js"; import { fabsf } from "../../../../../math/fabsf.js"; import { v3_dot } from "../../../../vec3/v3_dot.js"; import { compute_triangle_normal } from "../../../triangle/compute_triangle_normal.js"; const scratch_v3 = new Float32Array(3); const scratch_m3 = new Float32Array(9); /** * Quadric Error Metric * Triangular 4x4 matrix, see https://en.wikipedia.org/wiki/Triangular_matrix */ export class Quadratic3 { // Row 0 a2 = 0; ab = 0; ac = 0; ad = 0; // Row 1 b2 = 0; bc = 0; bd = 0; // Row 2 c2 = 0; cd = 0; // Row 3 d2 = 0; /** * * @param {ArrayLike<number>|number[]|Float32Array} result */ toVector3(result) { result[0] = this.ad; result[1] = this.bd; result[2] = this.cd; } /** * * @param {ArrayLike<number>|number[]|Float32Array} m3 */ toTensorM3(m3) { m3[0] = this.a2; m3[1] = this.ab; m3[2] = this.ac; m3[3] = this.ab; m3[4] = this.b2; m3[5] = this.bc; m3[6] = this.ac; m3[7] = this.bc; m3[8] = this.c2; } /** * Equivalent to taking a tensor followed by matrix inversion * @param {ArrayLike<number>|number[]|Float32Array} m3 * @param {number} epsilon * @returns {boolean} whether operation was successful or not */ toTensorM3Inverse(m3, epsilon) { const a2 = this.a2; const b2 = this.b2; const c2 = this.c2; const bc = this.bc; const ab = this.ab; const ac = this.ac; const det = a2 * (b2 * c2 - bc * bc) - ab * (ab * c2 - ac * bc) + ac * (ab * bc - ac * b2); if (fabsf(det) > epsilon) { const invdet = 1.0 / det; m3[0] = (b2 * c2 - bc * bc) * invdet; m3[3] = (bc * ac - ab * c2) * invdet; m3[6] = (ab * bc - b2 * ac) * invdet; m3[1] = (ac * bc - ab * c2) * invdet; m3[4] = (a2 * c2 - ac * ac) * invdet; m3[7] = (ab * ac - a2 * bc) * invdet; m3[2] = (ab * bc - ac * b2) * invdet; m3[5] = (ac * ab - a2 * bc) * invdet; m3[8] = (a2 * b2 - ab * ab) * invdet; return true; } return false; } /** * Set from plane expressed as a 4 component vector * @param {number} x plane normal * @param {number} y plane normal * @param {number} z plane normal * @param {number} w plane offset */ setFromVector4(x, y, z, w) { this.a2 = x * x; this.ab = x * y; this.ac = x * z; this.ad = x * w; this.b2 = y * y; this.bc = y * z; this.bd = y * w; this.c2 = z * z; this.cd = z * w; this.d2 = w * w; } /** * * @param {TopoTriangle} face */ setFromFace(face) { const v0 = face.vertices[0]; // compute triangle's plane compute_triangle_normal(scratch_v3, v0, face.vertices[1], face.vertices[2]); // compute plane offset const normal_x = scratch_v3[0]; const normal_y = scratch_v3[1]; const normal_z = scratch_v3[2]; const plane_constant = -v3_dot( normal_x, normal_y, normal_z, v0.x, v0.y, v0.z ); this.setFromVector4(normal_x, normal_y, normal_z, plane_constant); } /** * Squared distance from the given point to plane encoded by the quadratic * @param {number} x * @param {number} y * @param {number} z * @returns {number} */ evaluate(x, y, z) { // Evaluate vAv + 2bv + c return (x * x * this.a2) + (2 * x * y * this.ab) + (2 * x * z * this.ac) + (2 * x * this.ad) // A + (y * y * this.b2) + (2 * y * z * this.bc) + (2 * y * this.bd) // B + (z * z * this.c2) + (2 * z * this.cd) // C + this.d2; // D } /** * Find optimal vertex position based on the quadratic * * Based on Blender's implementation: https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/source/blender/blenlib/intern/quadric.c * * @param {number[]} out * @returns {boolean} */ optimize(out) { if (this.toTensorM3Inverse(scratch_m3, EPSILON)) { this.toVector3(out); vec3.transformMat3(out, out, scratch_m3); vec3.negate(out, out); return true; } return false; } /** * * @param {Quadratic3} Q */ add(Q) { // Accumulate coefficients // R0 this.a2 += Q.a2; this.ab += Q.ab; this.ac += Q.ac; this.ad += Q.ad; // R1 this.b2 += Q.b2; this.bc += Q.bc; this.bd += Q.bd; // R2 this.c2 += Q.c2; this.cd += Q.cd; // R3 this.d2 += Q.d2; } /** * * @param {number} v */ multiplyScalar(v) { // R0 this.a2 *= v; this.ab *= v; this.ac *= v; this.ad *= v; // R1 this.b2 *= v; this.bc *= v; this.bd *= v; // R2 this.c2 *= v; this.cd *= v; // R3 this.d2 *= v; } /** * * @returns {Quadratic3} */ clone() { const r = new Quadratic3(); r.copy(this); return r; } /** * * @param {Quadratic3} other */ copy(other) { // R0 this.a2 = other.a2; this.ab = other.ab; this.ac = other.ac; this.ad = other.ad; // R1 this.b2 = other.b2; this.bc = other.bc; this.bd = other.bd; // R2 this.c2 = other.c2; this.cd = other.cd; // R3 this.d2 = other.d2; } /** * Set all matrix values to 0 */ clear() { // Row 0 this.a2 = 0; this.ab = 0; this.ac = 0; this.ad = 0; // Row 1 this.b2 = 0; this.bc = 0; this.bd = 0; // Row 2 this.c2 = 0; this.cd = 0; // Row 3 this.d2 = 0; } }