UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

307 lines (247 loc) • 6.13 kB
import { vec3 } from "gl-matrix"; import { EPSILON } from "../../../../../math/EPSILON.js"; const scratch_m3 = new Float32Array(9); /** * Quadric Error Metric * Upper-triangular portion of a symmetric 4x4 matrix encoding a quadric form. * * Layout (10 elements): * [0]=a2 [1]=ab [2]=ac [3]=ad * [4]=b2 [5]=bc [6]=bd * [7]=c2 [8]=cd * [9]=d2 */ export class Quadric3 extends Float64Array { constructor() { super(10); } get a2() { return this[0]; } set a2(v) { this[0] = v; } get ab() { return this[1]; } set ab(v) { this[1] = v; } get ac() { return this[2]; } set ac(v) { this[2] = v; } get ad() { return this[3]; } set ad(v) { this[3] = v; } get b2() { return this[4]; } set b2(v) { this[4] = v; } get bc() { return this[5]; } set bc(v) { this[5] = v; } get bd() { return this[6]; } set bd(v) { this[6] = v; } get c2() { return this[7]; } set c2(v) { this[7] = v; } get cd() { return this[8]; } set cd(v) { this[8] = v; } get d2() { return this[9]; } set d2(v) { this[9] = v; } /** * * @param {ArrayLike<number>|number[]|Float32Array} result */ toVector3(result) { result[0] = this[3]; result[1] = this[6]; result[2] = this[8]; } /** * * @param {ArrayLike<number>|number[]|Float32Array} m3 */ toTensorM3(m3) { const ab = this[1]; const ac = this[2]; const bc = this[5]; m3[0] = this[0]; m3[1] = ab; m3[2] = ac; m3[3] = ab; m3[4] = this[4]; m3[5] = bc; m3[6] = ac; m3[7] = bc; m3[8] = this[7]; } /** * 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[0]; const ab = this[1]; const ac = this[2]; const b2 = this[4]; const bc = this[5]; const c2 = this[7]; const det = a2 * (b2 * c2 - bc * bc) - ab * (ab * c2 - ac * bc) + ac * (ab * bc - ac * b2); if (Math.abs(det) > epsilon) { // avoid division by zero 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; } // determinant is zero, matrix is singular, cannot invert 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[0] = x * x; this[1] = x * y; this[2] = x * z; this[3] = x * w; this[4] = y * y; this[5] = y * z; this[6] = y * w; this[7] = z * z; this[8] = z * w; this[9] = w * w; } /** * Squared distance from the given point to plane encoded by the quadric * @param {number} x * @param {number} y * @param {number} z * @returns {number} */ evaluate(x, y, z) { // Evaluate vAv + 2bv + c return (x * x * this[0]) + (2 * x * y * this[1]) + (2 * x * z * this[2]) + (2 * x * this[3]) // A + (y * y * this[4]) + (2 * y * z * this[5]) + (2 * y * this[6]) // B + (z * z * this[7]) + (2 * z * this[8]) // C + this[9]; // D } /** * Find the optimal vertex position based on the quadric * * @param {number[]} out vector3 * @returns {boolean} */ optimize(out) { // Based on Blender's implementation: https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/source/blender/blenlib/intern/quadric.c if (this.toTensorM3Inverse(scratch_m3, EPSILON)) { this.toVector3(out); vec3.transformMat3(out, out, scratch_m3); vec3.negate(out, out); return true; } return false; } /** * * @param {Quadric3} Q */ add(Q) { this[0] += Q[0]; this[1] += Q[1]; this[2] += Q[2]; this[3] += Q[3]; this[4] += Q[4]; this[5] += Q[5]; this[6] += Q[6]; this[7] += Q[7]; this[8] += Q[8]; this[9] += Q[9]; } /** * * @param {number} v */ multiplyScalar(v) { this[0] *= v; this[1] *= v; this[2] *= v; this[3] *= v; this[4] *= v; this[5] *= v; this[6] *= v; this[7] *= v; this[8] *= v; this[9] *= v; } /** * * @returns {Quadric3} */ clone() { const r = new Quadric3(); r.copy(this); return r; } /** * * @param {Quadric3} other */ copy(other) { this.set(other); } /** * Set all matrix values to 0 */ clear() { this.fill(0); } } /** * Useful for type checking * @readonly * @type {boolean} */ Quadric3.prototype.isQuadric3 = true;