reze-engine 0.1.0
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/README.md +3 -0
- package/dist/ammo-loader.d.ts +4 -0
- package/dist/ammo-loader.d.ts.map +1 -0
- package/dist/ammo-loader.js +29 -0
- package/dist/camera.d.ts +46 -0
- package/dist/camera.d.ts.map +1 -0
- package/dist/camera.js +303 -0
- package/dist/engine.d.ts +88 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +990 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/math.d.ts +52 -0
- package/dist/math.d.ts.map +1 -0
- package/dist/math.js +389 -0
- package/dist/model.d.ts +102 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +416 -0
- package/dist/physics.d.ts +82 -0
- package/dist/physics.d.ts.map +1 -0
- package/dist/physics.js +503 -0
- package/dist/pmx-loader.d.ts +47 -0
- package/dist/pmx-loader.d.ts.map +1 -0
- package/dist/pmx-loader.js +938 -0
- package/package.json +42 -0
- package/src/ammo-loader.ts +35 -0
- package/src/camera.ts +358 -0
- package/src/engine.ts +1166 -0
- package/src/index.ts +1 -0
- package/src/math.ts +505 -0
- package/src/model.ts +586 -0
- package/src/physics.ts +680 -0
- package/src/pmx-loader.ts +1031 -0
package/dist/model.js
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
const VERTEX_STRIDE = 8;
|
|
2
|
+
import { Mat4, Vec3, Quat } from "./math";
|
|
3
|
+
export class Model {
|
|
4
|
+
constructor(vertexData, indexData, textures, materials, skeleton, skinning, rigidbodies = [], joints = []) {
|
|
5
|
+
this.textures = [];
|
|
6
|
+
this.materials = [];
|
|
7
|
+
// Physics data from PMX
|
|
8
|
+
this.rigidbodies = [];
|
|
9
|
+
this.joints = [];
|
|
10
|
+
// Cached identity matrices to avoid allocations in computeWorldMatrices
|
|
11
|
+
this.cachedIdentityMat1 = Mat4.identity();
|
|
12
|
+
this.cachedIdentityMat2 = Mat4.identity();
|
|
13
|
+
this.vertexData = vertexData;
|
|
14
|
+
this.vertexCount = vertexData.length / VERTEX_STRIDE;
|
|
15
|
+
this.indexData = indexData;
|
|
16
|
+
this.textures = textures;
|
|
17
|
+
this.materials = materials;
|
|
18
|
+
this.skeleton = skeleton;
|
|
19
|
+
this.skinning = skinning;
|
|
20
|
+
this.rigidbodies = rigidbodies;
|
|
21
|
+
this.joints = joints;
|
|
22
|
+
// Initialize runtime skeleton pose state
|
|
23
|
+
const boneCount = skeleton.bones.length;
|
|
24
|
+
this.runtimeSkeleton = {
|
|
25
|
+
localRotations: new Float32Array(boneCount * 4),
|
|
26
|
+
localTranslations: new Float32Array(boneCount * 3),
|
|
27
|
+
worldMatrices: new Float32Array(boneCount * 16),
|
|
28
|
+
nameIndex: {}, // Will be populated by buildBoneLookups()
|
|
29
|
+
computedBones: new Array(boneCount).fill(false),
|
|
30
|
+
};
|
|
31
|
+
if (this.skeleton.bones.length > 0) {
|
|
32
|
+
this.buildBoneLookups();
|
|
33
|
+
this.initializeRuntimePose();
|
|
34
|
+
this.initializeRotTweenBuffers();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Build caches for O(1) bone lookups and populate children arrays
|
|
38
|
+
// Called during model initialization
|
|
39
|
+
buildBoneLookups() {
|
|
40
|
+
const nameToIndex = {};
|
|
41
|
+
// Initialize children arrays for all bones and build name index
|
|
42
|
+
for (let i = 0; i < this.skeleton.bones.length; i++) {
|
|
43
|
+
this.skeleton.bones[i].children = [];
|
|
44
|
+
nameToIndex[this.skeleton.bones[i].name] = i;
|
|
45
|
+
}
|
|
46
|
+
// Build parent->children relationships
|
|
47
|
+
for (let i = 0; i < this.skeleton.bones.length; i++) {
|
|
48
|
+
const bone = this.skeleton.bones[i];
|
|
49
|
+
const parentIdx = bone.parentIndex;
|
|
50
|
+
if (parentIdx >= 0 && parentIdx < this.skeleton.bones.length) {
|
|
51
|
+
this.skeleton.bones[parentIdx].children.push(i);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
this.runtimeSkeleton.nameIndex = nameToIndex;
|
|
55
|
+
}
|
|
56
|
+
initializeRotTweenBuffers() {
|
|
57
|
+
const n = this.skeleton.bones.length;
|
|
58
|
+
this.rotTweenState = {
|
|
59
|
+
active: new Uint8Array(n),
|
|
60
|
+
startQuat: new Float32Array(n * 4),
|
|
61
|
+
targetQuat: new Float32Array(n * 4),
|
|
62
|
+
startTimeMs: new Float32Array(n),
|
|
63
|
+
durationMs: new Float32Array(n),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
static easeInOut(t) {
|
|
67
|
+
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
|
|
68
|
+
}
|
|
69
|
+
static slerp(aX, aY, aZ, aW, bX, bY, bZ, bW, t) {
|
|
70
|
+
let cos = aX * bX + aY * bY + aZ * bZ + aW * bW;
|
|
71
|
+
let bx = bX, by = bY, bz = bZ, bw = bW;
|
|
72
|
+
if (cos < 0) {
|
|
73
|
+
cos = -cos;
|
|
74
|
+
bx = -bx;
|
|
75
|
+
by = -by;
|
|
76
|
+
bz = -bz;
|
|
77
|
+
bw = -bw;
|
|
78
|
+
}
|
|
79
|
+
if (cos > 0.9995) {
|
|
80
|
+
const x = aX + t * (bx - aX);
|
|
81
|
+
const y = aY + t * (by - aY);
|
|
82
|
+
const z = aZ + t * (bz - aZ);
|
|
83
|
+
const w = aW + t * (bw - aW);
|
|
84
|
+
const invLen = 1 / Math.hypot(x, y, z, w);
|
|
85
|
+
return [x * invLen, y * invLen, z * invLen, w * invLen];
|
|
86
|
+
}
|
|
87
|
+
const theta0 = Math.acos(cos);
|
|
88
|
+
const sinTheta0 = Math.sin(theta0);
|
|
89
|
+
const theta = theta0 * t;
|
|
90
|
+
const s0 = Math.sin(theta0 - theta) / sinTheta0;
|
|
91
|
+
const s1 = Math.sin(theta) / sinTheta0;
|
|
92
|
+
return [s0 * aX + s1 * bx, s0 * aY + s1 * by, s0 * aZ + s1 * bz, s0 * aW + s1 * bw];
|
|
93
|
+
}
|
|
94
|
+
updateRotationTweens() {
|
|
95
|
+
const state = this.rotTweenState;
|
|
96
|
+
const now = performance.now();
|
|
97
|
+
const rotations = this.runtimeSkeleton.localRotations;
|
|
98
|
+
for (let i = 0; i < this.skeleton.bones.length; i++) {
|
|
99
|
+
if (state.active[i] !== 1)
|
|
100
|
+
continue;
|
|
101
|
+
const startMs = state.startTimeMs[i];
|
|
102
|
+
const durMs = Math.max(1, state.durationMs[i]);
|
|
103
|
+
const t = Math.max(0, Math.min(1, (now - startMs) / durMs));
|
|
104
|
+
const e = Model.easeInOut(t);
|
|
105
|
+
const qi = i * 4;
|
|
106
|
+
const [x, y, z, w] = Model.slerp(state.startQuat[qi], state.startQuat[qi + 1], state.startQuat[qi + 2], state.startQuat[qi + 3], state.targetQuat[qi], state.targetQuat[qi + 1], state.targetQuat[qi + 2], state.targetQuat[qi + 3], e);
|
|
107
|
+
rotations[qi] = x;
|
|
108
|
+
rotations[qi + 1] = y;
|
|
109
|
+
rotations[qi + 2] = z;
|
|
110
|
+
rotations[qi + 3] = w;
|
|
111
|
+
if (t >= 1)
|
|
112
|
+
state.active[i] = 0;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Get interleaved vertex data for GPU upload
|
|
116
|
+
// Format: [x,y,z, nx,ny,nz, u,v, x,y,z, nx,ny,nz, u,v, ...]
|
|
117
|
+
getVertices() {
|
|
118
|
+
return this.vertexData;
|
|
119
|
+
}
|
|
120
|
+
// Get texture information
|
|
121
|
+
getTextures() {
|
|
122
|
+
return this.textures;
|
|
123
|
+
}
|
|
124
|
+
// Get material information
|
|
125
|
+
getMaterials() {
|
|
126
|
+
return this.materials;
|
|
127
|
+
}
|
|
128
|
+
// Get diffuse texture path for a material
|
|
129
|
+
getDiffuseTexturePath(materialIndex) {
|
|
130
|
+
if (materialIndex < 0 || materialIndex >= this.materials.length)
|
|
131
|
+
return null;
|
|
132
|
+
const material = this.materials[materialIndex];
|
|
133
|
+
if (material.diffuseTextureIndex < 0 || material.diffuseTextureIndex >= this.textures.length)
|
|
134
|
+
return null;
|
|
135
|
+
return this.textures[material.diffuseTextureIndex].path;
|
|
136
|
+
}
|
|
137
|
+
// Get vertex count
|
|
138
|
+
getVertexCount() {
|
|
139
|
+
return this.vertexCount;
|
|
140
|
+
}
|
|
141
|
+
// Get index data for GPU upload
|
|
142
|
+
getIndices() {
|
|
143
|
+
return this.indexData;
|
|
144
|
+
}
|
|
145
|
+
// Accessors for skeleton/skinning
|
|
146
|
+
getSkeleton() {
|
|
147
|
+
return this.skeleton;
|
|
148
|
+
}
|
|
149
|
+
getSkinning() {
|
|
150
|
+
return this.skinning;
|
|
151
|
+
}
|
|
152
|
+
// Accessors for physics data
|
|
153
|
+
getRigidbodies() {
|
|
154
|
+
return this.rigidbodies;
|
|
155
|
+
}
|
|
156
|
+
getJoints() {
|
|
157
|
+
return this.joints;
|
|
158
|
+
}
|
|
159
|
+
initializeRuntimePose() {
|
|
160
|
+
const rotations = this.runtimeSkeleton.localRotations;
|
|
161
|
+
for (let i = 0; i < this.skeleton.bones.length; i++) {
|
|
162
|
+
const qi = i * 4;
|
|
163
|
+
if (rotations[qi + 3] === 0) {
|
|
164
|
+
rotations[qi] = 0;
|
|
165
|
+
rotations[qi + 1] = 0;
|
|
166
|
+
rotations[qi + 2] = 0;
|
|
167
|
+
rotations[qi + 3] = 1;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// ------- Bone helpers (public API) -------
|
|
172
|
+
getBoneCount() {
|
|
173
|
+
return this.skeleton.bones.length;
|
|
174
|
+
}
|
|
175
|
+
getBoneNames() {
|
|
176
|
+
return this.skeleton.bones.map((b) => b.name);
|
|
177
|
+
}
|
|
178
|
+
getBoneIndexByName(name) {
|
|
179
|
+
return this.runtimeSkeleton.nameIndex[name] ?? -1;
|
|
180
|
+
}
|
|
181
|
+
getBoneName(index) {
|
|
182
|
+
if (index < 0 || index >= this.skeleton.bones.length)
|
|
183
|
+
return undefined;
|
|
184
|
+
return this.skeleton.bones[index].name;
|
|
185
|
+
}
|
|
186
|
+
getBoneLocal(index) {
|
|
187
|
+
if (index < 0 || index >= this.skeleton.bones.length)
|
|
188
|
+
return undefined;
|
|
189
|
+
const qi = index * 4;
|
|
190
|
+
const ti = index * 3;
|
|
191
|
+
const rot = this.runtimeSkeleton.localRotations;
|
|
192
|
+
const trans = this.runtimeSkeleton.localTranslations;
|
|
193
|
+
return {
|
|
194
|
+
rotation: new Quat(rot[qi], rot[qi + 1], rot[qi + 2], rot[qi + 3]),
|
|
195
|
+
translation: new Vec3(trans[ti], trans[ti + 1], trans[ti + 2]),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
rotateBones(indicesOrNames, quats, durationMs) {
|
|
199
|
+
const state = this.rotTweenState;
|
|
200
|
+
const normalized = quats.map((q) => q.normalize());
|
|
201
|
+
const now = performance.now();
|
|
202
|
+
const dur = durationMs && durationMs > 0 ? durationMs : 0;
|
|
203
|
+
for (let i = 0; i < indicesOrNames.length; i++) {
|
|
204
|
+
const input = indicesOrNames[i];
|
|
205
|
+
let idx;
|
|
206
|
+
if (typeof input === "number") {
|
|
207
|
+
idx = input;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
const resolved = this.getBoneIndexByName(input);
|
|
211
|
+
if (resolved < 0)
|
|
212
|
+
continue;
|
|
213
|
+
idx = resolved;
|
|
214
|
+
}
|
|
215
|
+
if (idx < 0 || idx >= this.skeleton.bones.length)
|
|
216
|
+
continue;
|
|
217
|
+
const qi = idx * 4;
|
|
218
|
+
const rotations = this.runtimeSkeleton.localRotations;
|
|
219
|
+
const [tx, ty, tz, tw] = normalized[i].toArray();
|
|
220
|
+
if (dur === 0) {
|
|
221
|
+
rotations[qi] = tx;
|
|
222
|
+
rotations[qi + 1] = ty;
|
|
223
|
+
rotations[qi + 2] = tz;
|
|
224
|
+
rotations[qi + 3] = tw;
|
|
225
|
+
state.active[idx] = 0;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
let sx = rotations[qi];
|
|
229
|
+
let sy = rotations[qi + 1];
|
|
230
|
+
let sz = rotations[qi + 2];
|
|
231
|
+
let sw = rotations[qi + 3];
|
|
232
|
+
if (state.active[idx] === 1) {
|
|
233
|
+
const startMs = state.startTimeMs[idx];
|
|
234
|
+
const prevDur = Math.max(1, state.durationMs[idx]);
|
|
235
|
+
const t = Math.max(0, Math.min(1, (now - startMs) / prevDur));
|
|
236
|
+
const e = Model.easeInOut(t);
|
|
237
|
+
const [cx, cy, cz, cw] = Model.slerp(state.startQuat[qi], state.startQuat[qi + 1], state.startQuat[qi + 2], state.startQuat[qi + 3], state.targetQuat[qi], state.targetQuat[qi + 1], state.targetQuat[qi + 2], state.targetQuat[qi + 3], e);
|
|
238
|
+
sx = cx;
|
|
239
|
+
sy = cy;
|
|
240
|
+
sz = cz;
|
|
241
|
+
sw = cw;
|
|
242
|
+
}
|
|
243
|
+
state.startQuat[qi] = sx;
|
|
244
|
+
state.startQuat[qi + 1] = sy;
|
|
245
|
+
state.startQuat[qi + 2] = sz;
|
|
246
|
+
state.startQuat[qi + 3] = sw;
|
|
247
|
+
state.targetQuat[qi] = tx;
|
|
248
|
+
state.targetQuat[qi + 1] = ty;
|
|
249
|
+
state.targetQuat[qi + 2] = tz;
|
|
250
|
+
state.targetQuat[qi + 3] = tw;
|
|
251
|
+
state.startTimeMs[idx] = now;
|
|
252
|
+
state.durationMs[idx] = dur;
|
|
253
|
+
state.active[idx] = 1;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
setBoneRotation(indexOrName, quat) {
|
|
257
|
+
const index = typeof indexOrName === "number" ? indexOrName : this.getBoneIndexByName(indexOrName);
|
|
258
|
+
if (index < 0 || index >= this.skeleton.bones.length)
|
|
259
|
+
return;
|
|
260
|
+
const qi = index * 4;
|
|
261
|
+
const rot = this.runtimeSkeleton.localRotations;
|
|
262
|
+
rot[qi] = quat.x;
|
|
263
|
+
rot[qi + 1] = quat.y;
|
|
264
|
+
rot[qi + 2] = quat.z;
|
|
265
|
+
rot[qi + 3] = quat.w;
|
|
266
|
+
}
|
|
267
|
+
setBoneTranslation(indexOrName, t) {
|
|
268
|
+
const index = typeof indexOrName === "number" ? indexOrName : this.getBoneIndexByName(indexOrName);
|
|
269
|
+
if (index < 0 || index >= this.skeleton.bones.length)
|
|
270
|
+
return;
|
|
271
|
+
const ti = index * 3;
|
|
272
|
+
const trans = this.runtimeSkeleton.localTranslations;
|
|
273
|
+
trans[ti] = t.x;
|
|
274
|
+
trans[ti + 1] = t.y;
|
|
275
|
+
trans[ti + 2] = t.z;
|
|
276
|
+
}
|
|
277
|
+
resetBone(index) {
|
|
278
|
+
const qi = index * 4;
|
|
279
|
+
const ti = index * 3;
|
|
280
|
+
const rot = this.runtimeSkeleton.localRotations;
|
|
281
|
+
const trans = this.runtimeSkeleton.localTranslations;
|
|
282
|
+
rot[qi] = 0;
|
|
283
|
+
rot[qi + 1] = 0;
|
|
284
|
+
rot[qi + 2] = 0;
|
|
285
|
+
rot[qi + 3] = 1;
|
|
286
|
+
trans[ti] = 0;
|
|
287
|
+
trans[ti + 1] = 0;
|
|
288
|
+
trans[ti + 2] = 0;
|
|
289
|
+
}
|
|
290
|
+
resetAllBones() {
|
|
291
|
+
const rot = this.runtimeSkeleton.localRotations;
|
|
292
|
+
const trans = this.runtimeSkeleton.localTranslations;
|
|
293
|
+
const count = this.getBoneCount();
|
|
294
|
+
for (let i = 0; i < count; i++) {
|
|
295
|
+
const qi = i * 4;
|
|
296
|
+
const ti = i * 3;
|
|
297
|
+
rot[qi] = 0;
|
|
298
|
+
rot[qi + 1] = 0;
|
|
299
|
+
rot[qi + 2] = 0;
|
|
300
|
+
rot[qi + 3] = 1;
|
|
301
|
+
trans[ti] = 0;
|
|
302
|
+
trans[ti + 1] = 0;
|
|
303
|
+
trans[ti + 2] = 0;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
getBoneWorldMatrix(index) {
|
|
307
|
+
this.evaluatePose();
|
|
308
|
+
const start = index * 16;
|
|
309
|
+
return this.runtimeSkeleton.worldMatrices.subarray(start, start + 16);
|
|
310
|
+
}
|
|
311
|
+
getBoneWorldMatrices() {
|
|
312
|
+
return this.runtimeSkeleton.worldMatrices;
|
|
313
|
+
}
|
|
314
|
+
getBoneInverseBindMatrices() {
|
|
315
|
+
return this.skeleton.inverseBindMatrices;
|
|
316
|
+
}
|
|
317
|
+
getBoneWorldPosition(index) {
|
|
318
|
+
this.evaluatePose();
|
|
319
|
+
const matIdx = index * 16;
|
|
320
|
+
const mat = this.runtimeSkeleton.worldMatrices;
|
|
321
|
+
return new Vec3(mat[matIdx + 12], mat[matIdx + 13], mat[matIdx + 14]);
|
|
322
|
+
}
|
|
323
|
+
getBoneWorldRotation(index) {
|
|
324
|
+
this.evaluatePose();
|
|
325
|
+
const start = index * 16;
|
|
326
|
+
return Mat4.toQuatFromArray(this.runtimeSkeleton.worldMatrices, start);
|
|
327
|
+
}
|
|
328
|
+
getBoneInverseBindMatrix(index) {
|
|
329
|
+
if (index < 0 || index >= this.skeleton.bones.length)
|
|
330
|
+
return undefined;
|
|
331
|
+
const start = index * 16;
|
|
332
|
+
return new Mat4(this.skeleton.inverseBindMatrices.subarray(start, start + 16));
|
|
333
|
+
}
|
|
334
|
+
getLocalRotations() {
|
|
335
|
+
return this.runtimeSkeleton.localRotations;
|
|
336
|
+
}
|
|
337
|
+
evaluatePose() {
|
|
338
|
+
this.updateRotationTweens();
|
|
339
|
+
this.computeWorldMatrices();
|
|
340
|
+
}
|
|
341
|
+
computeWorldMatrices() {
|
|
342
|
+
const bones = this.skeleton.bones;
|
|
343
|
+
const localRot = this.runtimeSkeleton.localRotations;
|
|
344
|
+
const localTrans = this.runtimeSkeleton.localTranslations;
|
|
345
|
+
const worldBuf = this.runtimeSkeleton.worldMatrices;
|
|
346
|
+
const computed = this.runtimeSkeleton.computedBones.fill(false);
|
|
347
|
+
const boneCount = bones.length;
|
|
348
|
+
if (boneCount === 0)
|
|
349
|
+
return;
|
|
350
|
+
const computeWorld = (i) => {
|
|
351
|
+
if (computed[i])
|
|
352
|
+
return;
|
|
353
|
+
const b = bones[i];
|
|
354
|
+
if (b.parentIndex >= boneCount) {
|
|
355
|
+
console.warn(`[RZM] bone ${i} parent out of range: ${b.parentIndex}`);
|
|
356
|
+
}
|
|
357
|
+
const qi = i * 4;
|
|
358
|
+
let rotateM = Mat4.fromQuat(localRot[qi], localRot[qi + 1], localRot[qi + 2], localRot[qi + 3]);
|
|
359
|
+
let addLocalTx = 0, addLocalTy = 0, addLocalTz = 0;
|
|
360
|
+
// Optimized append rotation check - only check necessary conditions
|
|
361
|
+
const appendParentIdx = b.appendParentIndex;
|
|
362
|
+
const hasAppend = b.appendRotate && appendParentIdx !== undefined && appendParentIdx >= 0 && appendParentIdx < boneCount;
|
|
363
|
+
if (hasAppend) {
|
|
364
|
+
const ratio = b.appendRatio === undefined ? 1 : Math.max(-1, Math.min(1, b.appendRatio));
|
|
365
|
+
const hasRatio = Math.abs(ratio) > 1e-6;
|
|
366
|
+
if (hasRatio) {
|
|
367
|
+
const apQi = appendParentIdx * 4;
|
|
368
|
+
const apTi = appendParentIdx * 3;
|
|
369
|
+
if (b.appendRotate) {
|
|
370
|
+
let ax = localRot[apQi];
|
|
371
|
+
let ay = localRot[apQi + 1];
|
|
372
|
+
let az = localRot[apQi + 2];
|
|
373
|
+
const aw = localRot[apQi + 3];
|
|
374
|
+
const absRatio = ratio < 0 ? -ratio : ratio;
|
|
375
|
+
if (ratio < 0) {
|
|
376
|
+
ax = -ax;
|
|
377
|
+
ay = -ay;
|
|
378
|
+
az = -az;
|
|
379
|
+
}
|
|
380
|
+
const [rx, ry, rz, rw] = Model.slerp(0, 0, 0, 1, ax, ay, az, aw, absRatio);
|
|
381
|
+
rotateM = Mat4.fromQuat(rx, ry, rz, rw).multiply(rotateM);
|
|
382
|
+
}
|
|
383
|
+
if (b.appendMove) {
|
|
384
|
+
const appendRatio = b.appendRatio ?? 1;
|
|
385
|
+
addLocalTx = localTrans[apTi] * appendRatio;
|
|
386
|
+
addLocalTy = localTrans[apTi + 1] * appendRatio;
|
|
387
|
+
addLocalTz = localTrans[apTi + 2] * appendRatio;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// Build local matrix: identity + bind translation, then rotation, then append translation
|
|
392
|
+
this.cachedIdentityMat1
|
|
393
|
+
.setIdentity()
|
|
394
|
+
.translateInPlace(b.bindTranslation[0], b.bindTranslation[1], b.bindTranslation[2]);
|
|
395
|
+
this.cachedIdentityMat2.setIdentity().translateInPlace(addLocalTx, addLocalTy, addLocalTz);
|
|
396
|
+
const localM = this.cachedIdentityMat1.multiply(rotateM).multiply(this.cachedIdentityMat2);
|
|
397
|
+
const worldOffset = i * 16;
|
|
398
|
+
if (b.parentIndex >= 0) {
|
|
399
|
+
const p = b.parentIndex;
|
|
400
|
+
if (!computed[p])
|
|
401
|
+
computeWorld(p);
|
|
402
|
+
const parentOffset = p * 16;
|
|
403
|
+
// Use cachedIdentityMat2 as temporary buffer for parent * local multiplication
|
|
404
|
+
Mat4.multiplyArrays(worldBuf, parentOffset, localM.values, 0, this.cachedIdentityMat2.values, 0);
|
|
405
|
+
worldBuf.subarray(worldOffset, worldOffset + 16).set(this.cachedIdentityMat2.values);
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
worldBuf.subarray(worldOffset, worldOffset + 16).set(localM.values);
|
|
409
|
+
}
|
|
410
|
+
computed[i] = true;
|
|
411
|
+
};
|
|
412
|
+
// Process all bones (recursion handles dependencies automatically)
|
|
413
|
+
for (let i = 0; i < boneCount; i++)
|
|
414
|
+
computeWorld(i);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Quat, Vec3, Mat4 } from "./math";
|
|
2
|
+
export declare enum RigidbodyShape {
|
|
3
|
+
Sphere = 0,
|
|
4
|
+
Box = 1,
|
|
5
|
+
Capsule = 2
|
|
6
|
+
}
|
|
7
|
+
export declare enum RigidbodyType {
|
|
8
|
+
Static = 0,
|
|
9
|
+
Dynamic = 1,
|
|
10
|
+
Kinematic = 2
|
|
11
|
+
}
|
|
12
|
+
export interface Rigidbody {
|
|
13
|
+
name: string;
|
|
14
|
+
englishName: string;
|
|
15
|
+
boneIndex: number;
|
|
16
|
+
group: number;
|
|
17
|
+
collisionMask: number;
|
|
18
|
+
shape: RigidbodyShape;
|
|
19
|
+
size: Vec3;
|
|
20
|
+
shapePosition: Vec3;
|
|
21
|
+
shapeRotation: Vec3;
|
|
22
|
+
mass: number;
|
|
23
|
+
linearDamping: number;
|
|
24
|
+
angularDamping: number;
|
|
25
|
+
restitution: number;
|
|
26
|
+
friction: number;
|
|
27
|
+
type: RigidbodyType;
|
|
28
|
+
bodyOffsetMatrixInverse: Mat4;
|
|
29
|
+
bodyOffsetMatrix?: Mat4;
|
|
30
|
+
}
|
|
31
|
+
export interface Joint {
|
|
32
|
+
name: string;
|
|
33
|
+
englishName: string;
|
|
34
|
+
type: number;
|
|
35
|
+
rigidbodyIndexA: number;
|
|
36
|
+
rigidbodyIndexB: number;
|
|
37
|
+
position: Vec3;
|
|
38
|
+
rotation: Vec3;
|
|
39
|
+
positionMin: Vec3;
|
|
40
|
+
positionMax: Vec3;
|
|
41
|
+
rotationMin: Vec3;
|
|
42
|
+
rotationMax: Vec3;
|
|
43
|
+
springPosition: Vec3;
|
|
44
|
+
springRotation: Vec3;
|
|
45
|
+
}
|
|
46
|
+
export declare class Physics {
|
|
47
|
+
private rigidbodies;
|
|
48
|
+
private joints;
|
|
49
|
+
private gravity;
|
|
50
|
+
private ammoInitialized;
|
|
51
|
+
private ammoPromise;
|
|
52
|
+
private ammo;
|
|
53
|
+
private dynamicsWorld;
|
|
54
|
+
private ammoRigidbodies;
|
|
55
|
+
private ammoConstraints;
|
|
56
|
+
private rigidbodiesInitialized;
|
|
57
|
+
private jointsCreated;
|
|
58
|
+
private firstFrame;
|
|
59
|
+
private forceDisableOffsetForConstraintFrame;
|
|
60
|
+
private zeroVector;
|
|
61
|
+
constructor(rigidbodies: Rigidbody[], joints?: Joint[]);
|
|
62
|
+
private initAmmo;
|
|
63
|
+
setGravity(gravity: Vec3): void;
|
|
64
|
+
getGravity(): Vec3;
|
|
65
|
+
getRigidbodies(): Rigidbody[];
|
|
66
|
+
getJoints(): Joint[];
|
|
67
|
+
getRigidbodyTransforms(): Array<{
|
|
68
|
+
position: Vec3;
|
|
69
|
+
rotation: Quat;
|
|
70
|
+
}>;
|
|
71
|
+
private createAmmoWorld;
|
|
72
|
+
private createAmmoRigidbodies;
|
|
73
|
+
private createAmmoJoints;
|
|
74
|
+
private normalizeAngle;
|
|
75
|
+
step(dt: number, boneWorldMatrices: Float32Array, boneInverseBindMatrices: Float32Array): void;
|
|
76
|
+
private computeBodyOffsets;
|
|
77
|
+
private positionBodiesFromBones;
|
|
78
|
+
private syncFromBones;
|
|
79
|
+
private stepAmmoPhysics;
|
|
80
|
+
private applyAmmoRigidbodiesToBones;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=physics.d.ts.map
|
|
@@ -0,0 +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;IActB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,YAAY,EAAE,uBAAuB,EAAE,YAAY,GAAG,IAAI;IAsC9F,OAAO,CAAC,kBAAkB;IA2B1B,OAAO,CAAC,uBAAuB;IAkD/B,OAAO,CAAC,aAAa;IAwDrB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,2BAA2B;CAqCpC"}
|