reze-engine 0.13.0 → 0.13.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 +32 -29
- package/dist/ik-solver.d.ts +5 -6
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/ik-solver.js +163 -98
- package/dist/math.d.ts +20 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +300 -0
- package/dist/model.d.ts +0 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +61 -46
- package/dist/physics.d.ts.map +1 -1
- package/dist/physics.js +27 -19
- package/package.json +1 -1
- package/src/ik-solver.ts +164 -111
- package/src/math.ts +291 -0
- package/src/model.ts +68 -55
- package/src/physics.ts +31 -23
package/dist/math.js
CHANGED
|
@@ -52,6 +52,48 @@ export class Vec3 {
|
|
|
52
52
|
this.z = other.z;
|
|
53
53
|
return this;
|
|
54
54
|
}
|
|
55
|
+
setXYZ(x, y, z) {
|
|
56
|
+
this.x = x;
|
|
57
|
+
this.y = y;
|
|
58
|
+
this.z = z;
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
// out = a - b (no allocation)
|
|
62
|
+
static subtractInto(a, b, out) {
|
|
63
|
+
out.x = a.x - b.x;
|
|
64
|
+
out.y = a.y - b.y;
|
|
65
|
+
out.z = a.z - b.z;
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
// out = a × b (no allocation). Safe when out === a or out === b.
|
|
69
|
+
static crossInto(a, b, out) {
|
|
70
|
+
const ax = a.x, ay = a.y, az = a.z;
|
|
71
|
+
const bx = b.x, by = b.y, bz = b.z;
|
|
72
|
+
out.x = ay * bz - az * by;
|
|
73
|
+
out.y = az * bx - ax * bz;
|
|
74
|
+
out.z = ax * by - ay * bx;
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
// Read translation from Mat4 values array (column-major) into out.
|
|
78
|
+
static setFromMat4Translation(m, out) {
|
|
79
|
+
out.x = m[12];
|
|
80
|
+
out.y = m[13];
|
|
81
|
+
out.z = m[14];
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
// Transform normal by the upper-left 3x3 of a Mat4 (column-major) into out.
|
|
85
|
+
// Safe when out === normal.
|
|
86
|
+
static transformMat4RotationInto(normal, m, out) {
|
|
87
|
+
const nx = normal.x, ny = normal.y, nz = normal.z;
|
|
88
|
+
out.x = m[0] * nx + m[4] * ny + m[8] * nz;
|
|
89
|
+
out.y = m[1] * nx + m[5] * ny + m[9] * nz;
|
|
90
|
+
out.z = m[2] * nx + m[6] * ny + m[10] * nz;
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
// In-place normalize returning length squared info via Vec3. Alias for normalize() but explicit.
|
|
94
|
+
normalizeInPlace() {
|
|
95
|
+
return this.normalize();
|
|
96
|
+
}
|
|
55
97
|
}
|
|
56
98
|
export class Quat {
|
|
57
99
|
constructor(x, y, z, w) {
|
|
@@ -158,6 +200,78 @@ export class Quat {
|
|
|
158
200
|
const s1 = Math.sin(theta) / sinTheta0;
|
|
159
201
|
return new Quat(s0 * a.x + s1 * bx, s0 * a.y + s1 * by, s0 * a.z + s1 * bz, s0 * a.w + s1 * bw);
|
|
160
202
|
}
|
|
203
|
+
// out = a * b (quaternion multiplication, rotation composition).
|
|
204
|
+
// Safe when out === a or out === b.
|
|
205
|
+
static multiplyInto(a, b, out) {
|
|
206
|
+
const ax = a.x, ay = a.y, az = a.z, aw = a.w;
|
|
207
|
+
const bx = b.x, by = b.y, bz = b.z, bw = b.w;
|
|
208
|
+
out.x = aw * bx + ax * bw + ay * bz - az * by;
|
|
209
|
+
out.y = aw * by - ax * bz + ay * bw + az * bx;
|
|
210
|
+
out.z = aw * bz + ax * by - ay * bx + az * bw;
|
|
211
|
+
out.w = aw * bw - ax * bx - ay * by - az * bz;
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
// out = quat from axis (unnormalized) and angle.
|
|
215
|
+
static fromAxisAngleInto(ax, ay, az, angle, out) {
|
|
216
|
+
const len = Math.sqrt(ax * ax + ay * ay + az * az);
|
|
217
|
+
const invLen = len > 0 ? 1 / len : 0;
|
|
218
|
+
const nx = ax * invLen, ny = ay * invLen, nz = az * invLen;
|
|
219
|
+
const half = angle * 0.5;
|
|
220
|
+
const s = Math.sin(half), c = Math.cos(half);
|
|
221
|
+
out.x = nx * s;
|
|
222
|
+
out.y = ny * s;
|
|
223
|
+
out.z = nz * s;
|
|
224
|
+
out.w = c;
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
// out = slerp(a, b, t). Safe when out === a or out === b.
|
|
228
|
+
static slerpInto(a, b, t, out) {
|
|
229
|
+
let cos = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
|
230
|
+
let bx = b.x, by = b.y, bz = b.z, bw = b.w;
|
|
231
|
+
if (cos < 0) {
|
|
232
|
+
cos = -cos;
|
|
233
|
+
bx = -bx;
|
|
234
|
+
by = -by;
|
|
235
|
+
bz = -bz;
|
|
236
|
+
bw = -bw;
|
|
237
|
+
}
|
|
238
|
+
if (cos > 0.9995) {
|
|
239
|
+
const x = a.x + t * (bx - a.x);
|
|
240
|
+
const y = a.y + t * (by - a.y);
|
|
241
|
+
const z = a.z + t * (bz - a.z);
|
|
242
|
+
const w = a.w + t * (bw - a.w);
|
|
243
|
+
const invLen = 1 / Math.hypot(x, y, z, w);
|
|
244
|
+
out.x = x * invLen;
|
|
245
|
+
out.y = y * invLen;
|
|
246
|
+
out.z = z * invLen;
|
|
247
|
+
out.w = w * invLen;
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
const theta0 = Math.acos(cos);
|
|
251
|
+
const sinTheta0 = Math.sin(theta0);
|
|
252
|
+
const theta = theta0 * t;
|
|
253
|
+
const s0 = Math.sin(theta0 - theta) / sinTheta0;
|
|
254
|
+
const s1 = Math.sin(theta) / sinTheta0;
|
|
255
|
+
out.x = s0 * a.x + s1 * bx;
|
|
256
|
+
out.y = s0 * a.y + s1 * by;
|
|
257
|
+
out.z = s0 * a.z + s1 * bz;
|
|
258
|
+
out.w = s0 * a.w + s1 * bw;
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
261
|
+
setXYZW(x, y, z, w) {
|
|
262
|
+
this.x = x;
|
|
263
|
+
this.y = y;
|
|
264
|
+
this.z = z;
|
|
265
|
+
this.w = w;
|
|
266
|
+
return this;
|
|
267
|
+
}
|
|
268
|
+
setIdentity() {
|
|
269
|
+
this.x = 0;
|
|
270
|
+
this.y = 0;
|
|
271
|
+
this.z = 0;
|
|
272
|
+
this.w = 1;
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
161
275
|
// Convert Euler angles to quaternion (ZXY order, left-handed, PMX format)
|
|
162
276
|
static fromEuler(rotX, rotY, rotZ) {
|
|
163
277
|
const cx = Math.cos(rotX * 0.5);
|
|
@@ -297,6 +411,124 @@ export class Mat4 {
|
|
|
297
411
|
clone() {
|
|
298
412
|
return new Mat4(this.values.slice());
|
|
299
413
|
}
|
|
414
|
+
// Write rotation matrix from quaternion into existing Float32Array (column-major).
|
|
415
|
+
static fromQuatInto(x, y, z, w, out, offset = 0) {
|
|
416
|
+
const x2 = x + x, y2 = y + y, z2 = z + z;
|
|
417
|
+
const xx = x * x2, xy = x * y2, xz = x * z2;
|
|
418
|
+
const yy = y * y2, yz = y * z2, zz = z * z2;
|
|
419
|
+
const wx = w * x2, wy = w * y2, wz = w * z2;
|
|
420
|
+
out[offset + 0] = 1 - (yy + zz);
|
|
421
|
+
out[offset + 1] = xy + wz;
|
|
422
|
+
out[offset + 2] = xz - wy;
|
|
423
|
+
out[offset + 3] = 0;
|
|
424
|
+
out[offset + 4] = xy - wz;
|
|
425
|
+
out[offset + 5] = 1 - (xx + zz);
|
|
426
|
+
out[offset + 6] = yz + wx;
|
|
427
|
+
out[offset + 7] = 0;
|
|
428
|
+
out[offset + 8] = xz + wy;
|
|
429
|
+
out[offset + 9] = yz - wx;
|
|
430
|
+
out[offset + 10] = 1 - (xx + yy);
|
|
431
|
+
out[offset + 11] = 0;
|
|
432
|
+
out[offset + 12] = 0;
|
|
433
|
+
out[offset + 13] = 0;
|
|
434
|
+
out[offset + 14] = 0;
|
|
435
|
+
out[offset + 15] = 1;
|
|
436
|
+
}
|
|
437
|
+
// Fused local transform: out = T(bindT) · R(quat) · T(localT).
|
|
438
|
+
// Result translation = bindT + R * localT; rotation column block = R.
|
|
439
|
+
// Column-major. Zero allocations.
|
|
440
|
+
static localTransformInto(bx, by, bz, qx, qy, qz, qw, lx, ly, lz, out) {
|
|
441
|
+
const x2 = qx + qx, y2 = qy + qy, z2 = qz + qz;
|
|
442
|
+
const xx = qx * x2, xy = qx * y2, xz = qx * z2;
|
|
443
|
+
const yy = qy * y2, yz = qy * z2, zz = qz * z2;
|
|
444
|
+
const wx = qw * x2, wy = qw * y2, wz = qw * z2;
|
|
445
|
+
const m00 = 1 - (yy + zz), m01 = xy + wz, m02 = xz - wy;
|
|
446
|
+
const m10 = xy - wz, m11 = 1 - (xx + zz), m12 = yz + wx;
|
|
447
|
+
const m20 = xz + wy, m21 = yz - wx, m22 = 1 - (xx + yy);
|
|
448
|
+
out[0] = m00;
|
|
449
|
+
out[1] = m01;
|
|
450
|
+
out[2] = m02;
|
|
451
|
+
out[3] = 0;
|
|
452
|
+
out[4] = m10;
|
|
453
|
+
out[5] = m11;
|
|
454
|
+
out[6] = m12;
|
|
455
|
+
out[7] = 0;
|
|
456
|
+
out[8] = m20;
|
|
457
|
+
out[9] = m21;
|
|
458
|
+
out[10] = m22;
|
|
459
|
+
out[11] = 0;
|
|
460
|
+
out[12] = bx + m00 * lx + m10 * ly + m20 * lz;
|
|
461
|
+
out[13] = by + m01 * lx + m11 * ly + m21 * lz;
|
|
462
|
+
out[14] = bz + m02 * lx + m12 * ly + m22 * lz;
|
|
463
|
+
out[15] = 1;
|
|
464
|
+
}
|
|
465
|
+
// Write position+rotation transform into existing Float32Array.
|
|
466
|
+
static fromPositionRotationInto(px, py, pz, qx, qy, qz, qw, out) {
|
|
467
|
+
Mat4.fromQuatInto(qx, qy, qz, qw, out, 0);
|
|
468
|
+
out[12] = px;
|
|
469
|
+
out[13] = py;
|
|
470
|
+
out[14] = pz;
|
|
471
|
+
}
|
|
472
|
+
// In-place 4x4 inverse into out array. Returns true on success, false if singular (out untouched).
|
|
473
|
+
static inverseInto(m, out) {
|
|
474
|
+
const a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3];
|
|
475
|
+
const a10 = m[4], a11 = m[5], a12 = m[6], a13 = m[7];
|
|
476
|
+
const a20 = m[8], a21 = m[9], a22 = m[10], a23 = m[11];
|
|
477
|
+
const a30 = m[12], a31 = m[13], a32 = m[14], a33 = m[15];
|
|
478
|
+
const b00 = a00 * a11 - a01 * a10;
|
|
479
|
+
const b01 = a00 * a12 - a02 * a10;
|
|
480
|
+
const b02 = a00 * a13 - a03 * a10;
|
|
481
|
+
const b03 = a01 * a12 - a02 * a11;
|
|
482
|
+
const b04 = a01 * a13 - a03 * a11;
|
|
483
|
+
const b05 = a02 * a13 - a03 * a12;
|
|
484
|
+
const b06 = a20 * a31 - a21 * a30;
|
|
485
|
+
const b07 = a20 * a32 - a22 * a30;
|
|
486
|
+
const b08 = a20 * a33 - a23 * a30;
|
|
487
|
+
const b09 = a21 * a32 - a22 * a31;
|
|
488
|
+
const b10 = a21 * a33 - a23 * a31;
|
|
489
|
+
const b11 = a22 * a33 - a23 * a32;
|
|
490
|
+
let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
|
|
491
|
+
if (Math.abs(det) < 1e-10)
|
|
492
|
+
return false;
|
|
493
|
+
det = 1.0 / det;
|
|
494
|
+
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
|
|
495
|
+
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
|
|
496
|
+
out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
|
|
497
|
+
out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
|
|
498
|
+
out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
|
|
499
|
+
out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
|
|
500
|
+
out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
|
|
501
|
+
out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
|
|
502
|
+
out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
|
|
503
|
+
out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
|
|
504
|
+
out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
|
|
505
|
+
out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
|
|
506
|
+
out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
|
|
507
|
+
out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
|
|
508
|
+
out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
|
|
509
|
+
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
// Copy only the rotation (upper-left 3x3) of src into dst, zero out translation, identity w.
|
|
513
|
+
// Column-major in both.
|
|
514
|
+
static copyRotationInto(src, dst) {
|
|
515
|
+
dst[0] = src[0];
|
|
516
|
+
dst[1] = src[1];
|
|
517
|
+
dst[2] = src[2];
|
|
518
|
+
dst[3] = 0;
|
|
519
|
+
dst[4] = src[4];
|
|
520
|
+
dst[5] = src[5];
|
|
521
|
+
dst[6] = src[6];
|
|
522
|
+
dst[7] = 0;
|
|
523
|
+
dst[8] = src[8];
|
|
524
|
+
dst[9] = src[9];
|
|
525
|
+
dst[10] = src[10];
|
|
526
|
+
dst[11] = 0;
|
|
527
|
+
dst[12] = 0;
|
|
528
|
+
dst[13] = 0;
|
|
529
|
+
dst[14] = 0;
|
|
530
|
+
dst[15] = 1;
|
|
531
|
+
}
|
|
300
532
|
static fromQuat(x, y, z, w) {
|
|
301
533
|
// Column-major rotation matrix from quaternion (matches glMatrix/WGSL)
|
|
302
534
|
const out = new Float32Array(16);
|
|
@@ -338,6 +570,48 @@ export class Mat4 {
|
|
|
338
570
|
toQuat() {
|
|
339
571
|
return Mat4.toQuatFromArray(this.values, 0);
|
|
340
572
|
}
|
|
573
|
+
// Extract quaternion from matrix array into an existing Quat (no allocation).
|
|
574
|
+
static toQuatFromArrayInto(m, offset, out) {
|
|
575
|
+
const m00 = m[offset + 0], m01 = m[offset + 4], m02 = m[offset + 8];
|
|
576
|
+
const m10 = m[offset + 1], m11 = m[offset + 5], m12 = m[offset + 9];
|
|
577
|
+
const m20 = m[offset + 2], m21 = m[offset + 6], m22 = m[offset + 10];
|
|
578
|
+
const trace = m00 + m11 + m22;
|
|
579
|
+
let x = 0, y = 0, z = 0, w = 1;
|
|
580
|
+
if (trace > 0) {
|
|
581
|
+
const s = Math.sqrt(trace + 1.0) * 2;
|
|
582
|
+
w = 0.25 * s;
|
|
583
|
+
x = (m21 - m12) / s;
|
|
584
|
+
y = (m02 - m20) / s;
|
|
585
|
+
z = (m10 - m01) / s;
|
|
586
|
+
}
|
|
587
|
+
else if (m00 > m11 && m00 > m22) {
|
|
588
|
+
const s = Math.sqrt(1.0 + m00 - m11 - m22) * 2;
|
|
589
|
+
w = (m21 - m12) / s;
|
|
590
|
+
x = 0.25 * s;
|
|
591
|
+
y = (m01 + m10) / s;
|
|
592
|
+
z = (m02 + m20) / s;
|
|
593
|
+
}
|
|
594
|
+
else if (m11 > m22) {
|
|
595
|
+
const s = Math.sqrt(1.0 + m11 - m00 - m22) * 2;
|
|
596
|
+
w = (m02 - m20) / s;
|
|
597
|
+
x = (m01 + m10) / s;
|
|
598
|
+
y = 0.25 * s;
|
|
599
|
+
z = (m12 + m21) / s;
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
const s = Math.sqrt(1.0 + m22 - m00 - m11) * 2;
|
|
603
|
+
w = (m10 - m01) / s;
|
|
604
|
+
x = (m02 + m20) / s;
|
|
605
|
+
y = (m12 + m21) / s;
|
|
606
|
+
z = 0.25 * s;
|
|
607
|
+
}
|
|
608
|
+
const invLen = 1 / Math.hypot(x, y, z, w);
|
|
609
|
+
out.x = x * invLen;
|
|
610
|
+
out.y = y * invLen;
|
|
611
|
+
out.z = z * invLen;
|
|
612
|
+
out.w = w * invLen;
|
|
613
|
+
return out;
|
|
614
|
+
}
|
|
341
615
|
// Static method to extract quaternion from matrix array (avoids creating Mat4 object)
|
|
342
616
|
static toQuatFromArray(m, offset) {
|
|
343
617
|
const m00 = m[offset + 0], m01 = m[offset + 4], m02 = m[offset + 8];
|
|
@@ -452,3 +726,29 @@ export class Mat4 {
|
|
|
452
726
|
return new Mat4(out);
|
|
453
727
|
}
|
|
454
728
|
}
|
|
729
|
+
// Preallocated scratch instances for hot paths. Each subsystem should use its own
|
|
730
|
+
// slot to avoid cross-call stomping. Bump the count if more call sites need scratch.
|
|
731
|
+
export const scratchMat4Values = [
|
|
732
|
+
new Float32Array(16),
|
|
733
|
+
new Float32Array(16),
|
|
734
|
+
new Float32Array(16),
|
|
735
|
+
new Float32Array(16),
|
|
736
|
+
new Float32Array(16),
|
|
737
|
+
new Float32Array(16),
|
|
738
|
+
];
|
|
739
|
+
export const scratchVec3 = [
|
|
740
|
+
new Vec3(0, 0, 0),
|
|
741
|
+
new Vec3(0, 0, 0),
|
|
742
|
+
new Vec3(0, 0, 0),
|
|
743
|
+
new Vec3(0, 0, 0),
|
|
744
|
+
new Vec3(0, 0, 0),
|
|
745
|
+
new Vec3(0, 0, 0),
|
|
746
|
+
new Vec3(0, 0, 0),
|
|
747
|
+
new Vec3(0, 0, 0),
|
|
748
|
+
];
|
|
749
|
+
export const scratchQuat = [
|
|
750
|
+
new Quat(0, 0, 0, 1),
|
|
751
|
+
new Quat(0, 0, 0, 1),
|
|
752
|
+
new Quat(0, 0, 0, 1),
|
|
753
|
+
new Quat(0, 0, 0, 1),
|
|
754
|
+
];
|
package/dist/model.d.ts
CHANGED
|
@@ -110,8 +110,6 @@ export declare class Model {
|
|
|
110
110
|
private runtimeSkeleton;
|
|
111
111
|
private runtimeMorph;
|
|
112
112
|
private morphsDirty;
|
|
113
|
-
private cachedIdentityMat1;
|
|
114
|
-
private cachedIdentityMat2;
|
|
115
113
|
private skinMatricesArray?;
|
|
116
114
|
private tweenState;
|
|
117
115
|
private tweenTimeMs;
|
package/dist/model.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|
package/dist/model.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Mat4, Quat, Vec3 } from "./math";
|
|
1
|
+
import { Mat4, Quat, Vec3, scratchMat4Values, scratchQuat } from "./math";
|
|
2
2
|
import { Engine } from "./engine";
|
|
3
3
|
import { joinAssetPath } from "./asset-reader";
|
|
4
4
|
import { IKSolverSystem } from "./ik-solver";
|
|
@@ -26,9 +26,6 @@ export class Model {
|
|
|
26
26
|
this.rigidbodies = [];
|
|
27
27
|
this.joints = [];
|
|
28
28
|
this.morphsDirty = false; // Flag indicating if morphs need to be applied
|
|
29
|
-
// Cached identity matrices to avoid allocations in computeWorldMatrices
|
|
30
|
-
this.cachedIdentityMat1 = Mat4.identity();
|
|
31
|
-
this.cachedIdentityMat2 = Mat4.identity();
|
|
32
29
|
this.tweenTimeMs = 0; // Time tracking for tweens (milliseconds)
|
|
33
30
|
// Animation: state and multiple slots (idle, walk, attack, etc.); commit/rollback for action-game style
|
|
34
31
|
this.animationState = new AnimationState();
|
|
@@ -864,15 +861,25 @@ export class Model {
|
|
|
864
861
|
this.computeSingleBoneWorldMatrix(b.parentIndex, applyIK);
|
|
865
862
|
}
|
|
866
863
|
// Get base rotation
|
|
867
|
-
|
|
868
|
-
|
|
864
|
+
const baseRot = localRot[boneIndex];
|
|
865
|
+
let fx = baseRot.x, fy = baseRot.y, fz = baseRot.z, fw = baseRot.w;
|
|
866
|
+
// Apply IK rotation if requested: finalRot = ik * base, then normalize
|
|
869
867
|
if (applyIK && ikChainInfo) {
|
|
870
868
|
const chainInfo = ikChainInfo[boneIndex];
|
|
871
869
|
if (chainInfo?.ikRotation) {
|
|
872
|
-
|
|
870
|
+
const ik = chainInfo.ikRotation;
|
|
871
|
+
const nx = ik.w * fx + ik.x * fw + ik.y * fz - ik.z * fy;
|
|
872
|
+
const ny = ik.w * fy - ik.x * fz + ik.y * fw + ik.z * fx;
|
|
873
|
+
const nz = ik.w * fz + ik.x * fy - ik.y * fx + ik.z * fw;
|
|
874
|
+
const nw = ik.w * fw - ik.x * fx - ik.y * fy - ik.z * fz;
|
|
875
|
+
const len = Math.sqrt(nx * nx + ny * ny + nz * nz + nw * nw);
|
|
876
|
+
const inv = len > 0 ? 1 / len : 0;
|
|
877
|
+
fx = nx * inv;
|
|
878
|
+
fy = ny * inv;
|
|
879
|
+
fz = nz * inv;
|
|
880
|
+
fw = nw * inv;
|
|
873
881
|
}
|
|
874
882
|
}
|
|
875
|
-
let rotateM = Mat4.fromQuat(boneRot.x, boneRot.y, boneRot.z, boneRot.w);
|
|
876
883
|
let addLocalTx = 0, addLocalTy = 0, addLocalTz = 0;
|
|
877
884
|
// Handle append transformations (same logic as computeWorldMatrices)
|
|
878
885
|
const appendParentIdx = b.appendParentIndex;
|
|
@@ -885,16 +892,11 @@ export class Model {
|
|
|
885
892
|
const hasRatio = Math.abs(ratio) > 1e-6;
|
|
886
893
|
if (hasRatio) {
|
|
887
894
|
if (b.appendRotate) {
|
|
888
|
-
//
|
|
889
|
-
// During IK solving, use only base local rotation (not IK rotations) to avoid
|
|
890
|
-
// conflicts with IK rotations that are still being computed incrementally
|
|
891
|
-
// IK rotations will be applied to localRotations after IK solving completes
|
|
895
|
+
// Recurse first (may touch scratch); all scratch use below happens after it unwinds
|
|
892
896
|
if (appendParentIdx >= 0) {
|
|
893
|
-
// Compute append parent's world matrix for dependency order, but use base rotation for append
|
|
894
897
|
this.computeSingleBoneWorldMatrix(appendParentIdx, applyIK);
|
|
895
898
|
}
|
|
896
|
-
|
|
897
|
-
let appendRot = localRot[appendParentIdx];
|
|
899
|
+
const appendRot = localRot[appendParentIdx];
|
|
898
900
|
let ax = appendRot.x, ay = appendRot.y, az = appendRot.z;
|
|
899
901
|
const aw = appendRot.w;
|
|
900
902
|
const absRatio = ratio < 0 ? -ratio : ratio;
|
|
@@ -903,9 +905,20 @@ export class Model {
|
|
|
903
905
|
ay = -ay;
|
|
904
906
|
az = -az;
|
|
905
907
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
908
|
+
// slerp(identity, appendQuat, absRatio) into scratchQuat[1]
|
|
909
|
+
scratchQuat[0].setXYZW(ax, ay, az, aw);
|
|
910
|
+
scratchQuat[2].setIdentity();
|
|
911
|
+
Quat.slerpInto(scratchQuat[2], scratchQuat[0], absRatio, scratchQuat[1]);
|
|
912
|
+
// finalRot = slerpResult * finalRot (rotation composition as quat mul)
|
|
913
|
+
const sx = scratchQuat[1].x, sy = scratchQuat[1].y, sz = scratchQuat[1].z, sw = scratchQuat[1].w;
|
|
914
|
+
const nx = sw * fx + sx * fw + sy * fz - sz * fy;
|
|
915
|
+
const ny = sw * fy - sx * fz + sy * fw + sz * fx;
|
|
916
|
+
const nz = sw * fz + sx * fy - sy * fx + sz * fw;
|
|
917
|
+
const nw = sw * fw - sx * fx - sy * fy - sz * fz;
|
|
918
|
+
fx = nx;
|
|
919
|
+
fy = ny;
|
|
920
|
+
fz = nz;
|
|
921
|
+
fw = nw;
|
|
909
922
|
}
|
|
910
923
|
if (b.appendMove) {
|
|
911
924
|
const appendTrans = localTrans[appendParentIdx];
|
|
@@ -919,18 +932,16 @@ export class Model {
|
|
|
919
932
|
const localTx = boneTrans.x + addLocalTx;
|
|
920
933
|
const localTy = boneTrans.y + addLocalTy;
|
|
921
934
|
const localTz = boneTrans.z + addLocalTz;
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
this.cachedIdentityMat2.setIdentity().translateInPlace(localTx, localTy, localTz);
|
|
926
|
-
const localM = this.cachedIdentityMat1.multiply(rotateM).multiply(this.cachedIdentityMat2);
|
|
935
|
+
// Fused local transform: T_bind · R(finalRot) · T_local → scratchMat4Values[0]
|
|
936
|
+
const localMVals = scratchMat4Values[0];
|
|
937
|
+
Mat4.localTransformInto(b.bindTranslation[0], b.bindTranslation[1], b.bindTranslation[2], fx, fy, fz, fw, localTx, localTy, localTz, localMVals);
|
|
927
938
|
const worldMat = worldMats[boneIndex];
|
|
928
939
|
if (b.parentIndex >= 0) {
|
|
929
940
|
const parentMat = worldMats[b.parentIndex];
|
|
930
|
-
Mat4.multiplyArrays(parentMat.values, 0,
|
|
941
|
+
Mat4.multiplyArrays(parentMat.values, 0, localMVals, 0, worldMat.values, 0);
|
|
931
942
|
}
|
|
932
943
|
else {
|
|
933
|
-
worldMat.values.set(
|
|
944
|
+
worldMat.values.set(localMVals);
|
|
934
945
|
}
|
|
935
946
|
}
|
|
936
947
|
computeWorldMatrices() {
|
|
@@ -950,10 +961,14 @@ export class Model {
|
|
|
950
961
|
if (b.parentIndex >= boneCount) {
|
|
951
962
|
console.warn(`[RZM] bone ${i} parent out of range: ${b.parentIndex}`);
|
|
952
963
|
}
|
|
964
|
+
// Ensure parent is computed FIRST, before we touch any scratch buffers.
|
|
965
|
+
// Recursion may itself use scratchMat4Values[0] / scratchQuat; doing it up
|
|
966
|
+
// front keeps the current frame's scratch slots untouched when we use them below.
|
|
967
|
+
if (b.parentIndex >= 0 && !computed[b.parentIndex])
|
|
968
|
+
computeWorld(b.parentIndex);
|
|
953
969
|
const boneRot = localRot[i];
|
|
954
|
-
let
|
|
970
|
+
let fx = boneRot.x, fy = boneRot.y, fz = boneRot.z, fw = boneRot.w;
|
|
955
971
|
let addLocalTx = 0, addLocalTy = 0, addLocalTz = 0;
|
|
956
|
-
// Optimized append rotation check - only check necessary conditions
|
|
957
972
|
const appendParentIdx = b.appendParentIndex;
|
|
958
973
|
const hasAppend = b.appendRotate && appendParentIdx !== undefined && appendParentIdx >= 0 && appendParentIdx < boneCount;
|
|
959
974
|
if (hasAppend) {
|
|
@@ -962,9 +977,7 @@ export class Model {
|
|
|
962
977
|
if (hasRatio) {
|
|
963
978
|
if (b.appendRotate) {
|
|
964
979
|
const appendRot = localRot[appendParentIdx];
|
|
965
|
-
let ax = appendRot.x;
|
|
966
|
-
let ay = appendRot.y;
|
|
967
|
-
let az = appendRot.z;
|
|
980
|
+
let ax = appendRot.x, ay = appendRot.y, az = appendRot.z;
|
|
968
981
|
const aw = appendRot.w;
|
|
969
982
|
const absRatio = ratio < 0 ? -ratio : ratio;
|
|
970
983
|
if (ratio < 0) {
|
|
@@ -972,9 +985,19 @@ export class Model {
|
|
|
972
985
|
ay = -ay;
|
|
973
986
|
az = -az;
|
|
974
987
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
988
|
+
scratchQuat[0].setXYZW(ax, ay, az, aw);
|
|
989
|
+
scratchQuat[2].setIdentity();
|
|
990
|
+
Quat.slerpInto(scratchQuat[2], scratchQuat[0], absRatio, scratchQuat[1]);
|
|
991
|
+
// finalRot = slerpResult * finalRot (quat mul)
|
|
992
|
+
const sx = scratchQuat[1].x, sy = scratchQuat[1].y, sz = scratchQuat[1].z, sw = scratchQuat[1].w;
|
|
993
|
+
const nx = sw * fx + sx * fw + sy * fz - sz * fy;
|
|
994
|
+
const ny = sw * fy - sx * fz + sy * fw + sz * fx;
|
|
995
|
+
const nz = sw * fz + sx * fy - sy * fx + sz * fw;
|
|
996
|
+
const nw = sw * fw - sx * fx - sy * fy - sz * fz;
|
|
997
|
+
fx = nx;
|
|
998
|
+
fy = ny;
|
|
999
|
+
fz = nz;
|
|
1000
|
+
fw = nw;
|
|
978
1001
|
}
|
|
979
1002
|
if (b.appendMove) {
|
|
980
1003
|
const appendTrans = localTrans[appendParentIdx];
|
|
@@ -985,27 +1008,19 @@ export class Model {
|
|
|
985
1008
|
}
|
|
986
1009
|
}
|
|
987
1010
|
}
|
|
988
|
-
// Build local matrix: identity + bind translation, then rotation, then local translation, then append translation
|
|
989
1011
|
const boneTrans = localTrans[i];
|
|
990
1012
|
const localTx = boneTrans.x + addLocalTx;
|
|
991
1013
|
const localTy = boneTrans.y + addLocalTy;
|
|
992
1014
|
const localTz = boneTrans.z + addLocalTz;
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
.translateInPlace(b.bindTranslation[0], b.bindTranslation[1], b.bindTranslation[2]);
|
|
996
|
-
this.cachedIdentityMat2.setIdentity().translateInPlace(localTx, localTy, localTz);
|
|
997
|
-
const localM = this.cachedIdentityMat1.multiply(rotateM).multiply(this.cachedIdentityMat2);
|
|
1015
|
+
const localMVals = scratchMat4Values[0];
|
|
1016
|
+
Mat4.localTransformInto(b.bindTranslation[0], b.bindTranslation[1], b.bindTranslation[2], fx, fy, fz, fw, localTx, localTy, localTz, localMVals);
|
|
998
1017
|
const worldMat = worldMats[i];
|
|
999
1018
|
if (b.parentIndex >= 0) {
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1002
|
-
computeWorld(p);
|
|
1003
|
-
const parentMat = worldMats[p];
|
|
1004
|
-
// Multiply parent world matrix by local matrix
|
|
1005
|
-
Mat4.multiplyArrays(parentMat.values, 0, localM.values, 0, worldMat.values, 0);
|
|
1019
|
+
const parentMat = worldMats[b.parentIndex];
|
|
1020
|
+
Mat4.multiplyArrays(parentMat.values, 0, localMVals, 0, worldMat.values, 0);
|
|
1006
1021
|
}
|
|
1007
1022
|
else {
|
|
1008
|
-
worldMat.values.set(
|
|
1023
|
+
worldMat.values.set(localMVals);
|
|
1009
1024
|
}
|
|
1010
1025
|
computed[i] = true;
|
|
1011
1026
|
};
|
package/dist/physics.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"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"}
|
package/dist/physics.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { Quat, Vec3, Mat4 } from "./math";
|
|
2
|
+
// Physics-local scratch pool for per-frame sync (syncFromBones, applyAmmoRigidbodiesToBones).
|
|
3
|
+
// Each method uses only these slots and completes synchronously before the next is called.
|
|
4
|
+
const _physMat = [
|
|
5
|
+
new Float32Array(16), new Float32Array(16), new Float32Array(16),
|
|
6
|
+
];
|
|
7
|
+
const _physQuat = new Quat(0, 0, 0, 1);
|
|
2
8
|
import { loadAmmo } from "./ammo-loader";
|
|
3
9
|
export var RigidbodyShape;
|
|
4
10
|
(function (RigidbodyShape) {
|
|
@@ -358,9 +364,9 @@ export class Physics {
|
|
|
358
364
|
this.firstFrame = false;
|
|
359
365
|
}
|
|
360
366
|
// Step order: 1) Sync Static/Kinematic from bones, 2) Step physics, 3) Apply dynamic to bones
|
|
361
|
-
this.syncFromBones(boneWorldMatrices,
|
|
367
|
+
this.syncFromBones(boneWorldMatrices, boneCount);
|
|
362
368
|
this.stepAmmoPhysics(dt);
|
|
363
|
-
this.applyAmmoRigidbodiesToBones(boneWorldMatrices,
|
|
369
|
+
this.applyAmmoRigidbodiesToBones(boneWorldMatrices, boneCount);
|
|
364
370
|
}
|
|
365
371
|
// Compute bodyOffsetMatrixInverse for all rigidbodies (called once during initialization)
|
|
366
372
|
computeBodyOffsets(boneInverseBindMatrices, boneCount) {
|
|
@@ -425,7 +431,7 @@ export class Physics {
|
|
|
425
431
|
}
|
|
426
432
|
}
|
|
427
433
|
// Sync Static (FollowBone) and Kinematic rigidbodies to follow bone transforms
|
|
428
|
-
syncFromBones(boneWorldMatrices,
|
|
434
|
+
syncFromBones(boneWorldMatrices, boneCount) {
|
|
429
435
|
if (!this.ammo || !this.dynamicsWorld)
|
|
430
436
|
return;
|
|
431
437
|
const Ammo = this.ammo;
|
|
@@ -440,14 +446,17 @@ export class Physics {
|
|
|
440
446
|
rb.boneIndex < boneCount) {
|
|
441
447
|
const boneIdx = rb.boneIndex;
|
|
442
448
|
const boneWorldMat = boneWorldMatrices[boneIdx];
|
|
443
|
-
//
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
449
|
+
// Lazy-cache bodyOffsetMatrix on first hit (cold path).
|
|
450
|
+
if (!rb.bodyOffsetMatrix)
|
|
451
|
+
rb.bodyOffsetMatrix = rb.bodyOffsetMatrixInverse.inverse();
|
|
452
|
+
// nodeWorld = boneWorld × bodyOffsetMatrix → _physMat[0]
|
|
453
|
+
Mat4.multiplyArrays(boneWorldMat.values, 0, rb.bodyOffsetMatrix.values, 0, _physMat[0], 0);
|
|
454
|
+
const nodeVals = _physMat[0];
|
|
455
|
+
const wx = nodeVals[12], wy = nodeVals[13], wz = nodeVals[14];
|
|
456
|
+
Mat4.toQuatFromArrayInto(nodeVals, 0, _physQuat);
|
|
448
457
|
const transform = new Ammo.btTransform();
|
|
449
|
-
const pos = new Ammo.btVector3(
|
|
450
|
-
const quat = new Ammo.btQuaternion(
|
|
458
|
+
const pos = new Ammo.btVector3(wx, wy, wz);
|
|
459
|
+
const quat = new Ammo.btQuaternion(_physQuat.x, _physQuat.y, _physQuat.z, _physQuat.w);
|
|
451
460
|
transform.setOrigin(pos);
|
|
452
461
|
transform.setRotation(quat);
|
|
453
462
|
ammoBody.setWorldTransform(transform);
|
|
@@ -472,7 +481,7 @@ export class Physics {
|
|
|
472
481
|
this.dynamicsWorld.stepSimulation(dt, maxSubSteps, fixedTimeStep);
|
|
473
482
|
}
|
|
474
483
|
// Apply dynamic rigidbody world transforms to bone world matrices in-place
|
|
475
|
-
applyAmmoRigidbodiesToBones(boneWorldMatrices,
|
|
484
|
+
applyAmmoRigidbodiesToBones(boneWorldMatrices, boneCount) {
|
|
476
485
|
if (!this.ammo || !this.dynamicsWorld)
|
|
477
486
|
return;
|
|
478
487
|
for (let i = 0; i < this.rigidbodies.length; i++) {
|
|
@@ -486,14 +495,13 @@ export class Physics {
|
|
|
486
495
|
const transform = ammoBody.getWorldTransform();
|
|
487
496
|
const origin = transform.getOrigin();
|
|
488
497
|
const rotation = transform.getRotation();
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
boneWorldMatrices[boneIdx].values.set(values);
|
|
498
|
+
// nodeWorldMatrix → _physMat[0] (from ammo position/rotation directly)
|
|
499
|
+
Mat4.fromPositionRotationInto(origin.x(), origin.y(), origin.z(), rotation.x(), rotation.y(), rotation.z(), rotation.w(), _physMat[0]);
|
|
500
|
+
// boneWorld = nodeWorld × bodyOffsetMatrixInverse → _physMat[1]
|
|
501
|
+
const boneVals = _physMat[1];
|
|
502
|
+
Mat4.multiplyArrays(_physMat[0], 0, rb.bodyOffsetMatrixInverse.values, 0, boneVals, 0);
|
|
503
|
+
if (!isNaN(boneVals[0]) && !isNaN(boneVals[15]) && Math.abs(boneVals[0]) < 1e6 && Math.abs(boneVals[15]) < 1e6) {
|
|
504
|
+
boneWorldMatrices[boneIdx].values.set(boneVals);
|
|
497
505
|
}
|
|
498
506
|
else {
|
|
499
507
|
console.warn(`[Physics] Invalid bone world matrix for rigidbody ${i} (${rb.name}), skipping update`);
|