f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 1// Minimal column-major 4x4 matrix helpers (WebGPU layout).
3export type Mat4 = Float32Array;
5export function perspective(fovY: number, aspect: number, near: number, far: number): Mat4 {
6 const f = 1 / Math.tan(fovY / 2);
7 const m = new Float32Array(16);
8 m[0] = f / aspect;
9 m[5] = f;
10 m[10] = far / (near - far);
11 m[11] = -1;
12 m[14] = (near * far) / (near - far);
13 return m;
14}
16export function lookAt(eye: number[], target: number[], up: number[]): Mat4 {
17 const z = normalize([eye[0] - target[0], eye[1] - target[1], eye[2] - target[2]]);
18 const x = normalize(cross(up, z));
19 const y = cross(z, x);
20 const m = new Float32Array(16);
21 m[0] = x[0]; m[1] = y[0]; m[2] = z[0];
22 m[4] = x[1]; m[5] = y[1]; m[6] = z[1];
23 m[8] = x[2]; m[9] = y[2]; m[10] = z[2];
24 m[12] = -dot(x, eye); m[13] = -dot(y, eye); m[14] = -dot(z, eye);
25 m[15] = 1;
26 return m;
27}
29export function multiply(a: Mat4, b: Mat4): Mat4 {
30 const out = new Float32Array(16);
31 for (let c = 0; c < 4; c++) {
32 for (let r = 0; r < 4; r++) {
33 let s = 0;
34 for (let k = 0; k < 4; k++) s += a[k * 4 + r] * b[c * 4 + k];
35 out[c * 4 + r] = s;
36 }
37 }
38 return out;
39}
41function cross(a: number[], b: number[]): number[] {
42 return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
43}
44function dot(a: number[], b: number[]): number {
45 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
46}
47function normalize(a: number[]): number[] {
48 const l = Math.hypot(a[0], a[1], a[2]) || 1;
49 return [a[0] / l, a[1] / l, a[2] / l];
50}