reze-engine 0.15.0 → 0.16.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 -3
- package/dist/camera.d.ts +2 -0
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +8 -3
- package/dist/engine.d.ts +19 -4
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +294 -27
- package/dist/gpu-profile.d.ts +19 -0
- package/dist/gpu-profile.d.ts.map +1 -0
- package/dist/gpu-profile.js +120 -0
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/ik-solver.js +13 -7
- package/dist/math.d.ts +2 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +65 -46
- package/dist/model.d.ts +28 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +259 -75
- package/dist/physics/body.d.ts +2 -0
- package/dist/physics/body.d.ts.map +1 -1
- package/dist/physics/body.js +30 -0
- package/dist/physics/constraint.d.ts +13 -0
- package/dist/physics/constraint.d.ts.map +1 -1
- package/dist/physics/constraint.js +13 -0
- package/dist/physics/contact.d.ts +28 -0
- package/dist/physics/contact.d.ts.map +1 -1
- package/dist/physics/contact.js +27 -32
- package/dist/physics/physics.d.ts +3 -0
- package/dist/physics/physics.d.ts.map +1 -1
- package/dist/physics/physics.js +57 -8
- package/dist/physics/profile.d.ts +18 -0
- package/dist/physics/profile.d.ts.map +1 -0
- package/dist/physics/profile.js +44 -0
- package/dist/physics/solver.d.ts.map +1 -1
- package/dist/physics/solver.js +436 -293
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +2 -30
- 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 +3 -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_rough.js +3 -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 +2 -2
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +8 -0
- 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/face.d.ts +1 -1
- package/dist/shaders/materials/face.d.ts.map +1 -1
- package/dist/shaders/materials/face.js +4 -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/stockings.d.ts +1 -1
- package/dist/shaders/materials/stockings.d.ts.map +1 -1
- package/dist/shaders/materials/stockings.js +13 -9
- package/dist/shaders/passes/composite.d.ts +1 -1
- package/dist/shaders/passes/composite.d.ts.map +1 -1
- package/dist/shaders/passes/composite.js +18 -12
- package/dist/shaders/passes/morph.d.ts +2 -0
- package/dist/shaders/passes/morph.d.ts.map +1 -0
- package/dist/shaders/passes/morph.js +49 -0
- package/package.json +1 -1
- package/src/camera.ts +9 -3
- package/src/engine.ts +336 -29
- package/src/ik-solver.ts +14 -7
- package/src/math.ts +41 -51
- package/src/model.ts +288 -91
- package/src/physics/body.ts +29 -0
- package/src/physics/constraint.ts +30 -0
- package/src/physics/contact.ts +43 -30
- package/src/physics/physics.ts +58 -17
- package/src/physics/solver.ts +431 -284
- package/src/pmx-loader.ts +2 -35
- package/src/shaders/materials/body.ts +3 -1
- package/src/shaders/materials/cloth_rough.ts +3 -1
- package/src/shaders/materials/common.ts +8 -0
- package/src/shaders/materials/face.ts +4 -1
- package/src/shaders/materials/stockings.ts +13 -9
- package/src/shaders/passes/composite.ts +18 -12
- package/src/shaders/passes/morph.ts +50 -0
- package/dist/physics-debug.d.ts +0 -30
- package/dist/physics-debug.d.ts.map +0 -1
- package/dist/physics-debug.js +0 -526
- package/dist/shaders/passes/physics-debug.d.ts +0 -2
- package/dist/shaders/passes/physics-debug.d.ts.map +0 -1
- package/dist/shaders/passes/physics-debug.js +0 -69
package/src/physics/contact.ts
CHANGED
|
@@ -37,27 +37,49 @@ export interface Contact {
|
|
|
37
37
|
appliedNormalImpulse: number
|
|
38
38
|
appliedFrictionImpulse1: number
|
|
39
39
|
appliedFrictionImpulse2: number
|
|
40
|
+
|
|
41
|
+
// Per-substep cache. Written by the solver's setup pass, read by iter.
|
|
42
|
+
// Normal row:
|
|
43
|
+
cAxN: number; cAyN: number; cAzN: number // rA × n
|
|
44
|
+
cBxN: number; cByN: number; cBzN: number // rB × n
|
|
45
|
+
jacInvN: number
|
|
46
|
+
bounceVel: number // restitution reference, captured at setup from initial relVelN
|
|
47
|
+
// Friction tangent 1:
|
|
48
|
+
t1x: number; t1y: number; t1z: number
|
|
49
|
+
cAxT1: number; cAyT1: number; cAzT1: number
|
|
50
|
+
cBxT1: number; cByT1: number; cBzT1: number
|
|
51
|
+
jacInvT1: number
|
|
52
|
+
// Friction tangent 2:
|
|
53
|
+
t2x: number; t2y: number; t2z: number
|
|
54
|
+
cAxT2: number; cAyT2: number; cAzT2: number
|
|
55
|
+
cBxT2: number; cByT2: number; cBzT2: number
|
|
56
|
+
jacInvT2: number
|
|
40
57
|
}
|
|
41
58
|
|
|
42
59
|
function makeContact(): Contact {
|
|
43
60
|
return {
|
|
44
|
-
bodyA: 0,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
rAz: 0,
|
|
49
|
-
rBx: 0,
|
|
50
|
-
rBy: 0,
|
|
51
|
-
rBz: 0,
|
|
52
|
-
nx: 0,
|
|
53
|
-
ny: 0,
|
|
54
|
-
nz: 0,
|
|
61
|
+
bodyA: 0, bodyB: 0,
|
|
62
|
+
rAx: 0, rAy: 0, rAz: 0,
|
|
63
|
+
rBx: 0, rBy: 0, rBz: 0,
|
|
64
|
+
nx: 0, ny: 0, nz: 0,
|
|
55
65
|
depth: 0,
|
|
56
66
|
friction: 0,
|
|
57
67
|
restitution: 0,
|
|
58
68
|
appliedNormalImpulse: 0,
|
|
59
69
|
appliedFrictionImpulse1: 0,
|
|
60
70
|
appliedFrictionImpulse2: 0,
|
|
71
|
+
cAxN: 0, cAyN: 0, cAzN: 0,
|
|
72
|
+
cBxN: 0, cByN: 0, cBzN: 0,
|
|
73
|
+
jacInvN: 0,
|
|
74
|
+
bounceVel: 0,
|
|
75
|
+
t1x: 0, t1y: 0, t1z: 0,
|
|
76
|
+
cAxT1: 0, cAyT1: 0, cAzT1: 0,
|
|
77
|
+
cBxT1: 0, cByT1: 0, cBzT1: 0,
|
|
78
|
+
jacInvT1: 0,
|
|
79
|
+
t2x: 0, t2y: 0, t2z: 0,
|
|
80
|
+
cAxT2: 0, cAyT2: 0, cAzT2: 0,
|
|
81
|
+
cBxT2: 0, cByT2: 0, cBzT2: 0,
|
|
82
|
+
jacInvT2: 0,
|
|
61
83
|
}
|
|
62
84
|
}
|
|
63
85
|
|
|
@@ -958,26 +980,17 @@ function flipLastNormal(pool: ContactPool): void {
|
|
|
958
980
|
c.nz = -c.nz
|
|
959
981
|
}
|
|
960
982
|
|
|
961
|
-
//
|
|
962
|
-
//
|
|
963
|
-
//
|
|
964
|
-
//
|
|
983
|
+
// Iterate the prebuilt candidate-pair list and AABB-test each pair. The
|
|
984
|
+
// static-static and group/mask filters were applied once at construction —
|
|
985
|
+
// see RigidBodyStore.getCollisionPairs. SAP / dynamic AABB tree pay off
|
|
986
|
+
// above ~500 bodies; below that this flat sweep wins on cache locality.
|
|
965
987
|
export function findContacts(store: RigidBodyStore, pool: ContactPool): void {
|
|
966
988
|
store.updateAabbs()
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
const gi = group[i]
|
|
974
|
-
const mi = mask[i]
|
|
975
|
-
const dynA = invMass[i] > 0
|
|
976
|
-
for (let j = i + 1; j < N; j++) {
|
|
977
|
-
if (!dynA && invMass[j] === 0) continue // both static — no resolution
|
|
978
|
-
if ((mi & group[j]) === 0 || (mask[j] & gi) === 0) continue
|
|
979
|
-
if (!aabbOverlap(store, i, j)) continue
|
|
980
|
-
generateContacts(store, i, j, pool)
|
|
981
|
-
}
|
|
989
|
+
const pairs = store.getCollisionPairs()
|
|
990
|
+
for (let p = 0; p < pairs.length; p += 2) {
|
|
991
|
+
const i = pairs[p]
|
|
992
|
+
const j = pairs[p + 1]
|
|
993
|
+
if (!aabbOverlap(store, i, j)) continue
|
|
994
|
+
generateContacts(store, i, j, pool)
|
|
982
995
|
}
|
|
983
996
|
}
|
package/src/physics/physics.ts
CHANGED
|
@@ -21,11 +21,16 @@ export class RezePhysics {
|
|
|
21
21
|
private constraints: SixDofSpringConstraint[]
|
|
22
22
|
private contacts: ContactPool
|
|
23
23
|
private firstFrame = true
|
|
24
|
-
// Fixed-timestep accumulator: physics runs at 75 Hz regardless of render
|
|
25
|
-
// rate, so spring impulse, damping, and integration are deterministic.
|
|
26
24
|
private timeAccum = 0
|
|
27
|
-
private readonly fixedTimeStep = 1 /
|
|
28
|
-
private readonly maxSubSteps =
|
|
25
|
+
private readonly fixedTimeStep = 1 / 60
|
|
26
|
+
private readonly maxSubSteps = 6
|
|
27
|
+
|
|
28
|
+
// Fixed-timestep render interpolation ("Fix Your Timestep"): the dynamic body pose is
|
|
29
|
+
// rendered as lerp(prev, curr, alpha) between the last two completed substeps, where
|
|
30
|
+
// alpha = leftover accumulator / fixedTimeStep. Removes the temporal aliasing that shows
|
|
31
|
+
// as hair/cloth judder when the render rate doesn't line up with the 60Hz physics step.
|
|
32
|
+
private prevPositions: Float32Array
|
|
33
|
+
private prevOrientations: Float32Array
|
|
29
34
|
|
|
30
35
|
constructor(rigidbodies: Rigidbody[], joints: Joint[] = []) {
|
|
31
36
|
this.rigidbodies = rigidbodies
|
|
@@ -34,6 +39,14 @@ export class RezePhysics {
|
|
|
34
39
|
this.world = new World(new Vec3(0, -98, 0))
|
|
35
40
|
this.constraints = buildConstraints(rigidbodies, joints)
|
|
36
41
|
this.contacts = new ContactPool()
|
|
42
|
+
this.prevPositions = new Float32Array(this.store.count * 3)
|
|
43
|
+
this.prevOrientations = new Float32Array(this.store.count * 4)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Snapshot the current body pose as the interpolation "previous" state.
|
|
47
|
+
private savePrevState(): void {
|
|
48
|
+
this.prevPositions.set(this.store.positions)
|
|
49
|
+
this.prevOrientations.set(this.store.orientations)
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
setGravity(gravity: Vec3): void {
|
|
@@ -72,6 +85,8 @@ export class RezePhysics {
|
|
|
72
85
|
reset(boneWorldMatrices: Mat4[]): void {
|
|
73
86
|
if (this.firstFrame) return
|
|
74
87
|
this.snapBodiesToBones(boneWorldMatrices)
|
|
88
|
+
this.savePrevState() // prev == curr after a snap, so no interpolation jump
|
|
89
|
+
this.timeAccum = 0
|
|
75
90
|
}
|
|
76
91
|
|
|
77
92
|
step(dt: number, boneWorldMatrices: Mat4[], boneInverseBindMatrices: Float32Array): void {
|
|
@@ -80,6 +95,7 @@ export class RezePhysics {
|
|
|
80
95
|
// Start at current bone pose, not the PMX bind pose, so animations
|
|
81
96
|
// that skip frame 0 don't pop bodies on first step.
|
|
82
97
|
this.snapBodiesToBones(boneWorldMatrices)
|
|
98
|
+
this.savePrevState()
|
|
83
99
|
this.firstFrame = false
|
|
84
100
|
}
|
|
85
101
|
|
|
@@ -89,17 +105,21 @@ export class RezePhysics {
|
|
|
89
105
|
this.syncFromBones(boneWorldMatrices, dt)
|
|
90
106
|
|
|
91
107
|
// Fixed-timestep substeps. The maxSubSteps cap prevents runaway after
|
|
92
|
-
// a long stall (tab backgrounded, etc.).
|
|
108
|
+
// a long stall (tab backgrounded, etc.). Snapshot the pose before each step so
|
|
109
|
+
// after the loop prevState is one substep behind the live (current) state.
|
|
93
110
|
this.timeAccum += dt
|
|
94
111
|
let sub = 0
|
|
95
112
|
while (this.timeAccum >= this.fixedTimeStep && sub < this.maxSubSteps) {
|
|
113
|
+
this.savePrevState()
|
|
96
114
|
this.world.step(this.store, this.constraints, this.contacts, this.fixedTimeStep)
|
|
97
115
|
this.timeAccum -= this.fixedTimeStep
|
|
98
116
|
sub++
|
|
99
117
|
}
|
|
100
118
|
if (sub === this.maxSubSteps) this.timeAccum = 0
|
|
101
119
|
|
|
102
|
-
|
|
120
|
+
// Fraction into the next (not-yet-taken) step; always in [0, 1).
|
|
121
|
+
const alpha = this.fixedTimeStep > 0 ? this.timeAccum / this.fixedTimeStep : 0
|
|
122
|
+
this.applyDynamicsToBones(boneWorldMatrices, alpha)
|
|
103
123
|
}
|
|
104
124
|
|
|
105
125
|
// Snap all bone-bound bodies to boneWorld × bodyOffset, zero velocities.
|
|
@@ -219,13 +239,18 @@ export class RezePhysics {
|
|
|
219
239
|
|
|
220
240
|
// Dynamic bodies write their transform back to the bone matrix:
|
|
221
241
|
// boneWorld = bodyWorld × bodyOffsetInverse.
|
|
222
|
-
|
|
242
|
+
// The body pose is the render-interpolated pose between the previous and current
|
|
243
|
+
// substep states (alpha = fraction into the next step), which removes fixed-step judder.
|
|
244
|
+
private applyDynamicsToBones(boneWorldMatrices: Mat4[], alpha: number): void {
|
|
223
245
|
const N = this.store.count
|
|
224
246
|
const inv = this.store.bodyOffsetInverse
|
|
225
247
|
const positions = this.store.positions
|
|
226
248
|
const orientations = this.store.orientations
|
|
249
|
+
const prevPos = this.prevPositions
|
|
250
|
+
const prevOri = this.prevOrientations
|
|
227
251
|
const types = this.store.type
|
|
228
252
|
const boneIdx = this.store.boneIndex
|
|
253
|
+
const oneMinus = 1 - alpha
|
|
229
254
|
|
|
230
255
|
for (let i = 0; i < N; i++) {
|
|
231
256
|
if (types[i] !== RigidbodyType.Dynamic) continue
|
|
@@ -234,16 +259,32 @@ export class RezePhysics {
|
|
|
234
259
|
|
|
235
260
|
const i3 = i * 3
|
|
236
261
|
const i4 = i * 4
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
262
|
+
|
|
263
|
+
// Position: straight lerp.
|
|
264
|
+
const px = prevPos[i3 + 0] * oneMinus + positions[i3 + 0] * alpha
|
|
265
|
+
const py = prevPos[i3 + 1] * oneMinus + positions[i3 + 1] * alpha
|
|
266
|
+
const pz = prevPos[i3 + 2] * oneMinus + positions[i3 + 2] * alpha
|
|
267
|
+
|
|
268
|
+
// Orientation: shortest-arc nlerp (bodies rotate little per fixed step, so nlerp
|
|
269
|
+
// tracks slerp closely and avoids the trig).
|
|
270
|
+
const ax = prevOri[i4 + 0], ay = prevOri[i4 + 1], az = prevOri[i4 + 2], aw = prevOri[i4 + 3]
|
|
271
|
+
let bx = orientations[i4 + 0], by = orientations[i4 + 1], bz = orientations[i4 + 2], bw = orientations[i4 + 3]
|
|
272
|
+
if (ax * bx + ay * by + az * bz + aw * bw < 0) {
|
|
273
|
+
bx = -bx; by = -by; bz = -bz; bw = -bw
|
|
274
|
+
}
|
|
275
|
+
let qx = ax * oneMinus + bx * alpha
|
|
276
|
+
let qy = ay * oneMinus + by * alpha
|
|
277
|
+
let qz = az * oneMinus + bz * alpha
|
|
278
|
+
let qw = aw * oneMinus + bw * alpha
|
|
279
|
+
const ql = Math.sqrt(qx * qx + qy * qy + qz * qz + qw * qw)
|
|
280
|
+
if (ql > 0) {
|
|
281
|
+
const invL = 1 / ql
|
|
282
|
+
qx *= invL; qy *= invL; qz *= invL; qw *= invL
|
|
283
|
+
} else {
|
|
284
|
+
qx = 0; qy = 0; qz = 0; qw = 1
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
Mat4.fromPositionRotationInto(px, py, pz, qx, qy, qz, qw, _bodyMat)
|
|
247
288
|
Mat4.multiplyArrays(_bodyMat, 0, inv, i * 16, _boneMat, 0)
|
|
248
289
|
|
|
249
290
|
// Sanity gate against NaN / extreme values — silently drop the update.
|