reze-engine 0.3.9 → 0.3.11
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 -3
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/ik-solver.js +16 -32
- package/dist/math.d.ts +4 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +72 -13
- package/dist/model.d.ts +5 -5
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +126 -134
- package/dist/physics.d.ts +2 -2
- package/dist/physics.d.ts.map +1 -1
- package/dist/physics.js +6 -10
- package/package.json +1 -1
- package/src/ik-solver.ts +31 -51
- package/src/math.ts +75 -11
- package/src/model.ts +136 -165
- package/src/physics.ts +11 -21
- 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_r.d.ts +0 -132
- package/dist/engine_r.d.ts.map +0 -1
- package/dist/engine_r.js +0 -1489
package/dist/model.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Mat4, Quat, Vec3,
|
|
1
|
+
import { Mat4, Quat, Vec3, bezierInterpolate } from "./math";
|
|
2
2
|
import { Physics } from "./physics";
|
|
3
3
|
import { IKSolverSystem } from "./ik-solver";
|
|
4
4
|
import { VMDLoader } from "./vmd-loader";
|
|
@@ -58,25 +58,24 @@ export class Model {
|
|
|
58
58
|
}
|
|
59
59
|
initializeRuntimeSkeleton() {
|
|
60
60
|
const boneCount = this.skeleton.bones.length;
|
|
61
|
+
// Pre-allocate object arrays for skeletal pose
|
|
62
|
+
const localRotations = new Array(boneCount);
|
|
63
|
+
const localTranslations = new Array(boneCount);
|
|
64
|
+
const worldMatrices = new Array(boneCount);
|
|
65
|
+
for (let i = 0; i < boneCount; i++) {
|
|
66
|
+
localRotations[i] = Quat.identity();
|
|
67
|
+
localTranslations[i] = Vec3.zeros();
|
|
68
|
+
worldMatrices[i] = Mat4.identity();
|
|
69
|
+
}
|
|
61
70
|
this.runtimeSkeleton = {
|
|
62
|
-
localRotations
|
|
63
|
-
localTranslations
|
|
64
|
-
worldMatrices
|
|
71
|
+
localRotations,
|
|
72
|
+
localTranslations,
|
|
73
|
+
worldMatrices,
|
|
65
74
|
nameIndex: this.skeleton.bones.reduce((acc, bone, index) => {
|
|
66
75
|
acc[bone.name] = index;
|
|
67
76
|
return acc;
|
|
68
77
|
}, {}),
|
|
69
78
|
};
|
|
70
|
-
const rotations = this.runtimeSkeleton.localRotations;
|
|
71
|
-
for (let i = 0; i < this.skeleton.bones.length; i++) {
|
|
72
|
-
const qi = i * 4;
|
|
73
|
-
if (rotations[qi + 3] === 0) {
|
|
74
|
-
rotations[qi] = 0;
|
|
75
|
-
rotations[qi + 1] = 0;
|
|
76
|
-
rotations[qi + 2] = 0;
|
|
77
|
-
rotations[qi + 3] = 1;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
79
|
// Initialize IK runtime state
|
|
81
80
|
this.initializeIKRuntime();
|
|
82
81
|
}
|
|
@@ -87,8 +86,8 @@ export class Model {
|
|
|
87
86
|
const ikChainInfo = new Array(boneCount);
|
|
88
87
|
for (let i = 0; i < boneCount; i++) {
|
|
89
88
|
ikChainInfo[i] = {
|
|
90
|
-
ikRotation:
|
|
91
|
-
localRotation:
|
|
89
|
+
ikRotation: Quat.identity(),
|
|
90
|
+
localRotation: Quat.identity(),
|
|
92
91
|
};
|
|
93
92
|
}
|
|
94
93
|
// Build IK solvers from bone data
|
|
@@ -114,17 +113,28 @@ export class Model {
|
|
|
114
113
|
initializeTweenBuffers() {
|
|
115
114
|
const boneCount = this.skeleton.bones.length;
|
|
116
115
|
const morphCount = this.morphing.morphs.length;
|
|
116
|
+
// Pre-allocate Quat and Vec3 arrays to avoid reallocation during tweens
|
|
117
|
+
const rotStartQuat = new Array(boneCount);
|
|
118
|
+
const rotTargetQuat = new Array(boneCount);
|
|
119
|
+
const transStartVec = new Array(boneCount);
|
|
120
|
+
const transTargetVec = new Array(boneCount);
|
|
121
|
+
for (let i = 0; i < boneCount; i++) {
|
|
122
|
+
rotStartQuat[i] = Quat.identity();
|
|
123
|
+
rotTargetQuat[i] = Quat.identity();
|
|
124
|
+
transStartVec[i] = Vec3.zeros();
|
|
125
|
+
transTargetVec[i] = Vec3.zeros();
|
|
126
|
+
}
|
|
117
127
|
this.tweenState = {
|
|
118
128
|
// Bone rotation tweens
|
|
119
129
|
rotActive: new Uint8Array(boneCount),
|
|
120
|
-
rotStartQuat
|
|
121
|
-
rotTargetQuat
|
|
130
|
+
rotStartQuat,
|
|
131
|
+
rotTargetQuat,
|
|
122
132
|
rotStartTimeMs: new Float32Array(boneCount),
|
|
123
133
|
rotDurationMs: new Float32Array(boneCount),
|
|
124
134
|
// Bone translation tweens
|
|
125
135
|
transActive: new Uint8Array(boneCount),
|
|
126
|
-
transStartVec
|
|
127
|
-
transTargetVec
|
|
136
|
+
transStartVec,
|
|
137
|
+
transTargetVec,
|
|
128
138
|
transStartTimeMs: new Float32Array(boneCount),
|
|
129
139
|
transDurationMs: new Float32Array(boneCount),
|
|
130
140
|
// Morph weight tweens
|
|
@@ -161,15 +171,9 @@ export class Model {
|
|
|
161
171
|
const startMs = state.rotStartTimeMs[i];
|
|
162
172
|
const durMs = Math.max(1, state.rotDurationMs[i]);
|
|
163
173
|
const t = Math.max(0, Math.min(1, (now - startMs) / durMs));
|
|
164
|
-
const e =
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
const targetQuat = new Quat(state.rotTargetQuat[qi], state.rotTargetQuat[qi + 1], state.rotTargetQuat[qi + 2], state.rotTargetQuat[qi + 3]);
|
|
168
|
-
const result = Quat.slerp(startQuat, targetQuat, e);
|
|
169
|
-
rotations[qi] = result.x;
|
|
170
|
-
rotations[qi + 1] = result.y;
|
|
171
|
-
rotations[qi + 2] = result.z;
|
|
172
|
-
rotations[qi + 3] = result.w;
|
|
174
|
+
const e = t; // Linear interpolation
|
|
175
|
+
const result = Quat.slerp(state.rotStartQuat[i], state.rotTargetQuat[i], e);
|
|
176
|
+
rotations[i].set(result);
|
|
173
177
|
if (t >= 1) {
|
|
174
178
|
state.rotActive[i] = 0;
|
|
175
179
|
}
|
|
@@ -182,13 +186,12 @@ export class Model {
|
|
|
182
186
|
const startMs = state.transStartTimeMs[i];
|
|
183
187
|
const durMs = Math.max(1, state.transDurationMs[i]);
|
|
184
188
|
const t = Math.max(0, Math.min(1, (now - startMs) / durMs));
|
|
185
|
-
const e =
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
translations[
|
|
189
|
-
|
|
190
|
-
translations[
|
|
191
|
-
state.transStartVec[ti + 2] + (state.transTargetVec[ti + 2] - state.transStartVec[ti + 2]) * e;
|
|
189
|
+
const e = t; // Linear interpolation
|
|
190
|
+
const startVec = state.transStartVec[i];
|
|
191
|
+
const targetVec = state.transTargetVec[i];
|
|
192
|
+
translations[i].x = startVec.x + (targetVec.x - startVec.x) * e;
|
|
193
|
+
translations[i].y = startVec.y + (targetVec.y - startVec.y) * e;
|
|
194
|
+
translations[i].z = startVec.z + (targetVec.z - startVec.z) * e;
|
|
192
195
|
if (t >= 1) {
|
|
193
196
|
state.transActive[i] = 0;
|
|
194
197
|
}
|
|
@@ -202,7 +205,7 @@ export class Model {
|
|
|
202
205
|
const startMs = state.morphStartTimeMs[i];
|
|
203
206
|
const durMs = Math.max(1, state.morphDurationMs[i]);
|
|
204
207
|
const t = Math.max(0, Math.min(1, (now - startMs) / durMs));
|
|
205
|
-
const e =
|
|
208
|
+
const e = t; // Linear interpolation
|
|
206
209
|
const oldWeight = weights[i];
|
|
207
210
|
weights[i] = state.morphStartWeight[i] + (state.morphTargetWeight[i] - state.morphStartWeight[i]) * e;
|
|
208
211
|
// Check if weight actually changed (accounting for floating point precision)
|
|
@@ -253,7 +256,8 @@ export class Model {
|
|
|
253
256
|
// ------- Bone helpers (public API) -------
|
|
254
257
|
rotateBones(names, quats, durationMs) {
|
|
255
258
|
const state = this.tweenState;
|
|
256
|
-
|
|
259
|
+
// Clone and normalize to avoid mutating input
|
|
260
|
+
quats.forEach((q) => q.normalize());
|
|
257
261
|
const now = this.tweenTimeMs;
|
|
258
262
|
const dur = durationMs && durationMs > 0 ? durationMs : 0;
|
|
259
263
|
for (let i = 0; i < names.length; i++) {
|
|
@@ -261,42 +265,34 @@ export class Model {
|
|
|
261
265
|
const idx = this.runtimeSkeleton.nameIndex[name] ?? -1;
|
|
262
266
|
if (idx < 0 || idx >= this.skeleton.bones.length)
|
|
263
267
|
continue;
|
|
264
|
-
const qi = idx * 4;
|
|
265
268
|
const rotations = this.runtimeSkeleton.localRotations;
|
|
266
|
-
const
|
|
269
|
+
const targetNorm = quats[i];
|
|
267
270
|
if (dur === 0) {
|
|
268
|
-
rotations[
|
|
269
|
-
rotations[qi + 1] = ty;
|
|
270
|
-
rotations[qi + 2] = tz;
|
|
271
|
-
rotations[qi + 3] = tw;
|
|
271
|
+
rotations[idx].set(targetNorm);
|
|
272
272
|
state.rotActive[idx] = 0;
|
|
273
273
|
continue;
|
|
274
274
|
}
|
|
275
|
-
|
|
276
|
-
let
|
|
277
|
-
let
|
|
278
|
-
let
|
|
275
|
+
const currentRot = rotations[idx];
|
|
276
|
+
let sx = currentRot.x;
|
|
277
|
+
let sy = currentRot.y;
|
|
278
|
+
let sz = currentRot.z;
|
|
279
|
+
let sw = currentRot.w;
|
|
279
280
|
if (state.rotActive[idx] === 1) {
|
|
280
281
|
const startMs = state.rotStartTimeMs[idx];
|
|
281
282
|
const prevDur = Math.max(1, state.rotDurationMs[idx]);
|
|
282
283
|
const t = Math.max(0, Math.min(1, (now - startMs) / prevDur));
|
|
283
|
-
const e =
|
|
284
|
-
const
|
|
285
|
-
const targetQuat = new Quat(state.rotTargetQuat[qi], state.rotTargetQuat[qi + 1], state.rotTargetQuat[qi + 2], state.rotTargetQuat[qi + 3]);
|
|
286
|
-
const result = Quat.slerp(startQuat, targetQuat, e);
|
|
284
|
+
const e = t; // Linear interpolation
|
|
285
|
+
const result = Quat.slerp(state.rotStartQuat[idx], state.rotTargetQuat[idx], e);
|
|
287
286
|
sx = result.x;
|
|
288
287
|
sy = result.y;
|
|
289
288
|
sz = result.z;
|
|
290
289
|
sw = result.w;
|
|
291
290
|
}
|
|
292
|
-
state.rotStartQuat[
|
|
293
|
-
state.rotStartQuat[
|
|
294
|
-
state.rotStartQuat[
|
|
295
|
-
state.rotStartQuat[
|
|
296
|
-
state.rotTargetQuat[
|
|
297
|
-
state.rotTargetQuat[qi + 1] = ty;
|
|
298
|
-
state.rotTargetQuat[qi + 2] = tz;
|
|
299
|
-
state.rotTargetQuat[qi + 3] = tw;
|
|
291
|
+
state.rotStartQuat[idx].x = sx;
|
|
292
|
+
state.rotStartQuat[idx].y = sy;
|
|
293
|
+
state.rotStartQuat[idx].z = sz;
|
|
294
|
+
state.rotStartQuat[idx].w = sw;
|
|
295
|
+
state.rotTargetQuat[idx].set(targetNorm);
|
|
300
296
|
state.rotStartTimeMs[idx] = now;
|
|
301
297
|
state.rotDurationMs[idx] = dur;
|
|
302
298
|
state.rotActive[idx] = 1;
|
|
@@ -308,7 +304,6 @@ export class Model {
|
|
|
308
304
|
const state = this.tweenState;
|
|
309
305
|
const now = this.tweenTimeMs;
|
|
310
306
|
const dur = durationMs && durationMs > 0 ? durationMs : 0;
|
|
311
|
-
const localRot = this.runtimeSkeleton.localRotations;
|
|
312
307
|
// Compute bind pose world positions for all bones
|
|
313
308
|
const skeleton = this.skeleton;
|
|
314
309
|
const computeBindPoseWorldPosition = (idx) => {
|
|
@@ -328,9 +323,8 @@ export class Model {
|
|
|
328
323
|
if (idx < 0 || idx >= this.skeleton.bones.length)
|
|
329
324
|
continue;
|
|
330
325
|
const bone = this.skeleton.bones[idx];
|
|
331
|
-
const ti = idx * 3;
|
|
332
|
-
const qi = idx * 4;
|
|
333
326
|
const translations = this.runtimeSkeleton.localTranslations;
|
|
327
|
+
const localRot = this.runtimeSkeleton.localRotations;
|
|
334
328
|
const vmdRelativeTranslation = relativeTranslations[i];
|
|
335
329
|
// VMD translation is relative to bind pose world position
|
|
336
330
|
// targetWorldPos = bindPoseWorldPos + vmdRelativeTranslation
|
|
@@ -343,7 +337,7 @@ export class Model {
|
|
|
343
337
|
parentBindPoseWorldPos = computeBindPoseWorldPosition(bone.parentIndex);
|
|
344
338
|
}
|
|
345
339
|
else {
|
|
346
|
-
parentBindPoseWorldPos =
|
|
340
|
+
parentBindPoseWorldPos = Vec3.zeros();
|
|
347
341
|
}
|
|
348
342
|
// Transform target world position to parent's local space
|
|
349
343
|
// In bind pose, parent's world matrix is just a translation
|
|
@@ -351,44 +345,55 @@ export class Model {
|
|
|
351
345
|
// Subtract bindTranslation to get position after bind translation
|
|
352
346
|
const afterBindTranslation = parentSpacePos.subtract(new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2]));
|
|
353
347
|
// Apply inverse rotation to get local translation
|
|
354
|
-
const localRotation =
|
|
355
|
-
|
|
348
|
+
const localRotation = localRot[idx];
|
|
349
|
+
// Clone to avoid mutating, then conjugate and normalize
|
|
350
|
+
const invRotation = localRotation.clone().conjugate().normalize();
|
|
356
351
|
const rotationMat = Mat4.fromQuat(invRotation.x, invRotation.y, invRotation.z, invRotation.w);
|
|
357
352
|
const rm = rotationMat.values;
|
|
358
353
|
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);
|
|
359
354
|
const [tx, ty, tz] = [localTranslation.x, localTranslation.y, localTranslation.z];
|
|
360
355
|
if (dur === 0) {
|
|
361
|
-
translations[
|
|
362
|
-
translations[
|
|
363
|
-
translations[
|
|
356
|
+
translations[idx].x = tx;
|
|
357
|
+
translations[idx].y = ty;
|
|
358
|
+
translations[idx].z = tz;
|
|
364
359
|
state.transActive[idx] = 0;
|
|
365
360
|
continue;
|
|
366
361
|
}
|
|
367
|
-
|
|
368
|
-
let
|
|
369
|
-
let
|
|
362
|
+
const currentTrans = translations[idx];
|
|
363
|
+
let sx = currentTrans.x;
|
|
364
|
+
let sy = currentTrans.y;
|
|
365
|
+
let sz = currentTrans.z;
|
|
370
366
|
if (state.transActive[idx] === 1) {
|
|
371
367
|
const startMs = state.transStartTimeMs[idx];
|
|
372
368
|
const prevDur = Math.max(1, state.transDurationMs[idx]);
|
|
373
369
|
const t = Math.max(0, Math.min(1, (now - startMs) / prevDur));
|
|
374
|
-
const e =
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
370
|
+
const e = t; // Linear interpolation
|
|
371
|
+
const startVec = state.transStartVec[idx];
|
|
372
|
+
const targetVec = state.transTargetVec[idx];
|
|
373
|
+
sx = startVec.x + (targetVec.x - startVec.x) * e;
|
|
374
|
+
sy = startVec.y + (targetVec.y - startVec.y) * e;
|
|
375
|
+
sz = startVec.z + (targetVec.z - startVec.z) * e;
|
|
378
376
|
}
|
|
379
|
-
state.transStartVec[
|
|
380
|
-
state.transStartVec[
|
|
381
|
-
state.transStartVec[
|
|
382
|
-
state.transTargetVec[
|
|
383
|
-
state.transTargetVec[
|
|
384
|
-
state.transTargetVec[
|
|
377
|
+
state.transStartVec[idx].x = sx;
|
|
378
|
+
state.transStartVec[idx].y = sy;
|
|
379
|
+
state.transStartVec[idx].z = sz;
|
|
380
|
+
state.transTargetVec[idx].x = tx;
|
|
381
|
+
state.transTargetVec[idx].y = ty;
|
|
382
|
+
state.transTargetVec[idx].z = tz;
|
|
385
383
|
state.transStartTimeMs[idx] = now;
|
|
386
384
|
state.transDurationMs[idx] = dur;
|
|
387
385
|
state.transActive[idx] = 1;
|
|
388
386
|
}
|
|
389
387
|
}
|
|
390
388
|
getBoneWorldMatrices() {
|
|
391
|
-
|
|
389
|
+
// Convert Mat4[] to Float32Array for WebGPU compatibility
|
|
390
|
+
const boneCount = this.skeleton.bones.length;
|
|
391
|
+
const worldMats = this.runtimeSkeleton.worldMatrices;
|
|
392
|
+
const result = new Float32Array(boneCount * 16);
|
|
393
|
+
for (let i = 0; i < boneCount; i++) {
|
|
394
|
+
result.set(worldMats[i].values, i * 16);
|
|
395
|
+
}
|
|
396
|
+
return result;
|
|
392
397
|
}
|
|
393
398
|
getBoneInverseBindMatrices() {
|
|
394
399
|
return this.skeleton.inverseBindMatrices;
|
|
@@ -398,17 +403,16 @@ export class Model {
|
|
|
398
403
|
const worldMats = this.runtimeSkeleton.worldMatrices;
|
|
399
404
|
const invBindMats = this.skeleton.inverseBindMatrices;
|
|
400
405
|
// Initialize cached array if needed or if bone count changed
|
|
401
|
-
if (!this.
|
|
402
|
-
this.
|
|
406
|
+
if (!this.skinMatricesArray || this.skinMatricesArray.length !== boneCount * 16) {
|
|
407
|
+
this.skinMatricesArray = new Float32Array(boneCount * 16);
|
|
403
408
|
}
|
|
404
|
-
const skinMatrices = this.
|
|
409
|
+
const skinMatrices = this.skinMatricesArray;
|
|
405
410
|
// Compute skin matrices: skinMatrix = worldMatrix × inverseBindMatrix
|
|
406
|
-
// Use Mat4.multiplyArrays to avoid creating Mat4 objects
|
|
407
411
|
for (let i = 0; i < boneCount; i++) {
|
|
408
|
-
const
|
|
412
|
+
const worldMat = worldMats[i];
|
|
409
413
|
const invBindOffset = i * 16;
|
|
410
414
|
const skinOffset = i * 16;
|
|
411
|
-
Mat4.multiplyArrays(
|
|
415
|
+
Mat4.multiplyArrays(worldMat.values, 0, invBindMats, invBindOffset, skinMatrices, skinOffset);
|
|
412
416
|
}
|
|
413
417
|
return skinMatrices;
|
|
414
418
|
}
|
|
@@ -434,7 +438,7 @@ export class Model {
|
|
|
434
438
|
const startMs = state.morphStartTimeMs[idx];
|
|
435
439
|
const prevDur = Math.max(1, state.morphDurationMs[idx]);
|
|
436
440
|
const t = Math.max(0, Math.min(1, (now - startMs) / prevDur));
|
|
437
|
-
const e =
|
|
441
|
+
const e = t; // Linear interpolation
|
|
438
442
|
startWeight = state.morphStartWeight[idx] + (state.morphTargetWeight[idx] - state.morphStartWeight[idx]) * e;
|
|
439
443
|
}
|
|
440
444
|
state.morphStartWeight[idx] = startWeight;
|
|
@@ -662,17 +666,12 @@ export class Model {
|
|
|
662
666
|
const boneIdx = this.runtimeSkeleton.nameIndex[boneName];
|
|
663
667
|
if (boneIdx === undefined)
|
|
664
668
|
continue;
|
|
665
|
-
const
|
|
666
|
-
const
|
|
669
|
+
const localRot = this.runtimeSkeleton.localRotations[boneIdx];
|
|
670
|
+
const localTrans = this.runtimeSkeleton.localTranslations[boneIdx];
|
|
667
671
|
if (!frameB) {
|
|
668
672
|
// No interpolation needed - direct assignment
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
this.runtimeSkeleton.localRotations[rotOffset + 2] = frameA.rotation.z;
|
|
672
|
-
this.runtimeSkeleton.localRotations[rotOffset + 3] = frameA.rotation.w;
|
|
673
|
-
this.runtimeSkeleton.localTranslations[transOffset] = frameA.translation.x;
|
|
674
|
-
this.runtimeSkeleton.localTranslations[transOffset + 1] = frameA.translation.y;
|
|
675
|
-
this.runtimeSkeleton.localTranslations[transOffset + 2] = frameA.translation.z;
|
|
673
|
+
localRot.set(frameA.rotation);
|
|
674
|
+
localTrans.set(frameA.translation);
|
|
676
675
|
}
|
|
677
676
|
else {
|
|
678
677
|
const timeA = keyFrames[idx].time;
|
|
@@ -682,7 +681,7 @@ export class Model {
|
|
|
682
681
|
const interp = frameB.interpolation;
|
|
683
682
|
// Pre-normalize interpolation values (avoid division in bezierInterpolate)
|
|
684
683
|
const rotT = bezierInterpolate(interp[0] * INV_127, interp[1] * INV_127, interp[2] * INV_127, interp[3] * INV_127, gradient);
|
|
685
|
-
// Use Quat.slerp
|
|
684
|
+
// Use Quat.slerp to interpolate rotation
|
|
686
685
|
const rotation = Quat.slerp(frameA.rotation, frameB.rotation, rotT);
|
|
687
686
|
// Interpolate translation using bezier for each component
|
|
688
687
|
// Inline getWeight to avoid function call overhead
|
|
@@ -690,17 +689,11 @@ export class Model {
|
|
|
690
689
|
const txWeight = getWeight(0);
|
|
691
690
|
const tyWeight = getWeight(16);
|
|
692
691
|
const tzWeight = getWeight(32);
|
|
693
|
-
// Direct
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
this.runtimeSkeleton.localTranslations[transOffset] =
|
|
699
|
-
frameA.translation.x + (frameB.translation.x - frameA.translation.x) * txWeight;
|
|
700
|
-
this.runtimeSkeleton.localTranslations[transOffset + 1] =
|
|
701
|
-
frameA.translation.y + (frameB.translation.y - frameA.translation.y) * tyWeight;
|
|
702
|
-
this.runtimeSkeleton.localTranslations[transOffset + 2] =
|
|
703
|
-
frameA.translation.z + (frameB.translation.z - frameA.translation.z) * tzWeight;
|
|
692
|
+
// Direct property writes to avoid object allocation
|
|
693
|
+
localRot.set(rotation);
|
|
694
|
+
localTrans.x = frameA.translation.x + (frameB.translation.x - frameA.translation.x) * txWeight;
|
|
695
|
+
localTrans.y = frameA.translation.y + (frameB.translation.y - frameA.translation.y) * tyWeight;
|
|
696
|
+
localTrans.z = frameA.translation.z + (frameB.translation.z - frameA.translation.z) * tzWeight;
|
|
704
697
|
}
|
|
705
698
|
}
|
|
706
699
|
// Process morph tracks
|
|
@@ -784,7 +777,7 @@ export class Model {
|
|
|
784
777
|
const bones = this.skeleton.bones;
|
|
785
778
|
const localRot = this.runtimeSkeleton.localRotations;
|
|
786
779
|
const localTrans = this.runtimeSkeleton.localTranslations;
|
|
787
|
-
const
|
|
780
|
+
const worldMats = this.runtimeSkeleton.worldMatrices;
|
|
788
781
|
const boneCount = bones.length;
|
|
789
782
|
if (boneCount === 0)
|
|
790
783
|
return;
|
|
@@ -797,8 +790,8 @@ export class Model {
|
|
|
797
790
|
if (b.parentIndex >= boneCount) {
|
|
798
791
|
console.warn(`[RZM] bone ${i} parent out of range: ${b.parentIndex}`);
|
|
799
792
|
}
|
|
800
|
-
const
|
|
801
|
-
let rotateM = Mat4.fromQuat(
|
|
793
|
+
const boneRot = localRot[i];
|
|
794
|
+
let rotateM = Mat4.fromQuat(boneRot.x, boneRot.y, boneRot.z, boneRot.w);
|
|
802
795
|
let addLocalTx = 0, addLocalTy = 0, addLocalTz = 0;
|
|
803
796
|
// Optimized append rotation check - only check necessary conditions
|
|
804
797
|
const appendParentIdx = b.appendParentIndex;
|
|
@@ -807,13 +800,12 @@ export class Model {
|
|
|
807
800
|
const ratio = b.appendRatio === undefined ? 1 : Math.max(-1, Math.min(1, b.appendRatio));
|
|
808
801
|
const hasRatio = Math.abs(ratio) > 1e-6;
|
|
809
802
|
if (hasRatio) {
|
|
810
|
-
const apQi = appendParentIdx * 4;
|
|
811
|
-
const apTi = appendParentIdx * 3;
|
|
812
803
|
if (b.appendRotate) {
|
|
813
|
-
|
|
814
|
-
let
|
|
815
|
-
let
|
|
816
|
-
|
|
804
|
+
const appendRot = localRot[appendParentIdx];
|
|
805
|
+
let ax = appendRot.x;
|
|
806
|
+
let ay = appendRot.y;
|
|
807
|
+
let az = appendRot.z;
|
|
808
|
+
const aw = appendRot.w;
|
|
817
809
|
const absRatio = ratio < 0 ? -ratio : ratio;
|
|
818
810
|
if (ratio < 0) {
|
|
819
811
|
ax = -ax;
|
|
@@ -821,39 +813,39 @@ export class Model {
|
|
|
821
813
|
az = -az;
|
|
822
814
|
}
|
|
823
815
|
const appendQuat = new Quat(ax, ay, az, aw);
|
|
824
|
-
const result = Quat.slerp(
|
|
816
|
+
const result = Quat.slerp(Quat.identity(), appendQuat, absRatio);
|
|
825
817
|
rotateM = Mat4.fromQuat(result.x, result.y, result.z, result.w).multiply(rotateM);
|
|
826
818
|
}
|
|
827
819
|
if (b.appendMove) {
|
|
820
|
+
const appendTrans = localTrans[appendParentIdx];
|
|
828
821
|
const appendRatio = b.appendRatio ?? 1;
|
|
829
|
-
addLocalTx =
|
|
830
|
-
addLocalTy =
|
|
831
|
-
addLocalTz =
|
|
822
|
+
addLocalTx = appendTrans.x * appendRatio;
|
|
823
|
+
addLocalTy = appendTrans.y * appendRatio;
|
|
824
|
+
addLocalTz = appendTrans.z * appendRatio;
|
|
832
825
|
}
|
|
833
826
|
}
|
|
834
827
|
}
|
|
835
828
|
// Build local matrix: identity + bind translation, then rotation, then local translation, then append translation
|
|
836
|
-
const
|
|
837
|
-
const localTx =
|
|
838
|
-
const localTy =
|
|
839
|
-
const localTz =
|
|
829
|
+
const boneTrans = localTrans[i];
|
|
830
|
+
const localTx = boneTrans.x + addLocalTx;
|
|
831
|
+
const localTy = boneTrans.y + addLocalTy;
|
|
832
|
+
const localTz = boneTrans.z + addLocalTz;
|
|
840
833
|
this.cachedIdentityMat1
|
|
841
834
|
.setIdentity()
|
|
842
835
|
.translateInPlace(b.bindTranslation[0], b.bindTranslation[1], b.bindTranslation[2]);
|
|
843
836
|
this.cachedIdentityMat2.setIdentity().translateInPlace(localTx, localTy, localTz);
|
|
844
837
|
const localM = this.cachedIdentityMat1.multiply(rotateM).multiply(this.cachedIdentityMat2);
|
|
845
|
-
const
|
|
838
|
+
const worldMat = worldMats[i];
|
|
846
839
|
if (b.parentIndex >= 0) {
|
|
847
840
|
const p = b.parentIndex;
|
|
848
841
|
if (!computed[p])
|
|
849
842
|
computeWorld(p);
|
|
850
|
-
const
|
|
851
|
-
//
|
|
852
|
-
Mat4.multiplyArrays(
|
|
853
|
-
worldBuf.subarray(worldOffset, worldOffset + 16).set(this.cachedIdentityMat2.values);
|
|
843
|
+
const parentMat = worldMats[p];
|
|
844
|
+
// Multiply parent world matrix by local matrix
|
|
845
|
+
Mat4.multiplyArrays(parentMat.values, 0, localM.values, 0, worldMat.values, 0);
|
|
854
846
|
}
|
|
855
847
|
else {
|
|
856
|
-
|
|
848
|
+
worldMat.values.set(localM.values);
|
|
857
849
|
}
|
|
858
850
|
computed[i] = true;
|
|
859
851
|
};
|
package/dist/physics.d.ts
CHANGED
|
@@ -72,8 +72,8 @@ export declare class Physics {
|
|
|
72
72
|
private createAmmoRigidbodies;
|
|
73
73
|
private createAmmoJoints;
|
|
74
74
|
private normalizeAngle;
|
|
75
|
-
reset(boneWorldMatrices:
|
|
76
|
-
step(dt: number, boneWorldMatrices:
|
|
75
|
+
reset(boneWorldMatrices: Mat4[], boneInverseBindMatrices: Float32Array): void;
|
|
76
|
+
step(dt: number, boneWorldMatrices: Mat4[], boneInverseBindMatrices: Float32Array): void;
|
|
77
77
|
private computeBodyOffsets;
|
|
78
78
|
private positionBodiesFromBones;
|
|
79
79
|
private syncFromBones;
|
package/dist/physics.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAIzC,oBAAY,cAAc;IACxB,MAAM,IAAI;IACV,GAAG,IAAI;IACP,OAAO,IAAI;CACZ;AAED,oBAAY,aAAa;IACvB,MAAM,IAAI;IACV,OAAO,IAAI;IACX,SAAS,IAAI;CACd;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,cAAc,CAAA;IACrB,IAAI,EAAE,IAAI,CAAA;IACV,aAAa,EAAE,IAAI,CAAA;IACnB,aAAa,EAAE,IAAI,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,aAAa,CAAA;IACnB,uBAAuB,EAAE,IAAI,CAAA;IAC7B,gBAAgB,CAAC,EAAE,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,IAAI,CAAA;IACd,QAAQ,EAAE,IAAI,CAAA;IACd,WAAW,EAAE,IAAI,CAAA;IACjB,WAAW,EAAE,IAAI,CAAA;IACjB,WAAW,EAAE,IAAI,CAAA;IACjB,WAAW,EAAE,IAAI,CAAA;IACjB,cAAc,EAAE,IAAI,CAAA;IACpB,cAAc,EAAE,IAAI,CAAA;CACrB;AAED,qBAAa,OAAO;IAClB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,WAAW,CAAqC;IACxD,OAAO,CAAC,IAAI,CAA4B;IAExC,OAAO,CAAC,aAAa,CAAY;IAEjC,OAAO,CAAC,eAAe,CAAY;IAEnC,OAAO,CAAC,eAAe,CAAY;IACnC,OAAO,CAAC,sBAAsB,CAAQ;IACtC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,oCAAoC,CAAO;IAEnD,OAAO,CAAC,UAAU,CAAY;gBAElB,WAAW,EAAE,SAAS,EAAE,EAAE,MAAM,GAAE,KAAK,EAAO;YAM5C,QAAQ;IAatB,UAAU,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI;IAU/B,UAAU,IAAI,IAAI;IAIlB,cAAc,IAAI,SAAS,EAAE;IAI7B,SAAS,IAAI,KAAK,EAAE;IAIpB,sBAAsB,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC;IA6CnE,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,qBAAqB;IA+F7B,OAAO,CAAC,gBAAgB;IA0KxB,OAAO,CAAC,cAAc;IAetB,KAAK,CAAC,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"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"}
|
package/dist/physics.js
CHANGED
|
@@ -336,7 +336,7 @@ export class Physics {
|
|
|
336
336
|
if (!this.ammoInitialized || !this.ammo || !this.dynamicsWorld) {
|
|
337
337
|
return;
|
|
338
338
|
}
|
|
339
|
-
const boneCount = boneWorldMatrices.length
|
|
339
|
+
const boneCount = boneWorldMatrices.length;
|
|
340
340
|
const Ammo = this.ammo;
|
|
341
341
|
// Ensure body offsets are computed
|
|
342
342
|
if (!this.rigidbodiesInitialized) {
|
|
@@ -351,9 +351,8 @@ export class Physics {
|
|
|
351
351
|
if (!ammoBody || rb.boneIndex < 0 || rb.boneIndex >= boneCount)
|
|
352
352
|
continue;
|
|
353
353
|
const boneIdx = rb.boneIndex;
|
|
354
|
-
const worldMatIdx = boneIdx * 16;
|
|
355
354
|
// Get bone world matrix
|
|
356
|
-
const boneWorldMat =
|
|
355
|
+
const boneWorldMat = boneWorldMatrices[boneIdx];
|
|
357
356
|
// Compute body world matrix: bodyWorld = boneWorld × bodyOffsetMatrix
|
|
358
357
|
// (like babylon-mmd: bodyWorldMatrix = bodyOffsetMatrix.multiplyToRef(bodyWorldMatrix))
|
|
359
358
|
const bodyOffsetMatrix = rb.bodyOffsetMatrix || rb.bodyOffsetMatrixInverse.inverse();
|
|
@@ -394,7 +393,7 @@ export class Physics {
|
|
|
394
393
|
if (!this.ammoInitialized || !this.ammo || !this.dynamicsWorld) {
|
|
395
394
|
return;
|
|
396
395
|
}
|
|
397
|
-
const boneCount = boneWorldMatrices.length
|
|
396
|
+
const boneCount = boneWorldMatrices.length;
|
|
398
397
|
if (this.firstFrame) {
|
|
399
398
|
if (!this.rigidbodiesInitialized) {
|
|
400
399
|
this.computeBodyOffsets(boneInverseBindMatrices, boneCount);
|
|
@@ -451,8 +450,7 @@ export class Physics {
|
|
|
451
450
|
if (!ammoBody || rb.boneIndex < 0 || rb.boneIndex >= boneCount)
|
|
452
451
|
continue;
|
|
453
452
|
const boneIdx = rb.boneIndex;
|
|
454
|
-
const
|
|
455
|
-
const boneWorldMat = new Mat4(boneWorldMatrices.subarray(worldMatIdx, worldMatIdx + 16));
|
|
453
|
+
const boneWorldMat = boneWorldMatrices[boneIdx];
|
|
456
454
|
// nodeWorld = boneWorld × shapeLocal (not shapeLocal × boneWorld)
|
|
457
455
|
const bodyOffsetMatrix = rb.bodyOffsetMatrix || rb.bodyOffsetMatrixInverse.inverse();
|
|
458
456
|
const nodeWorldMatrix = boneWorldMat.multiply(bodyOffsetMatrix);
|
|
@@ -494,8 +492,7 @@ export class Physics {
|
|
|
494
492
|
rb.boneIndex >= 0 &&
|
|
495
493
|
rb.boneIndex < boneCount) {
|
|
496
494
|
const boneIdx = rb.boneIndex;
|
|
497
|
-
const
|
|
498
|
-
const boneWorldMat = new Mat4(boneWorldMatrices.subarray(worldMatIdx, worldMatIdx + 16));
|
|
495
|
+
const boneWorldMat = boneWorldMatrices[boneIdx];
|
|
499
496
|
// nodeWorld = boneWorld × shapeLocal (not shapeLocal × boneWorld)
|
|
500
497
|
const bodyOffsetMatrix = rb.bodyOffsetMatrix || rb.bodyOffsetMatrixInverse.inverse();
|
|
501
498
|
const nodeWorldMatrix = boneWorldMat.multiply(bodyOffsetMatrix);
|
|
@@ -539,7 +536,6 @@ export class Physics {
|
|
|
539
536
|
// Only dynamic rigidbodies drive bones (Static/Kinematic follow bones)
|
|
540
537
|
if (rb.type === RigidbodyType.Dynamic && rb.boneIndex >= 0 && rb.boneIndex < boneCount) {
|
|
541
538
|
const boneIdx = rb.boneIndex;
|
|
542
|
-
const worldMatIdx = boneIdx * 16;
|
|
543
539
|
const transform = ammoBody.getWorldTransform();
|
|
544
540
|
const origin = transform.getOrigin();
|
|
545
541
|
const rotation = transform.getRotation();
|
|
@@ -550,7 +546,7 @@ export class Physics {
|
|
|
550
546
|
const boneWorldMat = nodeWorldMatrix.multiply(rb.bodyOffsetMatrixInverse);
|
|
551
547
|
const values = boneWorldMat.values;
|
|
552
548
|
if (!isNaN(values[0]) && !isNaN(values[15]) && Math.abs(values[0]) < 1e6 && Math.abs(values[15]) < 1e6) {
|
|
553
|
-
boneWorldMatrices.set(values
|
|
549
|
+
boneWorldMatrices[boneIdx].values.set(values);
|
|
554
550
|
}
|
|
555
551
|
else {
|
|
556
552
|
console.warn(`[Physics] Invalid bone world matrix for rigidbody ${i} (${rb.name}), skipping update`);
|