glmaths 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/mat4.ts ADDED
@@ -0,0 +1,1319 @@
1
+ import glm from '.'
2
+ import { equals } from './internalUtils'
3
+ import { Vec2 } from './vec2'
4
+ import { Vec3 } from './vec3'
5
+ import { Quat } from './quat'
6
+ import { Vec4 } from './vec4'
7
+
8
+ /**
9
+ * 4x4 Matrix in column-major order
10
+ * @extends Float32Array
11
+ */
12
+ export class Mat4 extends Float32Array {
13
+
14
+ static get identity() { return new Mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) }
15
+ static get Identity() { return new Mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) }
16
+ static get IDENTITY() { return new Mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) }
17
+
18
+ /**
19
+ * Creates a new 4x4 matrix
20
+ *
21
+ * @param {Number} m00 component in column 0, row 0
22
+ * @param {Number} m01 component in column 0, row 1
23
+ * @param {Number} m02 component in column 0, row 2
24
+ * @param {Number} m03 component in column 0, row 3
25
+ * @param {Number} m10 component in column 1, row 0
26
+ * @param {Number} m11 component in column 1, row 1
27
+ * @param {Number} m12 component in column 1, row 2
28
+ * @param {Number} m13 component in column 1, row 3
29
+ * @param {Number} m20 component in column 2, row 0
30
+ * @param {Number} m21 component in column 2, row 1
31
+ * @param {Number} m22 component in column 2, row 2
32
+ * @param {Number} m23 component in column 2, row 3
33
+ * @param {Number} m30 component in column 3, row 0
34
+ * @param {Number} m31 component in column 3, row 1
35
+ * @param {Number} m32 component in column 3, row 2
36
+ * @param {Number} m33 component in column 3, row 3
37
+ */
38
+ constructor(
39
+ m00 = 0, m01 = 0, m02 = 0, m03 = 0,
40
+ m10 = 0, m11 = 0, m12 = 0, m13 = 0,
41
+ m20 = 0, m21 = 0, m22 = 0, m23 = 0,
42
+ m30 = 0, m31 = 0, m32 = 0, m33 = 0
43
+ ) {
44
+ super(16)
45
+ this[0] = m00
46
+ this[1] = m01
47
+ this[2] = m02
48
+ this[3] = m03
49
+ this[4] = m10
50
+ this[5] = m11
51
+ this[6] = m12
52
+ this[7] = m13
53
+ this[8] = m20
54
+ this[9] = m21
55
+ this[10] = m22
56
+ this[11] = m23
57
+ this[12] = m30
58
+ this[13] = m31
59
+ this[14] = m32
60
+ this[15] = m33
61
+ }
62
+
63
+ /**
64
+ * Creates a new mat4 initialized with values from a matrix
65
+ *
66
+ * @returns {Mat4} a new 4x4 matrix
67
+ */
68
+ clone() {
69
+ return new Mat4(
70
+ this[0], this[1], this[2], this[3],
71
+ this[4], this[5], this[6], this[7],
72
+ this[8], this[9], this[10], this[11],
73
+ this[12], this[13], this[14], this[15]
74
+ )
75
+ }
76
+
77
+ /**
78
+ * Transposes a mat4
79
+ *
80
+ * @param {Mat4} out the receiving matrix, defaults to this
81
+ * @returns {Mat4} out
82
+ */
83
+ transpose(out = glm.ALWAYS_COPY ? new Mat4() : this) {
84
+ if (out === this) {
85
+ const a01 = this[1], a02 = this[2], a03 = this[3]
86
+ const a12 = this[6], a13 = this[7], a23 = this[11]
87
+ out[1] = this[4]
88
+ out[2] = this[8]
89
+ out[3] = this[12]
90
+ out[4] = a01
91
+ out[6] = this[9]
92
+ out[7] = this[13]
93
+ out[8] = a02
94
+ out[9] = a12
95
+ out[11] = this[14]
96
+ out[12] = a03
97
+ out[13] = a13
98
+ out[14] = a23
99
+ } else {
100
+ out[0] = this[0]; out[1] = this[4]; out[2] = this[8]; out[3] = this[12]
101
+ out[4] = this[1]; out[5] = this[5]; out[6] = this[9]; out[7] = this[13]
102
+ out[8] = this[2]; out[9] = this[6]; out[10] = this[10]; out[11] = this[14]
103
+ out[12] = this[3]; out[13] = this[7]; out[14] = this[11]; out[15] = this[15]
104
+ }
105
+ return out
106
+ }
107
+
108
+ /**
109
+ * Inverts a mat4
110
+ *
111
+ * @param {Mat4} out the receiving matrix, defaults to this
112
+ * @returns {Mat4} out
113
+ */
114
+ invert(out = glm.ALWAYS_COPY ? new Mat4() : this) {
115
+ const a00 = this[0], a01 = this[1], a02 = this[2], a03 = this[3]
116
+ const a10 = this[4], a11 = this[5], a12 = this[6], a13 = this[7]
117
+ const a20 = this[8], a21 = this[9], a22 = this[10], a23 = this[11]
118
+ const a30 = this[12], a31 = this[13], a32 = this[14], a33 = this[15]
119
+
120
+ const b00 = a00 * a11 - a01 * a10
121
+ const b01 = a00 * a12 - a02 * a10
122
+ const b02 = a00 * a13 - a03 * a10
123
+ const b03 = a01 * a12 - a02 * a11
124
+ const b04 = a01 * a13 - a03 * a11
125
+ const b05 = a02 * a13 - a03 * a12
126
+ const b06 = a20 * a31 - a21 * a30
127
+ const b07 = a20 * a32 - a22 * a30
128
+ const b08 = a20 * a33 - a23 * a30
129
+ const b09 = a21 * a32 - a22 * a31
130
+ const b10 = a21 * a33 - a23 * a31
131
+ const b11 = a22 * a33 - a23 * a32
132
+
133
+ let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
134
+ if (!det) return null
135
+ det = 1.0 / det
136
+
137
+ out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det
138
+ out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det
139
+ out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det
140
+ out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det
141
+ out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det
142
+ out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det
143
+ out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det
144
+ out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det
145
+ out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det
146
+ out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det
147
+ out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det
148
+ out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det
149
+ out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det
150
+ out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det
151
+ out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det
152
+ out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det
153
+ return out
154
+ }
155
+
156
+ /**
157
+ * Calculates the adjugate of a mat4
158
+ *
159
+ * @param {Mat4} out the receiving matrix, defaults to this
160
+ * @returns {Mat4} out
161
+ */
162
+ adjoint(out = glm.ALWAYS_COPY ? new Mat4() : this) {
163
+ const a00 = this[0], a01 = this[1], a02 = this[2], a03 = this[3]
164
+ const a10 = this[4], a11 = this[5], a12 = this[6], a13 = this[7]
165
+ const a20 = this[8], a21 = this[9], a22 = this[10], a23 = this[11]
166
+ const a30 = this[12], a31 = this[13], a32 = this[14], a33 = this[15]
167
+
168
+ const b00 = a00 * a11 - a01 * a10
169
+ const b01 = a00 * a12 - a02 * a10
170
+ const b02 = a00 * a13 - a03 * a10
171
+ const b03 = a01 * a12 - a02 * a11
172
+ const b04 = a01 * a13 - a03 * a11
173
+ const b05 = a02 * a13 - a03 * a12
174
+ const b06 = a20 * a31 - a21 * a30
175
+ const b07 = a20 * a32 - a22 * a30
176
+ const b08 = a20 * a33 - a23 * a30
177
+ const b09 = a21 * a32 - a22 * a31
178
+ const b10 = a21 * a33 - a23 * a31
179
+ const b11 = a22 * a33 - a23 * a32
180
+
181
+ out[0] = a11 * b11 - a12 * b10 + a13 * b09
182
+ out[1] = a02 * b10 - a01 * b11 - a03 * b09
183
+ out[2] = a31 * b05 - a32 * b04 + a33 * b03
184
+ out[3] = a22 * b04 - a21 * b05 - a23 * b03
185
+ out[4] = a12 * b08 - a10 * b11 - a13 * b07
186
+ out[5] = a00 * b11 - a02 * b08 + a03 * b07
187
+ out[6] = a32 * b02 - a30 * b05 - a33 * b01
188
+ out[7] = a20 * b05 - a22 * b02 + a23 * b01
189
+ out[8] = a10 * b10 - a11 * b08 + a13 * b06
190
+ out[9] = a01 * b08 - a00 * b10 - a03 * b06
191
+ out[10] = a30 * b04 - a31 * b02 + a33 * b00
192
+ out[11] = a21 * b02 - a20 * b04 - a23 * b00
193
+ out[12] = a11 * b07 - a10 * b09 - a12 * b06
194
+ out[13] = a00 * b09 - a01 * b07 + a02 * b06
195
+ out[14] = a31 * b01 - a30 * b03 - a32 * b00
196
+ out[15] = a20 * b03 - a21 * b01 + a22 * b00
197
+ return out
198
+ }
199
+
200
+ /**
201
+ * Calculates the determinant of a mat4
202
+ *
203
+ * @returns {Number} determinant of a mat4
204
+ */
205
+ determinant() {
206
+ const a00 = this[0], a01 = this[1], a02 = this[2], a03 = this[3]
207
+ const a10 = this[4], a11 = this[5], a12 = this[6], a13 = this[7]
208
+ const a20 = this[8], a21 = this[9], a22 = this[10], a23 = this[11]
209
+ const a30 = this[12], a31 = this[13], a32 = this[14], a33 = this[15]
210
+ return (
211
+ (a00 * a11 - a01 * a10) * (a22 * a33 - a23 * a32) -
212
+ (a00 * a12 - a02 * a10) * (a21 * a33 - a23 * a31) +
213
+ (a00 * a13 - a03 * a10) * (a21 * a32 - a22 * a31) +
214
+ (a01 * a12 - a02 * a11) * (a20 * a33 - a23 * a30) -
215
+ (a01 * a13 - a03 * a11) * (a20 * a32 - a22 * a30) +
216
+ (a02 * a13 - a03 * a12) * (a20 * a31 - a21 * a30)
217
+ )
218
+ }
219
+
220
+ /**
221
+ * Multiplies with another matrix, or transforms a vector
222
+ *
223
+ * @param {Vec2 | Vec3 | Vec4 | Mat4} b the second operand
224
+ * @param {Mat4} out the receiving matrix, defaults to this
225
+ * @returns {Mat4} out
226
+ */
227
+ multiply(b: Vec2): Vec2
228
+ multiply(b: Vec3): Vec3
229
+ multiply(b: Vec4): Vec4
230
+ multiply(b: Mat4, out?: Mat4): Mat4
231
+ multiply(b: Mat4 | Vec2 | Vec3 | Vec4, out = glm.ALWAYS_COPY ? new Mat4() : this) {
232
+ if (b instanceof Vec2)
233
+ return b.transformMat4(this)
234
+ if (b instanceof Vec3)
235
+ return b.transformMat4(this)
236
+ if (b instanceof Vec4)
237
+ return b.transformMat4(this)
238
+
239
+ const a00 = this[0], a01 = this[1], a02 = this[2], a03 = this[3]
240
+ const a10 = this[4], a11 = this[5], a12 = this[6], a13 = this[7]
241
+ const a20 = this[8], a21 = this[9], a22 = this[10], a23 = this[11]
242
+ const a30 = this[12], a31 = this[13], a32 = this[14], a33 = this[15]
243
+
244
+ let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]
245
+ out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30
246
+ out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31
247
+ out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32
248
+ out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33
249
+
250
+ b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]
251
+ out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30
252
+ out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31
253
+ out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32
254
+ out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33
255
+
256
+ b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]
257
+ out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30
258
+ out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31
259
+ out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32
260
+ out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33
261
+
262
+ b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]
263
+ out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30
264
+ out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31
265
+ out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32
266
+ out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33
267
+ return out
268
+ }
269
+
270
+ /**
271
+ * Translates a mat4 by the given Vec3
272
+ *
273
+ * @param {Vec3} v vector to translate by
274
+ * @param {Mat4} out the receiving matrix, defaults to this
275
+ * @returns {Mat4} out
276
+ */
277
+ translate(v: Vec3, out = glm.ALWAYS_COPY ? new Mat4() : this) {
278
+ const x = v[0], y = v[1], z = v[2]
279
+ if (out === this) {
280
+ out[12] = this[0] * x + this[4] * y + this[8] * z + this[12]
281
+ out[13] = this[1] * x + this[5] * y + this[9] * z + this[13]
282
+ out[14] = this[2] * x + this[6] * y + this[10] * z + this[14]
283
+ out[15] = this[3] * x + this[7] * y + this[11] * z + this[15]
284
+ } else {
285
+ const a00 = this[0], a01 = this[1], a02 = this[2], a03 = this[3]
286
+ const a10 = this[4], a11 = this[5], a12 = this[6], a13 = this[7]
287
+ const a20 = this[8], a21 = this[9], a22 = this[10], a23 = this[11]
288
+ out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03
289
+ out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13
290
+ out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23
291
+ out[12] = a00 * x + a10 * y + a20 * z + this[12]
292
+ out[13] = a01 * x + a11 * y + a21 * z + this[13]
293
+ out[14] = a02 * x + a12 * y + a22 * z + this[14]
294
+ out[15] = a03 * x + a13 * y + a23 * z + this[15]
295
+ }
296
+ return out
297
+ }
298
+
299
+ /**
300
+ * Scales a mat4 by the dimensions in the given Vec3 not using vectorization
301
+ *
302
+ * @param {Vec3} v the Vec3 to scale the matrix by
303
+ * @param {Mat4} out the receiving matrix, defaults to this
304
+ * @returns {Mat4} out
305
+ */
306
+ scale(v: Vec3, out = glm.ALWAYS_COPY ? new Mat4() : this) {
307
+ const x = v[0], y = v[1], z = v[2]
308
+ out[0] = this[0] * x; out[1] = this[1] * x; out[2] = this[2] * x; out[3] = this[3] * x
309
+ out[4] = this[4] * y; out[5] = this[5] * y; out[6] = this[6] * y; out[7] = this[7] * y
310
+ out[8] = this[8] * z; out[9] = this[9] * z; out[10] = this[10] * z; out[11] = this[11] * z
311
+ out[12] = this[12]; out[13] = this[13]; out[14] = this[14]; out[15] = this[15]
312
+ return out
313
+ }
314
+
315
+ /**
316
+ * Rotates a mat4 by the given angle around the given axis
317
+ *
318
+ * @param {Number} rad the angle to rotate the matrix by
319
+ * @param {Vec3} axis the axis to rotate around
320
+ * @param {Mat4} out the receiving matrix, defaults to this
321
+ * @returns {Mat4} out
322
+ */
323
+ rotate(rad: number, axis: Vec3, out = glm.ALWAYS_COPY ? new Mat4() : this) {
324
+ let x = axis[0], y = axis[1], z = axis[2]
325
+ let len = Math.sqrt(x * x + y * y + z * z)
326
+ if (len < glm.EPSILON) return null
327
+ len = 1 / len
328
+ x *= len; y *= len; z *= len
329
+
330
+ const s = Math.sin(rad), c = Math.cos(rad), t = 1 - c
331
+ const a00 = this[0], a01 = this[1], a02 = this[2], a03 = this[3]
332
+ const a10 = this[4], a11 = this[5], a12 = this[6], a13 = this[7]
333
+ const a20 = this[8], a21 = this[9], a22 = this[10], a23 = this[11]
334
+
335
+ const b00 = x * x * t + c, b01 = y * x * t + z * s, b02 = z * x * t - y * s
336
+ const b10 = x * y * t - z * s, b11 = y * y * t + c, b12 = z * y * t + x * s
337
+ const b20 = x * z * t + y * s, b21 = y * z * t - x * s, b22 = z * z * t + c
338
+
339
+ out[0] = a00 * b00 + a10 * b01 + a20 * b02
340
+ out[1] = a01 * b00 + a11 * b01 + a21 * b02
341
+ out[2] = a02 * b00 + a12 * b01 + a22 * b02
342
+ out[3] = a03 * b00 + a13 * b01 + a23 * b02
343
+ out[4] = a00 * b10 + a10 * b11 + a20 * b12
344
+ out[5] = a01 * b10 + a11 * b11 + a21 * b12
345
+ out[6] = a02 * b10 + a12 * b11 + a22 * b12
346
+ out[7] = a03 * b10 + a13 * b11 + a23 * b12
347
+ out[8] = a00 * b20 + a10 * b21 + a20 * b22
348
+ out[9] = a01 * b20 + a11 * b21 + a21 * b22
349
+ out[10] = a02 * b20 + a12 * b21 + a22 * b22
350
+ out[11] = a03 * b20 + a13 * b21 + a23 * b22
351
+ out[12] = this[12]; out[13] = this[13]; out[14] = this[14]; out[15] = this[15]
352
+ return out
353
+ }
354
+
355
+ /**
356
+ * Rotates a mat4 by the given angle around the X axis
357
+ *
358
+ * @param {Number} rad the angle to rotate the matrix by
359
+ * @param {Mat4} out the receiving matrix, defaults to this
360
+ * @returns {Mat4} out
361
+ */
362
+ rotateX(rad: number, out = glm.ALWAYS_COPY ? new Mat4() : this) {
363
+ const s = Math.sin(rad), c = Math.cos(rad)
364
+ const a10 = this[4], a11 = this[5], a12 = this[6], a13 = this[7]
365
+ const a20 = this[8], a21 = this[9], a22 = this[10], a23 = this[11]
366
+
367
+ out[4] = a10 * c + a20 * s; out[5] = a11 * c + a21 * s
368
+ out[6] = a12 * c + a22 * s; out[7] = a13 * c + a23 * s
369
+ out[8] = a20 * c - a10 * s; out[9] = a21 * c - a11 * s
370
+ out[10] = a22 * c - a12 * s; out[11] = a23 * c - a13 * s
371
+
372
+ if (out !== this) {
373
+ out[0] = this[0]; out[1] = this[1]; out[2] = this[2]; out[3] = this[3]
374
+ out[12] = this[12]; out[13] = this[13]; out[14] = this[14]; out[15] = this[15]
375
+ }
376
+ return out
377
+ }
378
+
379
+ /**
380
+ * Rotates a mat4 by the given angle around the Y axis
381
+ *
382
+ * @param {Number} rad the angle to rotate the matrix by
383
+ * @param {Mat4} out the receiving matrix, defaults to this
384
+ * @returns {Mat4} out
385
+ */
386
+ rotateY(rad: number, out = glm.ALWAYS_COPY ? new Mat4() : this) {
387
+ const s = Math.sin(rad), c = Math.cos(rad)
388
+ const a00 = this[0], a01 = this[1], a02 = this[2], a03 = this[3]
389
+ const a20 = this[8], a21 = this[9], a22 = this[10], a23 = this[11]
390
+
391
+ out[0] = a00 * c - a20 * s; out[1] = a01 * c - a21 * s
392
+ out[2] = a02 * c - a22 * s; out[3] = a03 * c - a23 * s
393
+ out[8] = a00 * s + a20 * c; out[9] = a01 * s + a21 * c
394
+ out[10] = a02 * s + a22 * c; out[11] = a03 * s + a23 * c
395
+
396
+ if (out !== this) {
397
+ out[4] = this[4]; out[5] = this[5]; out[6] = this[6]; out[7] = this[7]
398
+ out[12] = this[12]; out[13] = this[13]; out[14] = this[14]; out[15] = this[15]
399
+ }
400
+ return out
401
+ }
402
+
403
+ /**
404
+ * Rotates a mat4 by the given angle around the Z axis
405
+ *
406
+ * @param {Number} rad the angle to rotate the matrix by
407
+ * @param {Mat4} out the receiving matrix, defaults to this
408
+ * @returns {Mat4} out
409
+ */
410
+ rotateZ(rad: number, out = glm.ALWAYS_COPY ? new Mat4() : this) {
411
+ const s = Math.sin(rad), c = Math.cos(rad)
412
+ const a00 = this[0], a01 = this[1], a02 = this[2], a03 = this[3]
413
+ const a10 = this[4], a11 = this[5], a12 = this[6], a13 = this[7]
414
+
415
+ out[0] = a00 * c + a10 * s; out[1] = a01 * c + a11 * s
416
+ out[2] = a02 * c + a12 * s; out[3] = a03 * c + a13 * s
417
+ out[4] = a10 * c - a00 * s; out[5] = a11 * c - a01 * s
418
+ out[6] = a12 * c - a02 * s; out[7] = a13 * c - a03 * s
419
+
420
+ if (out !== this) {
421
+ out[8] = this[8]; out[9] = this[9]; out[10] = this[10]; out[11] = this[11]
422
+ out[12] = this[12]; out[13] = this[13]; out[14] = this[14]; out[15] = this[15]
423
+ }
424
+ return out
425
+ }
426
+
427
+ /**
428
+ * Returns the translation vector component of a transformation matrix
429
+ *
430
+ * @param {Vec3} out vector to receive the translation values, defaults to new Vec3()
431
+ * @returns {Vec3} out
432
+ */
433
+ getTranslation(out = new Vec3()) {
434
+ out[0] = this[12]; out[1] = this[13]; out[2] = this[14]
435
+ return out
436
+ }
437
+
438
+ /**
439
+ * Returns the scaling factor component of a transformation matrix
440
+ *
441
+ * @param {Vec3} out vector to receive the scaling factor values, defaults to new Vec3()
442
+ * @returns {Vec3} out
443
+ */
444
+ getScaling(out = new Vec3()) {
445
+ const m11 = this[0], m12 = this[1], m13 = this[2]
446
+ const m21 = this[4], m22 = this[5], m23 = this[6]
447
+ const m31 = this[8], m32 = this[9], m33 = this[10]
448
+ out[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13)
449
+ out[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23)
450
+ out[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33)
451
+ return out
452
+ }
453
+
454
+ /**
455
+ * Returns a quaternion representing the rotational component
456
+ * of a transformation matrix. If a matrix is built with
457
+ * fromRotationTranslation, the returned quaternion will be the
458
+ * same as the quaternion originally supplied.
459
+ *
460
+ * @param {Quat} out quaternion to receive the rotation values, defaults to new Quat()
461
+ * @returns {Quat} out
462
+ */
463
+ getRotation(out = new Quat()) {
464
+ const scaling = this.getScaling()
465
+ const is1 = 1 / scaling[0], is2 = 1 / scaling[1], is3 = 1 / scaling[2]
466
+
467
+ const sm11 = this[0] * is1, sm12 = this[1] * is2, sm13 = this[2] * is3
468
+ const sm21 = this[4] * is1, sm22 = this[5] * is2, sm23 = this[6] * is3
469
+ const sm31 = this[8] * is1, sm32 = this[9] * is2, sm33 = this[10] * is3
470
+
471
+ const trace = sm11 + sm22 + sm33
472
+ let S = 0
473
+
474
+ if (trace > 0) {
475
+ S = Math.sqrt(trace + 1.0) * 2
476
+ out[3] = 0.25 * S
477
+ out[0] = (sm23 - sm32) / S
478
+ out[1] = (sm31 - sm13) / S
479
+ out[2] = (sm12 - sm21) / S
480
+ } else if (sm11 > sm22 && sm11 > sm33) {
481
+ S = Math.sqrt(1.0 + sm11 - sm22 - sm33) * 2
482
+ out[3] = (sm23 - sm32) / S
483
+ out[0] = 0.25 * S
484
+ out[1] = (sm12 + sm21) / S
485
+ out[2] = (sm31 + sm13) / S
486
+ } else if (sm22 > sm33) {
487
+ S = Math.sqrt(1.0 + sm22 - sm11 - sm33) * 2
488
+ out[3] = (sm31 - sm13) / S
489
+ out[0] = (sm12 + sm21) / S
490
+ out[1] = 0.25 * S
491
+ out[2] = (sm23 + sm32) / S
492
+ } else {
493
+ S = Math.sqrt(1.0 + sm33 - sm11 - sm22) * 2
494
+ out[3] = (sm12 - sm21) / S
495
+ out[0] = (sm31 + sm13) / S
496
+ out[1] = (sm23 + sm32) / S
497
+ out[2] = 0.25 * S
498
+ }
499
+ return out
500
+ }
501
+
502
+ /**
503
+ * Decomposes a transformation matrix into its rotation, translation, and scale components
504
+ *
505
+ * @param {Quat} out_r quaternion to receive the rotation component, defaults to new Quat()
506
+ * @param {Vec3} out_t vector to receive the translation component, defaults to new Vec3()
507
+ * @param {Vec3} out_s vector to receive the scaling component, defaults to new Vec3()
508
+ * @returns {Quat} out_r
509
+ */
510
+ decompose(out_r = new Quat(), out_t = new Vec3(), out_s = new Vec3()) {
511
+ out_t[0] = this[12]; out_t[1] = this[13]; out_t[2] = this[14]
512
+
513
+ const m11 = this[0], m12 = this[1], m13 = this[2]
514
+ const m21 = this[4], m22 = this[5], m23 = this[6]
515
+ const m31 = this[8], m32 = this[9], m33 = this[10]
516
+
517
+ out_s[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13)
518
+ out_s[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23)
519
+ out_s[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33)
520
+
521
+ const is1 = 1 / out_s[0], is2 = 1 / out_s[1], is3 = 1 / out_s[2]
522
+
523
+ const sm11 = m11 * is1, sm12 = m12 * is2, sm13 = m13 * is3
524
+ const sm21 = m21 * is1, sm22 = m22 * is2, sm23 = m23 * is3
525
+ const sm31 = m31 * is1, sm32 = m32 * is2, sm33 = m33 * is3
526
+
527
+ const trace = sm11 + sm22 + sm33
528
+ let S = 0
529
+
530
+ if (trace > 0) {
531
+ S = Math.sqrt(trace + 1.0) * 2
532
+ out_r[3] = 0.25 * S
533
+ out_r[0] = (sm23 - sm32) / S
534
+ out_r[1] = (sm31 - sm13) / S
535
+ out_r[2] = (sm12 - sm21) / S
536
+ } else if (sm11 > sm22 && sm11 > sm33) {
537
+ S = Math.sqrt(1.0 + sm11 - sm22 - sm33) * 2
538
+ out_r[3] = (sm23 - sm32) / S
539
+ out_r[0] = 0.25 * S
540
+ out_r[1] = (sm12 + sm21) / S
541
+ out_r[2] = (sm31 + sm13) / S
542
+ } else if (sm22 > sm33) {
543
+ S = Math.sqrt(1.0 + sm22 - sm11 - sm33) * 2
544
+ out_r[3] = (sm31 - sm13) / S
545
+ out_r[0] = (sm12 + sm21) / S
546
+ out_r[1] = 0.25 * S
547
+ out_r[2] = (sm23 + sm32) / S
548
+ } else {
549
+ S = Math.sqrt(1.0 + sm33 - sm11 - sm22) * 2
550
+ out_r[3] = (sm12 - sm21) / S
551
+ out_r[0] = (sm31 + sm13) / S
552
+ out_r[1] = (sm23 + sm32) / S
553
+ out_r[2] = 0.25 * S
554
+ }
555
+ return out_r
556
+ }
557
+
558
+ /**
559
+ * Creates a matrix from a vector translation
560
+ *
561
+ * @param {Vec3} v translation vector
562
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
563
+ * @returns {Mat4} out
564
+ */
565
+ static fromTranslation(v: Vec3, out = new Mat4()) {
566
+ out[0] = out[5] = out[10] = out[15] = 1
567
+ out[1] = out[2] = out[3] = out[4] = out[6] = out[7] = out[8] = out[9] = out[11] = 0
568
+ out[12] = v[0]
569
+ out[13] = v[1]
570
+ out[14] = v[2]
571
+ return out
572
+ }
573
+
574
+ /**
575
+ * Creates a matrix from a vector scaling
576
+ *
577
+ * @param {Vec3} v scaling vector
578
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
579
+ * @returns {Mat4} out
580
+ */
581
+ static fromScaling(v: Vec3, out = new Mat4()) {
582
+ out[0] = v[0]
583
+ out[5] = v[1]
584
+ out[10] = v[2]
585
+ out[1] = out[2] = out[3] = out[4] = out[6] = out[7] = out[8] = out[9] = out[11] = out[12] = out[13] = out[14] = 0
586
+ out[15] = 1
587
+ return out
588
+ }
589
+
590
+ /**
591
+ * Creates a matrix from a given angle around a given axis
592
+ *
593
+ * @param {Number} rad the angle to rotate the matrix by
594
+ * @param {Vec3} axis the axis to rotate around
595
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
596
+ * @returns {Mat4} out
597
+ */
598
+ static fromRotation(rad: number, axis: Vec3, out = new Mat4()) {
599
+ let x = axis[0], y = axis[1], z = axis[2]
600
+ let len = Math.sqrt(x * x + y * y + z * z)
601
+ if (len < glm.EPSILON) return null
602
+ len = 1 / len
603
+ x *= len; y *= len; z *= len
604
+
605
+ const s = Math.sin(rad), c = Math.cos(rad), t = 1 - c
606
+
607
+ out[0] = x * x * t + c; out[1] = y * x * t + z * s; out[2] = z * x * t - y * s; out[3] = 0
608
+ out[4] = x * y * t - z * s; out[5] = y * y * t + c; out[6] = z * y * t + x * s; out[7] = 0
609
+ out[8] = x * z * t + y * s; out[9] = y * z * t - x * s; out[10] = z * z * t + c; out[11] = 0
610
+ out[12] = out[13] = out[14] = 0
611
+ out[15] = 1
612
+ return out
613
+ }
614
+
615
+ /**
616
+ * Creates a matrix from the given angle around the X axis
617
+ *
618
+ * @param {Number} rad the angle to rotate the matrix by
619
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
620
+ * @returns {Mat4} out
621
+ */
622
+ static fromXRotation(rad: number, out = new Mat4()) {
623
+ const s = Math.sin(rad), c = Math.cos(rad)
624
+ out[0] = 1
625
+ out[1] = out[2] = out[3] = out[4] = out[7] = out[8] = out[11] = out[12] = out[13] = out[14] = 0
626
+ out[5] = c
627
+ out[6] = s
628
+ out[9] = -s
629
+ out[10] = c
630
+ out[15] = 1
631
+ return out
632
+ }
633
+
634
+ /**
635
+ * Creates a matrix from the given angle around the Y axis
636
+ *
637
+ * @param {Number} rad the angle to rotate the matrix by
638
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
639
+ * @returns {Mat4} out
640
+ */
641
+ static fromYRotation(rad: number, out = new Mat4()) {
642
+ const s = Math.sin(rad), c = Math.cos(rad)
643
+ out[0] = c
644
+ out[1] = out[3] = out[4] = out[6] = out[7] = out[9] = out[11] = out[12] = out[13] = out[14] = 0
645
+ out[2] = -s
646
+ out[5] = out[15] = 1
647
+ out[8] = s
648
+ out[10] = c
649
+ return out
650
+ }
651
+
652
+ /**
653
+ * Creates a matrix from the given angle around the Z axis
654
+ *
655
+ * @param {Number} rad the angle to rotate the matrix by
656
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
657
+ * @returns {Mat4} out
658
+ */
659
+ static fromZRotation(rad: number, out = new Mat4()) {
660
+ const s = Math.sin(rad), c = Math.cos(rad)
661
+ out[0] = c
662
+ out[1] = s
663
+ out[4] = -s
664
+ out[5] = c
665
+ out[2] = out[3] = out[6] = out[7] = out[8] = out[9] = out[11] = out[12] = out[13] = out[14] = 0
666
+ out[10] = out[15] = 1
667
+ return out
668
+ }
669
+
670
+ /**
671
+ * Creates a matrix from a quaternion rotation and vector translation
672
+ *
673
+ * @param {Quat} q rotation quaternion
674
+ * @param {Vec3} v translation vector
675
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
676
+ * @returns {Mat4} out
677
+ */
678
+ static fromRotationTranslation(q: Quat, v: Vec3, out = new Mat4()) {
679
+ const x = q[0], y = q[1], z = q[2], w = q[3]
680
+ const x2 = x + x, y2 = y + y, z2 = z + z
681
+ const xx = x * x2, xy = x * y2, xz = x * z2
682
+ const yy = y * y2, yz = y * z2, zz = z * z2
683
+ const wx = w * x2, wy = w * y2, wz = w * z2
684
+
685
+ out[0] = 1 - (yy + zz); out[1] = xy + wz; out[2] = xz - wy
686
+ out[3] = out[7] = out[11] = 0
687
+ out[4] = xy - wz; out[5] = 1 - (xx + zz); out[6] = yz + wx
688
+ out[8] = xz + wy; out[9] = yz - wx; out[10] = 1 - (xx + yy)
689
+ out[12] = v[0]; out[13] = v[1]; out[14] = v[2]; out[15] = 1
690
+ return out
691
+ }
692
+
693
+ /**
694
+ * Creates a matrix from a quaternion rotation, vector translation, and vector scale
695
+ *
696
+ * @param {Quat} q rotation quaternion
697
+ * @param {Vec3} v translation vector
698
+ * @param {Vec3} s scaling vector
699
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
700
+ * @returns {Mat4} out
701
+ */
702
+ static fromRotationTranslationScale(q: Quat, v: Vec3, s: Vec3, out = new Mat4()) {
703
+ const x = q[0], y = q[1], z = q[2], w = q[3]
704
+ const x2 = x + x, y2 = y + y, z2 = z + z
705
+ const xx = x * x2, xy = x * y2, xz = x * z2
706
+ const yy = y * y2, yz = y * z2, zz = z * z2
707
+ const wx = w * x2, wy = w * y2, wz = w * z2
708
+ const sx = s[0], sy = s[1], sz = s[2]
709
+
710
+ out[0] = (1 - (yy + zz)) * sx; out[1] = (xy + wz) * sx; out[2] = (xz - wy) * sx;
711
+ out[3] = out[7] = out[11] = 0
712
+ out[4] = (xy - wz) * sy; out[5] = (1 - (xx + zz)) * sy; out[6] = (yz + wx) * sy;
713
+ out[8] = (xz + wy) * sz; out[9] = (yz - wx) * sz; out[10] = (1 - (xx + yy)) * sz;
714
+ out[12] = v[0]; out[13] = v[1]; out[14] = v[2]; out[15] = 1
715
+ return out
716
+ }
717
+
718
+ /**
719
+ * Creates a matrix from a quaternion rotation, vector translation, vector scale, and origin
720
+ *
721
+ * @param {Quat} q rotation quaternion
722
+ * @param {Vec3} v translation vector
723
+ * @param {Vec3} s scaling vector
724
+ * @param {Vec3} o the origin vector
725
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
726
+ * @returns {Mat4} out
727
+ */
728
+ static fromRotationTranslationScaleOrigin(q: Quat, v: Vec3, s: Vec3, o: Vec3, out = new Mat4()) {
729
+ const x = q[0], y = q[1], z = q[2], w = q[3]
730
+ const x2 = x + x, y2 = y + y, z2 = z + z
731
+ const xx = x * x2, xy = x * y2, xz = x * z2
732
+ const yy = y * y2, yz = y * z2, zz = z * z2
733
+ const wx = w * x2, wy = w * y2, wz = w * z2
734
+ const sx = s[0], sy = s[1], sz = s[2]
735
+ const ox = o[0], oy = o[1], oz = o[2]
736
+
737
+ const out0 = (1 - (yy + zz)) * sx
738
+ const out1 = (xy + wz) * sx
739
+ const out2 = (xz - wy) * sx
740
+ const out4 = (xy - wz) * sy
741
+ const out5 = (1 - (xx + zz)) * sy
742
+ const out6 = (yz + wx) * sy
743
+ const out8 = (xz + wy) * sz
744
+ const out9 = (yz - wx) * sz
745
+ const out10 = (1 - (xx + yy)) * sz
746
+
747
+ out[0] = out0; out[1] = out1; out[2] = out2
748
+ out[4] = out4; out[5] = out5; out[6] = out6
749
+ out[8] = out8; out[9] = out9; out[10] = out10
750
+ out[3] = out[7] = out[11] = 0
751
+ out[12] = v[0] + ox - (out0 * ox + out4 * oy + out8 * oz)
752
+ out[13] = v[1] + oy - (out1 * ox + out5 * oy + out9 * oz)
753
+ out[14] = v[2] + oz - (out2 * ox + out6 * oy + out10 * oz)
754
+ out[15] = 1
755
+ return out
756
+ }
757
+
758
+ /**
759
+ * Calculates a 4x4 matrix from the given quaternion
760
+ *
761
+ * @param {Quat} q quaternion to create matrix from
762
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
763
+ * @returns {Mat4} out
764
+ */
765
+ static fromQuat(q: Quat, out = new Mat4()) {
766
+ const x = q[0], y = q[1], z = q[2], w = q[3]
767
+ const x2 = x + x, y2 = y + y, z2 = z + z
768
+ const xx = x * x2, yx = y * x2, yy = y * y2
769
+ const zx = z * x2, zy = z * y2, zz = z * z2
770
+ const wx = w * x2, wy = w * y2, wz = w * z2
771
+
772
+ out[0] = 1 - yy - zz; out[1] = yx + wz; out[2] = zx - wy
773
+ out[4] = yx - wz; out[5] = 1 - xx - zz; out[6] = zy + wx
774
+ out[8] = zx + wy; out[9] = zy - wx; out[10] = 1 - xx - yy
775
+ out[3] = out[7] = out[11] = out[12] = out[13] = out[14] = 0
776
+ out[15] = 1
777
+ return out
778
+ }
779
+
780
+ /**
781
+ * Generates a frustum matrix with the given bounds
782
+ *
783
+ * @param {Number} left left bound of the frustum
784
+ * @param {Number} right right bound of the frustum
785
+ * @param {Number} bottom bottom bound of the frustum
786
+ * @param {Number} top top bound of the frustum
787
+ * @param {Number} near near bound of the frustum
788
+ * @param {Number} far far bound of the frustum
789
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
790
+ * @returns {Mat4} out
791
+ */
792
+ static frustum(left: number, right: number, bottom: number, top: number, near: number, far: number, out = new Mat4()) {
793
+ const rl = 1 / (right - left)
794
+ const tb = 1 / (top - bottom)
795
+ const nf = 1 / (near - far)
796
+ const lh = glm.LEFT_HANDED
797
+ out[0] = near * 2 * rl
798
+ out[5] = near * 2 * tb
799
+ out[8] = (right + left) * rl; out[9] = (top + bottom) * tb; out[10] = lh ? -(far + near) * nf : (far + near) * nf; out[11] = lh ? 1 : -1
800
+ out[1] = out[2] = out[3] = out[4] = out[6] = out[7] = out[12] = out[13] = out[15] = 0
801
+ out[14] = far * near * 2 * nf
802
+ return out
803
+ }
804
+
805
+ /**
806
+ * Generates a perspective projection matrix with the given bounds.
807
+ * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1],
808
+ * which matches WebGL/OpenGL's clip volume.
809
+ *
810
+ * @param {Number} fovy vertical field of view in radians
811
+ * @param {Number} aspect aspect ratio, typically viewport width / height
812
+ * @param {Number} near near bound of the frustum
813
+ * @param {Number | null} far far bound of the frustum, can be null or Infinity
814
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
815
+ * @returns {Mat4} out
816
+ */
817
+ static perspectiveNO(fovy: number, aspect: number, near: number, far: number | null, out = new Mat4()) {
818
+ const f = 1.0 / Math.tan(fovy / 2)
819
+ const lh = glm.LEFT_HANDED
820
+ out[0] = f / aspect
821
+ out[1] = out[2] = out[3] = out[4] = out[6] = out[7] = out[8] = out[9] = out[12] = out[13] = out[15] = 0
822
+ out[5] = f
823
+ out[11] = lh ? 1 : -1
824
+ if (far != null && far !== Infinity) {
825
+ const nf = 1 / (near - far)
826
+ out[10] = lh ? -(far + near) * nf : (far + near) * nf
827
+ out[14] = 2 * far * near * nf
828
+ } else {
829
+ out[10] = lh ? 1 : -1
830
+ out[14] = -2 * near
831
+ }
832
+ return out
833
+ }
834
+ static perspective = this.perspectiveNO
835
+
836
+ /**
837
+ * Generates a perspective projection matrix with the given bounds.
838
+ * The near/far clip planes correspond to a normalized device coordinate Z range of [0, 1],
839
+ * which matches WebGPU/Vulkan/DirectX/Metal's clip volume.
840
+ *
841
+ * @param {Number} fovy vertical field of view in radians
842
+ * @param {Number} aspect aspect ratio, typically viewport width / height
843
+ * @param {Number} near near bound of the frustum
844
+ * @param {Number | null} far far bound of the frustum, can be null or Infinity
845
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
846
+ * @returns {Mat4} out
847
+ */
848
+ static perspectiveZO(fovy: number, aspect: number, near: number, far: number | null, out = new Mat4()) {
849
+ const f = 1.0 / Math.tan(fovy / 2)
850
+ const lh = glm.LEFT_HANDED
851
+ out[0] = f / aspect; out[1] = 0; out[2] = 0; out[3] = 0
852
+ out[4] = 0; out[5] = f; out[6] = 0; out[7] = 0
853
+ out[8] = 0; out[9] = 0; out[11] = lh ? 1 : -1
854
+ out[12] = 0; out[13] = 0; out[15] = 0
855
+ if (far != null && far !== Infinity) {
856
+ const nf = 1 / (near - far)
857
+ out[10] = lh ? -far * nf : far * nf
858
+ out[14] = far * near * nf
859
+ } else {
860
+ out[10] = lh ? 1 : -1
861
+ out[14] = -near
862
+ }
863
+ return out
864
+ }
865
+
866
+ /**
867
+ * Generates a perspective projection matrix with the given field of view
868
+ *
869
+ * @param {Object} fov object containing upDegrees, downDegrees, leftDegrees, rightDegrees
870
+ * @param {Number} near near bound of the frustum
871
+ * @param {Number} far far bound of the frustum
872
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
873
+ * @returns {Mat4} out
874
+ */
875
+ static perspectiveFromFieldOfView(fov: { upDegrees: number, downDegrees: number, leftDegrees: number, rightDegrees: number }, near: number, far: number, out = new Mat4()) {
876
+ const upTan = Math.tan((fov.upDegrees * Math.PI) / 180.0)
877
+ const downTan = Math.tan((fov.downDegrees * Math.PI) / 180.0)
878
+ const leftTan = Math.tan((fov.leftDegrees * Math.PI) / 180.0)
879
+ const rightTan = Math.tan((fov.rightDegrees * Math.PI) / 180.0)
880
+ const xScale = 2.0 / (leftTan + rightTan)
881
+ const yScale = 2.0 / (upTan + downTan)
882
+
883
+ out[0] = xScale; out[1] = 0; out[2] = 0; out[3] = 0
884
+ out[4] = 0; out[5] = yScale; out[6] = 0; out[7] = 0
885
+ const lh = glm.LEFT_HANDED
886
+ out[8] = -((leftTan - rightTan) * xScale * 0.5)
887
+ out[9] = (upTan - downTan) * yScale * 0.5
888
+ out[10] = lh ? -far / (near - far) : far / (near - far)
889
+ out[11] = lh ? 1.0 : -1.0
890
+ out[12] = 0; out[13] = 0
891
+ out[14] = (far * near) / (near - far)
892
+ out[15] = 0
893
+ return out
894
+ }
895
+
896
+ /**
897
+ * Generates an orthogonal projection matrix with the given bounds.
898
+ * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1],
899
+ * which matches WebGL/OpenGL's clip volume.
900
+ *
901
+ * @param {Number} left left bound of the frustum
902
+ * @param {Number} right right bound of the frustum
903
+ * @param {Number} bottom bottom bound of the frustum
904
+ * @param {Number} top top bound of the frustum
905
+ * @param {Number} near near bound of the frustum
906
+ * @param {Number} far far bound of the frustum
907
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
908
+ * @returns {Mat4} out
909
+ */
910
+ static orthoNO(left: number, right: number, bottom: number, top: number, near: number, far: number, out = new Mat4()) {
911
+ const lr = 1 / (left - right)
912
+ const bt = 1 / (bottom - top)
913
+ const nf = 1 / (near - far)
914
+ const s = glm.LEFT_HANDED ? -1 : 1
915
+ out[0] = -2 * lr; out[1] = 0; out[2] = 0; out[3] = 0
916
+ out[4] = 0; out[5] = -2 * bt; out[6] = 0; out[7] = 0
917
+ out[8] = 0; out[9] = 0; out[10] = s * 2 * nf; out[11] = 0
918
+ out[12] = (left + right) * lr; out[13] = (top + bottom) * bt; out[14] = (far + near) * nf; out[15] = 1
919
+ return out
920
+ }
921
+ static ortho = this.orthoNO
922
+
923
+ /**
924
+ * Generates an orthogonal projection matrix with the given bounds.
925
+ * The near/far clip planes correspond to a normalized device coordinate Z range of [0, 1],
926
+ * which matches WebGPU/Vulkan/DirectX/Metal's clip volume.
927
+ *
928
+ * @param {Number} left left bound of the frustum
929
+ * @param {Number} right right bound of the frustum
930
+ * @param {Number} bottom bottom bound of the frustum
931
+ * @param {Number} top top bound of the frustum
932
+ * @param {Number} near near bound of the frustum
933
+ * @param {Number} far far bound of the frustum
934
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
935
+ * @returns {Mat4} out
936
+ */
937
+ static orthoZO(left: number, right: number, bottom: number, top: number, near: number, far: number, out = new Mat4()) {
938
+ const lr = 1 / (left - right)
939
+ const bt = 1 / (bottom - top)
940
+ const nf = 1 / (near - far)
941
+ const s = glm.LEFT_HANDED ? -1 : 1
942
+ out[0] = -2 * lr; out[1] = 0; out[2] = 0; out[3] = 0
943
+ out[4] = 0; out[5] = -2 * bt; out[6] = 0; out[7] = 0
944
+ out[8] = 0; out[9] = 0; out[10] = s * nf; out[11] = 0
945
+ out[12] = (left + right) * lr; out[13] = (top + bottom) * bt; out[14] = near * nf; out[15] = 1
946
+ return out
947
+ }
948
+
949
+ /**
950
+ * Generates a look-at matrix with the given eye position, focal point, and up axis.
951
+ * If you want a matrix that actually makes an object look at another object, use targetTo instead.
952
+ *
953
+ * @param {Vec3} eye position of the viewer
954
+ * @param {Vec3} center point the viewer is looking at
955
+ * @param {Vec3} up vector pointing up
956
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
957
+ * @returns {Mat4} out
958
+ */
959
+ static lookAt(eye: Vec3, center: Vec3, up: Vec3, out = new Mat4()) {
960
+ let x0, x1, x2, y0, y1, y2, z0, z1, z2, len
961
+
962
+ const eyex = eye[0], eyey = eye[1], eyez = eye[2]
963
+ const upx = up[0], upy = up[1], upz = up[2]
964
+ const centerx = center[0], centery = center[1], centerz = center[2]
965
+
966
+ if (eye.equals(center)) {
967
+ out[0] = 1; out[1] = 0; out[2] = 0; out[3] = 0
968
+ out[4] = 0; out[5] = 1; out[6] = 0; out[7] = 0
969
+ out[8] = 0; out[9] = 0; out[10] = 1; out[11] = 0
970
+ out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1
971
+ return out
972
+ }
973
+
974
+ if (glm.LEFT_HANDED) {
975
+ z0 = centerx - eyex; z1 = centery - eyey; z2 = centerz - eyez
976
+ } else {
977
+ z0 = eyex - centerx; z1 = eyey - centery; z2 = eyez - centerz
978
+ }
979
+ len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2)
980
+ z0 *= len; z1 *= len; z2 *= len
981
+
982
+ x0 = upy * z2 - upz * z1
983
+ x1 = upz * z0 - upx * z2
984
+ x2 = upx * z1 - upy * z0
985
+ len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2)
986
+ if (!len) { x0 = 0; x1 = 0; x2 = 0 }
987
+ else { len = 1 / len; x0 *= len; x1 *= len; x2 *= len }
988
+
989
+ y0 = z1 * x2 - z2 * x1
990
+ y1 = z2 * x0 - z0 * x2
991
+ y2 = z0 * x1 - z1 * x0
992
+ len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2)
993
+ if (!len) { y0 = 0; y1 = 0; y2 = 0 }
994
+ else { len = 1 / len; y0 *= len; y1 *= len; y2 *= len }
995
+
996
+ out[0] = x0; out[1] = y0; out[2] = z0; out[3] = 0
997
+ out[4] = x1; out[5] = y1; out[6] = z1; out[7] = 0
998
+ out[8] = x2; out[9] = y2; out[10] = z2; out[11] = 0
999
+ out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez)
1000
+ out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez)
1001
+ out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez)
1002
+ out[15] = 1
1003
+ return out
1004
+ }
1005
+
1006
+ /**
1007
+ * Generates a matrix that makes something look at a given point from a given eye position
1008
+ *
1009
+ * @param {Vec3} eye position of the viewer
1010
+ * @param {Vec3} target point the viewer is looking at
1011
+ * @param {Vec3} up vector pointing up
1012
+ * @param {Mat4} out the receiving matrix, defaults to new Mat4()
1013
+ * @returns {Mat4} out
1014
+ */
1015
+ static targetTo(eye: Vec3, target: Vec3, up: Vec3, out = new Mat4()) {
1016
+ const eyex = eye[0], eyey = eye[1], eyez = eye[2]
1017
+ const upx = up[0], upy = up[1], upz = up[2]
1018
+
1019
+ let z0, z1, z2
1020
+ if (glm.LEFT_HANDED) {
1021
+ z0 = target[0] - eyex; z1 = target[1] - eyey; z2 = target[2] - eyez
1022
+ } else {
1023
+ z0 = eyex - target[0]; z1 = eyey - target[1]; z2 = eyez - target[2]
1024
+ }
1025
+ let len = z0 * z0 + z1 * z1 + z2 * z2
1026
+ if (len > 0) {
1027
+ len = 1 / Math.sqrt(len)
1028
+ z0 *= len; z1 *= len; z2 *= len
1029
+ }
1030
+
1031
+ let x0 = upy * z2 - upz * z1
1032
+ let x1 = upz * z0 - upx * z2
1033
+ let x2 = upx * z1 - upy * z0
1034
+ len = x0 * x0 + x1 * x1 + x2 * x2
1035
+ if (len > 0) {
1036
+ len = 1 / Math.sqrt(len)
1037
+ x0 *= len; x1 *= len; x2 *= len
1038
+ }
1039
+
1040
+ out[0] = x0; out[1] = x1; out[2] = x2; out[3] = 0
1041
+ out[4] = z1 * x2 - z2 * x1; out[5] = z2 * x0 - z0 * x2; out[6] = z0 * x1 - z1 * x0; out[7] = 0
1042
+ out[8] = z0; out[9] = z1; out[10] = z2; out[11] = 0
1043
+ out[12] = eyex; out[13] = eyey; out[14] = eyez; out[15] = 1
1044
+ return out
1045
+ }
1046
+
1047
+ /**
1048
+ * Generates a perspective projection matrix with the far plane at infinity.
1049
+ * Uses clip space z range of [-1, 1].
1050
+ *
1051
+ * @param {Number} fovy Vertical field of view in radians
1052
+ * @param {Number} aspect Aspect ratio (width / height)
1053
+ * @param {Number} near Near bound of the frustum
1054
+ * @param {Mat4} out the receiving matrix, defaults to a new Mat4
1055
+ * @returns {Mat4} out
1056
+ */
1057
+ static infinitePerspective(fovy: number, aspect: number, near: number, out = new Mat4()) {
1058
+ const f = 1.0 / Math.tan(fovy / 2)
1059
+ const lh = glm.LEFT_HANDED
1060
+ out[0] = f / aspect
1061
+ out[1] = out[2] = out[3] = out[4] = out[6] = out[7] = out[8] = out[9] = out[12] = out[13] = out[15] = 0
1062
+ out[5] = f
1063
+ out[10] = lh ? 1 : -1
1064
+ out[11] = lh ? 1 : -1
1065
+ out[14] = -2 * near
1066
+ return out
1067
+ }
1068
+
1069
+ /**
1070
+ * Projects a 3D point to window coordinates using the given model and
1071
+ * projection matrices and viewport.
1072
+ *
1073
+ * @param {Vec3} obj the 3D point to project
1074
+ * @param {Mat4} model the model matrix
1075
+ * @param {Mat4} proj the projection matrix
1076
+ * @param {Vec4} viewport the viewport as [x, y, width, height]
1077
+ * @param {Vec3} out the receiving vector, defaults to a new Vec3
1078
+ * @returns {Vec3} out
1079
+ */
1080
+ static project(obj: Vec3, model: Mat4, proj: Mat4, viewport: Vec4, out = new Vec3()): Vec3 {
1081
+ const x = obj[0], y = obj[1], z = obj[2]
1082
+ const tx = model[0]*x + model[4]*y + model[8]*z + model[12]
1083
+ const ty = model[1]*x + model[5]*y + model[9]*z + model[13]
1084
+ const tz = model[2]*x + model[6]*y + model[10]*z + model[14]
1085
+ const tw = model[3]*x + model[7]*y + model[11]*z + model[15]
1086
+
1087
+ let px = proj[0]*tx + proj[4]*ty + proj[8]*tz + proj[12]*tw
1088
+ let py = proj[1]*tx + proj[5]*ty + proj[9]*tz + proj[13]*tw
1089
+ let pz = proj[2]*tx + proj[6]*ty + proj[10]*tz + proj[14]*tw
1090
+ let pw = proj[3]*tx + proj[7]*ty + proj[11]*tz + proj[15]*tw
1091
+
1092
+ pw = pw || 1.0
1093
+ px /= pw; py /= pw; pz /= pw
1094
+
1095
+ out[0] = viewport[0] + viewport[2] * (px * 0.5 + 0.5)
1096
+ out[1] = viewport[1] + viewport[3] * (py * 0.5 + 0.5)
1097
+ out[2] = pz * 0.5 + 0.5
1098
+ return out
1099
+ }
1100
+
1101
+ /**
1102
+ * Unprojects a 2D window coordinate back to 3D world coordinates using the
1103
+ * given model and projection matrices and viewport.
1104
+ *
1105
+ * @param {Vec3} win the window coordinate [x, y, z] where z is depth (0 to 1)
1106
+ * @param {Mat4} model the model matrix
1107
+ * @param {Mat4} proj the projection matrix
1108
+ * @param {Vec4} viewport the viewport as [x, y, width, height]
1109
+ * @param {Vec3} out the receiving vector, defaults to a new Vec3
1110
+ * @returns {Vec3 | null} out, or null if the combined matrix is not invertible
1111
+ */
1112
+ static unProject(win: Vec3, model: Mat4, proj: Mat4, viewport: Vec4, out = new Vec3()): Vec3 | null {
1113
+ const a00 = model[0], a01 = model[1], a02 = model[2], a03 = model[3]
1114
+ const a10 = model[4], a11 = model[5], a12 = model[6], a13 = model[7]
1115
+ const a20 = model[8], a21 = model[9], a22 = model[10], a23 = model[11]
1116
+ const a30 = model[12], a31 = model[13], a32 = model[14], a33 = model[15]
1117
+
1118
+ const pm = new Mat4()
1119
+ let b0 = proj[0], b1 = proj[1], b2 = proj[2], b3 = proj[3]
1120
+ pm[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30
1121
+ pm[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31
1122
+ pm[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32
1123
+ pm[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33
1124
+
1125
+ b0 = proj[4]; b1 = proj[5]; b2 = proj[6]; b3 = proj[7]
1126
+ pm[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30
1127
+ pm[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31
1128
+ pm[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32
1129
+ pm[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33
1130
+
1131
+ b0 = proj[8]; b1 = proj[9]; b2 = proj[10]; b3 = proj[11]
1132
+ pm[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30
1133
+ pm[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31
1134
+ pm[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32
1135
+ pm[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33
1136
+
1137
+ b0 = proj[12]; b1 = proj[13]; b2 = proj[14]; b3 = proj[15]
1138
+ pm[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30
1139
+ pm[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31
1140
+ pm[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32
1141
+ pm[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33
1142
+
1143
+ const inv = pm.invert()
1144
+ if (!inv) return null
1145
+
1146
+ const nx = (win[0] - viewport[0]) / viewport[2] * 2 - 1
1147
+ const ny = (win[1] - viewport[1]) / viewport[3] * 2 - 1
1148
+ const nz = win[2] * 2 - 1
1149
+
1150
+ let w = inv[3]*nx + inv[7]*ny + inv[11]*nz + inv[15]
1151
+ w = w || 1.0
1152
+ out[0] = (inv[0]*nx + inv[4]*ny + inv[8]*nz + inv[12]) / w
1153
+ out[1] = (inv[1]*nx + inv[5]*ny + inv[9]*nz + inv[13]) / w
1154
+ out[2] = (inv[2]*nx + inv[6]*ny + inv[10]*nz + inv[14]) / w
1155
+ return out
1156
+ }
1157
+
1158
+ /**
1159
+ * Returns Frobenius norm of a mat4
1160
+ *
1161
+ * @returns {Number} Frobenius norm
1162
+ */
1163
+ frob() {
1164
+ return Math.sqrt(
1165
+ this[0] * this[0] + this[1] * this[1] + this[2] * this[2] + this[3] * this[3] +
1166
+ this[4] * this[4] + this[5] * this[5] + this[6] * this[6] + this[7] * this[7] +
1167
+ this[8] * this[8] + this[9] * this[9] + this[10] * this[10] + this[11] * this[11] +
1168
+ this[12] * this[12] + this[13] * this[13] + this[14] * this[14] + this[15] * this[15]
1169
+ )
1170
+ }
1171
+
1172
+ /**
1173
+ * Adds two mat4's
1174
+ *
1175
+ * @param {Mat4} b the second operand
1176
+ * @param {Mat4} out the receiving matrix, defaults to this
1177
+ * @returns {Mat4} out
1178
+ */
1179
+ plus(b: Mat4, out = glm.ALWAYS_COPY ? new Mat4() : this) {
1180
+ out[0] = this[0] + b[0]; out[1] = this[1] + b[1]; out[2] = this[2] + b[2]; out[3] = this[3] + b[3]
1181
+ out[4] = this[4] + b[4]; out[5] = this[5] + b[5]; out[6] = this[6] + b[6]; out[7] = this[7] + b[7]
1182
+ out[8] = this[8] + b[8]; out[9] = this[9] + b[9]; out[10] = this[10] + b[10]; out[11] = this[11] + b[11]
1183
+ out[12] = this[12] + b[12]; out[13] = this[13] + b[13]; out[14] = this[14] + b[14]; out[15] = this[15] + b[15]
1184
+ return out
1185
+ }
1186
+
1187
+ /**
1188
+ * Subtracts matrix b from a mat4
1189
+ *
1190
+ * @param {Mat4} b the second operand
1191
+ * @param {Mat4} out the receiving matrix, defaults to this
1192
+ * @returns {Mat4} out
1193
+ */
1194
+ minus(b: Mat4, out = glm.ALWAYS_COPY ? new Mat4() : this) {
1195
+ out[0] = this[0] - b[0]; out[1] = this[1] - b[1]; out[2] = this[2] - b[2]; out[3] = this[3] - b[3]
1196
+ out[4] = this[4] - b[4]; out[5] = this[5] - b[5]; out[6] = this[6] - b[6]; out[7] = this[7] - b[7]
1197
+ out[8] = this[8] - b[8]; out[9] = this[9] - b[9]; out[10] = this[10] - b[10]; out[11] = this[11] - b[11]
1198
+ out[12] = this[12] - b[12]; out[13] = this[13] - b[13]; out[14] = this[14] - b[14]; out[15] = this[15] - b[15]
1199
+ return out
1200
+ }
1201
+
1202
+ /**
1203
+ * Multiplies each element of a mat4 by a scalar number
1204
+ *
1205
+ * @param {Number} b amount to scale the matrix's elements by
1206
+ * @param {Mat4} out the receiving matrix, defaults to this
1207
+ * @returns {Mat4} out
1208
+ */
1209
+ scaleScalar(b: number, out = glm.ALWAYS_COPY ? new Mat4() : this) {
1210
+ out[0] = this[0] * b; out[1] = this[1] * b; out[2] = this[2] * b; out[3] = this[3] * b
1211
+ out[4] = this[4] * b; out[5] = this[5] * b; out[6] = this[6] * b; out[7] = this[7] * b
1212
+ out[8] = this[8] * b; out[9] = this[9] * b; out[10] = this[10] * b; out[11] = this[11] * b
1213
+ out[12] = this[12] * b; out[13] = this[13] * b; out[14] = this[14] * b; out[15] = this[15] * b
1214
+ return out
1215
+ }
1216
+
1217
+ /**
1218
+ * Adds two mat4's after multiplying each element of the second operand by a scalar value
1219
+ *
1220
+ * @param {Mat4} b the second operand
1221
+ * @param {Number} scale the amount to scale b's elements by before adding
1222
+ * @param {Mat4} out the receiving matrix, defaults to this
1223
+ * @returns {Mat4} out
1224
+ */
1225
+ multiplyScalarAndAdd(b: Mat4, scale: number, out = glm.ALWAYS_COPY ? new Mat4() : this) {
1226
+ out[0] = this[0] + b[0] * scale; out[1] = this[1] + b[1] * scale
1227
+ out[2] = this[2] + b[2] * scale; out[3] = this[3] + b[3] * scale
1228
+ out[4] = this[4] + b[4] * scale; out[5] = this[5] + b[5] * scale
1229
+ out[6] = this[6] + b[6] * scale; out[7] = this[7] + b[7] * scale
1230
+ out[8] = this[8] + b[8] * scale; out[9] = this[9] + b[9] * scale
1231
+ out[10] = this[10] + b[10] * scale; out[11] = this[11] + b[11] * scale
1232
+ out[12] = this[12] + b[12] * scale; out[13] = this[13] + b[13] * scale
1233
+ out[14] = this[14] + b[14] * scale; out[15] = this[15] + b[15] * scale
1234
+ return out
1235
+ }
1236
+
1237
+ /**
1238
+ * Returns a string representation of a mat4
1239
+ *
1240
+ * @returns {String} string representation of the matrix
1241
+ */
1242
+ toString() {
1243
+ return `mat4(${this[0]}, ${this[1]}, ${this[2]}, ${this[3]},\t${this[4]}, ${this[5]}, ${this[6]}, ${this[7]},\t${this[8]}, ${this[9]}, ${this[10]}, ${this[11]},\t${this[12]}, ${this[13]}, ${this[14]}, ${this[15]})`
1244
+ }
1245
+
1246
+ /**
1247
+ * Returns whether a mat4 and another have exactly the same elements in the same position
1248
+ *
1249
+ * @param {Mat4} b the matrix to compare against
1250
+ * @returns {Boolean} true if the matrices are equal, false otherwise
1251
+ */
1252
+ exactEquals(b: Mat4) {
1253
+ return (
1254
+ this[0] === b[0] && this[1] === b[1] && this[2] === b[2] && this[3] === b[3] &&
1255
+ this[4] === b[4] && this[5] === b[5] && this[6] === b[6] && this[7] === b[7] &&
1256
+ this[8] === b[8] && this[9] === b[9] && this[10] === b[10] && this[11] === b[11] &&
1257
+ this[12] === b[12] && this[13] === b[13] && this[14] === b[14] && this[15] === b[15]
1258
+ )
1259
+ }
1260
+
1261
+ /**
1262
+ * Returns whether a mat4 and another are approximately equal
1263
+ *
1264
+ * @param {Mat4} b the matrix to compare against
1265
+ * @returns {Boolean} true if the matrices are approximately equal, false otherwise
1266
+ */
1267
+ equals(b: Mat4) {
1268
+ return (
1269
+ equals(this[0], b[0]) && equals(this[1], b[1]) && equals(this[2], b[2]) && equals(this[3], b[3]) &&
1270
+ equals(this[4], b[4]) && equals(this[5], b[5]) && equals(this[6], b[6]) && equals(this[7], b[7]) &&
1271
+ equals(this[8], b[8]) && equals(this[9], b[9]) && equals(this[10], b[10]) && equals(this[11], b[11]) &&
1272
+ equals(this[12], b[12]) && equals(this[13], b[13]) && equals(this[14], b[14]) && equals(this[15], b[15])
1273
+ )
1274
+ }
1275
+ }
1276
+
1277
+ export interface Mat4 {
1278
+ add: (b: Mat4, out?: Mat4) => Mat4
1279
+ sub: (b: Mat4, out?: Mat4) => Mat4
1280
+ subtract: (b: Mat4, out?: Mat4) => Mat4
1281
+ mul(b: Vec2): Vec2
1282
+ mul(b: Vec3): Vec3
1283
+ mul(b: Vec4): Vec4
1284
+ mul(b: Mat4, out?: Mat4): Mat4
1285
+ mult(b: Vec2): Vec2
1286
+ mult(b: Vec3): Vec3
1287
+ mult(b: Vec4): Vec4
1288
+ mult(b: Mat4, out?: Mat4): Mat4
1289
+ times(b: Vec2): Vec2
1290
+ times(b: Vec3): Vec3
1291
+ times(b: Vec4): Vec4
1292
+ times(b: Mat4, out?: Mat4): Mat4
1293
+ str: () => string
1294
+ multiplyScalar: (b: number, out?: Mat4) => Mat4
1295
+ }
1296
+
1297
+ // @aliases
1298
+ Mat4.prototype.add = Mat4.prototype.plus
1299
+ Mat4.prototype.sub = Mat4.prototype.minus
1300
+ Mat4.prototype.subtract = Mat4.prototype.minus
1301
+ Mat4.prototype.mul = Mat4.prototype.multiply
1302
+ Mat4.prototype.mult = Mat4.prototype.multiply
1303
+ Mat4.prototype.times = Mat4.prototype.multiply
1304
+ Mat4.prototype.str = Mat4.prototype.toString
1305
+ Mat4.prototype.multiplyScalar = Mat4.prototype.scaleScalar
1306
+
1307
+ export const mat4 = Object.assign(
1308
+ (...args: (number | Float32Array)[]): Mat4 => {
1309
+ const out = new Mat4()
1310
+ let i = 0
1311
+ for (const a of args) {
1312
+ if (typeof a === 'number') out[i++] = a
1313
+ else for (const v of a) out[i++] = v
1314
+ }
1315
+ return out
1316
+ },
1317
+ Mat4
1318
+ )
1319
+ export const mat4x4 = mat4