reze-engine 0.6.4 → 0.6.6

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.
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { Mat4, Quat, Vec3 } from "./math";
7
7
  import { Bone, IKSolver, IKChainInfo } from "./model";
8
+ export type UpdateWorldMatrixFn = (boneIndex: number, applyIK: boolean) => void;
8
9
  /**
9
10
  * Solve IK chains for a model
10
11
  */
@@ -14,7 +15,7 @@ export declare class IKSolverSystem {
14
15
  /**
15
16
  * Solve all IK chains
16
17
  */
17
- static solve(ikSolvers: IKSolver[], bones: Bone[], localRotations: Quat[], localTranslations: Vec3[], worldMatrices: Mat4[], ikChainInfo: IKChainInfo[]): void;
18
+ static solve(ikSolvers: IKSolver[], bones: Bone[], localRotations: Quat[], localTranslations: Vec3[], worldMatrices: Mat4[], ikChainInfo: IKChainInfo[], updateWorldMatrix?: UpdateWorldMatrixFn): void;
18
19
  private static solveIK;
19
20
  private static solveChain;
20
21
  private static limitAngle;
@@ -1 +1 @@
1
- {"version":3,"file":"ik-solver.d.ts","sourceRoot":"","sources":["../src/ik-solver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,IAAI,EAAU,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAoE7D;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAuB;IAExD;;OAEG;WACW,KAAK,CACjB,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,IAAI,EAAE,EACb,cAAc,EAAE,IAAI,EAAE,EACtB,iBAAiB,EAAE,IAAI,EAAE,EACzB,aAAa,EAAE,IAAI,EAAE,EACrB,WAAW,EAAE,WAAW,EAAE,GACzB,IAAI;IAMP,OAAO,CAAC,MAAM,CAAC,OAAO;IA2EtB,OAAO,CAAC,MAAM,CAAC,UAAU;IA4FzB,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,OAAO,CAAC,MAAM,CAAC,WAAW;IAM1B,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAKlC,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAmCjC,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAQ/B,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAqBvC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAc3C,OAAO,CAAC,MAAM,CAAC,eAAe;IAS9B,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAoCjC"}
1
+ {"version":3,"file":"ik-solver.d.ts","sourceRoot":"","sources":["../src/ik-solver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,IAAI,EAAU,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG7D,MAAM,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;AAoE/E;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAuB;IAExD;;OAEG;WACW,KAAK,CACjB,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,IAAI,EAAE,EACb,cAAc,EAAE,IAAI,EAAE,EACtB,iBAAiB,EAAE,IAAI,EAAE,EACzB,aAAa,EAAE,IAAI,EAAE,EACrB,WAAW,EAAE,WAAW,EAAE,EAC1B,iBAAiB,CAAC,EAAE,mBAAmB,GACtC,IAAI;IAMP,OAAO,CAAC,MAAM,CAAC,OAAO;IAoFtB,OAAO,CAAC,MAAM,CAAC,UAAU;IAqGzB,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,OAAO,CAAC,MAAM,CAAC,WAAW;IAM1B,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAKlC,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAmCjC,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAQ/B,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAqBvC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAc3C,OAAO,CAAC,MAAM,CAAC,eAAe;IAS9B,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAoCjC"}
package/dist/ik-solver.js CHANGED
@@ -74,12 +74,12 @@ export class IKSolverSystem {
74
74
  /**
75
75
  * Solve all IK chains
76
76
  */
77
- static solve(ikSolvers, bones, localRotations, localTranslations, worldMatrices, ikChainInfo) {
77
+ static solve(ikSolvers, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, updateWorldMatrix) {
78
78
  for (const solver of ikSolvers) {
79
- this.solveIK(solver, bones, localRotations, localTranslations, worldMatrices, ikChainInfo);
79
+ this.solveIK(solver, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, updateWorldMatrix);
80
80
  }
81
81
  }
82
- static solveIK(solver, bones, localRotations, localTranslations, worldMatrices, ikChainInfo) {
82
+ static solveIK(solver, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, updateWorldMatrix) {
83
83
  if (solver.links.length === 0)
84
84
  return;
85
85
  const ikBoneIndex = solver.ikBoneIndex;
@@ -99,10 +99,18 @@ export class IKSolverSystem {
99
99
  chains.push(new IKChain(link.boneIndex, link));
100
100
  }
101
101
  // Update chain bones and target bone world matrices (initial state, no IK yet)
102
- for (let i = chains.length - 1; i >= 0; i--) {
103
- this.updateWorldMatrix(chains[i].boneIndex, bones, localRotations, localTranslations, worldMatrices, undefined);
102
+ if (updateWorldMatrix) {
103
+ for (let i = chains.length - 1; i >= 0; i--) {
104
+ updateWorldMatrix(chains[i].boneIndex, false);
105
+ }
106
+ updateWorldMatrix(targetBoneIndex, false);
107
+ }
108
+ else {
109
+ for (let i = chains.length - 1; i >= 0; i--) {
110
+ this.updateWorldMatrix(chains[i].boneIndex, bones, localRotations, localTranslations, worldMatrices, undefined);
111
+ }
112
+ this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined);
104
113
  }
105
- this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined);
106
114
  if (this.getDistance(ikBoneIndex, targetBoneIndex, worldMatrices) < this.EPSILON)
107
115
  return;
108
116
  // Solve iteratively
@@ -112,7 +120,7 @@ export class IKSolverSystem {
112
120
  for (let chainIndex = 0; chainIndex < chains.length; chainIndex++) {
113
121
  const chain = chains[chainIndex];
114
122
  if (chain.solveAxis !== InternalSolveAxis.Fixed) {
115
- this.solveChain(chain, chainIndex, solver, ikBoneIndex, targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, i < halfIteration);
123
+ this.solveChain(chain, chainIndex, solver, ikBoneIndex, targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, i < halfIteration, updateWorldMatrix);
116
124
  }
117
125
  }
118
126
  if (this.getDistance(ikBoneIndex, targetBoneIndex, worldMatrices) < this.EPSILON)
@@ -128,7 +136,7 @@ export class IKSolverSystem {
128
136
  }
129
137
  }
130
138
  }
131
- static solveChain(chain, chainIndex, solver, ikBoneIndex, targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, useAxis) {
139
+ static solveChain(chain, chainIndex, solver, ikBoneIndex, targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, useAxis, updateWorldMatrix) {
132
140
  const chainBoneIndex = chain.boneIndex;
133
141
  const chainPosition = this.getWorldTranslation(chainBoneIndex, worldMatrices);
134
142
  const ikPosition = this.getWorldTranslation(ikBoneIndex, worldMatrices);
@@ -191,13 +199,22 @@ export class IKSolverSystem {
191
199
  chainInfo.ikRotation = chainInfo.ikRotation.multiply(localRot.clone().conjugate().normalize());
192
200
  }
193
201
  }
194
- // Update world matrices for affected bones (using IK-modified rotations)
195
- for (let i = chainIndex; i >= 0; i--) {
196
- const link = solver.links[i];
197
- this.updateWorldMatrix(link.boneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo);
202
+ // Update world matrices for affected bones (using callback - handles append correctly)
203
+ if (updateWorldMatrix) {
204
+ for (let i = chainIndex; i >= 0; i--) {
205
+ const link = solver.links[i];
206
+ updateWorldMatrix(link.boneIndex, true); // applyIK = true
207
+ }
208
+ updateWorldMatrix(targetBoneIndex, false);
209
+ }
210
+ else {
211
+ for (let i = chainIndex; i >= 0; i--) {
212
+ const link = solver.links[i];
213
+ this.updateWorldMatrix(link.boneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo);
214
+ }
215
+ this.updateWorldMatrix(ikBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined);
216
+ this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined);
198
217
  }
199
- this.updateWorldMatrix(ikBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined);
200
- this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined);
201
218
  }
202
219
  static limitAngle(angle, min, max, useAxis) {
203
220
  if (angle < min) {
package/dist/model.d.ts CHANGED
@@ -143,6 +143,15 @@ export declare class Model {
143
143
  getMorphWeights(): Float32Array;
144
144
  rotateBones(boneRotations: Record<string, Quat>, durationMs?: number): void;
145
145
  moveBones(boneTranslations: Record<string, Vec3>, durationMs?: number): void;
146
+ /**
147
+ * Convert VMD-style relative translation (relative to bind pose world position) to local translation
148
+ * This helper is used by both moveBones and getPoseAtTime to ensure consistent translation handling
149
+ * @param boneIdx - Bone index
150
+ * @param vmdRelativeTranslation - VMD relative translation
151
+ * @param rotation - Optional rotation to use for conversion. If not provided, uses current localRotation.
152
+ * Use animation rotation (from frame) to avoid conflicts when IK modifies rotation.
153
+ */
154
+ private convertVMDTranslationToLocal;
146
155
  getBoneWorldMatrices(): Float32Array;
147
156
  getBoneInverseBindMatrices(): Float32Array;
148
157
  getSkinMatrices(): Float32Array;
@@ -203,6 +212,8 @@ export declare class Model {
203
212
  */
204
213
  update(deltaTime: number): boolean;
205
214
  private solveIKChains;
215
+ private ikComputedSet;
216
+ private computeSingleBoneWorldMatrix;
206
217
  private computeWorldMatrices;
207
218
  }
208
219
  //# sourceMappingURL=model.d.ts.map
@@ -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
@@ -31,6 +31,8 @@ export class Model {
31
31
  // IK and Physics enable flags
32
32
  this.ikEnabled = true;
33
33
  this.physicsEnabled = true;
34
+ // Cached set to track which bones are being computed in current IK pass (to avoid infinite recursion)
35
+ this.ikComputedSet = new Set();
34
36
  // Store base vertex data (original positions before morphing)
35
37
  this.baseVertexData = new Float32Array(vertexData);
36
38
  this.vertexData = vertexData;
@@ -302,51 +304,13 @@ export class Model {
302
304
  const state = this.tweenState;
303
305
  const now = this.tweenTimeMs;
304
306
  const dur = durationMs && durationMs > 0 ? durationMs : 0;
305
- // Compute bind pose world positions for all bones
306
- const skeleton = this.skeleton;
307
- const computeBindPoseWorldPosition = (idx) => {
308
- const bone = skeleton.bones[idx];
309
- const bindPos = new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2]);
310
- if (bone.parentIndex >= 0 && bone.parentIndex < skeleton.bones.length) {
311
- const parentWorldPos = computeBindPoseWorldPosition(bone.parentIndex);
312
- return parentWorldPos.add(bindPos);
313
- }
314
- else {
315
- return bindPos;
316
- }
317
- };
318
307
  for (const [name, vmdRelativeTranslation] of Object.entries(boneTranslations)) {
319
308
  const idx = this.runtimeSkeleton.nameIndex[name] ?? -1;
320
309
  if (idx < 0 || idx >= this.skeleton.bones.length)
321
310
  continue;
322
- const bone = this.skeleton.bones[idx];
323
311
  const translations = this.runtimeSkeleton.localTranslations;
324
- const localRot = this.runtimeSkeleton.localRotations;
325
- // VMD translation is relative to bind pose world position
326
- // targetWorldPos = bindPoseWorldPos + vmdRelativeTranslation
327
- const bindPoseWorldPos = computeBindPoseWorldPosition(idx);
328
- const targetWorldPos = bindPoseWorldPos.add(vmdRelativeTranslation);
329
- // Convert target world position to local translation
330
- // We need parent's bind pose world position to transform to parent space
331
- let parentBindPoseWorldPos;
332
- if (bone.parentIndex >= 0) {
333
- parentBindPoseWorldPos = computeBindPoseWorldPosition(bone.parentIndex);
334
- }
335
- else {
336
- parentBindPoseWorldPos = Vec3.zeros();
337
- }
338
- // Transform target world position to parent's local space
339
- // In bind pose, parent's world matrix is just a translation
340
- const parentSpacePos = targetWorldPos.subtract(parentBindPoseWorldPos);
341
- // Subtract bindTranslation to get position after bind translation
342
- const afterBindTranslation = parentSpacePos.subtract(new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2]));
343
- // Apply inverse rotation to get local translation
344
- const localRotation = localRot[idx];
345
- // Clone to avoid mutating, then conjugate and normalize
346
- const invRotation = localRotation.clone().conjugate().normalize();
347
- const rotationMat = Mat4.fromQuat(invRotation.x, invRotation.y, invRotation.z, invRotation.w);
348
- const rm = rotationMat.values;
349
- const localTranslation = new Vec3(rm[0] * afterBindTranslation.x + rm[4] * afterBindTranslation.y + rm[8] * afterBindTranslation.z, rm[1] * afterBindTranslation.x + rm[5] * afterBindTranslation.y + rm[9] * afterBindTranslation.z, rm[2] * afterBindTranslation.x + rm[6] * afterBindTranslation.y + rm[10] * afterBindTranslation.z);
312
+ // Convert VMD relative translation to local translation
313
+ const localTranslation = this.convertVMDTranslationToLocal(idx, vmdRelativeTranslation);
350
314
  const [tx, ty, tz] = [localTranslation.x, localTranslation.y, localTranslation.z];
351
315
  if (dur === 0) {
352
316
  translations[idx].x = tx;
@@ -381,6 +345,60 @@ export class Model {
381
345
  state.transActive[idx] = 1;
382
346
  }
383
347
  }
348
+ /**
349
+ * Convert VMD-style relative translation (relative to bind pose world position) to local translation
350
+ * This helper is used by both moveBones and getPoseAtTime to ensure consistent translation handling
351
+ * @param boneIdx - Bone index
352
+ * @param vmdRelativeTranslation - VMD relative translation
353
+ * @param rotation - Optional rotation to use for conversion. If not provided, uses current localRotation.
354
+ * Use animation rotation (from frame) to avoid conflicts when IK modifies rotation.
355
+ */
356
+ convertVMDTranslationToLocal(boneIdx, vmdRelativeTranslation, rotation) {
357
+ const skeleton = this.skeleton;
358
+ const bones = skeleton.bones;
359
+ const localRot = this.runtimeSkeleton.localRotations;
360
+ // Compute bind pose world positions for all bones
361
+ const computeBindPoseWorldPosition = (idx) => {
362
+ const bone = bones[idx];
363
+ const bindPos = new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2]);
364
+ if (bone.parentIndex >= 0 && bone.parentIndex < bones.length) {
365
+ const parentWorldPos = computeBindPoseWorldPosition(bone.parentIndex);
366
+ return parentWorldPos.add(bindPos);
367
+ }
368
+ else {
369
+ return bindPos;
370
+ }
371
+ };
372
+ const bone = bones[boneIdx];
373
+ // VMD translation is relative to bind pose world position
374
+ // targetWorldPos = bindPoseWorldPos + vmdRelativeTranslation
375
+ const bindPoseWorldPos = computeBindPoseWorldPosition(boneIdx);
376
+ const targetWorldPos = bindPoseWorldPos.add(vmdRelativeTranslation);
377
+ // Convert target world position to local translation
378
+ // We need parent's bind pose world position to transform to parent space
379
+ let parentBindPoseWorldPos;
380
+ if (bone.parentIndex >= 0) {
381
+ parentBindPoseWorldPos = computeBindPoseWorldPosition(bone.parentIndex);
382
+ }
383
+ else {
384
+ parentBindPoseWorldPos = Vec3.zeros();
385
+ }
386
+ // Transform target world position to parent's local space
387
+ // In bind pose, parent's world matrix is just a translation
388
+ const parentSpacePos = targetWorldPos.subtract(parentBindPoseWorldPos);
389
+ // Subtract bindTranslation to get position after bind translation
390
+ const afterBindTranslation = parentSpacePos.subtract(new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2]));
391
+ // Apply inverse rotation to get local translation
392
+ // Use provided rotation (animation rotation) or fall back to current localRotation
393
+ // Using animation rotation prevents conflicts when IK modifies the rotation
394
+ const localRotation = rotation ?? localRot[boneIdx];
395
+ // Clone to avoid mutating, then conjugate and normalize
396
+ const invRotation = localRotation.clone().conjugate().normalize();
397
+ const rotationMat = Mat4.fromQuat(invRotation.x, invRotation.y, invRotation.z, invRotation.w);
398
+ const rm = rotationMat.values;
399
+ const localTranslation = new Vec3(rm[0] * afterBindTranslation.x + rm[4] * afterBindTranslation.y + rm[8] * afterBindTranslation.z, rm[1] * afterBindTranslation.x + rm[5] * afterBindTranslation.y + rm[9] * afterBindTranslation.z, rm[2] * afterBindTranslation.x + rm[6] * afterBindTranslation.y + rm[10] * afterBindTranslation.z);
400
+ return localTranslation;
401
+ }
384
402
  getBoneWorldMatrices() {
385
403
  // Convert Mat4[] to Float32Array for WebGPU compatibility
386
404
  const boneCount = this.skeleton.bones.length;
@@ -704,8 +722,13 @@ export class Model {
704
722
  const localTrans = this.runtimeSkeleton.localTranslations[boneIdx];
705
723
  if (!frameB) {
706
724
  // No interpolation needed - direct assignment
707
- localRot.set(frameA.rotation);
708
- localTrans.set(frameA.translation);
725
+ // Use animation frame's rotation for translation conversion to ensure consistency
726
+ // This prevents conflicts when IK later modifies the rotation
727
+ const frameRotation = frameA.rotation;
728
+ localRot.set(frameRotation);
729
+ // Convert VMD relative translation to local translation using animation rotation
730
+ const localTranslation = this.convertVMDTranslationToLocal(boneIdx, frameA.translation, frameRotation);
731
+ localTrans.set(localTranslation);
709
732
  }
710
733
  else {
711
734
  const timeA = keyFrames[idx].time;
@@ -717,17 +740,21 @@ export class Model {
717
740
  const rotT = bezierInterpolate(interp[0] * INV_127, interp[1] * INV_127, interp[2] * INV_127, interp[3] * INV_127, gradient);
718
741
  // Use Quat.slerp to interpolate rotation
719
742
  const rotation = Quat.slerp(frameA.rotation, frameB.rotation, rotT);
720
- // Interpolate translation using bezier for each component
743
+ // Interpolate VMD translation using bezier for each component
721
744
  // Inline getWeight to avoid function call overhead
722
745
  const getWeight = (offset) => bezierInterpolate(interp[offset] * INV_127, interp[offset + 8] * INV_127, interp[offset + 4] * INV_127, interp[offset + 12] * INV_127, gradient);
723
746
  const txWeight = getWeight(0);
724
747
  const tyWeight = getWeight(16);
725
748
  const tzWeight = getWeight(32);
749
+ // Interpolate VMD relative translations (relative to bind pose world position)
750
+ const interpolatedVMDTranslation = new Vec3(frameA.translation.x + (frameB.translation.x - frameA.translation.x) * txWeight, frameA.translation.y + (frameB.translation.y - frameA.translation.y) * tyWeight, frameA.translation.z + (frameB.translation.z - frameA.translation.z) * tzWeight);
751
+ // Convert interpolated VMD translation to local translation using animation rotation
752
+ // This ensures translation is computed for the animation rotation, not the runtime rotation
753
+ // that will be modified by IK, preventing conflicts
754
+ const localTranslation = this.convertVMDTranslationToLocal(boneIdx, interpolatedVMDTranslation, rotation);
726
755
  // Direct property writes to avoid object allocation
727
756
  localRot.set(rotation);
728
- localTrans.x = frameA.translation.x + (frameB.translation.x - frameA.translation.x) * txWeight;
729
- localTrans.y = frameA.translation.y + (frameB.translation.y - frameA.translation.y) * tyWeight;
730
- localTrans.z = frameA.translation.z + (frameB.translation.z - frameA.translation.z) * tzWeight;
757
+ localTrans.set(localTranslation);
731
758
  }
732
759
  }
733
760
  // Process morph tracks
@@ -807,7 +834,111 @@ export class Model {
807
834
  const ikChainInfo = this.runtimeSkeleton.ikChainInfo;
808
835
  if (!ikChainInfo)
809
836
  return;
810
- IKSolverSystem.solve(ikSolvers, this.skeleton.bones, this.runtimeSkeleton.localRotations, this.runtimeSkeleton.localTranslations, this.runtimeSkeleton.worldMatrices, ikChainInfo);
837
+ // Solve each IK solver sequentially, ensuring consistent state between solvers
838
+ for (const solver of ikSolvers) {
839
+ // Recompute ALL world matrices before each solver starts
840
+ // This ensures each solver sees the effects of previous solvers on localRotations
841
+ this.computeWorldMatrices();
842
+ // Clear computed set for this solver's pass
843
+ this.ikComputedSet.clear();
844
+ // Solve this IK chain
845
+ // Pass callback that uses model's world matrix computation (handles append correctly)
846
+ IKSolverSystem.solve([solver], // Solve one at a time
847
+ this.skeleton.bones, this.runtimeSkeleton.localRotations, this.runtimeSkeleton.localTranslations, this.runtimeSkeleton.worldMatrices, ikChainInfo, (boneIndex, applyIK) => {
848
+ // Clear computed set for each bone update to allow recomputation in same iteration
849
+ this.ikComputedSet.delete(boneIndex);
850
+ this.computeSingleBoneWorldMatrix(boneIndex, applyIK);
851
+ });
852
+ }
853
+ }
854
+ // Add this new method to compute a single bone's world matrix
855
+ // Recursively ensures parents are computed first to avoid using stale parent matrices
856
+ computeSingleBoneWorldMatrix(boneIndex, applyIK) {
857
+ const bones = this.skeleton.bones;
858
+ const localRot = this.runtimeSkeleton.localRotations;
859
+ const localTrans = this.runtimeSkeleton.localTranslations;
860
+ const worldMats = this.runtimeSkeleton.worldMatrices;
861
+ const ikChainInfo = this.runtimeSkeleton.ikChainInfo;
862
+ const b = bones[boneIndex];
863
+ // Prevent infinite recursion: if this bone is already being computed in this call chain, skip
864
+ if (this.ikComputedSet.has(boneIndex)) {
865
+ return;
866
+ }
867
+ // Mark this bone as being computed to prevent infinite recursion
868
+ this.ikComputedSet.add(boneIndex);
869
+ // Recursively compute parent first if it exists (ensures parent matrix is up-to-date)
870
+ if (b.parentIndex >= 0) {
871
+ this.computeSingleBoneWorldMatrix(b.parentIndex, applyIK);
872
+ }
873
+ // Get base rotation
874
+ let boneRot = localRot[boneIndex];
875
+ // Apply IK rotation if requested
876
+ if (applyIK && ikChainInfo) {
877
+ const chainInfo = ikChainInfo[boneIndex];
878
+ if (chainInfo?.ikRotation) {
879
+ boneRot = chainInfo.ikRotation.multiply(boneRot).normalize();
880
+ }
881
+ }
882
+ let rotateM = Mat4.fromQuat(boneRot.x, boneRot.y, boneRot.z, boneRot.w);
883
+ let addLocalTx = 0, addLocalTy = 0, addLocalTz = 0;
884
+ // Handle append transformations (same logic as computeWorldMatrices)
885
+ const appendParentIdx = b.appendParentIndex;
886
+ const hasAppend = b.appendRotate &&
887
+ appendParentIdx !== undefined &&
888
+ appendParentIdx >= 0 &&
889
+ appendParentIdx < bones.length;
890
+ if (hasAppend) {
891
+ const ratio = b.appendRatio === undefined ? 1 : Math.max(-1, Math.min(1, b.appendRatio));
892
+ const hasRatio = Math.abs(ratio) > 1e-6;
893
+ if (hasRatio) {
894
+ if (b.appendRotate) {
895
+ // Get append parent's rotation
896
+ // During IK solving, use only base local rotation (not IK rotations) to avoid
897
+ // conflicts with IK rotations that are still being computed incrementally
898
+ // IK rotations will be applied to localRotations after IK solving completes
899
+ if (appendParentIdx >= 0) {
900
+ // Compute append parent's world matrix for dependency order, but use base rotation for append
901
+ this.computeSingleBoneWorldMatrix(appendParentIdx, applyIK);
902
+ }
903
+ // Use append parent's base local rotation only (IK rotations are applied after solving)
904
+ let appendRot = localRot[appendParentIdx];
905
+ let ax = appendRot.x, ay = appendRot.y, az = appendRot.z;
906
+ const aw = appendRot.w;
907
+ const absRatio = ratio < 0 ? -ratio : ratio;
908
+ if (ratio < 0) {
909
+ ax = -ax;
910
+ ay = -ay;
911
+ az = -az;
912
+ }
913
+ const appendQuat = new Quat(ax, ay, az, aw);
914
+ const result = Quat.slerp(Quat.identity(), appendQuat, absRatio);
915
+ rotateM = Mat4.fromQuat(result.x, result.y, result.z, result.w).multiply(rotateM);
916
+ }
917
+ if (b.appendMove) {
918
+ const appendTrans = localTrans[appendParentIdx];
919
+ addLocalTx = appendTrans.x * ratio;
920
+ addLocalTy = appendTrans.y * ratio;
921
+ addLocalTz = appendTrans.z * ratio;
922
+ }
923
+ }
924
+ }
925
+ const boneTrans = localTrans[boneIndex];
926
+ const localTx = boneTrans.x + addLocalTx;
927
+ const localTy = boneTrans.y + addLocalTy;
928
+ const localTz = boneTrans.z + addLocalTz;
929
+ this.cachedIdentityMat1
930
+ .setIdentity()
931
+ .translateInPlace(b.bindTranslation[0], b.bindTranslation[1], b.bindTranslation[2]);
932
+ this.cachedIdentityMat2.setIdentity().translateInPlace(localTx, localTy, localTz);
933
+ const localM = this.cachedIdentityMat1.multiply(rotateM).multiply(this.cachedIdentityMat2);
934
+ const worldMat = worldMats[boneIndex];
935
+ if (b.parentIndex >= 0) {
936
+ const parentMat = worldMats[b.parentIndex];
937
+ Mat4.multiplyArrays(parentMat.values, 0, localM.values, 0, worldMat.values, 0);
938
+ }
939
+ else {
940
+ worldMat.values.set(localM.values);
941
+ }
811
942
  }
812
943
  computeWorldMatrices() {
813
944
  const bones = this.skeleton.bones;
@@ -0,0 +1,49 @@
1
+ import { Quat, Vec3, Mat4 } from "./math";
2
+ import type { Bone } from "./model";
3
+ /**
4
+ * Runtime bone state - encapsulates bone pose and world matrix
5
+ * Similar to babylon-mmd's IMmdRuntimeBone approach
6
+ */
7
+ export declare class RuntimeBone {
8
+ localRotation: Quat;
9
+ localTranslation: Vec3;
10
+ worldMatrix: Mat4;
11
+ readonly bone: Bone;
12
+ readonly index: number;
13
+ ikRotation?: Quat;
14
+ constructor(bone: Bone, index: number);
15
+ /**
16
+ * Set local rotation
17
+ */
18
+ setRotation(rotation: Quat): void;
19
+ /**
20
+ * Set local translation
21
+ */
22
+ setTranslation(translation: Vec3): void;
23
+ /**
24
+ * Get world position from world matrix
25
+ */
26
+ getWorldPosition(): Vec3;
27
+ /**
28
+ * Update world matrix based on local rotation, translation, and parent's world matrix
29
+ * Handles append rotations and translations
30
+ *
31
+ * @param parentWorldMatrix Parent's world matrix (null if root bone)
32
+ * @param allBones Array of all runtime bones (for append parent lookup)
33
+ * @param applyIK Whether to apply IK rotation (default: true). Set to false when computing initial world matrices before IK solving.
34
+ */
35
+ updateWorldMatrix(parentWorldMatrix: Mat4 | null, allBones: RuntimeBone[], applyIK?: boolean): void;
36
+ /**
37
+ * Reset IK rotation to identity
38
+ */
39
+ resetIKRotation(): void;
40
+ /**
41
+ * Set IK rotation delta (accumulated during IK solving)
42
+ */
43
+ setIKRotation(ikRotation: Quat): void;
44
+ /**
45
+ * Apply IK rotation to local rotation and clear IK rotation
46
+ */
47
+ applyIKRotation(): void;
48
+ }
49
+ //# sourceMappingURL=runtime-bone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-bone.d.ts","sourceRoot":"","sources":["../src/runtime-bone.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAEnC;;;GAGG;AACH,qBAAa,WAAW;IACf,aAAa,EAAE,IAAI,CAAA;IACnB,gBAAgB,EAAE,IAAI,CAAA;IACtB,WAAW,EAAE,IAAI,CAAA;IACxB,SAAgB,IAAI,EAAE,IAAI,CAAA;IAC1B,SAAgB,KAAK,EAAE,MAAM,CAAA;IAGtB,UAAU,CAAC,EAAE,IAAI,CAAA;gBAEZ,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM;IAQrC;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,IAAI,GAAG,IAAI;IAIjC;;OAEG;IACH,cAAc,CAAC,WAAW,EAAE,IAAI,GAAG,IAAI;IAIvC;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAKxB;;;;;;;OAOG;IACH,iBAAiB,CAAC,iBAAiB,EAAE,IAAI,GAAG,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,GAAE,OAAc,GAAG,IAAI;IA2EzG;;OAEG;IACH,eAAe,IAAI,IAAI;IAIvB;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,IAAI,GAAG,IAAI;IAIrC;;OAEG;IACH,eAAe,IAAI,IAAI;CAMxB"}
@@ -0,0 +1,121 @@
1
+ import { Quat, Vec3, Mat4 } from "./math";
2
+ /**
3
+ * Runtime bone state - encapsulates bone pose and world matrix
4
+ * Similar to babylon-mmd's IMmdRuntimeBone approach
5
+ */
6
+ export class RuntimeBone {
7
+ constructor(bone, index) {
8
+ this.bone = bone;
9
+ this.index = index;
10
+ this.localRotation = Quat.identity();
11
+ this.localTranslation = new Vec3(0, 0, 0);
12
+ this.worldMatrix = Mat4.identity();
13
+ }
14
+ /**
15
+ * Set local rotation
16
+ */
17
+ setRotation(rotation) {
18
+ this.localRotation.set(rotation);
19
+ }
20
+ /**
21
+ * Set local translation
22
+ */
23
+ setTranslation(translation) {
24
+ this.localTranslation.set(translation);
25
+ }
26
+ /**
27
+ * Get world position from world matrix
28
+ */
29
+ getWorldPosition() {
30
+ const m = this.worldMatrix.values;
31
+ return new Vec3(m[12], m[13], m[14]);
32
+ }
33
+ /**
34
+ * Update world matrix based on local rotation, translation, and parent's world matrix
35
+ * Handles append rotations and translations
36
+ *
37
+ * @param parentWorldMatrix Parent's world matrix (null if root bone)
38
+ * @param allBones Array of all runtime bones (for append parent lookup)
39
+ * @param applyIK Whether to apply IK rotation (default: true). Set to false when computing initial world matrices before IK solving.
40
+ */
41
+ updateWorldMatrix(parentWorldMatrix, allBones, applyIK = true) {
42
+ // Start with local rotation
43
+ let finalRot = this.localRotation;
44
+ // Apply IK rotation if present and applyIK is true
45
+ if (applyIK && this.ikRotation) {
46
+ finalRot = this.ikRotation.multiply(finalRot).normalize();
47
+ }
48
+ // Build rotation matrix
49
+ let rotateM = Mat4.fromQuat(finalRot.x, finalRot.y, finalRot.z, finalRot.w);
50
+ let addLocalTx = 0, addLocalTy = 0, addLocalTz = 0;
51
+ // Handle append rotation and translation
52
+ const appendParentIdx = this.bone.appendParentIndex;
53
+ const hasAppend = this.bone.appendRotate && appendParentIdx !== undefined && appendParentIdx >= 0 && appendParentIdx < allBones.length;
54
+ if (hasAppend) {
55
+ const ratio = this.bone.appendRatio === undefined ? 1 : Math.max(-1, Math.min(1, this.bone.appendRatio));
56
+ const hasRatio = Math.abs(ratio) > 1e-6;
57
+ if (hasRatio) {
58
+ if (this.bone.appendRotate) {
59
+ const appendParent = allBones[appendParentIdx];
60
+ const appendRot = appendParent.localRotation;
61
+ let ax = appendRot.x;
62
+ let ay = appendRot.y;
63
+ let az = appendRot.z;
64
+ const aw = appendRot.w;
65
+ const absRatio = ratio < 0 ? -ratio : ratio;
66
+ if (ratio < 0) {
67
+ ax = -ax;
68
+ ay = -ay;
69
+ az = -az;
70
+ }
71
+ const appendQuat = new Quat(ax, ay, az, aw);
72
+ const result = Quat.slerp(Quat.identity(), appendQuat, absRatio);
73
+ rotateM = Mat4.fromQuat(result.x, result.y, result.z, result.w).multiply(rotateM);
74
+ }
75
+ if (this.bone.appendMove) {
76
+ const appendParent = allBones[appendParentIdx];
77
+ const appendTrans = appendParent.localTranslation;
78
+ const appendRatio = this.bone.appendRatio ?? 1;
79
+ addLocalTx = appendTrans.x * appendRatio;
80
+ addLocalTy = appendTrans.y * appendRatio;
81
+ addLocalTz = appendTrans.z * appendRatio;
82
+ }
83
+ }
84
+ }
85
+ // Build local matrix: bind translation, then rotation, then local translation + append translation
86
+ const localTx = this.localTranslation.x + addLocalTx;
87
+ const localTy = this.localTranslation.y + addLocalTy;
88
+ const localTz = this.localTranslation.z + addLocalTz;
89
+ const bindMat = Mat4.identity().translateInPlace(this.bone.bindTranslation[0], this.bone.bindTranslation[1], this.bone.bindTranslation[2]);
90
+ const transMat = Mat4.identity().translateInPlace(localTx, localTy, localTz);
91
+ const localM = bindMat.multiply(rotateM).multiply(transMat);
92
+ // Compute world matrix
93
+ if (parentWorldMatrix) {
94
+ this.worldMatrix = parentWorldMatrix.multiply(localM);
95
+ }
96
+ else {
97
+ this.worldMatrix = localM;
98
+ }
99
+ }
100
+ /**
101
+ * Reset IK rotation to identity
102
+ */
103
+ resetIKRotation() {
104
+ this.ikRotation = undefined;
105
+ }
106
+ /**
107
+ * Set IK rotation delta (accumulated during IK solving)
108
+ */
109
+ setIKRotation(ikRotation) {
110
+ this.ikRotation = ikRotation;
111
+ }
112
+ /**
113
+ * Apply IK rotation to local rotation and clear IK rotation
114
+ */
115
+ applyIKRotation() {
116
+ if (this.ikRotation) {
117
+ this.localRotation = this.ikRotation.multiply(this.localRotation).normalize();
118
+ this.ikRotation = undefined;
119
+ }
120
+ }
121
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reze-engine",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "A WebGPU-based MMD model renderer",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",