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.
- package/dist/ik-solver.d.ts +2 -1
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/ik-solver.js +31 -14
- package/dist/model.d.ts +11 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +178 -47
- package/dist/runtime-bone.d.ts +49 -0
- package/dist/runtime-bone.d.ts.map +1 -0
- package/dist/runtime-bone.js +121 -0
- package/package.json +1 -1
- package/src/ik-solver.ts +37 -14
- package/src/model.ts +221 -65
- package/dist/bezier-interpolate.d.ts +0 -15
- package/dist/bezier-interpolate.d.ts.map +0 -1
- package/dist/bezier-interpolate.js +0 -40
- package/dist/engine_ts.d.ts +0 -143
- package/dist/engine_ts.d.ts.map +0 -1
- package/dist/engine_ts.js +0 -1575
- package/dist/ik.d.ts +0 -32
- package/dist/ik.d.ts.map +0 -1
- package/dist/ik.js +0 -337
- package/dist/player.d.ts +0 -64
- package/dist/player.d.ts.map +0 -1
- package/dist/player.js +0 -220
- package/dist/pool-scene.d.ts +0 -52
- package/dist/pool-scene.d.ts.map +0 -1
- package/dist/pool-scene.js +0 -1122
- package/dist/pool.d.ts +0 -38
- package/dist/pool.d.ts.map +0 -1
- package/dist/pool.js +0 -422
- package/dist/rzm-converter.d.ts +0 -12
- package/dist/rzm-converter.d.ts.map +0 -1
- package/dist/rzm-converter.js +0 -40
- package/dist/rzm-loader.d.ts +0 -24
- package/dist/rzm-loader.d.ts.map +0 -1
- package/dist/rzm-loader.js +0 -488
- package/dist/rzm-writer.d.ts +0 -27
- package/dist/rzm-writer.d.ts.map +0 -1
- package/dist/rzm-writer.js +0 -701
package/dist/ik-solver.d.ts
CHANGED
|
@@ -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;
|
package/dist/ik-solver.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
103
|
-
|
|
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
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
package/dist/model.d.ts.map
CHANGED
|
@@ -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
|
-
|
|
325
|
-
|
|
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
|
-
|
|
708
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
+
}
|