@woosh/meep-engine 2.146.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/control/first-person/FirstPersonPlayerController.d.ts +0 -11
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +8 -6
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +552 -551
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts +8 -3
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/LedgeGrab.js +213 -199
- package/src/engine/control/first-person/abilities/Mantle.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/Mantle.js +195 -188
- package/src/engine/control/first-person/abilities/WallRun.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/WallRun.js +183 -175
- package/src/engine/control/first-person/sensors/FirstPersonSensors.d.ts +9 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensors.d.ts.map +1 -1
- package/src/engine/control/first-person/sensors/FirstPersonSensors.js +87 -77
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts +8 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +229 -196
- package/src/engine/ecs/EntityManager.d.ts +34 -11
- package/src/engine/ecs/EntityManager.d.ts.map +1 -1
- package/src/engine/ecs/EntityManager.js +71 -42
- package/src/engine/interpolation/BinaryInterpolationAdapter.d.ts.map +1 -0
- package/src/engine/interpolation/Interpoland.d.ts +48 -0
- package/src/engine/interpolation/Interpoland.d.ts.map +1 -0
- package/src/engine/interpolation/Interpoland.js +49 -0
- package/src/engine/interpolation/Interpolated.d.ts +101 -0
- package/src/engine/interpolation/Interpolated.d.ts.map +1 -0
- package/src/engine/interpolation/Interpolated.js +149 -0
- package/src/engine/{network/sim → interpolation}/InterpolationLog.d.ts +1 -1
- package/src/engine/interpolation/InterpolationLog.d.ts.map +1 -0
- package/src/engine/{network/sim → interpolation}/InterpolationLog.js +2 -2
- package/src/engine/interpolation/InterpolationSystem.d.ts +116 -0
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -0
- package/src/engine/interpolation/InterpolationSystem.js +233 -0
- package/src/engine/interpolation/PoseInterpolationAdapter.d.ts +17 -0
- package/src/engine/interpolation/PoseInterpolationAdapter.d.ts.map +1 -0
- package/src/engine/interpolation/PoseInterpolationAdapter.js +61 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +35 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +57 -0
- package/src/engine/interpolation/pose_interpoland.d.ts +18 -0
- package/src/engine/interpolation/pose_interpoland.d.ts.map +1 -0
- package/src/engine/interpolation/pose_interpoland.js +27 -0
- package/src/engine/network/NetworkSession.d.ts +2 -2
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +2 -2
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.js +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.js +1 -1
- package/src/engine/physics/INTEPOLATION_SYSTEM_PLAN.md +287 -0
- package/src/engine/physics/PLAN.md +10 -9
- 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 +82 -15
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +387 -87
- 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 +19 -46
- 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 +52 -4
- 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
- package/src/engine/network/sim/BinaryInterpolationAdapter.d.ts.map +0 -1
- package/src/engine/network/sim/InterpolationLog.d.ts.map +0 -1
- /package/src/engine/{network/sim → interpolation}/BinaryInterpolationAdapter.d.ts +0 -0
- /package/src/engine/{network/sim → interpolation}/BinaryInterpolationAdapter.js +0 -0
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js";
|
|
2
3
|
import { BVH } from "../../../core/bvh2/bvh3/BVH.js";
|
|
3
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";
|
|
4
7
|
import Vector3 from "../../../core/geom/Vector3.js";
|
|
5
8
|
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
|
|
6
9
|
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
|
|
7
10
|
import { System } from "../../ecs/System.js";
|
|
8
11
|
import { Transform } from "../../ecs/transform/Transform.js";
|
|
12
|
+
import { Interpolated } from "../../interpolation/Interpolated.js";
|
|
9
13
|
import { body_id_index, BodyStorage } from "../body/BodyStorage.js";
|
|
10
|
-
import {
|
|
14
|
+
import { SBS_STRIDE, SolverBodyState } from "../body/SolverBodyState.js";
|
|
11
15
|
import { compute_fat_world_aabb } from "../broadphase/compute_fat_world_aabb.js";
|
|
12
16
|
import { generate_pairs } from "../broadphase/generate_pairs.js";
|
|
13
17
|
import { PairList } from "../broadphase/PairList.js";
|
|
14
|
-
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";
|
|
15
21
|
import { ContactEventBuffer, ContactEventKind } from "../events/ContactEventBuffer.js";
|
|
16
22
|
import { diff_manifolds } from "../events/diff_manifolds.js";
|
|
23
|
+
import { world_inverse_inertia_apply } from "../inertia/world_inverse_inertia.js";
|
|
17
24
|
import { integrate_position } from "../integration/integrate_position.js";
|
|
18
25
|
import { integrate_velocity_forces, integrate_velocity_gravity } from "../integration/integrate_velocity.js";
|
|
19
26
|
import { IslandBuilder } from "../island/IslandBuilder.js";
|
|
@@ -21,32 +28,28 @@ import { narrowphase_step } from "../narrowphase/narrowphase_step.js";
|
|
|
21
28
|
import { overlap_shape as overlap_shape_query } from "../queries/overlap_shape.js";
|
|
22
29
|
import { raycast as raycast_query } from "../queries/raycast.js";
|
|
23
30
|
import { shape_cast as shape_cast_query } from "../queries/shape_cast.js";
|
|
24
|
-
import { ccd_resolve } from "../ccd/linear_sweep.js";
|
|
25
|
-
import { returnTrue } from "../../../core/function/returnTrue.js";
|
|
26
31
|
import {
|
|
32
|
+
apply_restitution,
|
|
27
33
|
prepare_contacts,
|
|
28
|
-
refresh_contacts,
|
|
29
34
|
redetect_concave_contacts,
|
|
30
|
-
|
|
31
|
-
solve_velocity,
|
|
32
|
-
apply_restitution,
|
|
35
|
+
refresh_contacts,
|
|
33
36
|
solve_position,
|
|
37
|
+
solve_velocity,
|
|
38
|
+
warm_start_contacts,
|
|
34
39
|
} from "../solver/solve_contacts.js";
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
40
|
+
import { BodyKind } from "./BodyKind.js";
|
|
41
|
+
import { Collider, COLLIDER_UNBOUND } from "./Collider.js";
|
|
42
|
+
import { JOINT_UNALLOCATED, JOINT_WORLD } from "./Joint.js";
|
|
38
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";
|
|
39
47
|
|
|
40
48
|
/**
|
|
41
49
|
* Scratch for {@link applyImpulseAt}'s angular delta calculation.
|
|
42
50
|
* @type {Float64Array}
|
|
43
51
|
*/
|
|
44
52
|
const scratch_angular_delta = new Float64Array(3);
|
|
45
|
-
import { BodyKind } from "./BodyKind.js";
|
|
46
|
-
import { Collider, COLLIDER_UNBOUND } from "./Collider.js";
|
|
47
|
-
import { RIGID_BODY_UNALLOCATED, RigidBody } from "./RigidBody.js";
|
|
48
|
-
import { RigidBodyFlags } from "./RigidBodyFlags.js";
|
|
49
|
-
import { SleepState } from "./SleepState.js";
|
|
50
53
|
|
|
51
54
|
/**
|
|
52
55
|
* Reusable scratch buffer for world-AABB construction so the link path is
|
|
@@ -94,6 +97,7 @@ export class PhysicsSystem extends System {
|
|
|
94
97
|
|
|
95
98
|
/**
|
|
96
99
|
* @type {BodyStorage}
|
|
100
|
+
* @readonly
|
|
97
101
|
*/
|
|
98
102
|
this.storage = new BodyStorage();
|
|
99
103
|
|
|
@@ -110,6 +114,7 @@ export class PhysicsSystem extends System {
|
|
|
110
114
|
/**
|
|
111
115
|
* Persistent contact-manifold cache. One slot per active pair.
|
|
112
116
|
* @type {ManifoldStore}
|
|
117
|
+
* @readonly
|
|
113
118
|
*/
|
|
114
119
|
this.manifolds = new ManifoldStore();
|
|
115
120
|
|
|
@@ -137,6 +142,18 @@ export class PhysicsSystem extends System {
|
|
|
137
142
|
*/
|
|
138
143
|
this.islands = new IslandBuilder();
|
|
139
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
|
+
|
|
140
157
|
/**
|
|
141
158
|
* Velocity-squared threshold below which a body is eligible to start
|
|
142
159
|
* accumulating sleep time. Combined linear + angular kinetic-ish
|
|
@@ -238,7 +255,11 @@ export class PhysicsSystem extends System {
|
|
|
238
255
|
* @type {RigidBody[]}
|
|
239
256
|
*/
|
|
240
257
|
this.__bodies = [];
|
|
241
|
-
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @type {Transform[]}
|
|
261
|
+
* @readonly
|
|
262
|
+
*/
|
|
242
263
|
this.__transforms = [];
|
|
243
264
|
|
|
244
265
|
/**
|
|
@@ -312,13 +333,26 @@ export class PhysicsSystem extends System {
|
|
|
312
333
|
*/
|
|
313
334
|
this.__ccd_start_pos = new Float64Array(0);
|
|
314
335
|
|
|
336
|
+
|
|
315
337
|
/**
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
338
|
+
* Optional shared interpolation log to produce per-step pose snapshots
|
|
339
|
+
* into (the {@link InterpolationSystem}'s log). When set — and only then —
|
|
340
|
+
* each fixedUpdate restores every awake {@link Interpolated} body's live
|
|
341
|
+
* components from the previous tick (undoing render-time interpolation so
|
|
342
|
+
* the sim reads authoritative state), then records the post-step state
|
|
343
|
+
* under the current `entityManager.fixedStepTick`. Null on a headless /
|
|
344
|
+
* non-rendering world, where the producer work is skipped entirely.
|
|
345
|
+
* @type {InterpolationLog|null}
|
|
346
|
+
*/
|
|
347
|
+
this.interpolationLog = null;
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Reusable decode buffer for restoring interpolated snapshots.
|
|
319
351
|
* @private
|
|
352
|
+
* @type {BinaryBuffer}
|
|
320
353
|
*/
|
|
321
|
-
this.
|
|
354
|
+
this.__interp_scratch = new BinaryBuffer();
|
|
355
|
+
this.__interp_scratch.fromArrayBuffer(new ArrayBuffer(256));
|
|
322
356
|
}
|
|
323
357
|
|
|
324
358
|
/**
|
|
@@ -338,13 +372,21 @@ export class PhysicsSystem extends System {
|
|
|
338
372
|
__pair_filter(idA, idB) {
|
|
339
373
|
const idxA = body_id_index(idA);
|
|
340
374
|
const idxB = body_id_index(idB);
|
|
375
|
+
|
|
341
376
|
const rbA = this.__bodies[idxA];
|
|
342
377
|
const rbB = this.__bodies[idxB];
|
|
343
|
-
|
|
378
|
+
|
|
379
|
+
if (rbA === undefined || rbB === undefined) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
344
382
|
|
|
345
383
|
// Layer/mask gate (symmetric).
|
|
346
|
-
if (((rbA.layer & rbB.mask) | 0) === 0)
|
|
347
|
-
|
|
384
|
+
if (((rbA.layer & rbB.mask) | 0) === 0) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
if (((rbB.layer & rbA.mask) | 0) === 0) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
348
390
|
|
|
349
391
|
// User callback gate, if installed.
|
|
350
392
|
const fn = this.__contact_filter;
|
|
@@ -812,7 +854,7 @@ export class PhysicsSystem extends System {
|
|
|
812
854
|
if (rigidBody.kind !== BodyKind.Dynamic) {
|
|
813
855
|
return;
|
|
814
856
|
}
|
|
815
|
-
rigidBody.accumulatedForce.add(
|
|
857
|
+
rigidBody.accumulatedForce.add(force);
|
|
816
858
|
this.__wake_body(rigidBody);
|
|
817
859
|
}
|
|
818
860
|
|
|
@@ -829,6 +871,50 @@ export class PhysicsSystem extends System {
|
|
|
829
871
|
}
|
|
830
872
|
}
|
|
831
873
|
|
|
874
|
+
/**
|
|
875
|
+
* Teleport a body to a new pose, bypassing integration: writes the body's
|
|
876
|
+
* Transform directly and wakes it. For an interpolated body this also flags a
|
|
877
|
+
* render `snap` on its {@link Interpolated} component, so the producer keeps
|
|
878
|
+
* this pose (rather than restoring the previous tick over it) and the
|
|
879
|
+
* renderer shows the new pose without sliding across the jump.
|
|
880
|
+
*
|
|
881
|
+
* This is the authoritative way to reposition an interpolated body — a raw
|
|
882
|
+
* `Transform` write would be undone by the per-step restore. Velocity is left
|
|
883
|
+
* as-is; zero it via {@link setLinearVelocity} if the teleport should also
|
|
884
|
+
* stop the body.
|
|
885
|
+
*
|
|
886
|
+
* @param {RigidBody} rigidBody
|
|
887
|
+
* @param {Vector3|{x:number,y:number,z:number}} position world position
|
|
888
|
+
* @param {Quaternion|{x:number,y:number,z:number,w:number}} rotation world unit-quaternion rotation
|
|
889
|
+
*/
|
|
890
|
+
setPose(rigidBody, position, rotation) {
|
|
891
|
+
const idx = body_id_index(rigidBody._bodyId);
|
|
892
|
+
const transform = this.__transforms[idx];
|
|
893
|
+
if (transform === undefined) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
transform.position.set(position.x, position.y, position.z);
|
|
898
|
+
transform.rotation.set(rotation.x, rotation.y, rotation.z, rotation.w);
|
|
899
|
+
|
|
900
|
+
this.__wake_body(rigidBody);
|
|
901
|
+
|
|
902
|
+
// Flag a snap on the body's Interpolated component, if any, so the
|
|
903
|
+
// interpolation producer (restore) and consumer (blend) treat this pose
|
|
904
|
+
// as authoritative this frame instead of interpolating across the jump.
|
|
905
|
+
const em = this.entityManager;
|
|
906
|
+
if (em !== null && em !== undefined) {
|
|
907
|
+
const dataset = em.dataset;
|
|
908
|
+
if (dataset !== null && dataset !== undefined) {
|
|
909
|
+
const entity = this.storage.entity_at(idx);
|
|
910
|
+
const interpolated = dataset.getComponent(entity, Interpolated);
|
|
911
|
+
if (interpolated !== undefined && interpolated !== null) {
|
|
912
|
+
interpolated.snap = true;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
832
918
|
/**
|
|
833
919
|
* Force the body awake. Static bodies are ignored.
|
|
834
920
|
* @param {RigidBody} rigidBody
|
|
@@ -931,8 +1017,12 @@ export class PhysicsSystem extends System {
|
|
|
931
1017
|
rb.sleep_group_next = -1;
|
|
932
1018
|
rb.sleep_group_prev = -1;
|
|
933
1019
|
rb.sleepState = SleepState.Sleeping;
|
|
934
|
-
rb.linearVelocity[0] = 0;
|
|
935
|
-
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;
|
|
936
1026
|
storage.mark_sleeping(idx);
|
|
937
1027
|
return;
|
|
938
1028
|
}
|
|
@@ -946,22 +1036,16 @@ export class PhysicsSystem extends System {
|
|
|
946
1036
|
rb.sleep_group_next = next_idx;
|
|
947
1037
|
rb.sleep_group_prev = prev_idx;
|
|
948
1038
|
rb.sleepState = SleepState.Sleeping;
|
|
949
|
-
rb.linearVelocity[0] = 0;
|
|
950
|
-
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;
|
|
951
1045
|
storage.mark_sleeping(idx);
|
|
952
1046
|
}
|
|
953
1047
|
}
|
|
954
1048
|
|
|
955
|
-
/**
|
|
956
|
-
* Get the body index for a packed body id without revalidation. Used by
|
|
957
|
-
* query traversals that already trust the id came from a live BVH leaf.
|
|
958
|
-
* @param {number} packed_body_id
|
|
959
|
-
* @returns {number}
|
|
960
|
-
*/
|
|
961
|
-
__index_of(packed_body_id) {
|
|
962
|
-
return body_id_index(packed_body_id);
|
|
963
|
-
}
|
|
964
|
-
|
|
965
1049
|
/**
|
|
966
1050
|
* Broadphase raycast against both BVHs. Fills `result` with the nearest
|
|
967
1051
|
* hit and returns `true` on hit, `false` on miss.
|
|
@@ -1168,7 +1252,7 @@ export class PhysicsSystem extends System {
|
|
|
1168
1252
|
const lv = rb.linearVelocity;
|
|
1169
1253
|
const av = rb.angularVelocity;
|
|
1170
1254
|
const v_sqr = lv[0] * lv[0] + lv[1] * lv[1] + lv[2] * lv[2]
|
|
1171
|
-
|
|
1255
|
+
+ av[0] * av[0] + av[1] * av[1] + av[2] * av[2];
|
|
1172
1256
|
if (v_sqr > max_v_sqr) max_v_sqr = v_sqr;
|
|
1173
1257
|
}
|
|
1174
1258
|
|
|
@@ -1245,14 +1329,22 @@ export class PhysicsSystem extends System {
|
|
|
1245
1329
|
const pt = payload.point;
|
|
1246
1330
|
const nm = payload.normal;
|
|
1247
1331
|
if (has_contact) {
|
|
1248
|
-
const wax = data[slot_off],
|
|
1332
|
+
const wax = data[slot_off], way = data[slot_off + 1], waz = data[slot_off + 2];
|
|
1249
1333
|
const wbx = data[slot_off + 3], wby = data[slot_off + 4], wbz = data[slot_off + 5];
|
|
1250
|
-
pt[0] = (wax + wbx) * 0.5;
|
|
1251
|
-
|
|
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];
|
|
1252
1340
|
payload.depth = data[slot_off + 9];
|
|
1253
1341
|
} else {
|
|
1254
|
-
pt[0] = 0;
|
|
1255
|
-
|
|
1342
|
+
pt[0] = 0;
|
|
1343
|
+
pt[1] = 0;
|
|
1344
|
+
pt[2] = 0;
|
|
1345
|
+
nm[0] = 0;
|
|
1346
|
+
nm[1] = 0;
|
|
1347
|
+
nm[2] = 0;
|
|
1256
1348
|
payload.depth = 0;
|
|
1257
1349
|
}
|
|
1258
1350
|
payload.entityA = entA;
|
|
@@ -1260,9 +1352,16 @@ export class PhysicsSystem extends System {
|
|
|
1260
1352
|
|
|
1261
1353
|
let event_name;
|
|
1262
1354
|
let signal;
|
|
1263
|
-
if (kind === ContactEventKind.Begin)
|
|
1264
|
-
|
|
1265
|
-
|
|
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
|
+
}
|
|
1266
1365
|
|
|
1267
1366
|
signal.send1(payload);
|
|
1268
1367
|
|
|
@@ -1273,12 +1372,115 @@ export class PhysicsSystem extends System {
|
|
|
1273
1372
|
}
|
|
1274
1373
|
}
|
|
1275
1374
|
|
|
1375
|
+
/**
|
|
1376
|
+
* Producer — restore pass. At the top of a fixed step, reset every awake
|
|
1377
|
+
* {@link Interpolated} body's live components to their authoritative state
|
|
1378
|
+
* from the previous tick's snapshot, undoing any render-time interpolation
|
|
1379
|
+
* the {@link InterpolationSystem} wrote between frames so the sim integrates
|
|
1380
|
+
* from truth, not an interpolated pose. A body with no previous snapshot
|
|
1381
|
+
* (first step ever, or just woken) is left as-is — its live state is already
|
|
1382
|
+
* authoritative. No-op unless {@link interpolationLog} is wired.
|
|
1383
|
+
* @private
|
|
1384
|
+
*/
|
|
1385
|
+
__interp_restore() {
|
|
1386
|
+
const log = this.interpolationLog;
|
|
1387
|
+
const em = this.entityManager;
|
|
1388
|
+
if (log === null || em === null || em === undefined) return;
|
|
1389
|
+
const dataset = em.dataset;
|
|
1390
|
+
if (dataset === null || dataset === undefined) return;
|
|
1391
|
+
|
|
1392
|
+
const prev_tick = em.fixedStepTick - 1;
|
|
1393
|
+
if (prev_tick < 0) return;
|
|
1394
|
+
|
|
1395
|
+
const storage = this.storage;
|
|
1396
|
+
const count = storage.awake_count;
|
|
1397
|
+
const scratch = this.__interp_scratch;
|
|
1398
|
+
|
|
1399
|
+
for (let i = 0; i < count; i++) {
|
|
1400
|
+
const idx = storage.awake_at(i);
|
|
1401
|
+
const entity = storage.entity_at(idx);
|
|
1402
|
+
const interpolated = dataset.getComponent(entity, Interpolated);
|
|
1403
|
+
if (interpolated === undefined || interpolated === null) continue;
|
|
1404
|
+
// A teleported body (snap set, e.g. via setPose) keeps its live pose
|
|
1405
|
+
// this step — restoring the previous tick would undo the teleport.
|
|
1406
|
+
if (interpolated.snap) continue;
|
|
1407
|
+
const key = interpolated.key;
|
|
1408
|
+
if (key < 0) continue;
|
|
1409
|
+
|
|
1410
|
+
const interpolands = interpolated.interpolands;
|
|
1411
|
+
for (let k = 0; k < interpolands.length; k++) {
|
|
1412
|
+
const ip = interpolands[k];
|
|
1413
|
+
scratch.position = 0;
|
|
1414
|
+
// Snap to the previous tick's snapshot (both offsets equal → t irrelevant).
|
|
1415
|
+
const ok = log.interpolate(scratch, key, ip.type_id, prev_tick, prev_tick, 0, ip.interpolation_adapter);
|
|
1416
|
+
if (!ok) continue;
|
|
1417
|
+
const target = dataset.getComponent(entity, ip.component_class);
|
|
1418
|
+
if (target === undefined || target === null) continue;
|
|
1419
|
+
scratch.position = 0;
|
|
1420
|
+
ip.serialization_adapter.deserialize(scratch, target);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
/**
|
|
1426
|
+
* Producer — record pass. At the end of a fixed step, snapshot every awake
|
|
1427
|
+
* {@link Interpolated} body's live components into the shared log under the
|
|
1428
|
+
* current `entityManager.fixedStepTick`. The render-time
|
|
1429
|
+
* {@link InterpolationSystem} blends consecutive ticks from these snapshots.
|
|
1430
|
+
* Only awake (moving) bodies are recorded, so the log stays sparse. No-op
|
|
1431
|
+
* unless {@link interpolationLog} is wired.
|
|
1432
|
+
* @private
|
|
1433
|
+
*/
|
|
1434
|
+
__interp_record() {
|
|
1435
|
+
const log = this.interpolationLog;
|
|
1436
|
+
const em = this.entityManager;
|
|
1437
|
+
if (log === null || em === null || em === undefined) return;
|
|
1438
|
+
const dataset = em.dataset;
|
|
1439
|
+
if (dataset === null || dataset === undefined) return;
|
|
1440
|
+
|
|
1441
|
+
const tick = em.fixedStepTick;
|
|
1442
|
+
const storage = this.storage;
|
|
1443
|
+
const count = storage.awake_count;
|
|
1444
|
+
|
|
1445
|
+
log.begin_tick(tick);
|
|
1446
|
+
for (let i = 0; i < count; i++) {
|
|
1447
|
+
const idx = storage.awake_at(i);
|
|
1448
|
+
const entity = storage.entity_at(idx);
|
|
1449
|
+
const interpolated = dataset.getComponent(entity, Interpolated);
|
|
1450
|
+
if (interpolated === undefined || interpolated === null) continue;
|
|
1451
|
+
const key = interpolated.key;
|
|
1452
|
+
if (key < 0) continue;
|
|
1453
|
+
|
|
1454
|
+
const interpolands = interpolated.interpolands;
|
|
1455
|
+
for (let k = 0; k < interpolands.length; k++) {
|
|
1456
|
+
const ip = interpolands[k];
|
|
1457
|
+
const target = dataset.getComponent(entity, ip.component_class);
|
|
1458
|
+
if (target === undefined || target === null) continue;
|
|
1459
|
+
const buf = log.begin_record(key, ip.type_id);
|
|
1460
|
+
ip.serialization_adapter.serialize(buf, target);
|
|
1461
|
+
log.end_record();
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
log.end_tick();
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1276
1467
|
fixedUpdate(dt) {
|
|
1468
|
+
// Producer: restore authoritative pose (undo render interpolation)
|
|
1469
|
+
// before the sim reads any Transform this step.
|
|
1470
|
+
this.__interp_restore();
|
|
1471
|
+
|
|
1277
1472
|
const gx = this.gravity.x;
|
|
1278
1473
|
const gy = this.gravity.y;
|
|
1279
1474
|
const gz = this.gravity.z;
|
|
1280
1475
|
|
|
1281
|
-
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;
|
|
1282
1484
|
|
|
1283
1485
|
// Stage 1: consume the per-frame force / torque accumulators into
|
|
1284
1486
|
// velocity at the full `dt`, exactly once (a user force is a per-frame
|
|
@@ -1288,9 +1490,11 @@ export class PhysicsSystem extends System {
|
|
|
1288
1490
|
// that substep's contact warm-start, keeping resting stacks at zero
|
|
1289
1491
|
// velocity.
|
|
1290
1492
|
for (let i = 0; i < count; i++) {
|
|
1291
|
-
const idx =
|
|
1292
|
-
|
|
1293
|
-
const
|
|
1493
|
+
const idx = storage.awake_at(i);
|
|
1494
|
+
|
|
1495
|
+
const rb = bodies[idx];
|
|
1496
|
+
const tr = transforms[idx];
|
|
1497
|
+
|
|
1294
1498
|
integrate_velocity_forces(rb, tr, dt);
|
|
1295
1499
|
}
|
|
1296
1500
|
|
|
@@ -1300,19 +1504,30 @@ export class PhysicsSystem extends System {
|
|
|
1300
1504
|
// is a sub-millimetre slack difference, safely inside the margin.
|
|
1301
1505
|
const lists = this.__body_collider_lists;
|
|
1302
1506
|
for (let i = 0; i < count; i++) {
|
|
1303
|
-
|
|
1304
|
-
const
|
|
1507
|
+
|
|
1508
|
+
const idx = storage.awake_at(i);
|
|
1509
|
+
const rb = bodies[idx];
|
|
1305
1510
|
const list = lists[idx];
|
|
1306
|
-
|
|
1511
|
+
|
|
1512
|
+
if (list === undefined) {
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1307
1516
|
const lv = rb.linearVelocity;
|
|
1308
|
-
|
|
1517
|
+
|
|
1518
|
+
const list_length = list.length;
|
|
1519
|
+
|
|
1520
|
+
for (let k = 0; k < list_length; k++) {
|
|
1521
|
+
|
|
1309
1522
|
const entry = list[k];
|
|
1523
|
+
|
|
1310
1524
|
compute_fat_world_aabb(
|
|
1311
1525
|
scratch_world_aabb, 0,
|
|
1312
1526
|
entry.collider.shape, entry.transform,
|
|
1313
1527
|
lv[0], lv[1], lv[2],
|
|
1314
1528
|
dt
|
|
1315
1529
|
);
|
|
1530
|
+
|
|
1316
1531
|
this.dynamicBvh.node_move_aabb(entry.bvhNode, scratch_world_aabb);
|
|
1317
1532
|
}
|
|
1318
1533
|
}
|
|
@@ -1321,13 +1536,14 @@ export class PhysicsSystem extends System {
|
|
|
1321
1536
|
// outer-step motion, so the pair set stays valid across all substeps
|
|
1322
1537
|
// — broadphase runs once.
|
|
1323
1538
|
generate_pairs(
|
|
1324
|
-
|
|
1539
|
+
storage,
|
|
1325
1540
|
this.dynamicBvh,
|
|
1326
1541
|
this.staticBvh,
|
|
1327
|
-
|
|
1542
|
+
manifolds,
|
|
1328
1543
|
lists,
|
|
1329
1544
|
this.pairs,
|
|
1330
|
-
this.
|
|
1545
|
+
this.__pair_filter,
|
|
1546
|
+
this,
|
|
1331
1547
|
);
|
|
1332
1548
|
|
|
1333
1549
|
// Stage 4: wake propagation — through broadphase pairs, then through
|
|
@@ -1338,11 +1554,11 @@ export class PhysicsSystem extends System {
|
|
|
1338
1554
|
// Stage 5: narrowphase — once per outer step. The substep loop below
|
|
1339
1555
|
// re-derives each contact's penetration analytically from the moved
|
|
1340
1556
|
// poses rather than re-running geometry.
|
|
1341
|
-
narrowphase_step(this.pairs,
|
|
1557
|
+
narrowphase_step(this.pairs, manifolds, this.__body_collider_lists);
|
|
1342
1558
|
|
|
1343
1559
|
// Stage 6: partition awake bodies + touched contacts into islands.
|
|
1344
1560
|
// Consumed by the solver (flattened contact list) and the sleep test.
|
|
1345
|
-
this.islands.build(
|
|
1561
|
+
this.islands.build(storage, manifolds, bodies, this.__body_collider_lists, joints);
|
|
1346
1562
|
|
|
1347
1563
|
// Stage 7: TGS substep loop.
|
|
1348
1564
|
//
|
|
@@ -1359,7 +1575,7 @@ export class PhysicsSystem extends System {
|
|
|
1359
1575
|
// the loop.
|
|
1360
1576
|
const N = this.substeps;
|
|
1361
1577
|
const h = dt / N;
|
|
1362
|
-
const count_after_wake =
|
|
1578
|
+
const count_after_wake = storage.awake_count;
|
|
1363
1579
|
|
|
1364
1580
|
// CCD: capture start-of-step positions for flagged bodies over the
|
|
1365
1581
|
// post-wake awake set (poses are unchanged until the substep loop below
|
|
@@ -1368,21 +1584,37 @@ export class PhysicsSystem extends System {
|
|
|
1368
1584
|
// start matches the end the resolve pass reads. Zero-cost when no body
|
|
1369
1585
|
// is flagged.
|
|
1370
1586
|
const ccd_on = this.ccdEnabled;
|
|
1587
|
+
|
|
1371
1588
|
if (ccd_on) {
|
|
1372
|
-
|
|
1589
|
+
|
|
1590
|
+
const ccd_need = storage.high_water_mark * 3;
|
|
1591
|
+
|
|
1373
1592
|
if (this.__ccd_start_pos.length < ccd_need) {
|
|
1374
1593
|
this.__ccd_start_pos = new Float64Array(ccd_need);
|
|
1375
1594
|
}
|
|
1595
|
+
|
|
1376
1596
|
const ccd_start = this.__ccd_start_pos;
|
|
1597
|
+
|
|
1377
1598
|
for (let i = 0; i < count_after_wake; i++) {
|
|
1378
|
-
const idx =
|
|
1379
|
-
const rb =
|
|
1380
|
-
|
|
1381
|
-
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
|
+
|
|
1382
1609
|
const list = this.__body_collider_lists[idx];
|
|
1383
|
-
|
|
1610
|
+
|
|
1611
|
+
if (list === undefined || list.length === 0) {
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1384
1615
|
const cp = list[0].transform.position;
|
|
1385
1616
|
const cb = idx * 3;
|
|
1617
|
+
|
|
1386
1618
|
ccd_start[cb] = cp[0];
|
|
1387
1619
|
ccd_start[cb + 1] = cp[1];
|
|
1388
1620
|
ccd_start[cb + 2] = cp[2];
|
|
@@ -1395,55 +1627,117 @@ export class PhysicsSystem extends System {
|
|
|
1395
1627
|
// reallocate again this step.
|
|
1396
1628
|
this.__reset_pseudo_velocity();
|
|
1397
1629
|
const pseudoVel = this.__pseudo_velocity;
|
|
1398
|
-
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
|
+
}
|
|
1646
|
+
|
|
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];
|
|
1399
1663
|
|
|
1400
|
-
|
|
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);
|
|
1401
1688
|
|
|
1402
1689
|
for (let s = 0; s < N; s++) {
|
|
1690
|
+
|
|
1403
1691
|
// Gravity (+ damping) for this substep.
|
|
1404
1692
|
for (let i = 0; i < count_after_wake; i++) {
|
|
1405
|
-
const idx =
|
|
1406
|
-
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);
|
|
1407
1695
|
}
|
|
1408
1696
|
|
|
1409
1697
|
// Re-derive contact geometry at the current poses: concave pairs
|
|
1410
1698
|
// re-run narrowphase (fresh feature/normal as the body rocks),
|
|
1411
1699
|
// convex pairs rotate frozen anchors analytically. Then replay
|
|
1412
1700
|
// the per-substep warm-start and solve velocity.
|
|
1413
|
-
redetect_concave_contacts(
|
|
1414
|
-
refresh_contacts(
|
|
1415
|
-
warm_start_contacts(
|
|
1416
|
-
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);
|
|
1417
1705
|
|
|
1418
1706
|
// Joints share the substep: warm-start + velocity-solve the 6-DOF
|
|
1419
1707
|
// constraints on real velocity, coupled with the contacts above
|
|
1420
1708
|
// (a body touched by both sees one substep of Gauss-Seidel across
|
|
1421
1709
|
// contacts then joints). Position correction for locked DOFs is a
|
|
1422
1710
|
// SPOOK bias inside this solve, so no separate joint position pass.
|
|
1423
|
-
if (
|
|
1424
|
-
solve_joints(
|
|
1711
|
+
if (joints.length > 0) {
|
|
1712
|
+
solve_joints(joints, this, h, this.jointIterations);
|
|
1425
1713
|
}
|
|
1426
1714
|
|
|
1427
1715
|
// Position correction writes pseudo-velocity (zeroed first so it
|
|
1428
1716
|
// is a fresh per-substep correction), folded into the pose by the
|
|
1429
1717
|
// position integrate and then discarded.
|
|
1430
1718
|
pseudoVel.fill(0, 0, pseudo_len);
|
|
1431
|
-
solve_position(
|
|
1719
|
+
solve_position(manifolds, this, this.positionIterations);
|
|
1432
1720
|
|
|
1433
1721
|
for (let i = 0; i < count_after_wake; i++) {
|
|
1434
|
-
const idx =
|
|
1435
|
-
const rb =
|
|
1436
|
-
const tr =
|
|
1722
|
+
const idx = storage.awake_at(i);
|
|
1723
|
+
const rb = bodies[idx];
|
|
1724
|
+
const tr = transforms[idx];
|
|
1437
1725
|
const base = idx * 6;
|
|
1438
|
-
integrate_position(rb, tr, h,
|
|
1439
|
-
pseudoVel[base],
|
|
1726
|
+
integrate_position(ss_data, idx * SBS_STRIDE, rb, tr, h,
|
|
1727
|
+
pseudoVel[base], pseudoVel[base + 1], pseudoVel[base + 2],
|
|
1440
1728
|
pseudoVel[base + 3], pseudoVel[base + 4], pseudoVel[base + 5]);
|
|
1441
1729
|
}
|
|
1442
1730
|
}
|
|
1443
1731
|
|
|
1444
1732
|
// Stage 8: one-shot restitution, after the substep loop, keyed off
|
|
1445
1733
|
// the approach velocity captured at prepare time.
|
|
1446
|
-
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);
|
|
1447
1741
|
|
|
1448
1742
|
// Stage 8.5: continuous collision — sweep CCD-flagged fast movers along
|
|
1449
1743
|
// their net step translation and stop them at the first blocker, so they
|
|
@@ -1454,18 +1748,24 @@ export class PhysicsSystem extends System {
|
|
|
1454
1748
|
ccd_resolve(this);
|
|
1455
1749
|
}
|
|
1456
1750
|
|
|
1751
|
+
// Producer: record the post-step authoritative pose of every awake
|
|
1752
|
+
// interpolated body under this step's tick. Before the sleep test, so a
|
|
1753
|
+
// body that settles this step still records its final pose for the last
|
|
1754
|
+
// interpolation interval.
|
|
1755
|
+
this.__interp_record();
|
|
1756
|
+
|
|
1457
1757
|
// Stage 9: sleep test.
|
|
1458
1758
|
this.__sleep_test(dt);
|
|
1459
1759
|
|
|
1460
1760
|
// Stage 10: diff manifolds against the previous frame and dispatch
|
|
1461
1761
|
// Begin / Stay / End events. MUST run before advance_frame, which
|
|
1462
1762
|
// rolls the touched flags.
|
|
1463
|
-
diff_manifolds(
|
|
1763
|
+
diff_manifolds(manifolds, storage, this.contactEvents);
|
|
1464
1764
|
this.__dispatch_contact_events();
|
|
1465
1765
|
|
|
1466
1766
|
// Stage 11 (end-of-step): roll touched → prev_touched and evict slots
|
|
1467
1767
|
// whose pair has not been touched within the grace window.
|
|
1468
|
-
|
|
1768
|
+
manifolds.advance_frame();
|
|
1469
1769
|
}
|
|
1470
1770
|
}
|
|
1471
1771
|
|