@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
307 lines (247 loc) • 6.13 kB
JavaScript
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;