reze-engine 0.2.19 → 0.3.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/README.md +67 -66
- package/dist/bezier-interpolate.d.ts +15 -0
- package/dist/bezier-interpolate.d.ts.map +1 -0
- package/dist/bezier-interpolate.js +40 -0
- package/dist/engine.d.ts +12 -13
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +107 -175
- package/dist/ik-solver.d.ts +26 -0
- package/dist/ik-solver.d.ts.map +1 -0
- package/dist/ik-solver.js +372 -0
- package/dist/math.d.ts +1 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +8 -0
- package/dist/model.d.ts +46 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +201 -3
- package/dist/player.d.ts +100 -0
- package/dist/player.d.ts.map +1 -0
- package/dist/player.js +409 -0
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +57 -36
- package/dist/vmd-loader.d.ts +11 -1
- package/dist/vmd-loader.d.ts.map +1 -1
- package/dist/vmd-loader.js +91 -15
- package/package.json +1 -1
- package/src/bezier-interpolate.ts +47 -0
- package/src/camera.ts +358 -358
- package/src/engine.ts +123 -194
- package/src/ik-solver.ts +488 -0
- package/src/math.ts +555 -546
- package/src/model.ts +284 -3
- package/src/physics.ts +752 -752
- package/src/player.ts +490 -0
- package/src/pmx-loader.ts +1173 -1145
- package/src/vmd-loader.ts +276 -179
package/src/math.ts
CHANGED
|
@@ -1,546 +1,555 @@
|
|
|
1
|
-
// Easing function: ease-in-out quadratic
|
|
2
|
-
export function easeInOut(t: number): number {
|
|
3
|
-
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export class Vec3 {
|
|
7
|
-
x: number
|
|
8
|
-
y: number
|
|
9
|
-
z: number
|
|
10
|
-
|
|
11
|
-
constructor(x: number, y: number, z: number) {
|
|
12
|
-
this.x = x
|
|
13
|
-
this.y = y
|
|
14
|
-
this.z = z
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
add(other: Vec3): Vec3 {
|
|
18
|
-
return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
subtract(other: Vec3): Vec3 {
|
|
22
|
-
return new Vec3(this.x - other.x, this.y - other.y, this.z - other.z)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
length(): number {
|
|
26
|
-
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
normalize(): Vec3 {
|
|
30
|
-
const len = this.length()
|
|
31
|
-
if (len === 0) return new Vec3(0, 0, 0)
|
|
32
|
-
return new Vec3(this.x / len, this.y / len, this.z / len)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
cross(other: Vec3): Vec3 {
|
|
36
|
-
return new Vec3(
|
|
37
|
-
this.y * other.z - this.z * other.y,
|
|
38
|
-
this.z * other.x - this.x * other.z,
|
|
39
|
-
this.x * other.y - this.y * other.x
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
dot(other: Vec3): number {
|
|
44
|
-
return this.x * other.x + this.y * other.y + this.z * other.z
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
scale(scalar: number): Vec3 {
|
|
48
|
-
return new Vec3(this.x * scalar, this.y * scalar, this.z * scalar)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
clone(): Vec3 {
|
|
52
|
-
return new Vec3(this.x, this.y, this.z)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export class Quat {
|
|
57
|
-
x: number
|
|
58
|
-
y: number
|
|
59
|
-
z: number
|
|
60
|
-
w: number
|
|
61
|
-
|
|
62
|
-
constructor(x: number, y: number, z: number, w: number) {
|
|
63
|
-
this.x = x
|
|
64
|
-
this.y = y
|
|
65
|
-
this.z = z
|
|
66
|
-
this.w = w
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
add(other: Quat): Quat {
|
|
70
|
-
return new Quat(this.x + other.x, this.y + other.y, this.z + other.z, this.w + other.w)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
clone(): Quat {
|
|
74
|
-
return new Quat(this.x, this.y, this.z, this.w)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
multiply(other: Quat): Quat {
|
|
78
|
-
// Proper quaternion multiplication (not component-wise)
|
|
79
|
-
return new Quat(
|
|
80
|
-
this.w * other.x + this.x * other.w + this.y * other.z - this.z * other.y,
|
|
81
|
-
this.w * other.y - this.x * other.z + this.y * other.w + this.z * other.x,
|
|
82
|
-
this.w * other.z + this.x * other.y - this.y * other.x + this.z * other.w,
|
|
83
|
-
this.w * other.w - this.x * other.x - this.y * other.y - this.z * other.z
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
conjugate(): Quat {
|
|
88
|
-
// Conjugate (inverse for unit quaternions): (x, y, z, w) -> (-x, -y, -z, w)
|
|
89
|
-
return new Quat(-this.x, -this.y, -this.z, this.w)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
length(): number {
|
|
93
|
-
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
normalize(): Quat {
|
|
97
|
-
const len = this.length()
|
|
98
|
-
if (len === 0) return new Quat(0, 0, 0, 1)
|
|
99
|
-
return new Quat(this.x / len, this.y / len, this.z / len, this.w / len)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Rotate a vector by this quaternion: result = q * v * q^-1
|
|
103
|
-
rotateVec(v: Vec3): Vec3 {
|
|
104
|
-
// Treat v as pure quaternion (x, y, z, 0)
|
|
105
|
-
const qx = this.x,
|
|
106
|
-
qy = this.y,
|
|
107
|
-
qz = this.z,
|
|
108
|
-
qw = this.w
|
|
109
|
-
const vx = v.x,
|
|
110
|
-
vy = v.y,
|
|
111
|
-
vz = v.z
|
|
112
|
-
|
|
113
|
-
// t = 2 * cross(q.xyz, v)
|
|
114
|
-
const tx = 2 * (qy * vz - qz * vy)
|
|
115
|
-
const ty = 2 * (qz * vx - qx * vz)
|
|
116
|
-
const tz = 2 * (qx * vy - qy * vx)
|
|
117
|
-
|
|
118
|
-
// result = v + q.w * t + cross(q.xyz, t)
|
|
119
|
-
return new Vec3(
|
|
120
|
-
vx + qw * tx + (qy * tz - qz * ty),
|
|
121
|
-
vy + qw * ty + (qz * tx - qx * tz),
|
|
122
|
-
vz + qw * tz + (qx * ty - qy * tx)
|
|
123
|
-
)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Rotate a vector by this quaternion (Babylon.js style naming)
|
|
127
|
-
rotate(v: Vec3): Vec3 {
|
|
128
|
-
const qv = new Vec3(this.x, this.y, this.z)
|
|
129
|
-
const uv = qv.cross(v)
|
|
130
|
-
const uuv = qv.cross(uv)
|
|
131
|
-
return v.add(uv.scale(2 * this.w)).add(uuv.scale(2))
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Static method: create quaternion that rotates from one direction to another
|
|
135
|
-
static fromTo(from: Vec3, to: Vec3): Quat {
|
|
136
|
-
const dot = from.dot(to)
|
|
137
|
-
if (dot > 0.999999) return new Quat(0, 0, 0, 1) // Already aligned
|
|
138
|
-
if (dot < -0.999999) {
|
|
139
|
-
// 180 degrees
|
|
140
|
-
let axis = from.cross(new Vec3(1, 0, 0))
|
|
141
|
-
if (axis.length() < 0.001) axis = from.cross(new Vec3(0, 1, 0))
|
|
142
|
-
return new Quat(axis.x, axis.y, axis.z, 0).normalize()
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const axis = from.cross(to)
|
|
146
|
-
const w = Math.sqrt((1 + dot) * 2)
|
|
147
|
-
const invW = 1 / w
|
|
148
|
-
return new Quat(axis.x * invW, axis.y * invW, axis.z * invW, w * 0.5).normalize()
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// If
|
|
173
|
-
if (cos
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
//
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return new Mat4(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
0,
|
|
266
|
-
0,
|
|
267
|
-
|
|
268
|
-
0,
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
out[
|
|
377
|
-
out[
|
|
378
|
-
out[
|
|
379
|
-
out[
|
|
380
|
-
out[
|
|
381
|
-
out[
|
|
382
|
-
out[
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
return
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
//
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
w =
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
v
|
|
462
|
-
v[
|
|
463
|
-
v[
|
|
464
|
-
v[
|
|
465
|
-
v[
|
|
466
|
-
v[
|
|
467
|
-
v[
|
|
468
|
-
v[
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const
|
|
515
|
-
const
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
out[
|
|
537
|
-
out[
|
|
538
|
-
out[
|
|
539
|
-
out[
|
|
540
|
-
out[
|
|
541
|
-
out[
|
|
542
|
-
out[
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
1
|
+
// Easing function: ease-in-out quadratic
|
|
2
|
+
export function easeInOut(t: number): number {
|
|
3
|
+
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export class Vec3 {
|
|
7
|
+
x: number
|
|
8
|
+
y: number
|
|
9
|
+
z: number
|
|
10
|
+
|
|
11
|
+
constructor(x: number, y: number, z: number) {
|
|
12
|
+
this.x = x
|
|
13
|
+
this.y = y
|
|
14
|
+
this.z = z
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
add(other: Vec3): Vec3 {
|
|
18
|
+
return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
subtract(other: Vec3): Vec3 {
|
|
22
|
+
return new Vec3(this.x - other.x, this.y - other.y, this.z - other.z)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
length(): number {
|
|
26
|
+
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
normalize(): Vec3 {
|
|
30
|
+
const len = this.length()
|
|
31
|
+
if (len === 0) return new Vec3(0, 0, 0)
|
|
32
|
+
return new Vec3(this.x / len, this.y / len, this.z / len)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
cross(other: Vec3): Vec3 {
|
|
36
|
+
return new Vec3(
|
|
37
|
+
this.y * other.z - this.z * other.y,
|
|
38
|
+
this.z * other.x - this.x * other.z,
|
|
39
|
+
this.x * other.y - this.y * other.x
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
dot(other: Vec3): number {
|
|
44
|
+
return this.x * other.x + this.y * other.y + this.z * other.z
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
scale(scalar: number): Vec3 {
|
|
48
|
+
return new Vec3(this.x * scalar, this.y * scalar, this.z * scalar)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
clone(): Vec3 {
|
|
52
|
+
return new Vec3(this.x, this.y, this.z)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class Quat {
|
|
57
|
+
x: number
|
|
58
|
+
y: number
|
|
59
|
+
z: number
|
|
60
|
+
w: number
|
|
61
|
+
|
|
62
|
+
constructor(x: number, y: number, z: number, w: number) {
|
|
63
|
+
this.x = x
|
|
64
|
+
this.y = y
|
|
65
|
+
this.z = z
|
|
66
|
+
this.w = w
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
add(other: Quat): Quat {
|
|
70
|
+
return new Quat(this.x + other.x, this.y + other.y, this.z + other.z, this.w + other.w)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
clone(): Quat {
|
|
74
|
+
return new Quat(this.x, this.y, this.z, this.w)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
multiply(other: Quat): Quat {
|
|
78
|
+
// Proper quaternion multiplication (not component-wise)
|
|
79
|
+
return new Quat(
|
|
80
|
+
this.w * other.x + this.x * other.w + this.y * other.z - this.z * other.y,
|
|
81
|
+
this.w * other.y - this.x * other.z + this.y * other.w + this.z * other.x,
|
|
82
|
+
this.w * other.z + this.x * other.y - this.y * other.x + this.z * other.w,
|
|
83
|
+
this.w * other.w - this.x * other.x - this.y * other.y - this.z * other.z
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
conjugate(): Quat {
|
|
88
|
+
// Conjugate (inverse for unit quaternions): (x, y, z, w) -> (-x, -y, -z, w)
|
|
89
|
+
return new Quat(-this.x, -this.y, -this.z, this.w)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
length(): number {
|
|
93
|
+
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
normalize(): Quat {
|
|
97
|
+
const len = this.length()
|
|
98
|
+
if (len === 0) return new Quat(0, 0, 0, 1)
|
|
99
|
+
return new Quat(this.x / len, this.y / len, this.z / len, this.w / len)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Rotate a vector by this quaternion: result = q * v * q^-1
|
|
103
|
+
rotateVec(v: Vec3): Vec3 {
|
|
104
|
+
// Treat v as pure quaternion (x, y, z, 0)
|
|
105
|
+
const qx = this.x,
|
|
106
|
+
qy = this.y,
|
|
107
|
+
qz = this.z,
|
|
108
|
+
qw = this.w
|
|
109
|
+
const vx = v.x,
|
|
110
|
+
vy = v.y,
|
|
111
|
+
vz = v.z
|
|
112
|
+
|
|
113
|
+
// t = 2 * cross(q.xyz, v)
|
|
114
|
+
const tx = 2 * (qy * vz - qz * vy)
|
|
115
|
+
const ty = 2 * (qz * vx - qx * vz)
|
|
116
|
+
const tz = 2 * (qx * vy - qy * vx)
|
|
117
|
+
|
|
118
|
+
// result = v + q.w * t + cross(q.xyz, t)
|
|
119
|
+
return new Vec3(
|
|
120
|
+
vx + qw * tx + (qy * tz - qz * ty),
|
|
121
|
+
vy + qw * ty + (qz * tx - qx * tz),
|
|
122
|
+
vz + qw * tz + (qx * ty - qy * tx)
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Rotate a vector by this quaternion (Babylon.js style naming)
|
|
127
|
+
rotate(v: Vec3): Vec3 {
|
|
128
|
+
const qv = new Vec3(this.x, this.y, this.z)
|
|
129
|
+
const uv = qv.cross(v)
|
|
130
|
+
const uuv = qv.cross(uv)
|
|
131
|
+
return v.add(uv.scale(2 * this.w)).add(uuv.scale(2))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Static method: create quaternion that rotates from one direction to another
|
|
135
|
+
static fromTo(from: Vec3, to: Vec3): Quat {
|
|
136
|
+
const dot = from.dot(to)
|
|
137
|
+
if (dot > 0.999999) return new Quat(0, 0, 0, 1) // Already aligned
|
|
138
|
+
if (dot < -0.999999) {
|
|
139
|
+
// 180 degrees
|
|
140
|
+
let axis = from.cross(new Vec3(1, 0, 0))
|
|
141
|
+
if (axis.length() < 0.001) axis = from.cross(new Vec3(0, 1, 0))
|
|
142
|
+
return new Quat(axis.x, axis.y, axis.z, 0).normalize()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const axis = from.cross(to)
|
|
146
|
+
const w = Math.sqrt((1 + dot) * 2)
|
|
147
|
+
const invW = 1 / w
|
|
148
|
+
return new Quat(axis.x * invW, axis.y * invW, axis.z * invW, w * 0.5).normalize()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Static method: create quaternion from rotation axis and angle
|
|
152
|
+
static fromAxisAngle(axis: Vec3, angle: number): Quat {
|
|
153
|
+
const normalizedAxis = axis.normalize()
|
|
154
|
+
const halfAngle = angle * 0.5
|
|
155
|
+
const sinHalf = Math.sin(halfAngle)
|
|
156
|
+
const cosHalf = Math.cos(halfAngle)
|
|
157
|
+
return new Quat(normalizedAxis.x * sinHalf, normalizedAxis.y * sinHalf, normalizedAxis.z * sinHalf, cosHalf)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
toArray(): [number, number, number, number] {
|
|
161
|
+
return [this.x, this.y, this.z, this.w]
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Spherical linear interpolation between two quaternions
|
|
165
|
+
static slerp(a: Quat, b: Quat, t: number): Quat {
|
|
166
|
+
let cos = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
|
|
167
|
+
let bx = b.x,
|
|
168
|
+
by = b.y,
|
|
169
|
+
bz = b.z,
|
|
170
|
+
bw = b.w
|
|
171
|
+
|
|
172
|
+
// If dot product is negative, negate one quaternion to take shorter path
|
|
173
|
+
if (cos < 0) {
|
|
174
|
+
cos = -cos
|
|
175
|
+
bx = -bx
|
|
176
|
+
by = -by
|
|
177
|
+
bz = -bz
|
|
178
|
+
bw = -bw
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// If quaternions are very close, use linear interpolation
|
|
182
|
+
if (cos > 0.9995) {
|
|
183
|
+
const x = a.x + t * (bx - a.x)
|
|
184
|
+
const y = a.y + t * (by - a.y)
|
|
185
|
+
const z = a.z + t * (bz - a.z)
|
|
186
|
+
const w = a.w + t * (bw - a.w)
|
|
187
|
+
const invLen = 1 / Math.hypot(x, y, z, w)
|
|
188
|
+
return new Quat(x * invLen, y * invLen, z * invLen, w * invLen)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Standard SLERP
|
|
192
|
+
const theta0 = Math.acos(cos)
|
|
193
|
+
const sinTheta0 = Math.sin(theta0)
|
|
194
|
+
const theta = theta0 * t
|
|
195
|
+
const s0 = Math.sin(theta0 - theta) / sinTheta0
|
|
196
|
+
const s1 = Math.sin(theta) / sinTheta0
|
|
197
|
+
return new Quat(s0 * a.x + s1 * bx, s0 * a.y + s1 * by, s0 * a.z + s1 * bz, s0 * a.w + s1 * bw)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Convert Euler angles to quaternion (ZXY order, left-handed, PMX format)
|
|
201
|
+
static fromEuler(rotX: number, rotY: number, rotZ: number): Quat {
|
|
202
|
+
const cx = Math.cos(rotX * 0.5)
|
|
203
|
+
const sx = Math.sin(rotX * 0.5)
|
|
204
|
+
const cy = Math.cos(rotY * 0.5)
|
|
205
|
+
const sy = Math.sin(rotY * 0.5)
|
|
206
|
+
const cz = Math.cos(rotZ * 0.5)
|
|
207
|
+
const sz = Math.sin(rotZ * 0.5)
|
|
208
|
+
|
|
209
|
+
const w = cy * cx * cz + sy * sx * sz
|
|
210
|
+
const x = cy * sx * cz + sy * cx * sz
|
|
211
|
+
const y = sy * cx * cz - cy * sx * sz
|
|
212
|
+
const z = cy * cx * sz - sy * sx * cz
|
|
213
|
+
|
|
214
|
+
return new Quat(x, y, z, w).normalize()
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Convert quaternion to Euler angles (ZXY order, inverse of fromEuler)
|
|
218
|
+
toEuler(): Vec3 {
|
|
219
|
+
const qx = this.x
|
|
220
|
+
const qy = this.y
|
|
221
|
+
const qz = this.z
|
|
222
|
+
const qw = this.w
|
|
223
|
+
|
|
224
|
+
// ZXY order (left-handed)
|
|
225
|
+
// Roll (X): rotation around X axis
|
|
226
|
+
const sinr_cosp = 2 * (qw * qx + qy * qz)
|
|
227
|
+
const cosr_cosp = 1 - 2 * (qx * qx + qy * qy)
|
|
228
|
+
const rotX = Math.atan2(sinr_cosp, cosr_cosp)
|
|
229
|
+
|
|
230
|
+
// Pitch (Y): rotation around Y axis
|
|
231
|
+
const sinp = 2 * (qw * qy - qz * qx)
|
|
232
|
+
const rotY = Math.abs(sinp) >= 1 ? (sinp >= 0 ? Math.PI / 2 : -Math.PI / 2) : Math.asin(sinp)
|
|
233
|
+
|
|
234
|
+
// Yaw (Z): rotation around Z axis
|
|
235
|
+
const siny_cosp = 2 * (qw * qz + qx * qy)
|
|
236
|
+
const cosy_cosp = 1 - 2 * (qy * qy + qz * qz)
|
|
237
|
+
const rotZ = Math.atan2(siny_cosp, cosy_cosp)
|
|
238
|
+
|
|
239
|
+
return new Vec3(rotX, rotY, rotZ)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export class Mat4 {
|
|
244
|
+
values: Float32Array
|
|
245
|
+
|
|
246
|
+
constructor(values: Float32Array) {
|
|
247
|
+
this.values = values
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static identity(): Mat4 {
|
|
251
|
+
return new Mat4(new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]))
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Perspective matrix for LEFT-HANDED coordinate system (Z+ forward)
|
|
255
|
+
// For left-handed: Z goes from 0 (near) to 1 (far), +Z is forward
|
|
256
|
+
static perspective(fov: number, aspect: number, near: number, far: number): Mat4 {
|
|
257
|
+
const f = 1.0 / Math.tan(fov / 2)
|
|
258
|
+
const rangeInv = 1.0 / (far - near) // Positive for left-handed
|
|
259
|
+
|
|
260
|
+
return new Mat4(
|
|
261
|
+
new Float32Array([
|
|
262
|
+
f / aspect,
|
|
263
|
+
0,
|
|
264
|
+
0,
|
|
265
|
+
0,
|
|
266
|
+
0,
|
|
267
|
+
f,
|
|
268
|
+
0,
|
|
269
|
+
0,
|
|
270
|
+
0,
|
|
271
|
+
0,
|
|
272
|
+
(far + near) * rangeInv,
|
|
273
|
+
1, // Positive for left-handed (Z+ forward)
|
|
274
|
+
0,
|
|
275
|
+
0,
|
|
276
|
+
-near * far * rangeInv * 2, // Negated for left-handed
|
|
277
|
+
0,
|
|
278
|
+
])
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// LookAt matrix for LEFT-HANDED coordinate system (Z+ forward)
|
|
283
|
+
// For left-handed: camera looks along +Z direction
|
|
284
|
+
static lookAt(eye: Vec3, target: Vec3, up: Vec3): Mat4 {
|
|
285
|
+
// In left-handed: forward = target - eye (Z+ direction)
|
|
286
|
+
const forward = target.subtract(eye).normalize()
|
|
287
|
+
const right = up.cross(forward).normalize() // X+ is right
|
|
288
|
+
const upVec = forward.cross(right).normalize() // Y+ is up
|
|
289
|
+
|
|
290
|
+
return new Mat4(
|
|
291
|
+
new Float32Array([
|
|
292
|
+
right.x,
|
|
293
|
+
upVec.x,
|
|
294
|
+
forward.x,
|
|
295
|
+
0,
|
|
296
|
+
right.y,
|
|
297
|
+
upVec.y,
|
|
298
|
+
forward.y,
|
|
299
|
+
0,
|
|
300
|
+
right.z,
|
|
301
|
+
upVec.z,
|
|
302
|
+
forward.z,
|
|
303
|
+
0,
|
|
304
|
+
-right.dot(eye),
|
|
305
|
+
-upVec.dot(eye),
|
|
306
|
+
-forward.dot(eye),
|
|
307
|
+
1,
|
|
308
|
+
])
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
multiply(other: Mat4): Mat4 {
|
|
313
|
+
// Column-major multiplication (matches WGSL/GLSL convention):
|
|
314
|
+
// result = a * b
|
|
315
|
+
const out = new Float32Array(16)
|
|
316
|
+
const a = this.values
|
|
317
|
+
const b = other.values
|
|
318
|
+
for (let c = 0; c < 4; c++) {
|
|
319
|
+
const b0 = b[c * 4 + 0]
|
|
320
|
+
const b1 = b[c * 4 + 1]
|
|
321
|
+
const b2 = b[c * 4 + 2]
|
|
322
|
+
const b3 = b[c * 4 + 3]
|
|
323
|
+
out[c * 4 + 0] = a[0] * b0 + a[4] * b1 + a[8] * b2 + a[12] * b3
|
|
324
|
+
out[c * 4 + 1] = a[1] * b0 + a[5] * b1 + a[9] * b2 + a[13] * b3
|
|
325
|
+
out[c * 4 + 2] = a[2] * b0 + a[6] * b1 + a[10] * b2 + a[14] * b3
|
|
326
|
+
out[c * 4 + 3] = a[3] * b0 + a[7] * b1 + a[11] * b2 + a[15] * b3
|
|
327
|
+
}
|
|
328
|
+
return new Mat4(out)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Static method to multiply two matrix array segments directly into output array (no object creation)
|
|
332
|
+
// Column-major multiplication: result = a * b
|
|
333
|
+
static multiplyArrays(
|
|
334
|
+
a: Float32Array,
|
|
335
|
+
aOffset: number,
|
|
336
|
+
b: Float32Array,
|
|
337
|
+
bOffset: number,
|
|
338
|
+
out: Float32Array,
|
|
339
|
+
outOffset: number
|
|
340
|
+
): void {
|
|
341
|
+
for (let c = 0; c < 4; c++) {
|
|
342
|
+
const b0 = b[bOffset + c * 4 + 0]
|
|
343
|
+
const b1 = b[bOffset + c * 4 + 1]
|
|
344
|
+
const b2 = b[bOffset + c * 4 + 2]
|
|
345
|
+
const b3 = b[bOffset + c * 4 + 3]
|
|
346
|
+
out[outOffset + c * 4 + 0] =
|
|
347
|
+
a[aOffset + 0] * b0 + a[aOffset + 4] * b1 + a[aOffset + 8] * b2 + a[aOffset + 12] * b3
|
|
348
|
+
out[outOffset + c * 4 + 1] =
|
|
349
|
+
a[aOffset + 1] * b0 + a[aOffset + 5] * b1 + a[aOffset + 9] * b2 + a[aOffset + 13] * b3
|
|
350
|
+
out[outOffset + c * 4 + 2] =
|
|
351
|
+
a[aOffset + 2] * b0 + a[aOffset + 6] * b1 + a[aOffset + 10] * b2 + a[aOffset + 14] * b3
|
|
352
|
+
out[outOffset + c * 4 + 3] =
|
|
353
|
+
a[aOffset + 3] * b0 + a[aOffset + 7] * b1 + a[aOffset + 11] * b2 + a[aOffset + 15] * b3
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
clone(): Mat4 {
|
|
358
|
+
return new Mat4(this.values.slice())
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
static fromQuat(x: number, y: number, z: number, w: number): Mat4 {
|
|
362
|
+
// Column-major rotation matrix from quaternion (matches glMatrix/WGSL)
|
|
363
|
+
const out = new Float32Array(16)
|
|
364
|
+
const x2 = x + x,
|
|
365
|
+
y2 = y + y,
|
|
366
|
+
z2 = z + z
|
|
367
|
+
const xx = x * x2,
|
|
368
|
+
xy = x * y2,
|
|
369
|
+
xz = x * z2
|
|
370
|
+
const yy = y * y2,
|
|
371
|
+
yz = y * z2,
|
|
372
|
+
zz = z * z2
|
|
373
|
+
const wx = w * x2,
|
|
374
|
+
wy = w * y2,
|
|
375
|
+
wz = w * z2
|
|
376
|
+
out[0] = 1 - (yy + zz)
|
|
377
|
+
out[1] = xy + wz
|
|
378
|
+
out[2] = xz - wy
|
|
379
|
+
out[3] = 0
|
|
380
|
+
out[4] = xy - wz
|
|
381
|
+
out[5] = 1 - (xx + zz)
|
|
382
|
+
out[6] = yz + wx
|
|
383
|
+
out[7] = 0
|
|
384
|
+
out[8] = xz + wy
|
|
385
|
+
out[9] = yz - wx
|
|
386
|
+
out[10] = 1 - (xx + yy)
|
|
387
|
+
out[11] = 0
|
|
388
|
+
out[12] = 0
|
|
389
|
+
out[13] = 0
|
|
390
|
+
out[14] = 0
|
|
391
|
+
out[15] = 1
|
|
392
|
+
return new Mat4(out)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Create transform matrix from position and rotation
|
|
396
|
+
static fromPositionRotation(position: Vec3, rotation: Quat): Mat4 {
|
|
397
|
+
const rotMat = Mat4.fromQuat(rotation.x, rotation.y, rotation.z, rotation.w)
|
|
398
|
+
rotMat.values[12] = position.x
|
|
399
|
+
rotMat.values[13] = position.y
|
|
400
|
+
rotMat.values[14] = position.z
|
|
401
|
+
return rotMat
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Extract position from transform matrix
|
|
405
|
+
getPosition(): Vec3 {
|
|
406
|
+
return new Vec3(this.values[12], this.values[13], this.values[14])
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Extract quaternion rotation from this matrix (upper-left 3x3 rotation part)
|
|
410
|
+
toQuat(): Quat {
|
|
411
|
+
return Mat4.toQuatFromArray(this.values, 0)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Static method to extract quaternion from matrix array (avoids creating Mat4 object)
|
|
415
|
+
static toQuatFromArray(m: Float32Array, offset: number): Quat {
|
|
416
|
+
const m00 = m[offset + 0],
|
|
417
|
+
m01 = m[offset + 4],
|
|
418
|
+
m02 = m[offset + 8]
|
|
419
|
+
const m10 = m[offset + 1],
|
|
420
|
+
m11 = m[offset + 5],
|
|
421
|
+
m12 = m[offset + 9]
|
|
422
|
+
const m20 = m[offset + 2],
|
|
423
|
+
m21 = m[offset + 6],
|
|
424
|
+
m22 = m[offset + 10]
|
|
425
|
+
const trace = m00 + m11 + m22
|
|
426
|
+
let x = 0,
|
|
427
|
+
y = 0,
|
|
428
|
+
z = 0,
|
|
429
|
+
w = 1
|
|
430
|
+
if (trace > 0) {
|
|
431
|
+
const s = Math.sqrt(trace + 1.0) * 2
|
|
432
|
+
w = 0.25 * s
|
|
433
|
+
x = (m21 - m12) / s
|
|
434
|
+
y = (m02 - m20) / s
|
|
435
|
+
z = (m10 - m01) / s
|
|
436
|
+
} else if (m00 > m11 && m00 > m22) {
|
|
437
|
+
const s = Math.sqrt(1.0 + m00 - m11 - m22) * 2
|
|
438
|
+
w = (m21 - m12) / s
|
|
439
|
+
x = 0.25 * s
|
|
440
|
+
y = (m01 + m10) / s
|
|
441
|
+
z = (m02 + m20) / s
|
|
442
|
+
} else if (m11 > m22) {
|
|
443
|
+
const s = Math.sqrt(1.0 + m11 - m00 - m22) * 2
|
|
444
|
+
w = (m02 - m20) / s
|
|
445
|
+
x = (m01 + m10) / s
|
|
446
|
+
y = 0.25 * s
|
|
447
|
+
z = (m12 + m21) / s
|
|
448
|
+
} else {
|
|
449
|
+
const s = Math.sqrt(1.0 + m22 - m00 - m11) * 2
|
|
450
|
+
w = (m10 - m01) / s
|
|
451
|
+
x = (m02 + m20) / s
|
|
452
|
+
y = (m12 + m21) / s
|
|
453
|
+
z = 0.25 * s
|
|
454
|
+
}
|
|
455
|
+
const invLen = 1 / Math.hypot(x, y, z, w)
|
|
456
|
+
return new Quat(x * invLen, y * invLen, z * invLen, w * invLen)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Reset matrix to identity in place
|
|
460
|
+
setIdentity(): this {
|
|
461
|
+
const v = this.values
|
|
462
|
+
v[0] = 1
|
|
463
|
+
v[1] = 0
|
|
464
|
+
v[2] = 0
|
|
465
|
+
v[3] = 0
|
|
466
|
+
v[4] = 0
|
|
467
|
+
v[5] = 1
|
|
468
|
+
v[6] = 0
|
|
469
|
+
v[7] = 0
|
|
470
|
+
v[8] = 0
|
|
471
|
+
v[9] = 0
|
|
472
|
+
v[10] = 1
|
|
473
|
+
v[11] = 0
|
|
474
|
+
v[12] = 0
|
|
475
|
+
v[13] = 0
|
|
476
|
+
v[14] = 0
|
|
477
|
+
v[15] = 1
|
|
478
|
+
return this
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
translateInPlace(tx: number, ty: number, tz: number): this {
|
|
482
|
+
this.values[12] += tx
|
|
483
|
+
this.values[13] += ty
|
|
484
|
+
this.values[14] += tz
|
|
485
|
+
return this
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Full 4x4 matrix inverse using adjugate method
|
|
489
|
+
// This works for any invertible matrix, not just orthonormal transforms
|
|
490
|
+
// The previous implementation assumed orthonormal rotation matrices, which fails
|
|
491
|
+
// when matrices have scaling or are not perfectly orthonormal (e.g., after
|
|
492
|
+
// bone hierarchy transformations)
|
|
493
|
+
inverse(): Mat4 {
|
|
494
|
+
const m = this.values
|
|
495
|
+
const out = new Float32Array(16)
|
|
496
|
+
|
|
497
|
+
const a00 = m[0],
|
|
498
|
+
a01 = m[1],
|
|
499
|
+
a02 = m[2],
|
|
500
|
+
a03 = m[3]
|
|
501
|
+
const a10 = m[4],
|
|
502
|
+
a11 = m[5],
|
|
503
|
+
a12 = m[6],
|
|
504
|
+
a13 = m[7]
|
|
505
|
+
const a20 = m[8],
|
|
506
|
+
a21 = m[9],
|
|
507
|
+
a22 = m[10],
|
|
508
|
+
a23 = m[11]
|
|
509
|
+
const a30 = m[12],
|
|
510
|
+
a31 = m[13],
|
|
511
|
+
a32 = m[14],
|
|
512
|
+
a33 = m[15]
|
|
513
|
+
|
|
514
|
+
const b00 = a00 * a11 - a01 * a10
|
|
515
|
+
const b01 = a00 * a12 - a02 * a10
|
|
516
|
+
const b02 = a00 * a13 - a03 * a10
|
|
517
|
+
const b03 = a01 * a12 - a02 * a11
|
|
518
|
+
const b04 = a01 * a13 - a03 * a11
|
|
519
|
+
const b05 = a02 * a13 - a03 * a12
|
|
520
|
+
const b06 = a20 * a31 - a21 * a30
|
|
521
|
+
const b07 = a20 * a32 - a22 * a30
|
|
522
|
+
const b08 = a20 * a33 - a23 * a30
|
|
523
|
+
const b09 = a21 * a32 - a22 * a31
|
|
524
|
+
const b10 = a21 * a33 - a23 * a31
|
|
525
|
+
const b11 = a22 * a33 - a23 * a32
|
|
526
|
+
|
|
527
|
+
let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
|
|
528
|
+
|
|
529
|
+
if (Math.abs(det) < 1e-10) {
|
|
530
|
+
console.warn("Matrix is not invertible (determinant near zero)")
|
|
531
|
+
return Mat4.identity()
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
det = 1.0 / det
|
|
535
|
+
|
|
536
|
+
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det
|
|
537
|
+
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det
|
|
538
|
+
out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det
|
|
539
|
+
out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det
|
|
540
|
+
out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det
|
|
541
|
+
out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det
|
|
542
|
+
out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det
|
|
543
|
+
out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det
|
|
544
|
+
out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det
|
|
545
|
+
out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det
|
|
546
|
+
out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det
|
|
547
|
+
out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det
|
|
548
|
+
out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det
|
|
549
|
+
out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det
|
|
550
|
+
out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det
|
|
551
|
+
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det
|
|
552
|
+
|
|
553
|
+
return new Mat4(out)
|
|
554
|
+
}
|
|
555
|
+
}
|