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/dist/physics/contact.js
CHANGED
|
@@ -14,23 +14,28 @@ import { RigidbodyShape } from "./types";
|
|
|
14
14
|
export const CONTACT_MARGIN = 0.04;
|
|
15
15
|
function makeContact() {
|
|
16
16
|
return {
|
|
17
|
-
bodyA: 0,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
rAz: 0,
|
|
22
|
-
rBx: 0,
|
|
23
|
-
rBy: 0,
|
|
24
|
-
rBz: 0,
|
|
25
|
-
nx: 0,
|
|
26
|
-
ny: 0,
|
|
27
|
-
nz: 0,
|
|
17
|
+
bodyA: 0, bodyB: 0,
|
|
18
|
+
rAx: 0, rAy: 0, rAz: 0,
|
|
19
|
+
rBx: 0, rBy: 0, rBz: 0,
|
|
20
|
+
nx: 0, ny: 0, nz: 0,
|
|
28
21
|
depth: 0,
|
|
29
22
|
friction: 0,
|
|
30
23
|
restitution: 0,
|
|
31
24
|
appliedNormalImpulse: 0,
|
|
32
25
|
appliedFrictionImpulse1: 0,
|
|
33
26
|
appliedFrictionImpulse2: 0,
|
|
27
|
+
cAxN: 0, cAyN: 0, cAzN: 0,
|
|
28
|
+
cBxN: 0, cByN: 0, cBzN: 0,
|
|
29
|
+
jacInvN: 0,
|
|
30
|
+
bounceVel: 0,
|
|
31
|
+
t1x: 0, t1y: 0, t1z: 0,
|
|
32
|
+
cAxT1: 0, cAyT1: 0, cAzT1: 0,
|
|
33
|
+
cBxT1: 0, cByT1: 0, cBzT1: 0,
|
|
34
|
+
jacInvT1: 0,
|
|
35
|
+
t2x: 0, t2y: 0, t2z: 0,
|
|
36
|
+
cAxT2: 0, cAyT2: 0, cAzT2: 0,
|
|
37
|
+
cBxT2: 0, cByT2: 0, cBzT2: 0,
|
|
38
|
+
jacInvT2: 0,
|
|
34
39
|
};
|
|
35
40
|
}
|
|
36
41
|
// Pool of reusable Contact objects.
|
|
@@ -701,28 +706,18 @@ function flipLastNormal(pool) {
|
|
|
701
706
|
c.ny = -c.ny;
|
|
702
707
|
c.nz = -c.nz;
|
|
703
708
|
}
|
|
704
|
-
//
|
|
705
|
-
//
|
|
706
|
-
//
|
|
707
|
-
//
|
|
709
|
+
// Iterate the prebuilt candidate-pair list and AABB-test each pair. The
|
|
710
|
+
// static-static and group/mask filters were applied once at construction —
|
|
711
|
+
// see RigidBodyStore.getCollisionPairs. SAP / dynamic AABB tree pay off
|
|
712
|
+
// above ~500 bodies; below that this flat sweep wins on cache locality.
|
|
708
713
|
export function findContacts(store, pool) {
|
|
709
714
|
store.updateAabbs();
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
const dynA = invMass[i] > 0;
|
|
718
|
-
for (let j = i + 1; j < N; j++) {
|
|
719
|
-
if (!dynA && invMass[j] === 0)
|
|
720
|
-
continue; // both static — no resolution
|
|
721
|
-
if ((mi & group[j]) === 0 || (mask[j] & gi) === 0)
|
|
722
|
-
continue;
|
|
723
|
-
if (!aabbOverlap(store, i, j))
|
|
724
|
-
continue;
|
|
725
|
-
generateContacts(store, i, j, pool);
|
|
726
|
-
}
|
|
715
|
+
const pairs = store.getCollisionPairs();
|
|
716
|
+
for (let p = 0; p < pairs.length; p += 2) {
|
|
717
|
+
const i = pairs[p];
|
|
718
|
+
const j = pairs[p + 1];
|
|
719
|
+
if (!aabbOverlap(store, i, j))
|
|
720
|
+
continue;
|
|
721
|
+
generateContacts(store, i, j, pool);
|
|
727
722
|
}
|
|
728
723
|
}
|
|
@@ -12,7 +12,10 @@ export declare class RezePhysics {
|
|
|
12
12
|
private timeAccum;
|
|
13
13
|
private readonly fixedTimeStep;
|
|
14
14
|
private readonly maxSubSteps;
|
|
15
|
+
private prevPositions;
|
|
16
|
+
private prevOrientations;
|
|
15
17
|
constructor(rigidbodies: Rigidbody[], joints?: Joint[]);
|
|
18
|
+
private savePrevState;
|
|
16
19
|
setGravity(gravity: Vec3): void;
|
|
17
20
|
getGravity(): Vec3;
|
|
18
21
|
getRigidbodies(): Rigidbody[];
|
|
@@ -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;
|
|
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;gBAE1B,WAAW,EAAE,SAAS,EAAE,EAAE,MAAM,GAAE,KAAK,EAAO;IAY1D,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;IAOtC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,EAAE,uBAAuB,EAAE,YAAY,GAAG,IAAI;IAkCxF,OAAO,CAAC,iBAAiB;IAuCzB,OAAO,CAAC,aAAa;IA+ErB,OAAO,CAAC,oBAAoB;CAoD7B"}
|
package/dist/physics/physics.js
CHANGED
|
@@ -13,17 +13,22 @@ const _scratchQuat = new Quat(0, 0, 0, 1);
|
|
|
13
13
|
export class RezePhysics {
|
|
14
14
|
constructor(rigidbodies, joints = []) {
|
|
15
15
|
this.firstFrame = true;
|
|
16
|
-
// Fixed-timestep accumulator: physics runs at 75 Hz regardless of render
|
|
17
|
-
// rate, so spring impulse, damping, and integration are deterministic.
|
|
18
16
|
this.timeAccum = 0;
|
|
19
|
-
this.fixedTimeStep = 1 /
|
|
20
|
-
this.maxSubSteps =
|
|
17
|
+
this.fixedTimeStep = 1 / 60;
|
|
18
|
+
this.maxSubSteps = 6;
|
|
21
19
|
this.rigidbodies = rigidbodies;
|
|
22
20
|
this.joints = joints;
|
|
23
21
|
this.store = new RigidBodyStore(rigidbodies);
|
|
24
22
|
this.world = new World(new Vec3(0, -98, 0));
|
|
25
23
|
this.constraints = buildConstraints(rigidbodies, joints);
|
|
26
24
|
this.contacts = new ContactPool();
|
|
25
|
+
this.prevPositions = new Float32Array(this.store.count * 3);
|
|
26
|
+
this.prevOrientations = new Float32Array(this.store.count * 4);
|
|
27
|
+
}
|
|
28
|
+
// Snapshot the current body pose as the interpolation "previous" state.
|
|
29
|
+
savePrevState() {
|
|
30
|
+
this.prevPositions.set(this.store.positions);
|
|
31
|
+
this.prevOrientations.set(this.store.orientations);
|
|
27
32
|
}
|
|
28
33
|
setGravity(gravity) {
|
|
29
34
|
this.world.setGravity(gravity);
|
|
@@ -60,6 +65,8 @@ export class RezePhysics {
|
|
|
60
65
|
if (this.firstFrame)
|
|
61
66
|
return;
|
|
62
67
|
this.snapBodiesToBones(boneWorldMatrices);
|
|
68
|
+
this.savePrevState(); // prev == curr after a snap, so no interpolation jump
|
|
69
|
+
this.timeAccum = 0;
|
|
63
70
|
}
|
|
64
71
|
step(dt, boneWorldMatrices, boneInverseBindMatrices) {
|
|
65
72
|
if (this.firstFrame) {
|
|
@@ -67,6 +74,7 @@ export class RezePhysics {
|
|
|
67
74
|
// Start at current bone pose, not the PMX bind pose, so animations
|
|
68
75
|
// that skip frame 0 don't pop bodies on first step.
|
|
69
76
|
this.snapBodiesToBones(boneWorldMatrices);
|
|
77
|
+
this.savePrevState();
|
|
70
78
|
this.firstFrame = false;
|
|
71
79
|
}
|
|
72
80
|
// Sync once per render frame; kinematic targets don't change between
|
|
@@ -74,17 +82,21 @@ export class RezePhysics {
|
|
|
74
82
|
// bone-pose delta so joints feel the kinematic motion.
|
|
75
83
|
this.syncFromBones(boneWorldMatrices, dt);
|
|
76
84
|
// Fixed-timestep substeps. The maxSubSteps cap prevents runaway after
|
|
77
|
-
// a long stall (tab backgrounded, etc.).
|
|
85
|
+
// a long stall (tab backgrounded, etc.). Snapshot the pose before each step so
|
|
86
|
+
// after the loop prevState is one substep behind the live (current) state.
|
|
78
87
|
this.timeAccum += dt;
|
|
79
88
|
let sub = 0;
|
|
80
89
|
while (this.timeAccum >= this.fixedTimeStep && sub < this.maxSubSteps) {
|
|
90
|
+
this.savePrevState();
|
|
81
91
|
this.world.step(this.store, this.constraints, this.contacts, this.fixedTimeStep);
|
|
82
92
|
this.timeAccum -= this.fixedTimeStep;
|
|
83
93
|
sub++;
|
|
84
94
|
}
|
|
85
95
|
if (sub === this.maxSubSteps)
|
|
86
96
|
this.timeAccum = 0;
|
|
87
|
-
|
|
97
|
+
// Fraction into the next (not-yet-taken) step; always in [0, 1).
|
|
98
|
+
const alpha = this.fixedTimeStep > 0 ? this.timeAccum / this.fixedTimeStep : 0;
|
|
99
|
+
this.applyDynamicsToBones(boneWorldMatrices, alpha);
|
|
88
100
|
}
|
|
89
101
|
// Snap all bone-bound bodies to boneWorld × bodyOffset, zero velocities.
|
|
90
102
|
snapBodiesToBones(boneWorldMatrices) {
|
|
@@ -185,13 +197,18 @@ export class RezePhysics {
|
|
|
185
197
|
}
|
|
186
198
|
// Dynamic bodies write their transform back to the bone matrix:
|
|
187
199
|
// boneWorld = bodyWorld × bodyOffsetInverse.
|
|
188
|
-
|
|
200
|
+
// The body pose is the render-interpolated pose between the previous and current
|
|
201
|
+
// substep states (alpha = fraction into the next step), which removes fixed-step judder.
|
|
202
|
+
applyDynamicsToBones(boneWorldMatrices, alpha) {
|
|
189
203
|
const N = this.store.count;
|
|
190
204
|
const inv = this.store.bodyOffsetInverse;
|
|
191
205
|
const positions = this.store.positions;
|
|
192
206
|
const orientations = this.store.orientations;
|
|
207
|
+
const prevPos = this.prevPositions;
|
|
208
|
+
const prevOri = this.prevOrientations;
|
|
193
209
|
const types = this.store.type;
|
|
194
210
|
const boneIdx = this.store.boneIndex;
|
|
211
|
+
const oneMinus = 1 - alpha;
|
|
195
212
|
for (let i = 0; i < N; i++) {
|
|
196
213
|
if (types[i] !== RigidbodyType.Dynamic)
|
|
197
214
|
continue;
|
|
@@ -200,7 +217,39 @@ export class RezePhysics {
|
|
|
200
217
|
continue;
|
|
201
218
|
const i3 = i * 3;
|
|
202
219
|
const i4 = i * 4;
|
|
203
|
-
|
|
220
|
+
// Position: straight lerp.
|
|
221
|
+
const px = prevPos[i3 + 0] * oneMinus + positions[i3 + 0] * alpha;
|
|
222
|
+
const py = prevPos[i3 + 1] * oneMinus + positions[i3 + 1] * alpha;
|
|
223
|
+
const pz = prevPos[i3 + 2] * oneMinus + positions[i3 + 2] * alpha;
|
|
224
|
+
// Orientation: shortest-arc nlerp (bodies rotate little per fixed step, so nlerp
|
|
225
|
+
// tracks slerp closely and avoids the trig).
|
|
226
|
+
const ax = prevOri[i4 + 0], ay = prevOri[i4 + 1], az = prevOri[i4 + 2], aw = prevOri[i4 + 3];
|
|
227
|
+
let bx = orientations[i4 + 0], by = orientations[i4 + 1], bz = orientations[i4 + 2], bw = orientations[i4 + 3];
|
|
228
|
+
if (ax * bx + ay * by + az * bz + aw * bw < 0) {
|
|
229
|
+
bx = -bx;
|
|
230
|
+
by = -by;
|
|
231
|
+
bz = -bz;
|
|
232
|
+
bw = -bw;
|
|
233
|
+
}
|
|
234
|
+
let qx = ax * oneMinus + bx * alpha;
|
|
235
|
+
let qy = ay * oneMinus + by * alpha;
|
|
236
|
+
let qz = az * oneMinus + bz * alpha;
|
|
237
|
+
let qw = aw * oneMinus + bw * alpha;
|
|
238
|
+
const ql = Math.sqrt(qx * qx + qy * qy + qz * qz + qw * qw);
|
|
239
|
+
if (ql > 0) {
|
|
240
|
+
const invL = 1 / ql;
|
|
241
|
+
qx *= invL;
|
|
242
|
+
qy *= invL;
|
|
243
|
+
qz *= invL;
|
|
244
|
+
qw *= invL;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
qx = 0;
|
|
248
|
+
qy = 0;
|
|
249
|
+
qz = 0;
|
|
250
|
+
qw = 1;
|
|
251
|
+
}
|
|
252
|
+
Mat4.fromPositionRotationInto(px, py, pz, qx, qy, qz, qw, _bodyMat);
|
|
204
253
|
Mat4.multiplyArrays(_bodyMat, 0, inv, i * 16, _boneMat, 0);
|
|
205
254
|
// Sanity gate against NaN / extreme values — silently drop the update.
|
|
206
255
|
if (Number.isFinite(_boneMat[0]) && Math.abs(_boneMat[0]) < 1e6) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
declare class PhysicsProfiler {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
private sums;
|
|
4
|
+
private counts;
|
|
5
|
+
begin(): number;
|
|
6
|
+
end(label: string, t0: number): void;
|
|
7
|
+
record(label: string, ms: number): void;
|
|
8
|
+
snapshot(): Record<string, {
|
|
9
|
+
avgMs: number;
|
|
10
|
+
calls: number;
|
|
11
|
+
totalMs: number;
|
|
12
|
+
}>;
|
|
13
|
+
reset(): void;
|
|
14
|
+
}
|
|
15
|
+
export declare const profiler: PhysicsProfiler;
|
|
16
|
+
export type ProfileSnapshot = ReturnType<PhysicsProfiler["snapshot"]>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/physics/profile.ts"],"names":[],"mappings":"AAIA,cAAM,eAAe;IACnB,OAAO,UAAQ;IACf,OAAO,CAAC,IAAI,CAA8C;IAC1D,OAAO,CAAC,MAAM,CAA8C;IAE5D,KAAK,IAAI,MAAM;IAIf,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IASpC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAQvC,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAU7E,KAAK,IAAI,IAAI;CAId;AAED,eAAO,MAAM,QAAQ,iBAAwB,CAAA;AAC7C,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Per-phase wall-clock accumulator. Off by default — when `enabled` is false,
|
|
2
|
+
// begin()/end() short-circuit so production has zero overhead. Toggle via
|
|
3
|
+
// `RezePhysics.setProfileEnabled(true)` and read with `getProfile()`.
|
|
4
|
+
class PhysicsProfiler {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.enabled = false;
|
|
7
|
+
this.sums = Object.create(null);
|
|
8
|
+
this.counts = Object.create(null);
|
|
9
|
+
}
|
|
10
|
+
begin() {
|
|
11
|
+
return this.enabled ? performance.now() : 0;
|
|
12
|
+
}
|
|
13
|
+
end(label, t0) {
|
|
14
|
+
if (!this.enabled)
|
|
15
|
+
return;
|
|
16
|
+
const dt = performance.now() - t0;
|
|
17
|
+
this.sums[label] = (this.sums[label] ?? 0) + dt;
|
|
18
|
+
this.counts[label] = (this.counts[label] ?? 0) + 1;
|
|
19
|
+
}
|
|
20
|
+
// Direct accumulator for durations not measured via begin()/end() — used by
|
|
21
|
+
// GPU timestamp readback, which arrives async after the frame has ended.
|
|
22
|
+
record(label, ms) {
|
|
23
|
+
if (!this.enabled)
|
|
24
|
+
return;
|
|
25
|
+
this.sums[label] = (this.sums[label] ?? 0) + ms;
|
|
26
|
+
this.counts[label] = (this.counts[label] ?? 0) + 1;
|
|
27
|
+
}
|
|
28
|
+
// Snapshot since last reset(). avgMs is per call; totalMs is the cumulative
|
|
29
|
+
// window cost — divide by elapsed wall time to get a "% of frame budget".
|
|
30
|
+
snapshot() {
|
|
31
|
+
const out = {};
|
|
32
|
+
for (const k in this.sums) {
|
|
33
|
+
const total = this.sums[k];
|
|
34
|
+
const calls = this.counts[k];
|
|
35
|
+
out[k] = { avgMs: calls > 0 ? total / calls : 0, calls, totalMs: total };
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
reset() {
|
|
40
|
+
this.sums = Object.create(null);
|
|
41
|
+
this.counts = Object.create(null);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export const profiler = new PhysicsProfiler();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solver.d.ts","sourceRoot":"","sources":["../../src/physics/solver.ts"],"names":[],"mappings":"
|
|
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;AAWrD,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,CAyBN"}
|