reze-engine 0.13.0 → 0.13.2

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/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
@@ -96,6 +96,10 @@ export declare class Model {
96
96
  private _name;
97
97
  get name(): string;
98
98
  setName(value: string): void;
99
+ get position(): Vec3;
100
+ get rotation(): Quat;
101
+ setPosition(position: Vec3): void;
102
+ setRotation(rotation: Quat): void;
99
103
  private vertexData;
100
104
  private baseVertexData;
101
105
  private vertexCount;
@@ -110,8 +114,11 @@ export declare class Model {
110
114
  private runtimeSkeleton;
111
115
  private runtimeMorph;
112
116
  private morphsDirty;
113
- private cachedIdentityMat1;
114
- private cachedIdentityMat2;
117
+ private _position;
118
+ private _rotation;
119
+ private rootMatrixValues;
120
+ private rootMatrixDirty;
121
+ private rootIsIdentity;
115
122
  private skinMatricesArray?;
116
123
  private tweenState;
117
124
  private tweenTimeMs;
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
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";
@@ -13,6 +13,23 @@ export class Model {
13
13
  setName(value) {
14
14
  this._name = value;
15
15
  }
16
+ // Root transform public API. Instant setters — no tween baked in; wrap in
17
+ // your own lerp if you need smoothing. Changes are applied on the next
18
+ // getSkinMatrices() call (once per frame during rendering).
19
+ get position() {
20
+ return this._position;
21
+ }
22
+ get rotation() {
23
+ return this._rotation;
24
+ }
25
+ setPosition(position) {
26
+ this._position.set(position);
27
+ this.rootMatrixDirty = true;
28
+ }
29
+ setRotation(rotation) {
30
+ this._rotation.set(rotation);
31
+ this.rootMatrixDirty = true;
32
+ }
16
33
  /** Called by Engine when registering the model; enables loadVmd to resolve relative paths for folder uploads. */
17
34
  setAssetContext(reader, basePath) {
18
35
  this.assetReader = reader;
@@ -26,9 +43,15 @@ export class Model {
26
43
  this.rigidbodies = [];
27
44
  this.joints = [];
28
45
  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();
46
+ // Root transform model's placement in world space, independent of bones.
47
+ // Folded into skin matrices (see getSkinMatrices) so every pass (main VS,
48
+ // shadow VS, any future skinned pass) sees it without per-shader plumbing.
49
+ // Skip-when-identity flag avoids the extra mat mul per bone when unused.
50
+ this._position = Vec3.zeros();
51
+ this._rotation = Quat.identity();
52
+ this.rootMatrixValues = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
53
+ this.rootMatrixDirty = false;
54
+ this.rootIsIdentity = true;
32
55
  this.tweenTimeMs = 0; // Time tracking for tweens (milliseconds)
33
56
  // Animation: state and multiple slots (idle, walk, attack, etc.); commit/rollback for action-game style
34
57
  this.animationState = new AnimationState();
@@ -426,12 +449,32 @@ export class Model {
426
449
  this.skinMatricesArray = new Float32Array(boneCount * 16);
427
450
  }
428
451
  const skinMatrices = this.skinMatricesArray;
429
- // Compute skin matrices: skinMatrix = worldMatrix × inverseBindMatrix
430
- for (let i = 0; i < boneCount; i++) {
431
- const worldMat = worldMats[i];
432
- const invBindOffset = i * 16;
433
- const skinOffset = i * 16;
434
- Mat4.multiplyArrays(worldMat.values, 0, invBindMats, invBindOffset, skinMatrices, skinOffset);
452
+ // Rebuild root matrix + cache identity-shortcut flag only when pos/rot changed.
453
+ if (this.rootMatrixDirty) {
454
+ const p = this._position, r = this._rotation;
455
+ Mat4.fromPositionRotationInto(p.x, p.y, p.z, r.x, r.y, r.z, r.w, this.rootMatrixValues);
456
+ this.rootIsIdentity =
457
+ p.x === 0 && p.y === 0 && p.z === 0 &&
458
+ r.x === 0 && r.y === 0 && r.z === 0 && r.w === 1;
459
+ this.rootMatrixDirty = false;
460
+ }
461
+ if (this.rootIsIdentity) {
462
+ // skinMatrix = worldMatrix × inverseBindMatrix
463
+ for (let i = 0; i < boneCount; i++) {
464
+ const off = i * 16;
465
+ Mat4.multiplyArrays(worldMats[i].values, 0, invBindMats, off, skinMatrices, off);
466
+ }
467
+ }
468
+ else {
469
+ // skinMatrix = rootMatrix × worldMatrix × inverseBindMatrix
470
+ // Two-mul path. scratchMat4Values[1] — [0] is owned by computeWorldMatrices.
471
+ const rootVals = this.rootMatrixValues;
472
+ const tmp = scratchMat4Values[1];
473
+ for (let i = 0; i < boneCount; i++) {
474
+ const off = i * 16;
475
+ Mat4.multiplyArrays(rootVals, 0, worldMats[i].values, 0, tmp, 0);
476
+ Mat4.multiplyArrays(tmp, 0, invBindMats, off, skinMatrices, off);
477
+ }
435
478
  }
436
479
  return skinMatrices;
437
480
  }
@@ -864,15 +907,25 @@ export class Model {
864
907
  this.computeSingleBoneWorldMatrix(b.parentIndex, applyIK);
865
908
  }
866
909
  // Get base rotation
867
- let boneRot = localRot[boneIndex];
868
- // Apply IK rotation if requested
910
+ const baseRot = localRot[boneIndex];
911
+ let fx = baseRot.x, fy = baseRot.y, fz = baseRot.z, fw = baseRot.w;
912
+ // Apply IK rotation if requested: finalRot = ik * base, then normalize
869
913
  if (applyIK && ikChainInfo) {
870
914
  const chainInfo = ikChainInfo[boneIndex];
871
915
  if (chainInfo?.ikRotation) {
872
- boneRot = chainInfo.ikRotation.multiply(boneRot).normalize();
916
+ const ik = chainInfo.ikRotation;
917
+ const nx = ik.w * fx + ik.x * fw + ik.y * fz - ik.z * fy;
918
+ const ny = ik.w * fy - ik.x * fz + ik.y * fw + ik.z * fx;
919
+ const nz = ik.w * fz + ik.x * fy - ik.y * fx + ik.z * fw;
920
+ const nw = ik.w * fw - ik.x * fx - ik.y * fy - ik.z * fz;
921
+ const len = Math.sqrt(nx * nx + ny * ny + nz * nz + nw * nw);
922
+ const inv = len > 0 ? 1 / len : 0;
923
+ fx = nx * inv;
924
+ fy = ny * inv;
925
+ fz = nz * inv;
926
+ fw = nw * inv;
873
927
  }
874
928
  }
875
- let rotateM = Mat4.fromQuat(boneRot.x, boneRot.y, boneRot.z, boneRot.w);
876
929
  let addLocalTx = 0, addLocalTy = 0, addLocalTz = 0;
877
930
  // Handle append transformations (same logic as computeWorldMatrices)
878
931
  const appendParentIdx = b.appendParentIndex;
@@ -885,16 +938,11 @@ export class Model {
885
938
  const hasRatio = Math.abs(ratio) > 1e-6;
886
939
  if (hasRatio) {
887
940
  if (b.appendRotate) {
888
- // Get append parent's rotation
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
941
+ // Recurse first (may touch scratch); all scratch use below happens after it unwinds
892
942
  if (appendParentIdx >= 0) {
893
- // Compute append parent's world matrix for dependency order, but use base rotation for append
894
943
  this.computeSingleBoneWorldMatrix(appendParentIdx, applyIK);
895
944
  }
896
- // Use append parent's base local rotation only (IK rotations are applied after solving)
897
- let appendRot = localRot[appendParentIdx];
945
+ const appendRot = localRot[appendParentIdx];
898
946
  let ax = appendRot.x, ay = appendRot.y, az = appendRot.z;
899
947
  const aw = appendRot.w;
900
948
  const absRatio = ratio < 0 ? -ratio : ratio;
@@ -903,9 +951,20 @@ export class Model {
903
951
  ay = -ay;
904
952
  az = -az;
905
953
  }
906
- const appendQuat = new Quat(ax, ay, az, aw);
907
- const result = Quat.slerp(Quat.identity(), appendQuat, absRatio);
908
- rotateM = Mat4.fromQuat(result.x, result.y, result.z, result.w).multiply(rotateM);
954
+ // slerp(identity, appendQuat, absRatio) into scratchQuat[1]
955
+ scratchQuat[0].setXYZW(ax, ay, az, aw);
956
+ scratchQuat[2].setIdentity();
957
+ Quat.slerpInto(scratchQuat[2], scratchQuat[0], absRatio, scratchQuat[1]);
958
+ // finalRot = slerpResult * finalRot (rotation composition as quat mul)
959
+ const sx = scratchQuat[1].x, sy = scratchQuat[1].y, sz = scratchQuat[1].z, sw = scratchQuat[1].w;
960
+ const nx = sw * fx + sx * fw + sy * fz - sz * fy;
961
+ const ny = sw * fy - sx * fz + sy * fw + sz * fx;
962
+ const nz = sw * fz + sx * fy - sy * fx + sz * fw;
963
+ const nw = sw * fw - sx * fx - sy * fy - sz * fz;
964
+ fx = nx;
965
+ fy = ny;
966
+ fz = nz;
967
+ fw = nw;
909
968
  }
910
969
  if (b.appendMove) {
911
970
  const appendTrans = localTrans[appendParentIdx];
@@ -919,18 +978,16 @@ export class Model {
919
978
  const localTx = boneTrans.x + addLocalTx;
920
979
  const localTy = boneTrans.y + addLocalTy;
921
980
  const localTz = boneTrans.z + addLocalTz;
922
- this.cachedIdentityMat1
923
- .setIdentity()
924
- .translateInPlace(b.bindTranslation[0], b.bindTranslation[1], b.bindTranslation[2]);
925
- this.cachedIdentityMat2.setIdentity().translateInPlace(localTx, localTy, localTz);
926
- const localM = this.cachedIdentityMat1.multiply(rotateM).multiply(this.cachedIdentityMat2);
981
+ // Fused local transform: T_bind · R(finalRot) · T_local → scratchMat4Values[0]
982
+ const localMVals = scratchMat4Values[0];
983
+ Mat4.localTransformInto(b.bindTranslation[0], b.bindTranslation[1], b.bindTranslation[2], fx, fy, fz, fw, localTx, localTy, localTz, localMVals);
927
984
  const worldMat = worldMats[boneIndex];
928
985
  if (b.parentIndex >= 0) {
929
986
  const parentMat = worldMats[b.parentIndex];
930
- Mat4.multiplyArrays(parentMat.values, 0, localM.values, 0, worldMat.values, 0);
987
+ Mat4.multiplyArrays(parentMat.values, 0, localMVals, 0, worldMat.values, 0);
931
988
  }
932
989
  else {
933
- worldMat.values.set(localM.values);
990
+ worldMat.values.set(localMVals);
934
991
  }
935
992
  }
936
993
  computeWorldMatrices() {
@@ -950,10 +1007,14 @@ export class Model {
950
1007
  if (b.parentIndex >= boneCount) {
951
1008
  console.warn(`[RZM] bone ${i} parent out of range: ${b.parentIndex}`);
952
1009
  }
1010
+ // Ensure parent is computed FIRST, before we touch any scratch buffers.
1011
+ // Recursion may itself use scratchMat4Values[0] / scratchQuat; doing it up
1012
+ // front keeps the current frame's scratch slots untouched when we use them below.
1013
+ if (b.parentIndex >= 0 && !computed[b.parentIndex])
1014
+ computeWorld(b.parentIndex);
953
1015
  const boneRot = localRot[i];
954
- let rotateM = Mat4.fromQuat(boneRot.x, boneRot.y, boneRot.z, boneRot.w);
1016
+ let fx = boneRot.x, fy = boneRot.y, fz = boneRot.z, fw = boneRot.w;
955
1017
  let addLocalTx = 0, addLocalTy = 0, addLocalTz = 0;
956
- // Optimized append rotation check - only check necessary conditions
957
1018
  const appendParentIdx = b.appendParentIndex;
958
1019
  const hasAppend = b.appendRotate && appendParentIdx !== undefined && appendParentIdx >= 0 && appendParentIdx < boneCount;
959
1020
  if (hasAppend) {
@@ -962,9 +1023,7 @@ export class Model {
962
1023
  if (hasRatio) {
963
1024
  if (b.appendRotate) {
964
1025
  const appendRot = localRot[appendParentIdx];
965
- let ax = appendRot.x;
966
- let ay = appendRot.y;
967
- let az = appendRot.z;
1026
+ let ax = appendRot.x, ay = appendRot.y, az = appendRot.z;
968
1027
  const aw = appendRot.w;
969
1028
  const absRatio = ratio < 0 ? -ratio : ratio;
970
1029
  if (ratio < 0) {
@@ -972,9 +1031,19 @@ export class Model {
972
1031
  ay = -ay;
973
1032
  az = -az;
974
1033
  }
975
- const appendQuat = new Quat(ax, ay, az, aw);
976
- const result = Quat.slerp(Quat.identity(), appendQuat, absRatio);
977
- rotateM = Mat4.fromQuat(result.x, result.y, result.z, result.w).multiply(rotateM);
1034
+ scratchQuat[0].setXYZW(ax, ay, az, aw);
1035
+ scratchQuat[2].setIdentity();
1036
+ Quat.slerpInto(scratchQuat[2], scratchQuat[0], absRatio, scratchQuat[1]);
1037
+ // finalRot = slerpResult * finalRot (quat mul)
1038
+ const sx = scratchQuat[1].x, sy = scratchQuat[1].y, sz = scratchQuat[1].z, sw = scratchQuat[1].w;
1039
+ const nx = sw * fx + sx * fw + sy * fz - sz * fy;
1040
+ const ny = sw * fy - sx * fz + sy * fw + sz * fx;
1041
+ const nz = sw * fz + sx * fy - sy * fx + sz * fw;
1042
+ const nw = sw * fw - sx * fx - sy * fy - sz * fz;
1043
+ fx = nx;
1044
+ fy = ny;
1045
+ fz = nz;
1046
+ fw = nw;
978
1047
  }
979
1048
  if (b.appendMove) {
980
1049
  const appendTrans = localTrans[appendParentIdx];
@@ -985,27 +1054,19 @@ export class Model {
985
1054
  }
986
1055
  }
987
1056
  }
988
- // Build local matrix: identity + bind translation, then rotation, then local translation, then append translation
989
1057
  const boneTrans = localTrans[i];
990
1058
  const localTx = boneTrans.x + addLocalTx;
991
1059
  const localTy = boneTrans.y + addLocalTy;
992
1060
  const localTz = boneTrans.z + addLocalTz;
993
- this.cachedIdentityMat1
994
- .setIdentity()
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);
1061
+ const localMVals = scratchMat4Values[0];
1062
+ Mat4.localTransformInto(b.bindTranslation[0], b.bindTranslation[1], b.bindTranslation[2], fx, fy, fz, fw, localTx, localTy, localTz, localMVals);
998
1063
  const worldMat = worldMats[i];
999
1064
  if (b.parentIndex >= 0) {
1000
- const p = b.parentIndex;
1001
- if (!computed[p])
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);
1065
+ const parentMat = worldMats[b.parentIndex];
1066
+ Mat4.multiplyArrays(parentMat.values, 0, localMVals, 0, worldMat.values, 0);
1006
1067
  }
1007
1068
  else {
1008
- worldMat.values.set(localM.values);
1069
+ worldMat.values.set(localMVals);
1009
1070
  }
1010
1071
  computed[i] = true;
1011
1072
  };
@@ -1 +1 @@
1
- {"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"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"}