reze-engine 0.1.15 → 0.2.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/dist/engine.d.ts +8 -7
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +512 -730
- package/dist/physics.d.ts +1 -0
- package/dist/physics.d.ts.map +1 -1
- package/dist/physics.js +58 -0
- package/dist/vmd-loader.d.ts +25 -0
- package/dist/vmd-loader.d.ts.map +1 -0
- package/dist/vmd-loader.js +141 -0
- package/package.json +1 -1
- package/src/engine.ts +2455 -2659
- package/src/physics.ts +752 -680
- package/src/vmd-loader.ts +179 -0
package/dist/physics.d.ts
CHANGED
|
@@ -72,6 +72,7 @@ export declare class Physics {
|
|
|
72
72
|
private createAmmoRigidbodies;
|
|
73
73
|
private createAmmoJoints;
|
|
74
74
|
private normalizeAngle;
|
|
75
|
+
reset(boneWorldMatrices: Float32Array, boneInverseBindMatrices: Float32Array): void;
|
|
75
76
|
step(dt: number, boneWorldMatrices: Float32Array, boneInverseBindMatrices: Float32Array): void;
|
|
76
77
|
private computeBodyOffsets;
|
|
77
78
|
private positionBodiesFromBones;
|
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;
|
|
1
|
+
{"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"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"}
|
package/dist/physics.js
CHANGED
|
@@ -329,6 +329,64 @@ export class Physics {
|
|
|
329
329
|
}
|
|
330
330
|
return angle;
|
|
331
331
|
}
|
|
332
|
+
// Reset physics state (reposition bodies, clear velocities)
|
|
333
|
+
// Following babylon-mmd pattern: initialize all rigid body positions from current bone poses
|
|
334
|
+
// Call this when starting a new animation to prevent physics instability from sudden pose changes
|
|
335
|
+
reset(boneWorldMatrices, boneInverseBindMatrices) {
|
|
336
|
+
if (!this.ammoInitialized || !this.ammo || !this.dynamicsWorld) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const boneCount = boneWorldMatrices.length / 16;
|
|
340
|
+
const Ammo = this.ammo;
|
|
341
|
+
// Ensure body offsets are computed
|
|
342
|
+
if (!this.rigidbodiesInitialized) {
|
|
343
|
+
this.computeBodyOffsets(boneInverseBindMatrices, boneCount);
|
|
344
|
+
this.rigidbodiesInitialized = true;
|
|
345
|
+
}
|
|
346
|
+
// Reposition ALL rigid bodies from current bone poses (like babylon-mmd initialize)
|
|
347
|
+
// This ensures all bodies are correctly positioned before physics starts
|
|
348
|
+
for (let i = 0; i < this.rigidbodies.length; i++) {
|
|
349
|
+
const rb = this.rigidbodies[i];
|
|
350
|
+
const ammoBody = this.ammoRigidbodies[i];
|
|
351
|
+
if (!ammoBody || rb.boneIndex < 0 || rb.boneIndex >= boneCount)
|
|
352
|
+
continue;
|
|
353
|
+
const boneIdx = rb.boneIndex;
|
|
354
|
+
const worldMatIdx = boneIdx * 16;
|
|
355
|
+
// Get bone world matrix
|
|
356
|
+
const boneWorldMat = new Mat4(boneWorldMatrices.subarray(worldMatIdx, worldMatIdx + 16));
|
|
357
|
+
// Compute body world matrix: bodyWorld = boneWorld × bodyOffsetMatrix
|
|
358
|
+
// (like babylon-mmd: bodyWorldMatrix = bodyOffsetMatrix.multiplyToRef(bodyWorldMatrix))
|
|
359
|
+
const bodyOffsetMatrix = rb.bodyOffsetMatrix || rb.bodyOffsetMatrixInverse.inverse();
|
|
360
|
+
const bodyWorldMatrix = boneWorldMat.multiply(bodyOffsetMatrix);
|
|
361
|
+
const worldPos = bodyWorldMatrix.getPosition();
|
|
362
|
+
const worldRot = bodyWorldMatrix.toQuat();
|
|
363
|
+
// Set transform matrix
|
|
364
|
+
const transform = new Ammo.btTransform();
|
|
365
|
+
const pos = new Ammo.btVector3(worldPos.x, worldPos.y, worldPos.z);
|
|
366
|
+
const quat = new Ammo.btQuaternion(worldRot.x, worldRot.y, worldRot.z, worldRot.w);
|
|
367
|
+
transform.setOrigin(pos);
|
|
368
|
+
transform.setRotation(quat);
|
|
369
|
+
ammoBody.setWorldTransform(transform);
|
|
370
|
+
ammoBody.getMotionState().setWorldTransform(transform);
|
|
371
|
+
// Clear velocities for all rigidbodies
|
|
372
|
+
if (!this.zeroVector) {
|
|
373
|
+
this.zeroVector = new Ammo.btVector3(0, 0, 0);
|
|
374
|
+
}
|
|
375
|
+
ammoBody.setLinearVelocity(this.zeroVector);
|
|
376
|
+
ammoBody.setAngularVelocity(this.zeroVector);
|
|
377
|
+
// Explicitly activate dynamic rigidbodies after reset (wake them up)
|
|
378
|
+
// This is critical for dress pieces and other dynamic bodies to prevent teleporting
|
|
379
|
+
if (rb.type === RigidbodyType.Dynamic) {
|
|
380
|
+
ammoBody.activate(true); // Wake up the body
|
|
381
|
+
}
|
|
382
|
+
Ammo.destroy(pos);
|
|
383
|
+
Ammo.destroy(quat);
|
|
384
|
+
}
|
|
385
|
+
// Step simulation once to stabilize (like babylon-mmd)
|
|
386
|
+
if (this.dynamicsWorld.stepSimulation) {
|
|
387
|
+
this.dynamicsWorld.stepSimulation(0, 0, 0);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
332
390
|
// Syncs bones to rigidbodies, simulates dynamics, solves constraints
|
|
333
391
|
// Modifies boneWorldMatrices in-place for dynamic rigidbodies that drive bones
|
|
334
392
|
step(dt, boneWorldMatrices, boneInverseBindMatrices) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Quat } from "./math";
|
|
2
|
+
export interface BoneFrame {
|
|
3
|
+
boneName: string;
|
|
4
|
+
frame: number;
|
|
5
|
+
rotation: Quat;
|
|
6
|
+
}
|
|
7
|
+
export interface VMDKeyFrame {
|
|
8
|
+
time: number;
|
|
9
|
+
boneFrames: BoneFrame[];
|
|
10
|
+
}
|
|
11
|
+
export declare class VMDLoader {
|
|
12
|
+
private view;
|
|
13
|
+
private offset;
|
|
14
|
+
private decoder;
|
|
15
|
+
private constructor();
|
|
16
|
+
static load(url: string): Promise<VMDKeyFrame[]>;
|
|
17
|
+
static loadFromBuffer(buffer: ArrayBuffer): VMDKeyFrame[];
|
|
18
|
+
private parse;
|
|
19
|
+
private readBoneFrame;
|
|
20
|
+
private getUint32;
|
|
21
|
+
private getFloat32;
|
|
22
|
+
private getString;
|
|
23
|
+
private skip;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=vmd-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vmd-loader.d.ts","sourceRoot":"","sources":["../src/vmd-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAE7B,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,IAAI,CAAA;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,SAAS,EAAE,CAAA;CACxB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,OAAO,CAAa;IAE5B,OAAO;WAWM,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAKtD,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,EAAE;IAKzD,OAAO,CAAC,KAAK;IA8Db,OAAO,CAAC,aAAa;IA+CrB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,IAAI;CAMb"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Quat } from "./math";
|
|
2
|
+
export class VMDLoader {
|
|
3
|
+
constructor(buffer) {
|
|
4
|
+
this.offset = 0;
|
|
5
|
+
this.view = new DataView(buffer);
|
|
6
|
+
// Try to use Shift-JIS decoder, fallback to UTF-8 if not available
|
|
7
|
+
try {
|
|
8
|
+
this.decoder = new TextDecoder("shift-jis");
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
// Fallback to UTF-8 if Shift-JIS is not supported
|
|
12
|
+
this.decoder = new TextDecoder("utf-8");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
static async load(url) {
|
|
16
|
+
const loader = new VMDLoader(await fetch(url).then((r) => r.arrayBuffer()));
|
|
17
|
+
return loader.parse();
|
|
18
|
+
}
|
|
19
|
+
static loadFromBuffer(buffer) {
|
|
20
|
+
const loader = new VMDLoader(buffer);
|
|
21
|
+
return loader.parse();
|
|
22
|
+
}
|
|
23
|
+
parse() {
|
|
24
|
+
// Read header (30 bytes)
|
|
25
|
+
const header = this.getString(30);
|
|
26
|
+
if (!header.startsWith("Vocaloid Motion Data")) {
|
|
27
|
+
throw new Error("Invalid VMD file header");
|
|
28
|
+
}
|
|
29
|
+
// Skip model name (20 bytes)
|
|
30
|
+
this.skip(20);
|
|
31
|
+
// Read bone frame count (4 bytes, u32 little endian)
|
|
32
|
+
const boneFrameCount = this.getUint32();
|
|
33
|
+
// Read all bone frames
|
|
34
|
+
const allBoneFrames = [];
|
|
35
|
+
for (let i = 0; i < boneFrameCount; i++) {
|
|
36
|
+
const boneFrame = this.readBoneFrame();
|
|
37
|
+
// Convert frame number to time (assuming 30 FPS like the Rust code)
|
|
38
|
+
const FRAME_RATE = 30.0;
|
|
39
|
+
const time = boneFrame.frame / FRAME_RATE;
|
|
40
|
+
allBoneFrames.push({ time, boneFrame });
|
|
41
|
+
}
|
|
42
|
+
// Group by time and convert to VMDKeyFrame format
|
|
43
|
+
// Sort by time first
|
|
44
|
+
allBoneFrames.sort((a, b) => a.time - b.time);
|
|
45
|
+
const keyFrames = [];
|
|
46
|
+
let currentTime = -1.0;
|
|
47
|
+
let currentBoneFrames = [];
|
|
48
|
+
for (const { time, boneFrame } of allBoneFrames) {
|
|
49
|
+
if (Math.abs(time - currentTime) > 0.001) {
|
|
50
|
+
// New time frame
|
|
51
|
+
if (currentBoneFrames.length > 0) {
|
|
52
|
+
keyFrames.push({
|
|
53
|
+
time: currentTime,
|
|
54
|
+
boneFrames: currentBoneFrames,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
currentTime = time;
|
|
58
|
+
currentBoneFrames = [boneFrame];
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Same time frame
|
|
62
|
+
currentBoneFrames.push(boneFrame);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Add the last frame
|
|
66
|
+
if (currentBoneFrames.length > 0) {
|
|
67
|
+
keyFrames.push({
|
|
68
|
+
time: currentTime,
|
|
69
|
+
boneFrames: currentBoneFrames,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return keyFrames;
|
|
73
|
+
}
|
|
74
|
+
readBoneFrame() {
|
|
75
|
+
// Read bone name (15 bytes)
|
|
76
|
+
const nameBuffer = new Uint8Array(this.view.buffer, this.offset, 15);
|
|
77
|
+
this.offset += 15;
|
|
78
|
+
// Find the actual length of the bone name (stop at first null byte)
|
|
79
|
+
let nameLength = 15;
|
|
80
|
+
for (let i = 0; i < 15; i++) {
|
|
81
|
+
if (nameBuffer[i] === 0) {
|
|
82
|
+
nameLength = i;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Decode Shift-JIS bone name
|
|
87
|
+
let boneName;
|
|
88
|
+
try {
|
|
89
|
+
const nameSlice = nameBuffer.slice(0, nameLength);
|
|
90
|
+
boneName = this.decoder.decode(nameSlice);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Fallback to lossy decoding if there were encoding errors
|
|
94
|
+
boneName = String.fromCharCode(...nameBuffer.slice(0, nameLength));
|
|
95
|
+
}
|
|
96
|
+
// Read frame number (4 bytes, little endian)
|
|
97
|
+
const frame = this.getUint32();
|
|
98
|
+
// Skip position (12 bytes: 3 x f32, little endian)
|
|
99
|
+
this.skip(12);
|
|
100
|
+
// Read rotation quaternion (16 bytes: 4 x f32, little endian)
|
|
101
|
+
const rotX = this.getFloat32();
|
|
102
|
+
const rotY = this.getFloat32();
|
|
103
|
+
const rotZ = this.getFloat32();
|
|
104
|
+
const rotW = this.getFloat32();
|
|
105
|
+
const rotation = new Quat(rotX, rotY, rotZ, rotW);
|
|
106
|
+
// Skip interpolation parameters (64 bytes)
|
|
107
|
+
this.skip(64);
|
|
108
|
+
return {
|
|
109
|
+
boneName,
|
|
110
|
+
frame,
|
|
111
|
+
rotation,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
getUint32() {
|
|
115
|
+
if (this.offset + 4 > this.view.buffer.byteLength) {
|
|
116
|
+
throw new RangeError(`Offset ${this.offset} + 4 exceeds buffer bounds ${this.view.buffer.byteLength}`);
|
|
117
|
+
}
|
|
118
|
+
const v = this.view.getUint32(this.offset, true); // true = little endian
|
|
119
|
+
this.offset += 4;
|
|
120
|
+
return v;
|
|
121
|
+
}
|
|
122
|
+
getFloat32() {
|
|
123
|
+
if (this.offset + 4 > this.view.buffer.byteLength) {
|
|
124
|
+
throw new RangeError(`Offset ${this.offset} + 4 exceeds buffer bounds ${this.view.buffer.byteLength}`);
|
|
125
|
+
}
|
|
126
|
+
const v = this.view.getFloat32(this.offset, true); // true = little endian
|
|
127
|
+
this.offset += 4;
|
|
128
|
+
return v;
|
|
129
|
+
}
|
|
130
|
+
getString(len) {
|
|
131
|
+
const bytes = new Uint8Array(this.view.buffer, this.offset, len);
|
|
132
|
+
this.offset += len;
|
|
133
|
+
return String.fromCharCode(...bytes);
|
|
134
|
+
}
|
|
135
|
+
skip(bytes) {
|
|
136
|
+
if (this.offset + bytes > this.view.buffer.byteLength) {
|
|
137
|
+
throw new RangeError(`Offset ${this.offset} + ${bytes} exceeds buffer bounds ${this.view.buffer.byteLength}`);
|
|
138
|
+
}
|
|
139
|
+
this.offset += bytes;
|
|
140
|
+
}
|
|
141
|
+
}
|