reze-engine 0.16.3 → 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 +160 -21
- package/dist/model.d.ts +1 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/physics/body.d.ts +3 -1
- package/dist/physics/body.d.ts.map +1 -1
- package/dist/physics/body.js +84 -20
- package/dist/physics/constraint.d.ts +14 -1
- package/dist/physics/constraint.d.ts.map +1 -1
- package/dist/physics/constraint.js +43 -1
- package/dist/physics/solver.d.ts.map +1 -1
- package/dist/physics/solver.js +278 -110
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +1 -0
- package/dist/shaders/materials/body.d.ts +1 -1
- package/dist/shaders/materials/body.d.ts.map +1 -1
- 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_smooth.d.ts +1 -1
- package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -1
- 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 -122
- package/dist/shaders/materials/default.d.ts +1 -1
- package/dist/shaders/materials/default.d.ts.map +1 -1
- 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 +14 -0
- package/dist/shaders/materials/face.d.ts +1 -1
- package/dist/shaders/materials/face.d.ts.map +1 -1
- package/dist/shaders/materials/hair.d.ts +1 -1
- package/dist/shaders/materials/hair.d.ts.map +1 -1
- package/dist/shaders/materials/metal.d.ts +1 -1
- package/dist/shaders/materials/metal.d.ts.map +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/package.json +1 -1
- package/src/engine.ts +4198 -4045
- package/src/model.ts +3 -0
- package/src/physics/body.ts +82 -24
- package/src/physics/constraint.ts +74 -5
- package/src/physics/solver.ts +915 -752
- package/src/pmx-loader.ts +1 -0
- package/src/shaders/materials/common.ts +204 -189
- package/src/shaders/materials/eye.ts +14 -0
- package/src/shaders/materials/mmd_classic.ts +68 -0
package/src/model.ts
CHANGED
|
@@ -43,6 +43,9 @@ export interface Material {
|
|
|
43
43
|
sphereTextureIndex: number
|
|
44
44
|
sphereMode: number
|
|
45
45
|
toonTextureIndex: number
|
|
46
|
+
// True when toonTextureIndex refers to the shared toon set (toon01–10)
|
|
47
|
+
// instead of the model's texture table.
|
|
48
|
+
sharedToon: boolean
|
|
46
49
|
edgeFlag: number
|
|
47
50
|
edgeColor: [number, number, number, number]
|
|
48
51
|
edgeSize: number
|
package/src/physics/body.ts
CHANGED
|
@@ -12,10 +12,14 @@ export class RigidBodyStore {
|
|
|
12
12
|
readonly angularVelocities: Float32Array // 3*N
|
|
13
13
|
|
|
14
14
|
readonly invMass: Float32Array // N (0 for static / kinematic)
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
|
|
15
|
+
// Full anisotropic inertia. Local diagonal (body frame, Bullet's shape
|
|
16
|
+
// formulas) plus the per-substep world tensor I⁻¹ = R·diag·Rᵀ (9 floats
|
|
17
|
+
// row-major, symmetric). The old scalar approximation deposited constraint
|
|
18
|
+
// impulses into rotational modes at the wrong rates on elongated capsules
|
|
19
|
+
// (5:1 skirt/hair bodies are ~25× anisotropic), leaving cloth with several
|
|
20
|
+
// times the kinetic energy real Bullet retains — visible as perpetual boil.
|
|
21
|
+
readonly invInertiaLocal: Float32Array // 3*N
|
|
22
|
+
readonly invInertiaWorld: Float32Array // 9*N
|
|
19
23
|
readonly linearDamping: Float32Array
|
|
20
24
|
readonly angularDamping: Float32Array
|
|
21
25
|
readonly type: Uint8Array
|
|
@@ -58,7 +62,8 @@ export class RigidBodyStore {
|
|
|
58
62
|
this.linearVelocities = new Float32Array(N * 3)
|
|
59
63
|
this.angularVelocities = new Float32Array(N * 3)
|
|
60
64
|
this.invMass = new Float32Array(N)
|
|
61
|
-
this.
|
|
65
|
+
this.invInertiaLocal = new Float32Array(N * 3)
|
|
66
|
+
this.invInertiaWorld = new Float32Array(N * 9)
|
|
62
67
|
this.linearDamping = new Float32Array(N)
|
|
63
68
|
this.angularDamping = new Float32Array(N)
|
|
64
69
|
this.type = new Uint8Array(N)
|
|
@@ -92,7 +97,7 @@ export class RigidBodyStore {
|
|
|
92
97
|
|
|
93
98
|
const dynamic = rb.type === RigidbodyType.Dynamic && rb.mass > 0
|
|
94
99
|
this.invMass[i] = dynamic ? 1 / rb.mass : 0
|
|
95
|
-
|
|
100
|
+
if (dynamic) computeLocalInvInertia(rb, this.invInertiaLocal, i * 3)
|
|
96
101
|
this.linearDamping[i] = rb.linearDamping
|
|
97
102
|
this.angularDamping[i] = rb.angularDamping
|
|
98
103
|
this.type[i] = rb.type
|
|
@@ -109,6 +114,47 @@ export class RigidBodyStore {
|
|
|
109
114
|
}
|
|
110
115
|
}
|
|
111
116
|
|
|
117
|
+
// Refresh I⁻¹_world = R·diag(invInertiaLocal)·Rᵀ for every dynamic body.
|
|
118
|
+
// Called once per substep before constraint setup (orientations are
|
|
119
|
+
// constant during a solve).
|
|
120
|
+
updateInvInertiaWorld(): void {
|
|
121
|
+
const N = this.count
|
|
122
|
+
const ori = this.orientations
|
|
123
|
+
const local = this.invInertiaLocal
|
|
124
|
+
const W = this.invInertiaWorld
|
|
125
|
+
const invMass = this.invMass
|
|
126
|
+
|
|
127
|
+
for (let i = 0; i < N; i++) {
|
|
128
|
+
if (invMass[i] <= 0) continue
|
|
129
|
+
const i3 = i * 3
|
|
130
|
+
const i4 = i * 4
|
|
131
|
+
const i9 = i * 9
|
|
132
|
+
const qx = ori[i4 + 0], qy = ori[i4 + 1], qz = ori[i4 + 2], qw = ori[i4 + 3]
|
|
133
|
+
const x2 = qx + qx, y2 = qy + qy, z2 = qz + qz
|
|
134
|
+
const xx = qx * x2, yy = qy * y2, zz = qz * z2
|
|
135
|
+
const xy = qx * y2, xz = qx * z2, yz = qy * z2
|
|
136
|
+
const wx = qw * x2, wy = qw * y2, wz = qw * z2
|
|
137
|
+
// R columns (column-major rotation matrix)
|
|
138
|
+
const r00 = 1 - (yy + zz), r01 = xy - wz, r02 = xz + wy
|
|
139
|
+
const r10 = xy + wz, r11 = 1 - (xx + zz), r12 = yz - wx
|
|
140
|
+
const r20 = xz - wy, r21 = yz + wx, r22 = 1 - (xx + yy)
|
|
141
|
+
const d0 = local[i3 + 0], d1 = local[i3 + 1], d2 = local[i3 + 2]
|
|
142
|
+
// W = R·diag·Rᵀ (symmetric)
|
|
143
|
+
const a0 = r00 * d0, a1 = r01 * d1, a2 = r02 * d2
|
|
144
|
+
const b0 = r10 * d0, b1 = r11 * d1, b2 = r12 * d2
|
|
145
|
+
const c0 = r20 * d0, c1 = r21 * d1, c2 = r22 * d2
|
|
146
|
+
const w00 = a0 * r00 + a1 * r01 + a2 * r02
|
|
147
|
+
const w01 = a0 * r10 + a1 * r11 + a2 * r12
|
|
148
|
+
const w02 = a0 * r20 + a1 * r21 + a2 * r22
|
|
149
|
+
const w11 = b0 * r10 + b1 * r11 + b2 * r12
|
|
150
|
+
const w12 = b0 * r20 + b1 * r21 + b2 * r22
|
|
151
|
+
const w22 = c0 * r20 + c1 * r21 + c2 * r22
|
|
152
|
+
W[i9 + 0] = w00; W[i9 + 1] = w01; W[i9 + 2] = w02
|
|
153
|
+
W[i9 + 3] = w01; W[i9 + 4] = w11; W[i9 + 5] = w12
|
|
154
|
+
W[i9 + 6] = w02; W[i9 + 7] = w12; W[i9 + 8] = w22
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
112
158
|
// World-space AABBs for every body. Inflated by margin so contacts stay
|
|
113
159
|
// paired across small velocity jitter without recomputing per iteration.
|
|
114
160
|
updateAabbs(margin = 0.5): void {
|
|
@@ -287,36 +333,48 @@ const _scratchA = new Float32Array(16)
|
|
|
287
333
|
const _scratchB = new Float32Array(16)
|
|
288
334
|
const _scratchC = new Float32Array(16)
|
|
289
335
|
|
|
290
|
-
//
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
|
|
336
|
+
// Diagonal local inverse inertia, matching Bullet's calculateLocalInertia
|
|
337
|
+
// per shape (so behavior tracks Ammo-based MMD engines):
|
|
338
|
+
// Sphere: I = (2/5)·m·r² on every axis
|
|
339
|
+
// Box: Ix = m/12·(ly²+lz²), … with l = full extents (2·half)
|
|
340
|
+
// Capsule: Bullet's bounding-box approximation of the Y-axis capsule
|
|
341
|
+
// (lx = lz = 2r, ly = h + 2r)
|
|
342
|
+
function computeLocalInvInertia(rb: Rigidbody, out: Float32Array, o: number): void {
|
|
295
343
|
const m = rb.mass
|
|
296
|
-
if (m <= 0) return
|
|
297
|
-
let
|
|
344
|
+
if (m <= 0) return
|
|
345
|
+
let Ix: number, Iy: number, Iz: number
|
|
298
346
|
switch (rb.shape) {
|
|
299
347
|
case RigidbodyShape.Sphere: {
|
|
300
|
-
const
|
|
301
|
-
|
|
348
|
+
const I = 0.4 * m * rb.size.x * rb.size.x
|
|
349
|
+
Ix = I; Iy = I; Iz = I
|
|
302
350
|
break
|
|
303
351
|
}
|
|
304
352
|
case RigidbodyShape.Box: {
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
353
|
+
const lx2 = 4 * rb.size.x * rb.size.x
|
|
354
|
+
const ly2 = 4 * rb.size.y * rb.size.y
|
|
355
|
+
const lz2 = 4 * rb.size.z * rb.size.z
|
|
356
|
+
Ix = (m / 12) * (ly2 + lz2)
|
|
357
|
+
Iy = (m / 12) * (lx2 + lz2)
|
|
358
|
+
Iz = (m / 12) * (lx2 + ly2)
|
|
308
359
|
break
|
|
309
360
|
}
|
|
310
361
|
case RigidbodyShape.Capsule: {
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
|
|
362
|
+
const lx = 2 * rb.size.x
|
|
363
|
+
const ly = rb.size.y + 2 * rb.size.x
|
|
364
|
+
const lx2 = lx * lx
|
|
365
|
+
const ly2 = ly * ly
|
|
366
|
+
Ix = (m / 12) * (ly2 + lx2)
|
|
367
|
+
Iy = (m / 12) * (lx2 + lx2)
|
|
368
|
+
Iz = (m / 12) * (lx2 + ly2)
|
|
314
369
|
break
|
|
315
370
|
}
|
|
316
|
-
default:
|
|
317
|
-
|
|
371
|
+
default: {
|
|
372
|
+
Ix = m; Iy = m; Iz = m
|
|
373
|
+
}
|
|
318
374
|
}
|
|
319
|
-
|
|
375
|
+
out[o + 0] = Ix > 0 ? 1 / Ix : 0
|
|
376
|
+
out[o + 1] = Iy > 0 ? 1 / Iy : 0
|
|
377
|
+
out[o + 2] = Iz > 0 ? 1 / Iz : 0
|
|
320
378
|
}
|
|
321
379
|
|
|
322
380
|
function identity16(out: Float32Array, offset: number): void {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Mat4 } from "../math"
|
|
2
2
|
import type { Joint, Rigidbody } from "./types"
|
|
3
|
+
import { RigidbodyType } from "./types"
|
|
3
4
|
|
|
4
5
|
// 6DOF spring constraint trimmed to what MMD uses. Connects bodyA and bodyB
|
|
5
6
|
// via local-space anchor frames; at simulate time the world frames are
|
|
@@ -26,6 +27,11 @@ export interface SixDofSpringConstraint {
|
|
|
26
27
|
springEnabled: Uint8Array // length 6
|
|
27
28
|
springStiffness: Float32Array // length 6 (k)
|
|
28
29
|
equilibriumPoint: Float32Array// length 6, baked at setup time
|
|
30
|
+
// True for joints that close a cycle in the joint graph (e.g. the
|
|
31
|
+
// horizontal ring welds of cross-linked skirt lattices). Loop edges get
|
|
32
|
+
// reduced limit-correction rates in the solver — a cycle over-determines
|
|
33
|
+
// positions and full-rate corrections chatter (see LOOP_ERP_SCALE).
|
|
34
|
+
isLoop: boolean
|
|
29
35
|
|
|
30
36
|
// Per-substep cache. Filled by solver's setup pass once before SI iters,
|
|
31
37
|
// read by the velocity-only iter loop. None of these depend on lv/av — only
|
|
@@ -34,8 +40,8 @@ export interface SixDofSpringConstraint {
|
|
|
34
40
|
cacheLeverA: Float32Array // 3: rA = anchor − posA (world-space)
|
|
35
41
|
cacheLeverB: Float32Array // 3
|
|
36
42
|
cacheLinAxes: Float32Array // 9: 3 linear axes × xyz, world-space
|
|
37
|
-
cacheLinCrossA: Float32Array // 9: (rA × ax) per axis
|
|
38
|
-
cacheLinCrossB: Float32Array // 9
|
|
43
|
+
cacheLinCrossA: Float32Array // 9: I⁻¹A·(rA × ax) per axis
|
|
44
|
+
cacheLinCrossB: Float32Array // 9: I⁻¹B·(rB × ax) per axis
|
|
39
45
|
cacheLinJacInv: Float32Array // 3: 1/(im+im+cA²·ii+cB²·ii) per axis
|
|
40
46
|
// Limit rows are unilateral (Bullet-style): the per-substep accumulated
|
|
41
47
|
// impulse is clamped to the corrective sign, so a limit can push a body
|
|
@@ -45,21 +51,46 @@ export interface SixDofSpringConstraint {
|
|
|
45
51
|
cacheLinActive: Uint8Array // 3
|
|
46
52
|
cacheLinLimitImp: Float32Array // 3: accumulated limit impulse (per substep)
|
|
47
53
|
// Spring rows are velocity-target drives; setup clamps k to the deadbeat
|
|
48
|
-
// stability bound so they cannot pump energy.
|
|
54
|
+
// stability bound so they cannot pump energy. maxImp bounds the per-substep
|
|
55
|
+
// accumulated impulse by the real spring force (k·|err|·dt) for rows that
|
|
56
|
+
// must stay force-limited (loop-edge welds); Infinity for authored springs.
|
|
49
57
|
cacheLinSpringTarget: Float32Array // 3
|
|
58
|
+
cacheLinSpringMaxImp: Float32Array // 3
|
|
59
|
+
cacheLinSpringImp: Float32Array // 3
|
|
50
60
|
cacheLinSpringActive: Uint8Array // 3
|
|
51
61
|
cacheAngAxes: Float32Array // 9 (spring rows)
|
|
52
62
|
cacheAngTargetVel: Float32Array // 3 (spring rows)
|
|
53
63
|
cacheAngActive: Uint8Array // 3 (spring rows)
|
|
54
|
-
|
|
64
|
+
// Per-axis angular Jacobians and tensor-multiplied axes: with full inertia
|
|
65
|
+
// tensors the effective mass differs per axis, and impulse application
|
|
66
|
+
// needs I⁻¹·axis per body.
|
|
67
|
+
cacheAngJacInv: Float32Array // 3: 1/(axᵀ(I⁻¹A+I⁻¹B)ax) per axis
|
|
68
|
+
// Angular spring force clamp (|k·err|·dt) + per-substep accumulator —
|
|
69
|
+
// unclamped spring velocity-drives pump energy slowly (the restoring
|
|
70
|
+
// magnitude on derived euler axes is orientation-dependent by up to ~25%,
|
|
71
|
+
// and a mis-scaled oscillator restoring force injects per cycle).
|
|
72
|
+
cacheAngSpringMaxImp: Float32Array // 3
|
|
73
|
+
cacheAngSpringImp: Float32Array // 3
|
|
74
|
+
cacheAngWA: Float32Array // 9: I⁻¹A·axis per axis
|
|
75
|
+
cacheAngWB: Float32Array // 9
|
|
55
76
|
// Single geodesic limit row: shortest rotation from the current relative
|
|
56
77
|
// orientation to the euler-clamped target. Per-axis euler limit rows are
|
|
57
78
|
// geometrically inconsistent for large violations (asin singularity) and
|
|
58
79
|
// pump energy instead of converging. Unilateral like the linear limits.
|
|
59
80
|
cacheAngLimAxis: Float32Array // 3, world-space unit axis
|
|
81
|
+
cacheAngLimWA: Float32Array // 3: I⁻¹A·axis
|
|
82
|
+
cacheAngLimWB: Float32Array // 3
|
|
83
|
+
cacheAngLimJacInv: number
|
|
60
84
|
cacheAngLimTarget: number // target relative angular velocity along axis
|
|
61
85
|
cacheAngLimActive: number
|
|
62
86
|
cacheAngLimImp: number // accumulated impulse (per substep)
|
|
87
|
+
// Per-axis angular limit rows for the small-violation regime. act:
|
|
88
|
+
// 1 = bilateral (locked axis), 2 = unilateral stop (ranged axis) — a
|
|
89
|
+
// bilateral row on a ranged axis brakes natural recovery and pumps
|
|
90
|
+
// energy into swinging cloth.
|
|
91
|
+
cacheAngPATarget: Float32Array // 3
|
|
92
|
+
cacheAngPAActive: Uint8Array // 3
|
|
93
|
+
cacheAngPAImp: Float32Array // 3
|
|
63
94
|
}
|
|
64
95
|
|
|
65
96
|
// Stop-limit ERP. PMX rigs are tuned against MMD's stiff limit response;
|
|
@@ -141,6 +172,7 @@ export function buildConstraints(
|
|
|
141
172
|
springEnabled,
|
|
142
173
|
springStiffness,
|
|
143
174
|
equilibriumPoint: new Float32Array(6),
|
|
175
|
+
isLoop: false,
|
|
144
176
|
cacheSkip: false,
|
|
145
177
|
cacheLeverA: new Float32Array(3),
|
|
146
178
|
cacheLeverB: new Float32Array(3),
|
|
@@ -152,18 +184,55 @@ export function buildConstraints(
|
|
|
152
184
|
cacheLinActive: new Uint8Array(3),
|
|
153
185
|
cacheLinLimitImp: new Float32Array(3),
|
|
154
186
|
cacheLinSpringTarget: new Float32Array(3),
|
|
187
|
+
cacheLinSpringMaxImp: new Float32Array(3),
|
|
188
|
+
cacheLinSpringImp: new Float32Array(3),
|
|
155
189
|
cacheLinSpringActive: new Uint8Array(3),
|
|
156
190
|
cacheAngAxes: new Float32Array(9),
|
|
157
191
|
cacheAngTargetVel: new Float32Array(3),
|
|
158
192
|
cacheAngActive: new Uint8Array(3),
|
|
159
|
-
cacheAngJacInv:
|
|
193
|
+
cacheAngJacInv: new Float32Array(3),
|
|
194
|
+
cacheAngSpringMaxImp: new Float32Array(3),
|
|
195
|
+
cacheAngSpringImp: new Float32Array(3),
|
|
196
|
+
cacheAngWA: new Float32Array(9),
|
|
197
|
+
cacheAngWB: new Float32Array(9),
|
|
160
198
|
cacheAngLimAxis: new Float32Array(3),
|
|
199
|
+
cacheAngLimWA: new Float32Array(3),
|
|
200
|
+
cacheAngLimWB: new Float32Array(3),
|
|
201
|
+
cacheAngLimJacInv: 0,
|
|
161
202
|
cacheAngLimTarget: 0,
|
|
162
203
|
cacheAngLimActive: 0,
|
|
163
204
|
cacheAngLimImp: 0,
|
|
205
|
+
cacheAngPATarget: new Float32Array(3),
|
|
206
|
+
cacheAngPAActive: new Uint8Array(3),
|
|
207
|
+
cacheAngPAImp: new Float32Array(3),
|
|
164
208
|
})
|
|
165
209
|
}
|
|
166
210
|
|
|
211
|
+
// Mark loop-closing joints via union-find over the joint graph. All
|
|
212
|
+
// bone-follow bodies count as one "world" component (they're each anchored
|
|
213
|
+
// to the skeleton), so a joint bridging two separately anchored subtrees
|
|
214
|
+
// also closes a kinematic cycle. One edge per cycle gets softened, which
|
|
215
|
+
// releases the over-determination regardless of which edge it is.
|
|
216
|
+
const parent = new Int32Array(rigidbodies.length + 1)
|
|
217
|
+
for (let i = 0; i < parent.length; i++) parent[i] = i
|
|
218
|
+
const find = (x: number): number => {
|
|
219
|
+
while (parent[x] !== x) {
|
|
220
|
+
parent[x] = parent[parent[x]]
|
|
221
|
+
x = parent[x]
|
|
222
|
+
}
|
|
223
|
+
return x
|
|
224
|
+
}
|
|
225
|
+
const world = rigidbodies.length
|
|
226
|
+
for (let i = 0; i < rigidbodies.length; i++) {
|
|
227
|
+
if (rigidbodies[i].type !== RigidbodyType.Dynamic) parent[find(i)] = find(world)
|
|
228
|
+
}
|
|
229
|
+
for (const con of out) {
|
|
230
|
+
const ra = find(con.bodyA)
|
|
231
|
+
const rb = find(con.bodyB)
|
|
232
|
+
if (ra === rb) con.isLoop = true
|
|
233
|
+
else parent[ra] = rb
|
|
234
|
+
}
|
|
235
|
+
|
|
167
236
|
return out
|
|
168
237
|
}
|
|
169
238
|
|