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