@woosh/meep-engine 2.147.0 → 2.148.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/package.json +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts +4 -4
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.js +48 -52
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts +23 -21
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.js +41 -406
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts +5 -4
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js +400 -395
- package/src/engine/physics/body/SolverBodyState.d.ts +142 -0
- package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -0
- package/src/engine/physics/body/SolverBodyState.js +251 -0
- package/src/engine/physics/broadphase/generate_pairs.d.ts +2 -1
- package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -1
- package/src/engine/physics/broadphase/generate_pairs.js +5 -3
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +691 -673
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +21 -18
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +223 -91
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +23 -0
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
- package/src/engine/physics/inertia/world_inverse_inertia.js +116 -77
- package/src/engine/physics/integration/integrate_position.d.ts +11 -1
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_position.js +97 -79
- package/src/engine/physics/integration/integrate_velocity.d.ts +12 -3
- package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_velocity.js +201 -160
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +750 -665
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_triangle_contact.js +4 -34
- package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts +16 -0
- package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/clip_against_axis_uv.js +49 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +24 -3
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +7 -4
- package/src/engine/physics/solver/solve_contacts.d.ts +2 -2
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +1341 -1173
|
@@ -2,19 +2,25 @@ import { assert } from "../../../core/assert.js";
|
|
|
2
2
|
import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js";
|
|
3
3
|
import { BVH } from "../../../core/bvh2/bvh3/BVH.js";
|
|
4
4
|
import Signal from "../../../core/events/signal/Signal.js";
|
|
5
|
+
import { returnTrue } from "../../../core/function/returnTrue.js";
|
|
6
|
+
import { aabb3_transform_oriented } from "../../../core/geom/3d/aabb/aabb3_transform_oriented.js";
|
|
5
7
|
import Vector3 from "../../../core/geom/Vector3.js";
|
|
6
8
|
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
|
|
7
9
|
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
|
|
8
10
|
import { System } from "../../ecs/System.js";
|
|
9
11
|
import { Transform } from "../../ecs/transform/Transform.js";
|
|
12
|
+
import { Interpolated } from "../../interpolation/Interpolated.js";
|
|
10
13
|
import { body_id_index, BodyStorage } from "../body/BodyStorage.js";
|
|
11
|
-
import {
|
|
14
|
+
import { SBS_STRIDE, SolverBodyState } from "../body/SolverBodyState.js";
|
|
12
15
|
import { compute_fat_world_aabb } from "../broadphase/compute_fat_world_aabb.js";
|
|
13
16
|
import { generate_pairs } from "../broadphase/generate_pairs.js";
|
|
14
17
|
import { PairList } from "../broadphase/PairList.js";
|
|
15
|
-
import {
|
|
18
|
+
import { ccd_resolve } from "../ccd/linear_sweep.js";
|
|
19
|
+
import { solve_joints } from "../constraint/solve_constraints.js";
|
|
20
|
+
import { ManifoldStore } from "../contact/ManifoldStore.js";
|
|
16
21
|
import { ContactEventBuffer, ContactEventKind } from "../events/ContactEventBuffer.js";
|
|
17
22
|
import { diff_manifolds } from "../events/diff_manifolds.js";
|
|
23
|
+
import { world_inverse_inertia_apply } from "../inertia/world_inverse_inertia.js";
|
|
18
24
|
import { integrate_position } from "../integration/integrate_position.js";
|
|
19
25
|
import { integrate_velocity_forces, integrate_velocity_gravity } from "../integration/integrate_velocity.js";
|
|
20
26
|
import { IslandBuilder } from "../island/IslandBuilder.js";
|
|
@@ -22,33 +28,28 @@ import { narrowphase_step } from "../narrowphase/narrowphase_step.js";
|
|
|
22
28
|
import { overlap_shape as overlap_shape_query } from "../queries/overlap_shape.js";
|
|
23
29
|
import { raycast as raycast_query } from "../queries/raycast.js";
|
|
24
30
|
import { shape_cast as shape_cast_query } from "../queries/shape_cast.js";
|
|
25
|
-
import { ccd_resolve } from "../ccd/linear_sweep.js";
|
|
26
|
-
import { returnTrue } from "../../../core/function/returnTrue.js";
|
|
27
31
|
import {
|
|
32
|
+
apply_restitution,
|
|
28
33
|
prepare_contacts,
|
|
29
|
-
refresh_contacts,
|
|
30
34
|
redetect_concave_contacts,
|
|
31
|
-
|
|
32
|
-
solve_velocity,
|
|
33
|
-
apply_restitution,
|
|
35
|
+
refresh_contacts,
|
|
34
36
|
solve_position,
|
|
37
|
+
solve_velocity,
|
|
38
|
+
warm_start_contacts,
|
|
35
39
|
} from "../solver/solve_contacts.js";
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
40
|
+
import { BodyKind } from "./BodyKind.js";
|
|
41
|
+
import { Collider, COLLIDER_UNBOUND } from "./Collider.js";
|
|
42
|
+
import { JOINT_UNALLOCATED, JOINT_WORLD } from "./Joint.js";
|
|
39
43
|
import { PhysicsEvents } from "./PhysicsEvents.js";
|
|
44
|
+
import { RIGID_BODY_UNALLOCATED, RigidBody } from "./RigidBody.js";
|
|
45
|
+
import { RigidBodyFlags } from "./RigidBodyFlags.js";
|
|
46
|
+
import { SleepState } from "./SleepState.js";
|
|
40
47
|
|
|
41
48
|
/**
|
|
42
49
|
* Scratch for {@link applyImpulseAt}'s angular delta calculation.
|
|
43
50
|
* @type {Float64Array}
|
|
44
51
|
*/
|
|
45
52
|
const scratch_angular_delta = new Float64Array(3);
|
|
46
|
-
import { BodyKind } from "./BodyKind.js";
|
|
47
|
-
import { Collider, COLLIDER_UNBOUND } from "./Collider.js";
|
|
48
|
-
import { RIGID_BODY_UNALLOCATED, RigidBody } from "./RigidBody.js";
|
|
49
|
-
import { RigidBodyFlags } from "./RigidBodyFlags.js";
|
|
50
|
-
import { SleepState } from "./SleepState.js";
|
|
51
|
-
import { Interpolated } from "../../interpolation/Interpolated.js";
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* Reusable scratch buffer for world-AABB construction so the link path is
|
|
@@ -96,6 +97,7 @@ export class PhysicsSystem extends System {
|
|
|
96
97
|
|
|
97
98
|
/**
|
|
98
99
|
* @type {BodyStorage}
|
|
100
|
+
* @readonly
|
|
99
101
|
*/
|
|
100
102
|
this.storage = new BodyStorage();
|
|
101
103
|
|
|
@@ -112,6 +114,7 @@ export class PhysicsSystem extends System {
|
|
|
112
114
|
/**
|
|
113
115
|
* Persistent contact-manifold cache. One slot per active pair.
|
|
114
116
|
* @type {ManifoldStore}
|
|
117
|
+
* @readonly
|
|
115
118
|
*/
|
|
116
119
|
this.manifolds = new ManifoldStore();
|
|
117
120
|
|
|
@@ -139,6 +142,18 @@ export class PhysicsSystem extends System {
|
|
|
139
142
|
*/
|
|
140
143
|
this.islands = new IslandBuilder();
|
|
141
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Data-oriented mirror of the per-body solver hot state (velocity,
|
|
147
|
+
* inverse mass / inertia, orientation), packed into one ArrayBuffer
|
|
148
|
+
* and indexed by body slot. Gathered from the `RigidBody` /
|
|
149
|
+
* `Transform` components once per step (after islands are built),
|
|
150
|
+
* mutated in place by the TGS substep loop, and the persistent
|
|
151
|
+
* velocity scattered back at the end. Keeps the solver's hottest inner
|
|
152
|
+
* loop free of component-object dereferences.
|
|
153
|
+
* @type {SolverBodyState}
|
|
154
|
+
*/
|
|
155
|
+
this.__solver_state = new SolverBodyState();
|
|
156
|
+
|
|
142
157
|
/**
|
|
143
158
|
* Velocity-squared threshold below which a body is eligible to start
|
|
144
159
|
* accumulating sleep time. Combined linear + angular kinetic-ish
|
|
@@ -240,7 +255,11 @@ export class PhysicsSystem extends System {
|
|
|
240
255
|
* @type {RigidBody[]}
|
|
241
256
|
*/
|
|
242
257
|
this.__bodies = [];
|
|
243
|
-
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @type {Transform[]}
|
|
261
|
+
* @readonly
|
|
262
|
+
*/
|
|
244
263
|
this.__transforms = [];
|
|
245
264
|
|
|
246
265
|
/**
|
|
@@ -314,13 +333,6 @@ export class PhysicsSystem extends System {
|
|
|
314
333
|
*/
|
|
315
334
|
this.__ccd_start_pos = new Float64Array(0);
|
|
316
335
|
|
|
317
|
-
/**
|
|
318
|
-
* Bound reference to {@link __pair_filter} so we hand the same
|
|
319
|
-
* callable to {@link generate_pairs} each step without per-step
|
|
320
|
-
* allocation.
|
|
321
|
-
* @private
|
|
322
|
-
*/
|
|
323
|
-
this.__pair_filter_bound = (idA, idB) => this.__pair_filter(idA, idB);
|
|
324
336
|
|
|
325
337
|
/**
|
|
326
338
|
* Optional shared interpolation log to produce per-step pose snapshots
|
|
@@ -360,13 +372,21 @@ export class PhysicsSystem extends System {
|
|
|
360
372
|
__pair_filter(idA, idB) {
|
|
361
373
|
const idxA = body_id_index(idA);
|
|
362
374
|
const idxB = body_id_index(idB);
|
|
375
|
+
|
|
363
376
|
const rbA = this.__bodies[idxA];
|
|
364
377
|
const rbB = this.__bodies[idxB];
|
|
365
|
-
|
|
378
|
+
|
|
379
|
+
if (rbA === undefined || rbB === undefined) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
366
382
|
|
|
367
383
|
// Layer/mask gate (symmetric).
|
|
368
|
-
if (((rbA.layer & rbB.mask) | 0) === 0)
|
|
369
|
-
|
|
384
|
+
if (((rbA.layer & rbB.mask) | 0) === 0) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
if (((rbB.layer & rbA.mask) | 0) === 0) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
370
390
|
|
|
371
391
|
// User callback gate, if installed.
|
|
372
392
|
const fn = this.__contact_filter;
|
|
@@ -834,7 +854,7 @@ export class PhysicsSystem extends System {
|
|
|
834
854
|
if (rigidBody.kind !== BodyKind.Dynamic) {
|
|
835
855
|
return;
|
|
836
856
|
}
|
|
837
|
-
rigidBody.accumulatedForce.add(
|
|
857
|
+
rigidBody.accumulatedForce.add(force);
|
|
838
858
|
this.__wake_body(rigidBody);
|
|
839
859
|
}
|
|
840
860
|
|
|
@@ -997,8 +1017,12 @@ export class PhysicsSystem extends System {
|
|
|
997
1017
|
rb.sleep_group_next = -1;
|
|
998
1018
|
rb.sleep_group_prev = -1;
|
|
999
1019
|
rb.sleepState = SleepState.Sleeping;
|
|
1000
|
-
rb.linearVelocity[0] = 0;
|
|
1001
|
-
rb.
|
|
1020
|
+
rb.linearVelocity[0] = 0;
|
|
1021
|
+
rb.linearVelocity[1] = 0;
|
|
1022
|
+
rb.linearVelocity[2] = 0;
|
|
1023
|
+
rb.angularVelocity[0] = 0;
|
|
1024
|
+
rb.angularVelocity[1] = 0;
|
|
1025
|
+
rb.angularVelocity[2] = 0;
|
|
1002
1026
|
storage.mark_sleeping(idx);
|
|
1003
1027
|
return;
|
|
1004
1028
|
}
|
|
@@ -1012,22 +1036,16 @@ export class PhysicsSystem extends System {
|
|
|
1012
1036
|
rb.sleep_group_next = next_idx;
|
|
1013
1037
|
rb.sleep_group_prev = prev_idx;
|
|
1014
1038
|
rb.sleepState = SleepState.Sleeping;
|
|
1015
|
-
rb.linearVelocity[0] = 0;
|
|
1016
|
-
rb.
|
|
1039
|
+
rb.linearVelocity[0] = 0;
|
|
1040
|
+
rb.linearVelocity[1] = 0;
|
|
1041
|
+
rb.linearVelocity[2] = 0;
|
|
1042
|
+
rb.angularVelocity[0] = 0;
|
|
1043
|
+
rb.angularVelocity[1] = 0;
|
|
1044
|
+
rb.angularVelocity[2] = 0;
|
|
1017
1045
|
storage.mark_sleeping(idx);
|
|
1018
1046
|
}
|
|
1019
1047
|
}
|
|
1020
1048
|
|
|
1021
|
-
/**
|
|
1022
|
-
* Get the body index for a packed body id without revalidation. Used by
|
|
1023
|
-
* query traversals that already trust the id came from a live BVH leaf.
|
|
1024
|
-
* @param {number} packed_body_id
|
|
1025
|
-
* @returns {number}
|
|
1026
|
-
*/
|
|
1027
|
-
__index_of(packed_body_id) {
|
|
1028
|
-
return body_id_index(packed_body_id);
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
1049
|
/**
|
|
1032
1050
|
* Broadphase raycast against both BVHs. Fills `result` with the nearest
|
|
1033
1051
|
* hit and returns `true` on hit, `false` on miss.
|
|
@@ -1234,7 +1252,7 @@ export class PhysicsSystem extends System {
|
|
|
1234
1252
|
const lv = rb.linearVelocity;
|
|
1235
1253
|
const av = rb.angularVelocity;
|
|
1236
1254
|
const v_sqr = lv[0] * lv[0] + lv[1] * lv[1] + lv[2] * lv[2]
|
|
1237
|
-
|
|
1255
|
+
+ av[0] * av[0] + av[1] * av[1] + av[2] * av[2];
|
|
1238
1256
|
if (v_sqr > max_v_sqr) max_v_sqr = v_sqr;
|
|
1239
1257
|
}
|
|
1240
1258
|
|
|
@@ -1311,14 +1329,22 @@ export class PhysicsSystem extends System {
|
|
|
1311
1329
|
const pt = payload.point;
|
|
1312
1330
|
const nm = payload.normal;
|
|
1313
1331
|
if (has_contact) {
|
|
1314
|
-
const wax = data[slot_off],
|
|
1332
|
+
const wax = data[slot_off], way = data[slot_off + 1], waz = data[slot_off + 2];
|
|
1315
1333
|
const wbx = data[slot_off + 3], wby = data[slot_off + 4], wbz = data[slot_off + 5];
|
|
1316
|
-
pt[0] = (wax + wbx) * 0.5;
|
|
1317
|
-
|
|
1334
|
+
pt[0] = (wax + wbx) * 0.5;
|
|
1335
|
+
pt[1] = (way + wby) * 0.5;
|
|
1336
|
+
pt[2] = (waz + wbz) * 0.5;
|
|
1337
|
+
nm[0] = data[slot_off + 6];
|
|
1338
|
+
nm[1] = data[slot_off + 7];
|
|
1339
|
+
nm[2] = data[slot_off + 8];
|
|
1318
1340
|
payload.depth = data[slot_off + 9];
|
|
1319
1341
|
} else {
|
|
1320
|
-
pt[0] = 0;
|
|
1321
|
-
|
|
1342
|
+
pt[0] = 0;
|
|
1343
|
+
pt[1] = 0;
|
|
1344
|
+
pt[2] = 0;
|
|
1345
|
+
nm[0] = 0;
|
|
1346
|
+
nm[1] = 0;
|
|
1347
|
+
nm[2] = 0;
|
|
1322
1348
|
payload.depth = 0;
|
|
1323
1349
|
}
|
|
1324
1350
|
payload.entityA = entA;
|
|
@@ -1326,9 +1352,16 @@ export class PhysicsSystem extends System {
|
|
|
1326
1352
|
|
|
1327
1353
|
let event_name;
|
|
1328
1354
|
let signal;
|
|
1329
|
-
if (kind === ContactEventKind.Begin)
|
|
1330
|
-
|
|
1331
|
-
|
|
1355
|
+
if (kind === ContactEventKind.Begin) {
|
|
1356
|
+
event_name = PhysicsEvents.ContactBegin;
|
|
1357
|
+
signal = this.onContactBegin;
|
|
1358
|
+
} else if (kind === ContactEventKind.Stay) {
|
|
1359
|
+
event_name = PhysicsEvents.ContactStay;
|
|
1360
|
+
signal = this.onContactStay;
|
|
1361
|
+
} else {
|
|
1362
|
+
event_name = PhysicsEvents.ContactEnd;
|
|
1363
|
+
signal = this.onContactEnd;
|
|
1364
|
+
}
|
|
1332
1365
|
|
|
1333
1366
|
signal.send1(payload);
|
|
1334
1367
|
|
|
@@ -1440,7 +1473,14 @@ export class PhysicsSystem extends System {
|
|
|
1440
1473
|
const gy = this.gravity.y;
|
|
1441
1474
|
const gz = this.gravity.z;
|
|
1442
1475
|
|
|
1443
|
-
const
|
|
1476
|
+
const storage = this.storage;
|
|
1477
|
+
const bodies = this.__bodies;
|
|
1478
|
+
const transforms = this.__transforms;
|
|
1479
|
+
const joints = this.__joints;
|
|
1480
|
+
const manifolds = this.manifolds;
|
|
1481
|
+
|
|
1482
|
+
|
|
1483
|
+
const count = storage.awake_count;
|
|
1444
1484
|
|
|
1445
1485
|
// Stage 1: consume the per-frame force / torque accumulators into
|
|
1446
1486
|
// velocity at the full `dt`, exactly once (a user force is a per-frame
|
|
@@ -1450,9 +1490,11 @@ export class PhysicsSystem extends System {
|
|
|
1450
1490
|
// that substep's contact warm-start, keeping resting stacks at zero
|
|
1451
1491
|
// velocity.
|
|
1452
1492
|
for (let i = 0; i < count; i++) {
|
|
1453
|
-
const idx =
|
|
1454
|
-
|
|
1455
|
-
const
|
|
1493
|
+
const idx = storage.awake_at(i);
|
|
1494
|
+
|
|
1495
|
+
const rb = bodies[idx];
|
|
1496
|
+
const tr = transforms[idx];
|
|
1497
|
+
|
|
1456
1498
|
integrate_velocity_forces(rb, tr, dt);
|
|
1457
1499
|
}
|
|
1458
1500
|
|
|
@@ -1462,19 +1504,30 @@ export class PhysicsSystem extends System {
|
|
|
1462
1504
|
// is a sub-millimetre slack difference, safely inside the margin.
|
|
1463
1505
|
const lists = this.__body_collider_lists;
|
|
1464
1506
|
for (let i = 0; i < count; i++) {
|
|
1465
|
-
|
|
1466
|
-
const
|
|
1507
|
+
|
|
1508
|
+
const idx = storage.awake_at(i);
|
|
1509
|
+
const rb = bodies[idx];
|
|
1467
1510
|
const list = lists[idx];
|
|
1468
|
-
|
|
1511
|
+
|
|
1512
|
+
if (list === undefined) {
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1469
1516
|
const lv = rb.linearVelocity;
|
|
1470
|
-
|
|
1517
|
+
|
|
1518
|
+
const list_length = list.length;
|
|
1519
|
+
|
|
1520
|
+
for (let k = 0; k < list_length; k++) {
|
|
1521
|
+
|
|
1471
1522
|
const entry = list[k];
|
|
1523
|
+
|
|
1472
1524
|
compute_fat_world_aabb(
|
|
1473
1525
|
scratch_world_aabb, 0,
|
|
1474
1526
|
entry.collider.shape, entry.transform,
|
|
1475
1527
|
lv[0], lv[1], lv[2],
|
|
1476
1528
|
dt
|
|
1477
1529
|
);
|
|
1530
|
+
|
|
1478
1531
|
this.dynamicBvh.node_move_aabb(entry.bvhNode, scratch_world_aabb);
|
|
1479
1532
|
}
|
|
1480
1533
|
}
|
|
@@ -1483,13 +1536,14 @@ export class PhysicsSystem extends System {
|
|
|
1483
1536
|
// outer-step motion, so the pair set stays valid across all substeps
|
|
1484
1537
|
// — broadphase runs once.
|
|
1485
1538
|
generate_pairs(
|
|
1486
|
-
|
|
1539
|
+
storage,
|
|
1487
1540
|
this.dynamicBvh,
|
|
1488
1541
|
this.staticBvh,
|
|
1489
|
-
|
|
1542
|
+
manifolds,
|
|
1490
1543
|
lists,
|
|
1491
1544
|
this.pairs,
|
|
1492
|
-
this.
|
|
1545
|
+
this.__pair_filter,
|
|
1546
|
+
this,
|
|
1493
1547
|
);
|
|
1494
1548
|
|
|
1495
1549
|
// Stage 4: wake propagation — through broadphase pairs, then through
|
|
@@ -1500,11 +1554,11 @@ export class PhysicsSystem extends System {
|
|
|
1500
1554
|
// Stage 5: narrowphase — once per outer step. The substep loop below
|
|
1501
1555
|
// re-derives each contact's penetration analytically from the moved
|
|
1502
1556
|
// poses rather than re-running geometry.
|
|
1503
|
-
narrowphase_step(this.pairs,
|
|
1557
|
+
narrowphase_step(this.pairs, manifolds, this.__body_collider_lists);
|
|
1504
1558
|
|
|
1505
1559
|
// Stage 6: partition awake bodies + touched contacts into islands.
|
|
1506
1560
|
// Consumed by the solver (flattened contact list) and the sleep test.
|
|
1507
|
-
this.islands.build(
|
|
1561
|
+
this.islands.build(storage, manifolds, bodies, this.__body_collider_lists, joints);
|
|
1508
1562
|
|
|
1509
1563
|
// Stage 7: TGS substep loop.
|
|
1510
1564
|
//
|
|
@@ -1521,7 +1575,7 @@ export class PhysicsSystem extends System {
|
|
|
1521
1575
|
// the loop.
|
|
1522
1576
|
const N = this.substeps;
|
|
1523
1577
|
const h = dt / N;
|
|
1524
|
-
const count_after_wake =
|
|
1578
|
+
const count_after_wake = storage.awake_count;
|
|
1525
1579
|
|
|
1526
1580
|
// CCD: capture start-of-step positions for flagged bodies over the
|
|
1527
1581
|
// post-wake awake set (poses are unchanged until the substep loop below
|
|
@@ -1530,21 +1584,37 @@ export class PhysicsSystem extends System {
|
|
|
1530
1584
|
// start matches the end the resolve pass reads. Zero-cost when no body
|
|
1531
1585
|
// is flagged.
|
|
1532
1586
|
const ccd_on = this.ccdEnabled;
|
|
1587
|
+
|
|
1533
1588
|
if (ccd_on) {
|
|
1534
|
-
|
|
1589
|
+
|
|
1590
|
+
const ccd_need = storage.high_water_mark * 3;
|
|
1591
|
+
|
|
1535
1592
|
if (this.__ccd_start_pos.length < ccd_need) {
|
|
1536
1593
|
this.__ccd_start_pos = new Float64Array(ccd_need);
|
|
1537
1594
|
}
|
|
1595
|
+
|
|
1538
1596
|
const ccd_start = this.__ccd_start_pos;
|
|
1597
|
+
|
|
1539
1598
|
for (let i = 0; i < count_after_wake; i++) {
|
|
1540
|
-
const idx =
|
|
1541
|
-
const rb =
|
|
1542
|
-
|
|
1543
|
-
if (
|
|
1599
|
+
const idx = storage.awake_at(i);
|
|
1600
|
+
const rb = bodies[idx];
|
|
1601
|
+
|
|
1602
|
+
if (rb.kind !== BodyKind.Dynamic) {
|
|
1603
|
+
continue;
|
|
1604
|
+
}
|
|
1605
|
+
if ((rb.flags & RigidBodyFlags.CCD) === 0) {
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1544
1609
|
const list = this.__body_collider_lists[idx];
|
|
1545
|
-
|
|
1610
|
+
|
|
1611
|
+
if (list === undefined || list.length === 0) {
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1546
1615
|
const cp = list[0].transform.position;
|
|
1547
1616
|
const cb = idx * 3;
|
|
1617
|
+
|
|
1548
1618
|
ccd_start[cb] = cp[0];
|
|
1549
1619
|
ccd_start[cb + 1] = cp[1];
|
|
1550
1620
|
ccd_start[cb + 2] = cp[2];
|
|
@@ -1557,55 +1627,117 @@ export class PhysicsSystem extends System {
|
|
|
1557
1627
|
// reallocate again this step.
|
|
1558
1628
|
this.__reset_pseudo_velocity();
|
|
1559
1629
|
const pseudoVel = this.__pseudo_velocity;
|
|
1560
|
-
const pseudo_len =
|
|
1630
|
+
const pseudo_len = storage.high_water_mark * 6;
|
|
1631
|
+
|
|
1632
|
+
// Gather the data-oriented solver state for every body the substep loop
|
|
1633
|
+
// will touch: the post-wake awake set (all dynamics, at their post-force
|
|
1634
|
+
// velocity) plus the static / kinematic anchors and jointed partners
|
|
1635
|
+
// referenced by this step's contacts and joints. From here the substep
|
|
1636
|
+
// loop reads / writes velocity + orientation through `ss_data` with no
|
|
1637
|
+
// component-object dereference; persistent velocity is scattered back
|
|
1638
|
+
// onto the RigidBodies after the solve.
|
|
1639
|
+
const solver_state = this.__solver_state;
|
|
1640
|
+
solver_state.begin(storage.high_water_mark);
|
|
1641
|
+
|
|
1642
|
+
for (let i = 0; i < count_after_wake; i++) {
|
|
1643
|
+
const idx = storage.awake_at(i);
|
|
1644
|
+
solver_state.gather(idx, bodies[idx], transforms[idx]);
|
|
1645
|
+
}
|
|
1561
1646
|
|
|
1562
|
-
|
|
1647
|
+
const island_contacts = this.islands.contact_data;
|
|
1648
|
+
const island_contact_total = this.islands.contact_offsets[this.islands.island_count];
|
|
1649
|
+
|
|
1650
|
+
for (let i = 0; i < island_contact_total; i++) {
|
|
1651
|
+
const slot = island_contacts[i];
|
|
1652
|
+
|
|
1653
|
+
const ia = body_id_index(manifolds.bodyA(slot));
|
|
1654
|
+
const ib = body_id_index(manifolds.bodyB(slot));
|
|
1655
|
+
|
|
1656
|
+
solver_state.gather(ia, bodies[ia], transforms[ia]);
|
|
1657
|
+
solver_state.gather(ib, bodies[ib], transforms[ib]);
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
const joint_count = joints.length;
|
|
1661
|
+
for (let i = 0; i < joint_count; i++) {
|
|
1662
|
+
const joint = joints[i];
|
|
1663
|
+
|
|
1664
|
+
if (joint === undefined || joint === null) {
|
|
1665
|
+
continue;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
if (!storage.is_valid(joint._bodyIdA)) {
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
const ia = body_id_index(joint._bodyIdA);
|
|
1673
|
+
|
|
1674
|
+
solver_state.gather(ia, bodies[ia], transforms[ia]);
|
|
1675
|
+
|
|
1676
|
+
if (joint._bodyIdB !== JOINT_WORLD && storage.is_valid(joint._bodyIdB)) {
|
|
1677
|
+
|
|
1678
|
+
const ib = body_id_index(joint._bodyIdB);
|
|
1679
|
+
|
|
1680
|
+
solver_state.gather(ib, bodies[ib], transforms[ib]);
|
|
1681
|
+
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
const ss_data = solver_state.data;
|
|
1686
|
+
|
|
1687
|
+
prepare_contacts(manifolds, this, h);
|
|
1563
1688
|
|
|
1564
1689
|
for (let s = 0; s < N; s++) {
|
|
1690
|
+
|
|
1565
1691
|
// Gravity (+ damping) for this substep.
|
|
1566
1692
|
for (let i = 0; i < count_after_wake; i++) {
|
|
1567
|
-
const idx =
|
|
1568
|
-
integrate_velocity_gravity(
|
|
1693
|
+
const idx = storage.awake_at(i);
|
|
1694
|
+
integrate_velocity_gravity(ss_data, idx * SBS_STRIDE, bodies[idx], gx, gy, gz, h);
|
|
1569
1695
|
}
|
|
1570
1696
|
|
|
1571
1697
|
// Re-derive contact geometry at the current poses: concave pairs
|
|
1572
1698
|
// re-run narrowphase (fresh feature/normal as the body rocks),
|
|
1573
1699
|
// convex pairs rotate frozen anchors analytically. Then replay
|
|
1574
1700
|
// the per-substep warm-start and solve velocity.
|
|
1575
|
-
redetect_concave_contacts(
|
|
1576
|
-
refresh_contacts(
|
|
1577
|
-
warm_start_contacts(
|
|
1578
|
-
solve_velocity(
|
|
1701
|
+
redetect_concave_contacts(manifolds, this);
|
|
1702
|
+
refresh_contacts(manifolds, this.__transforms);
|
|
1703
|
+
warm_start_contacts(manifolds, this);
|
|
1704
|
+
solve_velocity(manifolds, this, this.velocityIterations);
|
|
1579
1705
|
|
|
1580
1706
|
// Joints share the substep: warm-start + velocity-solve the 6-DOF
|
|
1581
1707
|
// constraints on real velocity, coupled with the contacts above
|
|
1582
1708
|
// (a body touched by both sees one substep of Gauss-Seidel across
|
|
1583
1709
|
// contacts then joints). Position correction for locked DOFs is a
|
|
1584
1710
|
// SPOOK bias inside this solve, so no separate joint position pass.
|
|
1585
|
-
if (
|
|
1586
|
-
solve_joints(
|
|
1711
|
+
if (joints.length > 0) {
|
|
1712
|
+
solve_joints(joints, this, h, this.jointIterations);
|
|
1587
1713
|
}
|
|
1588
1714
|
|
|
1589
1715
|
// Position correction writes pseudo-velocity (zeroed first so it
|
|
1590
1716
|
// is a fresh per-substep correction), folded into the pose by the
|
|
1591
1717
|
// position integrate and then discarded.
|
|
1592
1718
|
pseudoVel.fill(0, 0, pseudo_len);
|
|
1593
|
-
solve_position(
|
|
1719
|
+
solve_position(manifolds, this, this.positionIterations);
|
|
1594
1720
|
|
|
1595
1721
|
for (let i = 0; i < count_after_wake; i++) {
|
|
1596
|
-
const idx =
|
|
1597
|
-
const rb =
|
|
1598
|
-
const tr =
|
|
1722
|
+
const idx = storage.awake_at(i);
|
|
1723
|
+
const rb = bodies[idx];
|
|
1724
|
+
const tr = transforms[idx];
|
|
1599
1725
|
const base = idx * 6;
|
|
1600
|
-
integrate_position(rb, tr, h,
|
|
1601
|
-
pseudoVel[base],
|
|
1726
|
+
integrate_position(ss_data, idx * SBS_STRIDE, rb, tr, h,
|
|
1727
|
+
pseudoVel[base], pseudoVel[base + 1], pseudoVel[base + 2],
|
|
1602
1728
|
pseudoVel[base + 3], pseudoVel[base + 4], pseudoVel[base + 5]);
|
|
1603
1729
|
}
|
|
1604
1730
|
}
|
|
1605
1731
|
|
|
1606
1732
|
// Stage 8: one-shot restitution, after the substep loop, keyed off
|
|
1607
1733
|
// the approach velocity captured at prepare time.
|
|
1608
|
-
apply_restitution(
|
|
1734
|
+
apply_restitution(manifolds, this);
|
|
1735
|
+
|
|
1736
|
+
// Scatter the solved persistent linear / angular velocity back onto the
|
|
1737
|
+
// RigidBody components (pose was written through to the Transforms each
|
|
1738
|
+
// substep). After this the bodies are authoritative again for CCD,
|
|
1739
|
+
// interpolation recording, and the sleep test below.
|
|
1740
|
+
solver_state.scatter(bodies);
|
|
1609
1741
|
|
|
1610
1742
|
// Stage 8.5: continuous collision — sweep CCD-flagged fast movers along
|
|
1611
1743
|
// their net step translation and stop them at the first blocker, so they
|
|
@@ -1628,12 +1760,12 @@ export class PhysicsSystem extends System {
|
|
|
1628
1760
|
// Stage 10: diff manifolds against the previous frame and dispatch
|
|
1629
1761
|
// Begin / Stay / End events. MUST run before advance_frame, which
|
|
1630
1762
|
// rolls the touched flags.
|
|
1631
|
-
diff_manifolds(
|
|
1763
|
+
diff_manifolds(manifolds, storage, this.contactEvents);
|
|
1632
1764
|
this.__dispatch_contact_events();
|
|
1633
1765
|
|
|
1634
1766
|
// Stage 11 (end-of-step): roll touched → prev_touched and evict slots
|
|
1635
1767
|
// whose pair has not been touched within the grace window.
|
|
1636
|
-
|
|
1768
|
+
manifolds.advance_frame();
|
|
1637
1769
|
}
|
|
1638
1770
|
}
|
|
1639
1771
|
|
|
@@ -41,4 +41,27 @@ export function world_inverse_inertia_apply(result: number[] | Float64Array, res
|
|
|
41
41
|
z: number;
|
|
42
42
|
w: number;
|
|
43
43
|
}, vx: number, vy: number, vz: number): void;
|
|
44
|
+
/**
|
|
45
|
+
* Raw-float counterpart of {@link world_inverse_inertia_apply}: the same
|
|
46
|
+
* `I_w⁻¹ = R · diag · R^T` application, with the inverse-inertia diagonal and
|
|
47
|
+
* orientation quaternion passed as scalars rather than read off
|
|
48
|
+
* `{x,y,z}` / `{x,y,z,w}` objects. The data-oriented contact / joint solver
|
|
49
|
+
* reads these straight out of its `SolverBodyState` typed array, avoiding the
|
|
50
|
+
* per-impulse object dereference on the hot path. The arithmetic is identical
|
|
51
|
+
* to the object version (same operation order), so results are bit-identical.
|
|
52
|
+
*
|
|
53
|
+
* @param {number[]|Float64Array} result length >= 3
|
|
54
|
+
* @param {number} result_offset
|
|
55
|
+
* @param {number} ix
|
|
56
|
+
* @param {number} iy
|
|
57
|
+
* @param {number} iz Body-frame inverse inertia diagonal.
|
|
58
|
+
* @param {number} qx
|
|
59
|
+
* @param {number} qy
|
|
60
|
+
* @param {number} qz
|
|
61
|
+
* @param {number} qw Body orientation as a unit quaternion.
|
|
62
|
+
* @param {number} vx
|
|
63
|
+
* @param {number} vy
|
|
64
|
+
* @param {number} vz
|
|
65
|
+
*/
|
|
66
|
+
export function world_inverse_inertia_apply_raw(result: number[] | Float64Array, result_offset: number, ix: number, iy: number, iz: number, qx: number, qy: number, qz: number, qw: number, vx: number, vy: number, vz: number): void;
|
|
44
67
|
//# sourceMappingURL=world_inverse_inertia.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"world_inverse_inertia.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/inertia/world_inverse_inertia.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,oDAVW,MAAM,EAAE,GAAC,YAAY,iBACrB,MAAM,qBACN;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAC,YAEjC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAC,MAE5C,MAAM,MACN,MAAM,MACN,MAAM,
|
|
1
|
+
{"version":3,"file":"world_inverse_inertia.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/inertia/world_inverse_inertia.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,oDAVW,MAAM,EAAE,GAAC,YAAY,iBACrB,MAAM,qBACN;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAC,YAEjC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAC,MAE5C,MAAM,MACN,MAAM,MACN,MAAM,QAkBhB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wDAbW,MAAM,EAAE,GAAC,YAAY,iBACrB,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QA4ChB"}
|