reze-engine 0.16.2 → 0.17.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 +3 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +165 -26
- package/dist/model.d.ts +5 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +14 -1
- package/dist/physics/body.d.ts +4 -1
- package/dist/physics/body.d.ts.map +1 -1
- package/dist/physics/body.js +86 -20
- package/dist/physics/constraint.d.ts +15 -2
- package/dist/physics/constraint.d.ts.map +1 -1
- package/dist/physics/constraint.js +49 -2
- package/dist/physics/physics.d.ts +3 -0
- package/dist/physics/physics.d.ts.map +1 -1
- package/dist/physics/physics.js +69 -1
- package/dist/physics/solver.d.ts.map +1 -1
- package/dist/physics/solver.js +289 -113
- package/dist/physics/types.d.ts +2 -1
- package/dist/physics/types.d.ts.map +1 -1
- package/dist/physics/types.js +5 -0
- package/dist/physics/world.d.ts +3 -0
- package/dist/physics/world.d.ts.map +1 -1
- package/dist/physics/world.js +19 -3
- package/dist/pmx-loader.d.ts +2 -0
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +38 -20
- package/dist/shaders/materials/body.d.ts +1 -1
- package/dist/shaders/materials/body.d.ts.map +1 -1
- package/dist/shaders/materials/body.js +90 -85
- package/dist/shaders/materials/cloth_rough.d.ts +1 -1
- package/dist/shaders/materials/cloth_rough.d.ts.map +1 -1
- package/dist/shaders/materials/cloth_rough.js +1 -1
- package/dist/shaders/materials/cloth_smooth.d.ts +1 -1
- package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -1
- package/dist/shaders/materials/cloth_smooth.js +2 -2
- package/dist/shaders/materials/common.d.ts +1 -1
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +137 -112
- package/dist/shaders/materials/default.d.ts +1 -1
- package/dist/shaders/materials/default.d.ts.map +1 -1
- package/dist/shaders/materials/default.js +37 -32
- package/dist/shaders/materials/eye.d.ts +1 -1
- package/dist/shaders/materials/eye.d.ts.map +1 -1
- package/dist/shaders/materials/eye.js +54 -35
- package/dist/shaders/materials/face.d.ts +1 -1
- package/dist/shaders/materials/face.d.ts.map +1 -1
- package/dist/shaders/materials/face.js +90 -85
- package/dist/shaders/materials/hair.d.ts +1 -1
- package/dist/shaders/materials/hair.d.ts.map +1 -1
- package/dist/shaders/materials/hair.js +85 -75
- package/dist/shaders/materials/metal.d.ts +1 -1
- package/dist/shaders/materials/metal.d.ts.map +1 -1
- package/dist/shaders/materials/metal.js +1 -1
- package/dist/shaders/materials/mmd_classic.d.ts +2 -0
- package/dist/shaders/materials/mmd_classic.d.ts.map +1 -0
- package/dist/shaders/materials/mmd_classic.js +66 -0
- package/dist/shaders/materials/stockings.d.ts +1 -1
- package/dist/shaders/materials/stockings.d.ts.map +1 -1
- package/dist/shaders/materials/stockings.js +1 -1
- package/package.json +1 -1
- package/src/engine.ts +4198 -4030
- package/src/model.ts +21 -1
- package/src/physics/body.ts +87 -24
- package/src/physics/constraint.ts +80 -6
- package/src/physics/physics.ts +68 -1
- package/src/physics/solver.ts +915 -744
- package/src/physics/types.ts +10 -2
- package/src/physics/world.ts +20 -3
- package/src/pmx-loader.ts +39 -20
- package/src/shaders/materials/body.ts +97 -92
- package/src/shaders/materials/cloth_rough.ts +1 -1
- package/src/shaders/materials/cloth_smooth.ts +2 -2
- package/src/shaders/materials/common.ts +204 -179
- package/src/shaders/materials/default.ts +45 -40
- package/src/shaders/materials/eye.ts +62 -43
- package/src/shaders/materials/face.ts +97 -92
- package/src/shaders/materials/hair.ts +92 -82
- package/src/shaders/materials/metal.ts +1 -1
- package/src/shaders/materials/mmd_classic.ts +68 -0
- package/src/shaders/materials/stockings.ts +1 -1
package/dist/physics/body.js
CHANGED
|
@@ -17,10 +17,12 @@ export class RigidBodyStore {
|
|
|
17
17
|
this.linearVelocities = new Float32Array(N * 3);
|
|
18
18
|
this.angularVelocities = new Float32Array(N * 3);
|
|
19
19
|
this.invMass = new Float32Array(N);
|
|
20
|
-
this.
|
|
20
|
+
this.invInertiaLocal = new Float32Array(N * 3);
|
|
21
|
+
this.invInertiaWorld = new Float32Array(N * 9);
|
|
21
22
|
this.linearDamping = new Float32Array(N);
|
|
22
23
|
this.angularDamping = new Float32Array(N);
|
|
23
24
|
this.type = new Uint8Array(N);
|
|
25
|
+
this.aligned = new Uint8Array(N);
|
|
24
26
|
this.boneIndex = new Int32Array(N);
|
|
25
27
|
this.bodyOffsetMatrix = new Float32Array(N * 16);
|
|
26
28
|
this.bodyOffsetInverse = new Float32Array(N * 16);
|
|
@@ -46,10 +48,12 @@ export class RigidBodyStore {
|
|
|
46
48
|
this.orientations[i4 + 3] = q.w;
|
|
47
49
|
const dynamic = rb.type === RigidbodyType.Dynamic && rb.mass > 0;
|
|
48
50
|
this.invMass[i] = dynamic ? 1 / rb.mass : 0;
|
|
49
|
-
|
|
51
|
+
if (dynamic)
|
|
52
|
+
computeLocalInvInertia(rb, this.invInertiaLocal, i * 3);
|
|
50
53
|
this.linearDamping[i] = rb.linearDamping;
|
|
51
54
|
this.angularDamping[i] = rb.angularDamping;
|
|
52
55
|
this.type[i] = rb.type;
|
|
56
|
+
this.aligned[i] = rb.aligned ? 1 : 0;
|
|
53
57
|
this.boneIndex[i] = rb.boneIndex;
|
|
54
58
|
this.friction[i] = rb.friction;
|
|
55
59
|
this.restitution[i] = rb.restitution;
|
|
@@ -61,6 +65,52 @@ export class RigidBodyStore {
|
|
|
61
65
|
this.size[i * 3 + 2] = rb.size.z;
|
|
62
66
|
}
|
|
63
67
|
}
|
|
68
|
+
// Refresh I⁻¹_world = R·diag(invInertiaLocal)·Rᵀ for every dynamic body.
|
|
69
|
+
// Called once per substep before constraint setup (orientations are
|
|
70
|
+
// constant during a solve).
|
|
71
|
+
updateInvInertiaWorld() {
|
|
72
|
+
const N = this.count;
|
|
73
|
+
const ori = this.orientations;
|
|
74
|
+
const local = this.invInertiaLocal;
|
|
75
|
+
const W = this.invInertiaWorld;
|
|
76
|
+
const invMass = this.invMass;
|
|
77
|
+
for (let i = 0; i < N; i++) {
|
|
78
|
+
if (invMass[i] <= 0)
|
|
79
|
+
continue;
|
|
80
|
+
const i3 = i * 3;
|
|
81
|
+
const i4 = i * 4;
|
|
82
|
+
const i9 = i * 9;
|
|
83
|
+
const qx = ori[i4 + 0], qy = ori[i4 + 1], qz = ori[i4 + 2], qw = ori[i4 + 3];
|
|
84
|
+
const x2 = qx + qx, y2 = qy + qy, z2 = qz + qz;
|
|
85
|
+
const xx = qx * x2, yy = qy * y2, zz = qz * z2;
|
|
86
|
+
const xy = qx * y2, xz = qx * z2, yz = qy * z2;
|
|
87
|
+
const wx = qw * x2, wy = qw * y2, wz = qw * z2;
|
|
88
|
+
// R columns (column-major rotation matrix)
|
|
89
|
+
const r00 = 1 - (yy + zz), r01 = xy - wz, r02 = xz + wy;
|
|
90
|
+
const r10 = xy + wz, r11 = 1 - (xx + zz), r12 = yz - wx;
|
|
91
|
+
const r20 = xz - wy, r21 = yz + wx, r22 = 1 - (xx + yy);
|
|
92
|
+
const d0 = local[i3 + 0], d1 = local[i3 + 1], d2 = local[i3 + 2];
|
|
93
|
+
// W = R·diag·Rᵀ (symmetric)
|
|
94
|
+
const a0 = r00 * d0, a1 = r01 * d1, a2 = r02 * d2;
|
|
95
|
+
const b0 = r10 * d0, b1 = r11 * d1, b2 = r12 * d2;
|
|
96
|
+
const c0 = r20 * d0, c1 = r21 * d1, c2 = r22 * d2;
|
|
97
|
+
const w00 = a0 * r00 + a1 * r01 + a2 * r02;
|
|
98
|
+
const w01 = a0 * r10 + a1 * r11 + a2 * r12;
|
|
99
|
+
const w02 = a0 * r20 + a1 * r21 + a2 * r22;
|
|
100
|
+
const w11 = b0 * r10 + b1 * r11 + b2 * r12;
|
|
101
|
+
const w12 = b0 * r20 + b1 * r21 + b2 * r22;
|
|
102
|
+
const w22 = c0 * r20 + c1 * r21 + c2 * r22;
|
|
103
|
+
W[i9 + 0] = w00;
|
|
104
|
+
W[i9 + 1] = w01;
|
|
105
|
+
W[i9 + 2] = w02;
|
|
106
|
+
W[i9 + 3] = w01;
|
|
107
|
+
W[i9 + 4] = w11;
|
|
108
|
+
W[i9 + 5] = w12;
|
|
109
|
+
W[i9 + 6] = w02;
|
|
110
|
+
W[i9 + 7] = w12;
|
|
111
|
+
W[i9 + 8] = w22;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
64
114
|
// World-space AABBs for every body. Inflated by margin so contacts stay
|
|
65
115
|
// paired across small velocity jitter without recomputing per iteration.
|
|
66
116
|
updateAabbs(margin = 0.5) {
|
|
@@ -193,37 +243,53 @@ export class RigidBodyStore {
|
|
|
193
243
|
const _scratchA = new Float32Array(16);
|
|
194
244
|
const _scratchB = new Float32Array(16);
|
|
195
245
|
const _scratchC = new Float32Array(16);
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
|
|
246
|
+
// Diagonal local inverse inertia, matching Bullet's calculateLocalInertia
|
|
247
|
+
// per shape (so behavior tracks Ammo-based MMD engines):
|
|
248
|
+
// Sphere: I = (2/5)·m·r² on every axis
|
|
249
|
+
// Box: Ix = m/12·(ly²+lz²), … with l = full extents (2·half)
|
|
250
|
+
// Capsule: Bullet's bounding-box approximation of the Y-axis capsule
|
|
251
|
+
// (lx = lz = 2r, ly = h + 2r)
|
|
252
|
+
function computeLocalInvInertia(rb, out, o) {
|
|
201
253
|
const m = rb.mass;
|
|
202
254
|
if (m <= 0)
|
|
203
|
-
return
|
|
204
|
-
let
|
|
255
|
+
return;
|
|
256
|
+
let Ix, Iy, Iz;
|
|
205
257
|
switch (rb.shape) {
|
|
206
258
|
case RigidbodyShape.Sphere: {
|
|
207
|
-
const
|
|
208
|
-
|
|
259
|
+
const I = 0.4 * m * rb.size.x * rb.size.x;
|
|
260
|
+
Ix = I;
|
|
261
|
+
Iy = I;
|
|
262
|
+
Iz = I;
|
|
209
263
|
break;
|
|
210
264
|
}
|
|
211
265
|
case RigidbodyShape.Box: {
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
|
|
266
|
+
const lx2 = 4 * rb.size.x * rb.size.x;
|
|
267
|
+
const ly2 = 4 * rb.size.y * rb.size.y;
|
|
268
|
+
const lz2 = 4 * rb.size.z * rb.size.z;
|
|
269
|
+
Ix = (m / 12) * (ly2 + lz2);
|
|
270
|
+
Iy = (m / 12) * (lx2 + lz2);
|
|
271
|
+
Iz = (m / 12) * (lx2 + ly2);
|
|
215
272
|
break;
|
|
216
273
|
}
|
|
217
274
|
case RigidbodyShape.Capsule: {
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
|
|
275
|
+
const lx = 2 * rb.size.x;
|
|
276
|
+
const ly = rb.size.y + 2 * rb.size.x;
|
|
277
|
+
const lx2 = lx * lx;
|
|
278
|
+
const ly2 = ly * ly;
|
|
279
|
+
Ix = (m / 12) * (ly2 + lx2);
|
|
280
|
+
Iy = (m / 12) * (lx2 + lx2);
|
|
281
|
+
Iz = (m / 12) * (lx2 + ly2);
|
|
221
282
|
break;
|
|
222
283
|
}
|
|
223
|
-
default:
|
|
224
|
-
|
|
284
|
+
default: {
|
|
285
|
+
Ix = m;
|
|
286
|
+
Iy = m;
|
|
287
|
+
Iz = m;
|
|
288
|
+
}
|
|
225
289
|
}
|
|
226
|
-
|
|
290
|
+
out[o + 0] = Ix > 0 ? 1 / Ix : 0;
|
|
291
|
+
out[o + 1] = Iy > 0 ? 1 / Iy : 0;
|
|
292
|
+
out[o + 2] = Iz > 0 ? 1 / Iz : 0;
|
|
227
293
|
}
|
|
228
294
|
function identity16(out, offset) {
|
|
229
295
|
out[offset + 0] = 1;
|
|
@@ -11,6 +11,7 @@ export interface SixDofSpringConstraint {
|
|
|
11
11
|
springEnabled: Uint8Array;
|
|
12
12
|
springStiffness: Float32Array;
|
|
13
13
|
equilibriumPoint: Float32Array;
|
|
14
|
+
isLoop: boolean;
|
|
14
15
|
cacheSkip: boolean;
|
|
15
16
|
cacheLeverA: Float32Array;
|
|
16
17
|
cacheLeverB: Float32Array;
|
|
@@ -22,16 +23,28 @@ export interface SixDofSpringConstraint {
|
|
|
22
23
|
cacheLinActive: Uint8Array;
|
|
23
24
|
cacheLinLimitImp: Float32Array;
|
|
24
25
|
cacheLinSpringTarget: Float32Array;
|
|
26
|
+
cacheLinSpringMaxImp: Float32Array;
|
|
27
|
+
cacheLinSpringImp: Float32Array;
|
|
25
28
|
cacheLinSpringActive: Uint8Array;
|
|
26
29
|
cacheAngAxes: Float32Array;
|
|
27
30
|
cacheAngTargetVel: Float32Array;
|
|
28
31
|
cacheAngActive: Uint8Array;
|
|
29
|
-
cacheAngJacInv:
|
|
32
|
+
cacheAngJacInv: Float32Array;
|
|
33
|
+
cacheAngSpringMaxImp: Float32Array;
|
|
34
|
+
cacheAngSpringImp: Float32Array;
|
|
35
|
+
cacheAngWA: Float32Array;
|
|
36
|
+
cacheAngWB: Float32Array;
|
|
30
37
|
cacheAngLimAxis: Float32Array;
|
|
38
|
+
cacheAngLimWA: Float32Array;
|
|
39
|
+
cacheAngLimWB: Float32Array;
|
|
40
|
+
cacheAngLimJacInv: number;
|
|
31
41
|
cacheAngLimTarget: number;
|
|
32
42
|
cacheAngLimActive: number;
|
|
33
43
|
cacheAngLimImp: number;
|
|
44
|
+
cacheAngPATarget: Float32Array;
|
|
45
|
+
cacheAngPAActive: Uint8Array;
|
|
46
|
+
cacheAngPAImp: Float32Array;
|
|
34
47
|
}
|
|
35
|
-
export declare const STOP_ERP = 0.
|
|
48
|
+
export declare const STOP_ERP = 0.45;
|
|
36
49
|
export declare function buildConstraints(rigidbodies: Rigidbody[], joints: Joint[]): SixDofSpringConstraint[];
|
|
37
50
|
//# sourceMappingURL=constraint.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constraint.d.ts","sourceRoot":"","sources":["../../src/physics/constraint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"constraint.d.ts","sourceRoot":"","sources":["../../src/physics/constraint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAY/C,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IAEb,MAAM,EAAE,YAAY,CAAA;IACpB,MAAM,EAAE,YAAY,CAAA;IAGpB,SAAS,EAAE,YAAY,CAAA;IACvB,SAAS,EAAE,YAAY,CAAA;IACvB,UAAU,EAAE,YAAY,CAAA;IACxB,UAAU,EAAE,YAAY,CAAA;IAExB,aAAa,EAAE,UAAU,CAAA;IACzB,eAAe,EAAE,YAAY,CAAA;IAC7B,gBAAgB,EAAE,YAAY,CAAA;IAK9B,MAAM,EAAE,OAAO,CAAA;IAKf,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,YAAY,CAAA;IACzB,WAAW,EAAE,YAAY,CAAA;IACzB,YAAY,EAAE,YAAY,CAAA;IAC1B,cAAc,EAAE,YAAY,CAAA;IAC5B,cAAc,EAAE,YAAY,CAAA;IAC5B,cAAc,EAAE,YAAY,CAAA;IAK5B,iBAAiB,EAAE,YAAY,CAAA;IAC/B,cAAc,EAAE,UAAU,CAAA;IAC1B,gBAAgB,EAAE,YAAY,CAAA;IAK9B,oBAAoB,EAAE,YAAY,CAAA;IAClC,oBAAoB,EAAE,YAAY,CAAA;IAClC,iBAAiB,EAAE,YAAY,CAAA;IAC/B,oBAAoB,EAAE,UAAU,CAAA;IAChC,YAAY,EAAE,YAAY,CAAA;IAC1B,iBAAiB,EAAE,YAAY,CAAA;IAC/B,cAAc,EAAE,UAAU,CAAA;IAI1B,cAAc,EAAE,YAAY,CAAA;IAK5B,oBAAoB,EAAE,YAAY,CAAA;IAClC,iBAAiB,EAAE,YAAY,CAAA;IAC/B,UAAU,EAAE,YAAY,CAAA;IACxB,UAAU,EAAE,YAAY,CAAA;IAKxB,eAAe,EAAE,YAAY,CAAA;IAC7B,aAAa,EAAE,YAAY,CAAA;IAC3B,aAAa,EAAE,YAAY,CAAA;IAC3B,iBAAiB,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;IAKtB,gBAAgB,EAAE,YAAY,CAAA;IAC9B,gBAAgB,EAAE,UAAU,CAAA;IAC5B,aAAa,EAAE,YAAY,CAAA;CAC5B;AAOD,eAAO,MAAM,QAAQ,OAAO,CAAA;AAM5B,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,SAAS,EAAE,EACxB,MAAM,EAAE,KAAK,EAAE,GACd,sBAAsB,EAAE,CA+H1B"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Mat4 } from "../math";
|
|
2
|
-
|
|
2
|
+
import { RigidbodyType } from "./types";
|
|
3
|
+
// Stop-limit ERP. PMX rigs are tuned against MMD's stiff limit response;
|
|
4
|
+
// lowering this makes cloth resting against its limits sink visibly deeper
|
|
5
|
+
// (equilibrium penetration scales with 1/ERP). Rest chatter at this
|
|
6
|
+
// stiffness was fixed at the source (spring double-drive, unilateral
|
|
7
|
+
// stops) — don't lower ERP to paper over jitter.
|
|
8
|
+
export const STOP_ERP = 0.45;
|
|
3
9
|
// Build per-joint constraints from PMX data:
|
|
4
10
|
// frameA = (bodyA_worldBind)^-1 · jointWorldBind
|
|
5
11
|
// frameB = (bodyB_worldBind)^-1 · jointWorldBind
|
|
@@ -64,6 +70,7 @@ export function buildConstraints(rigidbodies, joints) {
|
|
|
64
70
|
springEnabled,
|
|
65
71
|
springStiffness,
|
|
66
72
|
equilibriumPoint: new Float32Array(6),
|
|
73
|
+
isLoop: false,
|
|
67
74
|
cacheSkip: false,
|
|
68
75
|
cacheLeverA: new Float32Array(3),
|
|
69
76
|
cacheLeverB: new Float32Array(3),
|
|
@@ -75,17 +82,57 @@ export function buildConstraints(rigidbodies, joints) {
|
|
|
75
82
|
cacheLinActive: new Uint8Array(3),
|
|
76
83
|
cacheLinLimitImp: new Float32Array(3),
|
|
77
84
|
cacheLinSpringTarget: new Float32Array(3),
|
|
85
|
+
cacheLinSpringMaxImp: new Float32Array(3),
|
|
86
|
+
cacheLinSpringImp: new Float32Array(3),
|
|
78
87
|
cacheLinSpringActive: new Uint8Array(3),
|
|
79
88
|
cacheAngAxes: new Float32Array(9),
|
|
80
89
|
cacheAngTargetVel: new Float32Array(3),
|
|
81
90
|
cacheAngActive: new Uint8Array(3),
|
|
82
|
-
cacheAngJacInv:
|
|
91
|
+
cacheAngJacInv: new Float32Array(3),
|
|
92
|
+
cacheAngSpringMaxImp: new Float32Array(3),
|
|
93
|
+
cacheAngSpringImp: new Float32Array(3),
|
|
94
|
+
cacheAngWA: new Float32Array(9),
|
|
95
|
+
cacheAngWB: new Float32Array(9),
|
|
83
96
|
cacheAngLimAxis: new Float32Array(3),
|
|
97
|
+
cacheAngLimWA: new Float32Array(3),
|
|
98
|
+
cacheAngLimWB: new Float32Array(3),
|
|
99
|
+
cacheAngLimJacInv: 0,
|
|
84
100
|
cacheAngLimTarget: 0,
|
|
85
101
|
cacheAngLimActive: 0,
|
|
86
102
|
cacheAngLimImp: 0,
|
|
103
|
+
cacheAngPATarget: new Float32Array(3),
|
|
104
|
+
cacheAngPAActive: new Uint8Array(3),
|
|
105
|
+
cacheAngPAImp: new Float32Array(3),
|
|
87
106
|
});
|
|
88
107
|
}
|
|
108
|
+
// Mark loop-closing joints via union-find over the joint graph. All
|
|
109
|
+
// bone-follow bodies count as one "world" component (they're each anchored
|
|
110
|
+
// to the skeleton), so a joint bridging two separately anchored subtrees
|
|
111
|
+
// also closes a kinematic cycle. One edge per cycle gets softened, which
|
|
112
|
+
// releases the over-determination regardless of which edge it is.
|
|
113
|
+
const parent = new Int32Array(rigidbodies.length + 1);
|
|
114
|
+
for (let i = 0; i < parent.length; i++)
|
|
115
|
+
parent[i] = i;
|
|
116
|
+
const find = (x) => {
|
|
117
|
+
while (parent[x] !== x) {
|
|
118
|
+
parent[x] = parent[parent[x]];
|
|
119
|
+
x = parent[x];
|
|
120
|
+
}
|
|
121
|
+
return x;
|
|
122
|
+
};
|
|
123
|
+
const world = rigidbodies.length;
|
|
124
|
+
for (let i = 0; i < rigidbodies.length; i++) {
|
|
125
|
+
if (rigidbodies[i].type !== RigidbodyType.Dynamic)
|
|
126
|
+
parent[find(i)] = find(world);
|
|
127
|
+
}
|
|
128
|
+
for (const con of out) {
|
|
129
|
+
const ra = find(con.bodyA);
|
|
130
|
+
const rb = find(con.bodyB);
|
|
131
|
+
if (ra === rb)
|
|
132
|
+
con.isLoop = true;
|
|
133
|
+
else
|
|
134
|
+
parent[ra] = rb;
|
|
135
|
+
}
|
|
89
136
|
return out;
|
|
90
137
|
}
|
|
91
138
|
// frame = bodyWorldBind^-1 · jointWorld. False if bodyWorldBind is singular.
|
|
@@ -20,6 +20,8 @@ export declare class RezePhysics {
|
|
|
20
20
|
private kinTargetAngVel;
|
|
21
21
|
private kinRoot;
|
|
22
22
|
private teleportFlags;
|
|
23
|
+
private alignPinned;
|
|
24
|
+
teleportCount: number;
|
|
23
25
|
constructor(rigidbodies: Rigidbody[], joints?: Joint[]);
|
|
24
26
|
private buildKinematicRoots;
|
|
25
27
|
private savePrevState;
|
|
@@ -39,6 +41,7 @@ export declare class RezePhysics {
|
|
|
39
41
|
private advanceKinematicToTargets;
|
|
40
42
|
private snapKinematicToTargets;
|
|
41
43
|
private carryDynamicThroughTeleport;
|
|
44
|
+
private alignPinnedBodiesToBones;
|
|
42
45
|
private restoreNonFiniteBodies;
|
|
43
46
|
private applyDynamicsToBones;
|
|
44
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../../src/physics/physics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAYvC,qBAAa,WAAW;IACtB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,KAAK,CAAO;IACpB,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAI;IAMhC,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,gBAAgB,CAAc;IAQtC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,eAAe,CAAc;IAMrC,OAAO,CAAC,OAAO,CAAY;IAE3B,OAAO,CAAC,aAAa,CAAY;
|
|
1
|
+
{"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../../src/physics/physics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAYvC,qBAAa,WAAW;IACtB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,KAAK,CAAO;IACpB,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAI;IAMhC,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,gBAAgB,CAAc;IAQtC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,eAAe,CAAc;IAMrC,OAAO,CAAC,OAAO,CAAY;IAE3B,OAAO,CAAC,aAAa,CAAY;IAMjC,OAAO,CAAC,WAAW,CAAY;IAG/B,aAAa,SAAI;gBAEL,WAAW,EAAE,SAAS,EAAE,EAAE,MAAM,GAAE,KAAK,EAAO;IA8B1D,OAAO,CAAC,mBAAmB;IA4B3B,OAAO,CAAC,aAAa;IAKrB,UAAU,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI;IAG/B,UAAU,IAAI,IAAI;IAGlB,cAAc,IAAI,SAAS,EAAE;IAG7B,SAAS,IAAI,KAAK,EAAE;IAGpB,QAAQ,IAAI,cAAc;IAI1B,sBAAsB,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC;IAiBnE,KAAK,CAAC,iBAAiB,EAAE,IAAI,EAAE,GAAG,IAAI;IActC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,EAAE,uBAAuB,EAAE,YAAY,GAAG,IAAI;IAiExF,OAAO,CAAC,iBAAiB;IAuCzB,OAAO,CAAC,uBAAuB;IAqF/B,OAAO,CAAC,yBAAyB;IAgEjC,OAAO,CAAC,sBAAsB;IAmC9B,OAAO,CAAC,2BAA2B;IA6EnC,OAAO,CAAC,wBAAwB;IA2BhC,OAAO,CAAC,sBAAsB;IAqC9B,OAAO,CAAC,oBAAoB;CA6D7B"}
|
package/dist/physics/physics.js
CHANGED
|
@@ -16,6 +16,9 @@ export class RezePhysics {
|
|
|
16
16
|
this.timeAccum = 0;
|
|
17
17
|
this.fixedTimeStep = 1 / 60;
|
|
18
18
|
this.maxSubSteps = 6;
|
|
19
|
+
// Debug counter: frames on which a teleport (scrub/jump discontinuity)
|
|
20
|
+
// was detected and settled.
|
|
21
|
+
this.teleportCount = 0;
|
|
19
22
|
this.rigidbodies = rigidbodies;
|
|
20
23
|
this.joints = joints;
|
|
21
24
|
this.store = new RigidBodyStore(rigidbodies);
|
|
@@ -30,6 +33,18 @@ export class RezePhysics {
|
|
|
30
33
|
this.kinTargetAngVel = new Float32Array(this.store.count * 3);
|
|
31
34
|
this.kinRoot = this.buildKinematicRoots();
|
|
32
35
|
this.teleportFlags = new Uint8Array(this.store.count);
|
|
36
|
+
this.alignPinned = new Uint8Array(this.store.count);
|
|
37
|
+
const types = this.store.type;
|
|
38
|
+
for (const c of this.constraints) {
|
|
39
|
+
const tA = types[c.bodyA];
|
|
40
|
+
const tB = types[c.bodyB];
|
|
41
|
+
const aFollows = tA === RigidbodyType.Static || tA === RigidbodyType.Kinematic;
|
|
42
|
+
const bFollows = tB === RigidbodyType.Static || tB === RigidbodyType.Kinematic;
|
|
43
|
+
if (aFollows && this.store.aligned[c.bodyB])
|
|
44
|
+
this.alignPinned[c.bodyB] = 1;
|
|
45
|
+
if (bFollows && this.store.aligned[c.bodyA])
|
|
46
|
+
this.alignPinned[c.bodyA] = 1;
|
|
47
|
+
}
|
|
33
48
|
}
|
|
34
49
|
// BFS over the joint graph from every kinematic body, assigning each
|
|
35
50
|
// reachable dynamic body the kinematic body it (transitively) hangs off.
|
|
@@ -101,6 +116,13 @@ export class RezePhysics {
|
|
|
101
116
|
return;
|
|
102
117
|
this.snapBodiesToBones(boneWorldMatrices);
|
|
103
118
|
this.savePrevState(); // prev == curr after a snap, so no interpolation jump
|
|
119
|
+
// Reseed kinematic targets from the snapped pose — otherwise the next
|
|
120
|
+
// step derives the target-trajectory velocity against the pre-reset
|
|
121
|
+
// targets and feeds one frame of enormous anchor velocity to the solver.
|
|
122
|
+
this.kinTargetPos.set(this.store.positions);
|
|
123
|
+
this.kinTargetOri.set(this.store.orientations);
|
|
124
|
+
this.kinTargetVel.fill(0);
|
|
125
|
+
this.kinTargetAngVel.fill(0);
|
|
104
126
|
this.timeAccum = 0;
|
|
105
127
|
}
|
|
106
128
|
step(dt, boneWorldMatrices, boneInverseBindMatrices) {
|
|
@@ -124,6 +146,7 @@ export class RezePhysics {
|
|
|
124
146
|
// raw derived velocity is what used to explode the solver. Chains under
|
|
125
147
|
// unaffected roots keep their momentum untouched.
|
|
126
148
|
if (this.computeKinematicTargets(boneWorldMatrices, dt)) {
|
|
149
|
+
this.teleportCount++;
|
|
127
150
|
this.carryDynamicThroughTeleport();
|
|
128
151
|
this.snapKinematicToTargets(true);
|
|
129
152
|
this.savePrevState(); // prev == curr so interpolation doesn't streak
|
|
@@ -155,6 +178,9 @@ export class RezePhysics {
|
|
|
155
178
|
this.timeAccum = 0;
|
|
156
179
|
this.snapKinematicToTargets(false);
|
|
157
180
|
}
|
|
181
|
+
// MMD mode-2 bone alignment for pinned (depth-1) bodies: position
|
|
182
|
+
// re-pins to the animated bone, rotation stays simulated.
|
|
183
|
+
this.alignPinnedBodiesToBones(boneWorldMatrices);
|
|
158
184
|
// Fraction into the next (not-yet-taken) step; always in [0, 1).
|
|
159
185
|
const alpha = this.fixedTimeStep > 0 ? this.timeAccum / this.fixedTimeStep : 0;
|
|
160
186
|
this.applyDynamicsToBones(boneWorldMatrices, alpha);
|
|
@@ -452,6 +478,32 @@ export class RezePhysics {
|
|
|
452
478
|
av[i3 + 2] = 0;
|
|
453
479
|
}
|
|
454
480
|
}
|
|
481
|
+
// Overwrite pinned mode-2 bodies' positions with boneWorld × bodyOffset
|
|
482
|
+
// from the animated bone pose, keeping simulated orientation and
|
|
483
|
+
// velocities. prevPositions follows so interpolation doesn't streak.
|
|
484
|
+
alignPinnedBodiesToBones(boneWorldMatrices) {
|
|
485
|
+
const N = this.store.count;
|
|
486
|
+
const pinned = this.alignPinned;
|
|
487
|
+
const boneIdx = this.store.boneIndex;
|
|
488
|
+
const offsets = this.store.bodyOffsetMatrix;
|
|
489
|
+
const positions = this.store.positions;
|
|
490
|
+
const prevPos = this.prevPositions;
|
|
491
|
+
for (let i = 0; i < N; i++) {
|
|
492
|
+
if (!pinned[i])
|
|
493
|
+
continue;
|
|
494
|
+
const b = boneIdx[i];
|
|
495
|
+
if (b < 0 || b >= boneWorldMatrices.length)
|
|
496
|
+
continue;
|
|
497
|
+
Mat4.multiplyArrays(boneWorldMatrices[b].values, 0, offsets, i * 16, _bodyMat, 0);
|
|
498
|
+
const i3 = i * 3;
|
|
499
|
+
positions[i3 + 0] = _bodyMat[12];
|
|
500
|
+
positions[i3 + 1] = _bodyMat[13];
|
|
501
|
+
positions[i3 + 2] = _bodyMat[14];
|
|
502
|
+
prevPos[i3 + 0] = _bodyMat[12];
|
|
503
|
+
prevPos[i3 + 1] = _bodyMat[13];
|
|
504
|
+
prevPos[i3 + 2] = _bodyMat[14];
|
|
505
|
+
}
|
|
506
|
+
}
|
|
455
507
|
// Backstop: if a dynamic body's state went non-finite despite the velocity
|
|
456
508
|
// caps, restore its previous-substep pose with zero velocity instead of
|
|
457
509
|
// letting NaNs spread through constraints and contacts. Runs after every
|
|
@@ -550,7 +602,23 @@ export class RezePhysics {
|
|
|
550
602
|
Mat4.multiplyArrays(_bodyMat, 0, inv, i * 16, _boneMat, 0);
|
|
551
603
|
// Sanity gate against NaN / extreme values — silently drop the update.
|
|
552
604
|
if (Number.isFinite(_boneMat[0]) && Math.abs(_boneMat[0]) < 1e6) {
|
|
553
|
-
boneWorldMatrices[b].values
|
|
605
|
+
const v = boneWorldMatrices[b].values;
|
|
606
|
+
if (this.alignPinned[i]) {
|
|
607
|
+
// Pinned mode-2: the bone keeps its animated position, physics
|
|
608
|
+
// drives rotation only.
|
|
609
|
+
v[0] = _boneMat[0];
|
|
610
|
+
v[1] = _boneMat[1];
|
|
611
|
+
v[2] = _boneMat[2];
|
|
612
|
+
v[4] = _boneMat[4];
|
|
613
|
+
v[5] = _boneMat[5];
|
|
614
|
+
v[6] = _boneMat[6];
|
|
615
|
+
v[8] = _boneMat[8];
|
|
616
|
+
v[9] = _boneMat[9];
|
|
617
|
+
v[10] = _boneMat[10];
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
v.set(_boneMat);
|
|
621
|
+
}
|
|
554
622
|
}
|
|
555
623
|
}
|
|
556
624
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solver.d.ts","sourceRoot":"","sources":["../../src/physics/solver.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"solver.d.ts","sourceRoot":"","sources":["../../src/physics/solver.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,WAAW,CAAA;AAyDrD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EACrB,WAAW,EAAE,sBAAsB,EAAE,EACrC,QAAQ,EAAE,WAAW,EACrB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,MAAM,GACjB,IAAI,CA4BN"}
|