mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 07:18:29 +08:00
334 lines
8.6 KiB
TypeScript
334 lines
8.6 KiB
TypeScript
import { assert } from "../util";
|
|
import { Quat } from "./quaternions";
|
|
|
|
export class Vec2 {
|
|
constructor(public x: number, public y: number) {}
|
|
|
|
get u(): number {
|
|
return this.x;
|
|
}
|
|
|
|
get v(): number {
|
|
return this.y;
|
|
}
|
|
}
|
|
|
|
export function vec2_diff(v: Vec2, w: Vec2): Vec2 {
|
|
return new Vec2(v.x - w.x, v.y - w.y);
|
|
}
|
|
|
|
export class Vec3 {
|
|
constructor(public x: number, public y: number, public z: number) {}
|
|
}
|
|
|
|
/**
|
|
* Stores data in column-major order.
|
|
*/
|
|
export class Mat3 {
|
|
// prettier-ignore
|
|
static of(
|
|
m00: number, m01: number, m02: number,
|
|
m10: number, m11: number, m12: number,
|
|
m20: number, m21: number, m22: number,
|
|
): Mat3 {
|
|
return new Mat3(new Float32Array([
|
|
m00, m10, m20,
|
|
m01, m11, m21,
|
|
m02, m12, m22,
|
|
]));
|
|
}
|
|
|
|
static identity(): Mat3 {
|
|
// prettier-ignore
|
|
return Mat3.of(
|
|
1, 0, 0,
|
|
0, 1, 0,
|
|
0, 0, 1,
|
|
)
|
|
}
|
|
|
|
constructor(readonly data: Float32Array) {
|
|
assert(data.length === 9, "data should be of length 9.");
|
|
}
|
|
|
|
get(i: number, j: number): number {
|
|
return this.data[i + j * 3];
|
|
}
|
|
|
|
set(i: number, j: number, value: number): void {
|
|
this.data[i + j * 3] = value;
|
|
}
|
|
|
|
/**
|
|
* @returns a copy of this matrix.
|
|
*/
|
|
clone(): Mat3 {
|
|
return new Mat3(new Float32Array(this.data));
|
|
}
|
|
|
|
/**
|
|
* Transposes this matrix in-place.
|
|
*/
|
|
transpose(): void {
|
|
let tmp: number;
|
|
const m = this.data;
|
|
|
|
tmp = m[1];
|
|
m[1] = m[3];
|
|
m[3] = tmp;
|
|
|
|
tmp = m[2];
|
|
m[2] = m[6];
|
|
m[6] = tmp;
|
|
|
|
tmp = m[5];
|
|
m[5] = m[7];
|
|
m[7] = tmp;
|
|
}
|
|
|
|
/**
|
|
* Computes the inverse of this matrix and returns it as a new {@link Mat3}.
|
|
*
|
|
* @returns the inverse of this matrix.
|
|
*/
|
|
inverse(): Mat3 {
|
|
const m = this.clone();
|
|
m.invert();
|
|
return m;
|
|
}
|
|
|
|
/**
|
|
* Computes the inverse of this matrix in-place. Will revert to identity if this matrix is
|
|
* degenerate.
|
|
*/
|
|
invert(): void {
|
|
const n11 = this.data[0];
|
|
const n21 = this.data[1];
|
|
const n31 = this.data[2];
|
|
const n12 = this.data[3];
|
|
const n22 = this.data[4];
|
|
const n32 = this.data[5];
|
|
const n13 = this.data[6];
|
|
const n23 = this.data[7];
|
|
const n33 = this.data[8];
|
|
const t11 = n33 * n22 - n32 * n23;
|
|
const t12 = n32 * n13 - n33 * n12;
|
|
const t13 = n23 * n12 - n22 * n13;
|
|
const det = n11 * t11 + n21 * t12 + n31 * t13;
|
|
|
|
if (det === 0) {
|
|
// Revert to identity if matrix is degenerate.
|
|
this.data[0] = 1;
|
|
this.data[1] = 0;
|
|
this.data[2] = 0;
|
|
|
|
this.data[3] = 0;
|
|
this.data[4] = 1;
|
|
this.data[5] = 0;
|
|
|
|
this.data[6] = 0;
|
|
this.data[7] = 0;
|
|
this.data[8] = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
const det_inv = 1 / det;
|
|
|
|
this.data[0] = t11 * det_inv;
|
|
this.data[1] = (n31 * n23 - n33 * n21) * det_inv;
|
|
this.data[2] = (n32 * n21 - n31 * n22) * det_inv;
|
|
|
|
this.data[3] = t12 * det_inv;
|
|
this.data[4] = (n33 * n11 - n31 * n13) * det_inv;
|
|
this.data[5] = (n31 * n12 - n32 * n11) * det_inv;
|
|
|
|
this.data[6] = t13 * det_inv;
|
|
this.data[7] = (n21 * n13 - n23 * n11) * det_inv;
|
|
this.data[8] = (n22 * n11 - n21 * n12) * det_inv;
|
|
}
|
|
}
|
|
|
|
export function mat3_product(a: Mat3, b: Mat3): Mat3 {
|
|
const c = new Mat3(new Float32Array(9));
|
|
mat3_product_into_array(c.data, a, b);
|
|
return c;
|
|
}
|
|
|
|
export function mat3_multiply(a: Mat3, b: Mat3): void {
|
|
const array = new Float32Array(9);
|
|
mat3_product_into_array(array, a, b);
|
|
a.data.set(array);
|
|
}
|
|
|
|
function mat3_product_into_array(array: Float32Array, a: Mat3, b: Mat3): void {
|
|
for (let i = 0; i < 3; i++) {
|
|
for (let j = 0; j < 3; j++) {
|
|
for (let k = 0; k < 3; k++) {
|
|
array[i + j * 3] += a.data[i + k * 3] * b.data[k + j * 3];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Computes the product of `m` and `v` and stores the result in `v`.
|
|
*/
|
|
export function mat3_vec3_multiply(m: Mat3, v: Vec3): void {
|
|
const x = m.get(0, 0) * v.x + m.get(0, 1) * v.y + m.get(0, 2) * v.z;
|
|
const y = m.get(1, 0) * v.x + m.get(1, 1) * v.y + m.get(1, 2) * v.z;
|
|
const z = m.get(2, 0) * v.x + m.get(2, 1) * v.y + m.get(2, 2) * v.z;
|
|
v.x = x;
|
|
v.y = y;
|
|
v.z = z;
|
|
}
|
|
|
|
/**
|
|
* Stores data in column-major order.
|
|
*/
|
|
export class Mat4 {
|
|
// prettier-ignore
|
|
static of(
|
|
m00: number, m01: number, m02: number, m03: number,
|
|
m10: number, m11: number, m12: number, m13: number,
|
|
m20: number, m21: number, m22: number, m23: number,
|
|
m30: number, m31: number, m32: number, m33: number,
|
|
): Mat4 {
|
|
return new Mat4(new Float32Array([
|
|
m00, m10, m20, m30,
|
|
m01, m11, m21, m31,
|
|
m02, m12, m22, m32,
|
|
m03, m13, m23, m33,
|
|
]));
|
|
}
|
|
|
|
static identity(): Mat4 {
|
|
// prettier-ignore
|
|
return Mat4.of(
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, 0, 0, 1,
|
|
)
|
|
}
|
|
|
|
static translation(x: number, y: number, z: number): Mat4 {
|
|
// prettier-ignore
|
|
return Mat4.of(
|
|
1, 0, 0, x,
|
|
0, 1, 0, y,
|
|
0, 0, 1, z,
|
|
0, 0, 0, 1,
|
|
);
|
|
}
|
|
|
|
static scale(x: number, y: number, z: number): Mat4 {
|
|
// prettier-ignore
|
|
return Mat4.of(
|
|
x, 0, 0, 1,
|
|
0, y, 0, 1,
|
|
0, 0, z, 1,
|
|
0, 0, 0, 1,
|
|
);
|
|
}
|
|
|
|
static compose(translation: Vec3, rotation: Quat, scale: Vec3): Mat4 {
|
|
const w = rotation.w;
|
|
const x = rotation.x;
|
|
const y = rotation.y;
|
|
const z = rotation.z;
|
|
const x2 = x + x;
|
|
const y2 = y + y;
|
|
const z2 = z + z;
|
|
const xx = x * x2;
|
|
const xy = x * y2;
|
|
const xz = x * z2;
|
|
const yy = y * y2;
|
|
const yz = y * z2;
|
|
const zz = z * z2;
|
|
const wx = w * x2;
|
|
const wy = w * y2;
|
|
const wz = w * z2;
|
|
|
|
const sx = scale.x;
|
|
const sy = scale.y;
|
|
const sz = scale.z;
|
|
|
|
// prettier-ignore
|
|
return Mat4.of(
|
|
(1 - (yy + zz)) * sx, (xy - wz) * sy, (xz + wy) * sz, translation.x,
|
|
(xy + wz) * sx, (1 - (xx + zz)) * sy, (yz - wx) * sz, translation.y,
|
|
(xz - wy) * sx, (yz + wx) * sy, (1 - (xx + yy)) * sz, translation.z,
|
|
0, 0, 0, 1
|
|
)
|
|
}
|
|
|
|
constructor(readonly data: Float32Array) {
|
|
assert(data.length === 16, "data should be of length 16.");
|
|
}
|
|
|
|
get(i: number, j: number): number {
|
|
return this.data[i + j * 4];
|
|
}
|
|
|
|
set(i: number, j: number, value: number): void {
|
|
this.data[i + j * 4] = value;
|
|
}
|
|
|
|
clone(): Mat4 {
|
|
return new Mat4(new Float32Array(this.data));
|
|
}
|
|
|
|
/**
|
|
* Computes a 3 x 3 surface normal transformation matrix.
|
|
*/
|
|
normal_mat3(): Mat3 {
|
|
// prettier-ignore
|
|
const m = Mat3.of(
|
|
this.data[0], this.data[4], this.data[8],
|
|
this.data[1], this.data[5], this.data[9],
|
|
this.data[2], this.data[6], this.data[10],
|
|
);
|
|
m.invert();
|
|
m.transpose();
|
|
return m;
|
|
}
|
|
}
|
|
|
|
export function mat4_product(a: Mat4, b: Mat4): Mat4 {
|
|
const c = new Mat4(new Float32Array(16));
|
|
mat4_product_into_array(c.data, a, b);
|
|
return c;
|
|
}
|
|
|
|
/**
|
|
* Computes the product of `a` and `b` and stores the result in `a`.
|
|
*/
|
|
export function mat4_multiply(a: Mat4, b: Mat4): void {
|
|
const array = new Float32Array(16);
|
|
mat4_product_into_array(array, a, b);
|
|
a.data.set(array);
|
|
}
|
|
|
|
function mat4_product_into_array(array: Float32Array, a: Mat4, b: Mat4): void {
|
|
for (let i = 0; i < 4; i++) {
|
|
for (let j = 0; j < 4; j++) {
|
|
for (let k = 0; k < 4; k++) {
|
|
array[i + j * 4] += a.data[i + k * 4] * b.data[k + j * 4];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Computes the product of `m` and `v` and stores the result in `v`. Assumes `m` is affine.
|
|
*/
|
|
export function mat4_vec3_multiply(m: Mat4, v: Vec3): void {
|
|
const x = m.get(0, 0) * v.x + m.get(0, 1) * v.y + m.get(0, 2) * v.z + m.get(0, 3);
|
|
const y = m.get(1, 0) * v.x + m.get(1, 1) * v.y + m.get(1, 2) * v.z + m.get(1, 3);
|
|
const z = m.get(2, 0) * v.x + m.get(2, 1) * v.y + m.get(2, 2) * v.z + m.get(2, 3);
|
|
v.x = x;
|
|
v.y = y;
|
|
v.z = z;
|
|
}
|