reze-engine 0.6.5 → 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
@@ -146,6 +146,10 @@ export declare class Model {
146
146
  /**
147
147
  * Convert VMD-style relative translation (relative to bind pose world position) to local translation
148
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.
149
153
  */
150
154
  private convertVMDTranslationToLocal;
151
155
  getBoneWorldMatrices(): Float32Array;
@@ -208,6 +212,8 @@ export declare class Model {
208
212
  */
209
213
  update(deltaTime: number): boolean;
210
214
  private solveIKChains;
215
+ private ikComputedSet;
216
+ private computeSingleBoneWorldMatrix;
211
217
  private computeWorldMatrices;
212
218
  }
213
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;
@@ -346,8 +348,12 @@ export class Model {
346
348
  /**
347
349
  * Convert VMD-style relative translation (relative to bind pose world position) to local translation
348
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.
349
355
  */
350
- convertVMDTranslationToLocal(boneIdx, vmdRelativeTranslation) {
356
+ convertVMDTranslationToLocal(boneIdx, vmdRelativeTranslation, rotation) {
351
357
  const skeleton = this.skeleton;
352
358
  const bones = skeleton.bones;
353
359
  const localRot = this.runtimeSkeleton.localRotations;
@@ -383,7 +389,9 @@ export class Model {
383
389
  // Subtract bindTranslation to get position after bind translation
384
390
  const afterBindTranslation = parentSpacePos.subtract(new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2]));
385
391
  // Apply inverse rotation to get local translation
386
- const localRotation = localRot[boneIdx];
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];
387
395
  // Clone to avoid mutating, then conjugate and normalize
388
396
  const invRotation = localRotation.clone().conjugate().normalize();
389
397
  const rotationMat = Mat4.fromQuat(invRotation.x, invRotation.y, invRotation.z, invRotation.w);
@@ -714,9 +722,12 @@ export class Model {
714
722
  const localTrans = this.runtimeSkeleton.localTranslations[boneIdx];
715
723
  if (!frameB) {
716
724
  // No interpolation needed - direct assignment
717
- localRot.set(frameA.rotation);
718
- // Convert VMD relative translation to local translation
719
- const localTranslation = this.convertVMDTranslationToLocal(boneIdx, 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);
720
731
  localTrans.set(localTranslation);
721
732
  }
722
733
  else {
@@ -737,8 +748,10 @@ export class Model {
737
748
  const tzWeight = getWeight(32);
738
749
  // Interpolate VMD relative translations (relative to bind pose world position)
739
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);
740
- // Convert interpolated VMD translation to local translation
741
- const localTranslation = this.convertVMDTranslationToLocal(boneIdx, interpolatedVMDTranslation);
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);
742
755
  // Direct property writes to avoid object allocation
743
756
  localRot.set(rotation);
744
757
  localTrans.set(localTranslation);
@@ -821,7 +834,111 @@ export class Model {
821
834
  const ikChainInfo = this.runtimeSkeleton.ikChainInfo;
822
835
  if (!ikChainInfo)
823
836
  return;
824
- 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
+ }
825
942
  }
826
943
  computeWorldMatrices() {
827
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.5",
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",
package/src/ik-solver.ts CHANGED
@@ -7,6 +7,9 @@
7
7
  import { Mat4, Quat, Vec3 } from "./math"
8
8
  import { Bone, IKLink, IKSolver, IKChainInfo } from "./model"
9
9
 
10
+ // Callback type for updating world matrix (provided by model to handle append transformations)
11
+ export type UpdateWorldMatrixFn = (boneIndex: number, applyIK: boolean) => void
12
+
10
13
  const enum InternalEulerRotationOrder {
11
14
  YXZ = 0,
12
15
  ZYX = 1,
@@ -89,10 +92,11 @@ export class IKSolverSystem {
89
92
  localRotations: Quat[],
90
93
  localTranslations: Vec3[],
91
94
  worldMatrices: Mat4[],
92
- ikChainInfo: IKChainInfo[]
95
+ ikChainInfo: IKChainInfo[],
96
+ updateWorldMatrix?: UpdateWorldMatrixFn
93
97
  ): void {
94
98
  for (const solver of ikSolvers) {
95
- this.solveIK(solver, bones, localRotations, localTranslations, worldMatrices, ikChainInfo)
99
+ this.solveIK(solver, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, updateWorldMatrix)
96
100
  }
97
101
  }
98
102
 
@@ -102,7 +106,8 @@ export class IKSolverSystem {
102
106
  localRotations: Quat[],
103
107
  localTranslations: Vec3[],
104
108
  worldMatrices: Mat4[],
105
- ikChainInfo: IKChainInfo[]
109
+ ikChainInfo: IKChainInfo[],
110
+ updateWorldMatrix?: UpdateWorldMatrixFn
106
111
  ): void {
107
112
  if (solver.links.length === 0) return
108
113
 
@@ -126,10 +131,17 @@ export class IKSolverSystem {
126
131
  }
127
132
 
128
133
  // Update chain bones and target bone world matrices (initial state, no IK yet)
129
- for (let i = chains.length - 1; i >= 0; i--) {
130
- this.updateWorldMatrix(chains[i].boneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
134
+ if (updateWorldMatrix) {
135
+ for (let i = chains.length - 1; i >= 0; i--) {
136
+ updateWorldMatrix(chains[i].boneIndex, false)
137
+ }
138
+ updateWorldMatrix(targetBoneIndex, false)
139
+ } else {
140
+ for (let i = chains.length - 1; i >= 0; i--) {
141
+ this.updateWorldMatrix(chains[i].boneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
142
+ }
143
+ this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
131
144
  }
132
- this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
133
145
 
134
146
  if (this.getDistance(ikBoneIndex, targetBoneIndex, worldMatrices) < this.EPSILON) return
135
147
 
@@ -152,7 +164,8 @@ export class IKSolverSystem {
152
164
  localTranslations,
153
165
  worldMatrices,
154
166
  ikChainInfo,
155
- i < halfIteration
167
+ i < halfIteration,
168
+ updateWorldMatrix
156
169
  )
157
170
  }
158
171
  }
@@ -182,7 +195,8 @@ export class IKSolverSystem {
182
195
  localTranslations: Vec3[],
183
196
  worldMatrices: Mat4[],
184
197
  ikChainInfo: IKChainInfo[],
185
- useAxis: boolean
198
+ useAxis: boolean,
199
+ updateWorldMatrix?: UpdateWorldMatrixFn
186
200
  ): void {
187
201
  const chainBoneIndex = chain.boneIndex
188
202
  const chainPosition = this.getWorldTranslation(chainBoneIndex, worldMatrices)
@@ -254,13 +268,21 @@ export class IKSolverSystem {
254
268
  }
255
269
  }
256
270
 
257
- // Update world matrices for affected bones (using IK-modified rotations)
258
- for (let i = chainIndex; i >= 0; i--) {
259
- const link = solver.links[i]
260
- this.updateWorldMatrix(link.boneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo)
271
+ // Update world matrices for affected bones (using callback - handles append correctly)
272
+ if (updateWorldMatrix) {
273
+ for (let i = chainIndex; i >= 0; i--) {
274
+ const link = solver.links[i]
275
+ updateWorldMatrix(link.boneIndex, true) // applyIK = true
276
+ }
277
+ updateWorldMatrix(targetBoneIndex, false)
278
+ } else {
279
+ for (let i = chainIndex; i >= 0; i--) {
280
+ const link = solver.links[i]
281
+ this.updateWorldMatrix(link.boneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo)
282
+ }
283
+ this.updateWorldMatrix(ikBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
284
+ this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
261
285
  }
262
- this.updateWorldMatrix(ikBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
263
- this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
264
286
  }
265
287
 
266
288
  private static limitAngle(angle: number, min: number, max: number, useAxis: boolean): number {
@@ -410,3 +432,4 @@ export class IKSolverSystem {
410
432
  }
411
433
  }
412
434
  }
435
+