@woosh/meep-engine 2.140.0 → 2.141.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/geom/3d/quaternion/quat3_multiply.d.ts +21 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.js +25 -0
- package/src/engine/physics/PLAN.md +152 -35
- package/src/engine/physics/REVIEW_002.md +151 -0
- package/src/engine/physics/constraint/DofMode.d.ts +28 -0
- package/src/engine/physics/constraint/DofMode.d.ts.map +1 -0
- package/src/engine/physics/constraint/DofMode.js +35 -0
- package/src/engine/physics/constraint/solve_constraints.d.ts +16 -0
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -0
- package/src/engine/physics/constraint/solve_constraints.js +436 -0
- package/src/engine/physics/ecs/Joint.d.ts +179 -0
- package/src/engine/physics/ecs/Joint.d.ts.map +1 -0
- package/src/engine/physics/ecs/Joint.js +234 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +52 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +126 -4
- package/src/engine/physics/fluid/FluidField.d.ts +14 -10
- package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidField.js +14 -10
- package/src/engine/physics/fluid/FluidSimulator.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidSimulator.js +0 -1
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts +17 -10
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +18 -11
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts +13 -10
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +18 -13
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts +4 -3
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +15 -11
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +24 -22
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +26 -22
- package/src/engine/physics/island/IslandBuilder.d.ts +4 -1
- package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
- package/src/engine/physics/island/IslandBuilder.js +33 -16
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +27 -1
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts +33 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +75 -0
- package/src/engine/physics/solver/solve_contacts.d.ts +28 -0
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +169 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import Vector3 from "../../../core/geom/Vector3.js";
|
|
2
|
+
import Quaternion from "../../../core/geom/Quaternion.js";
|
|
3
|
+
import { DofMode } from "../constraint/DofMode.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sentinel for {@link Joint#_jointId} before the joint is linked into a
|
|
7
|
+
* {@link PhysicsSystem}.
|
|
8
|
+
* @type {number}
|
|
9
|
+
*/
|
|
10
|
+
export const JOINT_UNALLOCATED = -1;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `entityB` value meaning "the world" — body A is constrained to a fixed
|
|
14
|
+
* world anchor rather than to a second body.
|
|
15
|
+
* @type {number}
|
|
16
|
+
*/
|
|
17
|
+
export const JOINT_WORLD = -1;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A configurable 6-DOF constraint between two bodies (or a body and the
|
|
21
|
+
* world). Connects body A's anchor frame to body B's; each of the 6 relative
|
|
22
|
+
* degrees of freedom (3 linear, 3 angular) is independently
|
|
23
|
+
* locked / free / limited / springy / motorised ({@link DofMode}). One
|
|
24
|
+
* constraint type expresses the whole joint taxonomy — see DofMode.
|
|
25
|
+
*
|
|
26
|
+
* The default configuration is a **ball-socket** (point-to-point): the three
|
|
27
|
+
* linear DOFs LOCKED (the two anchor points are held coincident), the three
|
|
28
|
+
* angular DOFs FREE. That alone gives chains, ropes, and pendulums.
|
|
29
|
+
*
|
|
30
|
+
* Anchors live in body-local frames. When `entityB === JOINT_WORLD`,
|
|
31
|
+
* `localAnchorB` is interpreted as a fixed **world** point instead of a
|
|
32
|
+
* local anchor on a second body.
|
|
33
|
+
*
|
|
34
|
+
* Solver state ({@link dofImpulse}, the `_*` fields) is owned by the
|
|
35
|
+
* PhysicsSystem; user code must not write it.
|
|
36
|
+
*
|
|
37
|
+
* @author Alex Goldring
|
|
38
|
+
* @copyright Company Named Limited (c) 2026
|
|
39
|
+
*/
|
|
40
|
+
export class Joint {
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Entity owning body A (the constraint's first body — always a real body).
|
|
44
|
+
* @type {number}
|
|
45
|
+
*/
|
|
46
|
+
entityA = -1;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Entity owning body B, or {@link JOINT_WORLD} to anchor A to the world.
|
|
50
|
+
* @type {number}
|
|
51
|
+
*/
|
|
52
|
+
entityB = JOINT_WORLD;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Anchor point on body A, in A's local frame.
|
|
56
|
+
* @readonly
|
|
57
|
+
* @type {Vector3}
|
|
58
|
+
*/
|
|
59
|
+
localAnchorA = new Vector3(0, 0, 0);
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Anchor point on body B in B's local frame — or, when
|
|
63
|
+
* `entityB === JOINT_WORLD`, a fixed world-space anchor point.
|
|
64
|
+
* @readonly
|
|
65
|
+
* @type {Vector3}
|
|
66
|
+
*/
|
|
67
|
+
localAnchorB = new Vector3(0, 0, 0);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Orientation of the joint's reference frame on body A, in A's local
|
|
71
|
+
* space (a unit quaternion; identity = the joint frame is the body
|
|
72
|
+
* frame). The 3 linear and 3 angular DOFs are expressed in this frame:
|
|
73
|
+
* locked/limited/etc. axes are the frame's axes, not world axes. For a
|
|
74
|
+
* hinge, the free angular axis is one of these frame axes — orient the
|
|
75
|
+
* frame so that axis is the hinge axis.
|
|
76
|
+
* @readonly
|
|
77
|
+
* @type {Quaternion}
|
|
78
|
+
*/
|
|
79
|
+
localBasisA = new Quaternion();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Orientation of the joint's reference frame on body B, in B's local
|
|
83
|
+
* space. At rest a fully-locked joint holds frame B aligned to frame A.
|
|
84
|
+
* When `entityB === JOINT_WORLD`, the world frame is treated as identity
|
|
85
|
+
* (angular locks hold A's frame to world axes).
|
|
86
|
+
* @readonly
|
|
87
|
+
* @type {Quaternion}
|
|
88
|
+
*/
|
|
89
|
+
localBasisB = new Quaternion();
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Per-DOF mode, 6 entries: `[lin x, lin y, lin z, ang x, ang y, ang z]`.
|
|
93
|
+
* Default = ball-socket (linear locked, angular free).
|
|
94
|
+
* @readonly
|
|
95
|
+
* @type {Uint8Array}
|
|
96
|
+
*/
|
|
97
|
+
dofMode = Uint8Array.from([
|
|
98
|
+
DofMode.LOCKED, DofMode.LOCKED, DofMode.LOCKED,
|
|
99
|
+
DofMode.FREE, DofMode.FREE, DofMode.FREE,
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Lower / upper bounds per DOF, used by {@link DofMode.LIMITED}. Linear in
|
|
104
|
+
* metres, angular in radians. (Reserved — consumed once the LIMITED mode
|
|
105
|
+
* lands.)
|
|
106
|
+
* @readonly
|
|
107
|
+
* @type {Float64Array}
|
|
108
|
+
*/
|
|
109
|
+
dofLowerLimit = new Float64Array(6);
|
|
110
|
+
/** @readonly @type {Float64Array} */
|
|
111
|
+
dofUpperLimit = new Float64Array(6);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Spring stiffness / damping per DOF, used by {@link DofMode.SPRING}.
|
|
115
|
+
* (Reserved — consumed once the SPRING mode lands.)
|
|
116
|
+
* @readonly
|
|
117
|
+
* @type {Float64Array}
|
|
118
|
+
*/
|
|
119
|
+
dofStiffness = new Float64Array(6);
|
|
120
|
+
/** @readonly @type {Float64Array} */
|
|
121
|
+
dofDamping = new Float64Array(6);
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Motor target velocity / max force per DOF, used by
|
|
125
|
+
* {@link DofMode.MOTOR}. (Reserved — consumed once the MOTOR mode lands.)
|
|
126
|
+
* @readonly
|
|
127
|
+
* @type {Float64Array}
|
|
128
|
+
*/
|
|
129
|
+
dofMotorTargetVelocity = new Float64Array(6);
|
|
130
|
+
/** @readonly @type {Float64Array} */
|
|
131
|
+
dofMotorMaxForce = new Float64Array(6);
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Accumulated constraint impulse per DOF — warm-start state carried across
|
|
135
|
+
* frames. System-owned.
|
|
136
|
+
* @readonly
|
|
137
|
+
* @type {Float64Array}
|
|
138
|
+
*/
|
|
139
|
+
dofImpulse = new Float64Array(6);
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Resolved packed body id of A, set at link time. System-private.
|
|
143
|
+
* @type {number}
|
|
144
|
+
*/
|
|
145
|
+
_bodyIdA = -1;
|
|
146
|
+
/**
|
|
147
|
+
* Resolved packed body id of B, or {@link JOINT_WORLD}. System-private.
|
|
148
|
+
* @type {number}
|
|
149
|
+
*/
|
|
150
|
+
_bodyIdB = JOINT_WORLD;
|
|
151
|
+
/**
|
|
152
|
+
* Packed joint handle assigned at link. System-private.
|
|
153
|
+
* @type {number}
|
|
154
|
+
*/
|
|
155
|
+
_jointId = JOINT_UNALLOCATED;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Configure this joint as a ball-socket (point-to-point) between two
|
|
159
|
+
* anchors. Linear DOFs locked, angular free — the default, provided as a
|
|
160
|
+
* named helper for clarity at call sites.
|
|
161
|
+
* @returns {Joint} this
|
|
162
|
+
*/
|
|
163
|
+
asBallSocket() {
|
|
164
|
+
this.dofMode[0] = DofMode.LOCKED;
|
|
165
|
+
this.dofMode[1] = DofMode.LOCKED;
|
|
166
|
+
this.dofMode[2] = DofMode.LOCKED;
|
|
167
|
+
this.dofMode[3] = DofMode.FREE;
|
|
168
|
+
this.dofMode[4] = DofMode.FREE;
|
|
169
|
+
this.dofMode[5] = DofMode.FREE;
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Configure as a weld (fixed) joint: all 6 DOFs locked, holding the two
|
|
175
|
+
* bodies rigidly in their relative pose.
|
|
176
|
+
* @returns {Joint} this
|
|
177
|
+
*/
|
|
178
|
+
asWeld() {
|
|
179
|
+
for (let i = 0; i < 6; i++) this.dofMode[i] = DofMode.LOCKED;
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Configure as a hinge (revolute): 3 linear + 2 angular locked, free to
|
|
185
|
+
* rotate about a single frame axis. Orient {@link localBasisA} /
|
|
186
|
+
* {@link localBasisB} so that frame axis is the desired hinge axis.
|
|
187
|
+
*
|
|
188
|
+
* @param {number} [freeAngularAxis] which frame axis is free: 0 = x
|
|
189
|
+
* (default), 1 = y, 2 = z.
|
|
190
|
+
* @returns {Joint} this
|
|
191
|
+
*/
|
|
192
|
+
asHinge(freeAngularAxis = 0) {
|
|
193
|
+
this.dofMode[0] = DofMode.LOCKED;
|
|
194
|
+
this.dofMode[1] = DofMode.LOCKED;
|
|
195
|
+
this.dofMode[2] = DofMode.LOCKED;
|
|
196
|
+
this.dofMode[3] = DofMode.LOCKED;
|
|
197
|
+
this.dofMode[4] = DofMode.LOCKED;
|
|
198
|
+
this.dofMode[5] = DofMode.LOCKED;
|
|
199
|
+
this.dofMode[3 + freeAngularAxis] = DofMode.FREE;
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Configure as a prismatic (slider): 2 linear + 3 angular locked, free to
|
|
205
|
+
* translate along a single frame axis. Orient {@link localBasisA} /
|
|
206
|
+
* {@link localBasisB} so that frame axis is the desired slide direction.
|
|
207
|
+
*
|
|
208
|
+
* @param {number} [freeLinearAxis] which frame axis is free: 0 = x
|
|
209
|
+
* (default), 1 = y, 2 = z.
|
|
210
|
+
* @returns {Joint} this
|
|
211
|
+
*/
|
|
212
|
+
asPrismatic(freeLinearAxis = 0) {
|
|
213
|
+
this.dofMode[0] = DofMode.LOCKED;
|
|
214
|
+
this.dofMode[1] = DofMode.LOCKED;
|
|
215
|
+
this.dofMode[2] = DofMode.LOCKED;
|
|
216
|
+
this.dofMode[freeLinearAxis] = DofMode.FREE;
|
|
217
|
+
this.dofMode[3] = DofMode.LOCKED;
|
|
218
|
+
this.dofMode[4] = DofMode.LOCKED;
|
|
219
|
+
this.dofMode[5] = DofMode.LOCKED;
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* @readonly
|
|
226
|
+
* @type {string}
|
|
227
|
+
*/
|
|
228
|
+
Joint.typeName = "Joint";
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* @readonly
|
|
232
|
+
* @type {boolean}
|
|
233
|
+
*/
|
|
234
|
+
Joint.prototype.isJoint = true;
|
|
@@ -157,6 +157,27 @@ export class PhysicsSystem extends System<any, any, any, any, any> {
|
|
|
157
157
|
entity: number;
|
|
158
158
|
bvhNode: number;
|
|
159
159
|
}>>;
|
|
160
|
+
/**
|
|
161
|
+
* Live {@link Joint} (6-DOF constraint) instances, in a sparse array
|
|
162
|
+
* indexed by joint id. Solved alongside contacts inside the TGS
|
|
163
|
+
* substep loop. Holes (unlinked joints) are `undefined`.
|
|
164
|
+
* @type {Joint[]}
|
|
165
|
+
*/
|
|
166
|
+
__joints: Joint[];
|
|
167
|
+
/**
|
|
168
|
+
* Lowest free index in {@link __joints} for slot reuse on link.
|
|
169
|
+
* @private
|
|
170
|
+
* @type {number}
|
|
171
|
+
*/
|
|
172
|
+
private __joint_free;
|
|
173
|
+
/**
|
|
174
|
+
* Velocity iterations per substep for the joint solver. Joints (and
|
|
175
|
+
* especially joint chains) want a few more iterations than contacts to
|
|
176
|
+
* propagate impulses along the chain; cheap because joints are far
|
|
177
|
+
* fewer than contacts.
|
|
178
|
+
* @type {number}
|
|
179
|
+
*/
|
|
180
|
+
jointIterations: number;
|
|
160
181
|
/**
|
|
161
182
|
* Per-body pseudo-velocity for the Catto split-impulse position
|
|
162
183
|
* pass (TGS Phase 1). Flat layout, 6 doubles per body slot index:
|
|
@@ -305,6 +326,23 @@ export class PhysicsSystem extends System<any, any, any, any, any> {
|
|
|
305
326
|
* @returns {number} body index or -1
|
|
306
327
|
*/
|
|
307
328
|
private __find_body_index_by_entity;
|
|
329
|
+
/**
|
|
330
|
+
* Register a {@link Joint} (6-DOF constraint). Resolves the joint's
|
|
331
|
+
* entities to packed body ids and adds it to the active set, where it is
|
|
332
|
+
* solved alongside contacts in the TGS substep loop. Body A must be a
|
|
333
|
+
* linked body; body B is either a linked body or {@link JOINT_WORLD}
|
|
334
|
+
* (anchoring A to a fixed world point — `localAnchorB` is then a world
|
|
335
|
+
* coordinate).
|
|
336
|
+
*
|
|
337
|
+
* @param {Joint} joint
|
|
338
|
+
*/
|
|
339
|
+
link_joint(joint: Joint): void;
|
|
340
|
+
/**
|
|
341
|
+
* Remove a previously {@link link_joint}'d joint from the active set.
|
|
342
|
+
* Idempotent.
|
|
343
|
+
* @param {Joint} joint
|
|
344
|
+
*/
|
|
345
|
+
unlink_joint(joint: Joint): void;
|
|
308
346
|
/**
|
|
309
347
|
* Resolve a packed body id to its entity, or `-1` if the id is stale.
|
|
310
348
|
* @param {number} packed_body_id
|
|
@@ -567,6 +605,20 @@ export class PhysicsSystem extends System<any, any, any, any, any> {
|
|
|
567
605
|
* @private
|
|
568
606
|
*/
|
|
569
607
|
private __wake_pairs;
|
|
608
|
+
/**
|
|
609
|
+
* Wake propagation across joints: if a joint connects an awake body to a
|
|
610
|
+
* sleeping one, wake the sleeper so the constraint stays coupled (a joint
|
|
611
|
+
* with one body asleep and one awake would otherwise be skipped by the
|
|
612
|
+
* solver, letting the awake side drift). A common trigger is a
|
|
613
|
+
* kinematic/motor-driven body pulling on a sleeping chain.
|
|
614
|
+
*
|
|
615
|
+
* Bodies that slept together as one island share a sleep group, so the
|
|
616
|
+
* usual atomic wake already revives the whole chain when any member is
|
|
617
|
+
* hit; this catches the cases that atomic wake doesn't (joint spanning
|
|
618
|
+
* separate groups, kinematic driver).
|
|
619
|
+
* @private
|
|
620
|
+
*/
|
|
621
|
+
private __wake_joints;
|
|
570
622
|
/**
|
|
571
623
|
* Per-island atomic sleep test. Walks each island once and applies the
|
|
572
624
|
* decision uniformly across all members:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PhysicsSystem.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/ecs/PhysicsSystem.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PhysicsSystem.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/ecs/PhysicsSystem.js"],"names":[],"mappings":"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"}
|
|
@@ -25,11 +25,14 @@ import { returnTrue } from "../../../core/function/returnTrue.js";
|
|
|
25
25
|
import {
|
|
26
26
|
prepare_contacts,
|
|
27
27
|
refresh_contacts,
|
|
28
|
+
redetect_concave_contacts,
|
|
28
29
|
warm_start_contacts,
|
|
29
30
|
solve_velocity,
|
|
30
31
|
apply_restitution,
|
|
31
32
|
solve_position,
|
|
32
33
|
} from "../solver/solve_contacts.js";
|
|
34
|
+
import { solve_joints } from "../constraint/solve_constraints.js";
|
|
35
|
+
import { JOINT_WORLD, JOINT_UNALLOCATED } from "./Joint.js";
|
|
33
36
|
import { world_inverse_inertia_apply } from "../inertia/world_inverse_inertia.js";
|
|
34
37
|
import { PhysicsEvents } from "./PhysicsEvents.js";
|
|
35
38
|
|
|
@@ -248,6 +251,30 @@ export class PhysicsSystem extends System {
|
|
|
248
251
|
*/
|
|
249
252
|
this.__body_collider_lists = [];
|
|
250
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Live {@link Joint} (6-DOF constraint) instances, in a sparse array
|
|
256
|
+
* indexed by joint id. Solved alongside contacts inside the TGS
|
|
257
|
+
* substep loop. Holes (unlinked joints) are `undefined`.
|
|
258
|
+
* @type {Joint[]}
|
|
259
|
+
*/
|
|
260
|
+
this.__joints = [];
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Lowest free index in {@link __joints} for slot reuse on link.
|
|
264
|
+
* @private
|
|
265
|
+
* @type {number}
|
|
266
|
+
*/
|
|
267
|
+
this.__joint_free = [];
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Velocity iterations per substep for the joint solver. Joints (and
|
|
271
|
+
* especially joint chains) want a few more iterations than contacts to
|
|
272
|
+
* propagate impulses along the chain; cheap because joints are far
|
|
273
|
+
* fewer than contacts.
|
|
274
|
+
* @type {number}
|
|
275
|
+
*/
|
|
276
|
+
this.jointIterations = 8;
|
|
277
|
+
|
|
251
278
|
/**
|
|
252
279
|
* Per-body pseudo-velocity for the Catto split-impulse position
|
|
253
280
|
* pass (TGS Phase 1). Flat layout, 6 doubles per body slot index:
|
|
@@ -570,6 +597,53 @@ export class PhysicsSystem extends System {
|
|
|
570
597
|
return -1;
|
|
571
598
|
}
|
|
572
599
|
|
|
600
|
+
/**
|
|
601
|
+
* Register a {@link Joint} (6-DOF constraint). Resolves the joint's
|
|
602
|
+
* entities to packed body ids and adds it to the active set, where it is
|
|
603
|
+
* solved alongside contacts in the TGS substep loop. Body A must be a
|
|
604
|
+
* linked body; body B is either a linked body or {@link JOINT_WORLD}
|
|
605
|
+
* (anchoring A to a fixed world point — `localAnchorB` is then a world
|
|
606
|
+
* coordinate).
|
|
607
|
+
*
|
|
608
|
+
* @param {Joint} joint
|
|
609
|
+
*/
|
|
610
|
+
link_joint(joint) {
|
|
611
|
+
const idxA = this.__find_body_index_by_entity(joint.entityA);
|
|
612
|
+
assert.notEqual(idxA, -1, `link_joint: no body for entityA ${joint.entityA}`);
|
|
613
|
+
joint._bodyIdA = this.__bodies[idxA]._bodyId;
|
|
614
|
+
|
|
615
|
+
if (joint.entityB === JOINT_WORLD) {
|
|
616
|
+
joint._bodyIdB = JOINT_WORLD;
|
|
617
|
+
} else {
|
|
618
|
+
const idxB = this.__find_body_index_by_entity(joint.entityB);
|
|
619
|
+
assert.notEqual(idxB, -1, `link_joint: no body for entityB ${joint.entityB}`);
|
|
620
|
+
joint._bodyIdB = this.__bodies[idxB]._bodyId;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Reuse a freed slot if available so joint ids stay dense-ish.
|
|
624
|
+
let id;
|
|
625
|
+
if (this.__joint_free.length > 0) {
|
|
626
|
+
id = this.__joint_free.pop();
|
|
627
|
+
} else {
|
|
628
|
+
id = this.__joints.length;
|
|
629
|
+
}
|
|
630
|
+
joint._jointId = id;
|
|
631
|
+
this.__joints[id] = joint;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Remove a previously {@link link_joint}'d joint from the active set.
|
|
636
|
+
* Idempotent.
|
|
637
|
+
* @param {Joint} joint
|
|
638
|
+
*/
|
|
639
|
+
unlink_joint(joint) {
|
|
640
|
+
const id = joint._jointId;
|
|
641
|
+
if (id === JOINT_UNALLOCATED || this.__joints[id] !== joint) return;
|
|
642
|
+
this.__joints[id] = undefined;
|
|
643
|
+
this.__joint_free.push(id);
|
|
644
|
+
joint._jointId = JOINT_UNALLOCATED;
|
|
645
|
+
}
|
|
646
|
+
|
|
573
647
|
/**
|
|
574
648
|
* Resolve a packed body id to its entity, or `-1` if the id is stale.
|
|
575
649
|
* @param {number} packed_body_id
|
|
@@ -990,6 +1064,40 @@ export class PhysicsSystem extends System {
|
|
|
990
1064
|
}
|
|
991
1065
|
}
|
|
992
1066
|
|
|
1067
|
+
/**
|
|
1068
|
+
* Wake propagation across joints: if a joint connects an awake body to a
|
|
1069
|
+
* sleeping one, wake the sleeper so the constraint stays coupled (a joint
|
|
1070
|
+
* with one body asleep and one awake would otherwise be skipped by the
|
|
1071
|
+
* solver, letting the awake side drift). A common trigger is a
|
|
1072
|
+
* kinematic/motor-driven body pulling on a sleeping chain.
|
|
1073
|
+
*
|
|
1074
|
+
* Bodies that slept together as one island share a sleep group, so the
|
|
1075
|
+
* usual atomic wake already revives the whole chain when any member is
|
|
1076
|
+
* hit; this catches the cases that atomic wake doesn't (joint spanning
|
|
1077
|
+
* separate groups, kinematic driver).
|
|
1078
|
+
* @private
|
|
1079
|
+
*/
|
|
1080
|
+
__wake_joints() {
|
|
1081
|
+
const joints = this.__joints;
|
|
1082
|
+
const n = joints.length;
|
|
1083
|
+
if (n === 0) return;
|
|
1084
|
+
const bodies = this.__bodies;
|
|
1085
|
+
const storage = this.storage;
|
|
1086
|
+
for (let i = 0; i < n; i++) {
|
|
1087
|
+
const joint = joints[i];
|
|
1088
|
+
if (joint === undefined || joint === null) continue;
|
|
1089
|
+
if (joint._bodyIdB === JOINT_WORLD) continue;
|
|
1090
|
+
if (!storage.is_valid(joint._bodyIdA) || !storage.is_valid(joint._bodyIdB)) continue;
|
|
1091
|
+
const a = bodies[body_id_index(joint._bodyIdA)];
|
|
1092
|
+
const b = bodies[body_id_index(joint._bodyIdB)];
|
|
1093
|
+
if (a === undefined || b === undefined) continue;
|
|
1094
|
+
const aSleep = a.sleepState === SleepState.Sleeping;
|
|
1095
|
+
const bSleep = b.sleepState === SleepState.Sleeping;
|
|
1096
|
+
if (aSleep === bSleep) continue; // both awake or both asleep — leave as is
|
|
1097
|
+
if (aSleep) this.__wake_body(a); else this.__wake_body(b);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
993
1101
|
/**
|
|
994
1102
|
* Per-island atomic sleep test. Walks each island once and applies the
|
|
995
1103
|
* decision uniformly across all members:
|
|
@@ -1203,8 +1311,10 @@ export class PhysicsSystem extends System {
|
|
|
1203
1311
|
this.__pair_filter_bound,
|
|
1204
1312
|
);
|
|
1205
1313
|
|
|
1206
|
-
// Stage 4: wake propagation
|
|
1314
|
+
// Stage 4: wake propagation — through broadphase pairs, then through
|
|
1315
|
+
// joints (a joint must not have one body awake and one asleep).
|
|
1207
1316
|
this.__wake_pairs();
|
|
1317
|
+
this.__wake_joints();
|
|
1208
1318
|
|
|
1209
1319
|
// Stage 5: narrowphase — once per outer step. The substep loop below
|
|
1210
1320
|
// re-derives each contact's penetration analytically from the moved
|
|
@@ -1213,7 +1323,7 @@ export class PhysicsSystem extends System {
|
|
|
1213
1323
|
|
|
1214
1324
|
// Stage 6: partition awake bodies + touched contacts into islands.
|
|
1215
1325
|
// Consumed by the solver (flattened contact list) and the sleep test.
|
|
1216
|
-
this.islands.build(this.storage, this.manifolds, this.__bodies, this.__body_collider_lists);
|
|
1326
|
+
this.islands.build(this.storage, this.manifolds, this.__bodies, this.__body_collider_lists, this.__joints);
|
|
1217
1327
|
|
|
1218
1328
|
// Stage 7: TGS substep loop.
|
|
1219
1329
|
//
|
|
@@ -1249,12 +1359,24 @@ export class PhysicsSystem extends System {
|
|
|
1249
1359
|
integrate_velocity_gravity(this.__bodies[idx], this.__transforms[idx], gx, gy, gz, h);
|
|
1250
1360
|
}
|
|
1251
1361
|
|
|
1252
|
-
// Re-derive contact geometry at the current poses
|
|
1253
|
-
//
|
|
1362
|
+
// Re-derive contact geometry at the current poses: concave pairs
|
|
1363
|
+
// re-run narrowphase (fresh feature/normal as the body rocks),
|
|
1364
|
+
// convex pairs rotate frozen anchors analytically. Then replay
|
|
1365
|
+
// the per-substep warm-start and solve velocity.
|
|
1366
|
+
redetect_concave_contacts(this.manifolds, this);
|
|
1254
1367
|
refresh_contacts(this.manifolds, this);
|
|
1255
1368
|
warm_start_contacts(this.manifolds, this);
|
|
1256
1369
|
solve_velocity(this.manifolds, this, this.velocityIterations);
|
|
1257
1370
|
|
|
1371
|
+
// Joints share the substep: warm-start + velocity-solve the 6-DOF
|
|
1372
|
+
// constraints on real velocity, coupled with the contacts above
|
|
1373
|
+
// (a body touched by both sees one substep of Gauss-Seidel across
|
|
1374
|
+
// contacts then joints). Position correction for locked DOFs is a
|
|
1375
|
+
// SPOOK bias inside this solve, so no separate joint position pass.
|
|
1376
|
+
if (this.__joints.length > 0) {
|
|
1377
|
+
solve_joints(this.__joints, this, h, this.jointIterations);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1258
1380
|
// Position correction writes pseudo-velocity (zeroed first so it
|
|
1259
1381
|
// is a fresh per-substep correction), folded into the pose by the
|
|
1260
1382
|
// position integrate and then discarded.
|
|
@@ -45,17 +45,21 @@ export class FluidField {
|
|
|
45
45
|
* Per-cell pre-baked neighbourhood mask consumed by the pressure SOR loop.
|
|
46
46
|
* One byte per cell encoding which of the 6 cardinal neighbours are fluid:
|
|
47
47
|
*
|
|
48
|
-
* bit 0 (= 1)
|
|
49
|
-
* bit 1 (= 2)
|
|
50
|
-
* bit 2 (= 4)
|
|
51
|
-
* bit 3 (= 8)
|
|
52
|
-
* bit 4 (= 16)
|
|
53
|
-
* bit 5 (= 32)
|
|
48
|
+
* bit 0 (= 1) : -x neighbour is fluid (in-bounds AND non-solid)
|
|
49
|
+
* bit 1 (= 2) : +x neighbour is fluid
|
|
50
|
+
* bit 2 (= 4) : -y neighbour is fluid
|
|
51
|
+
* bit 3 (= 8) : +y neighbour is fluid
|
|
52
|
+
* bit 4 (= 16) : -z neighbour is fluid
|
|
53
|
+
* bit 5 (= 32) : +z neighbour is fluid
|
|
54
|
+
* bit 7 (= 128) : this cell is itself solid
|
|
54
55
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* "this
|
|
58
|
-
*
|
|
56
|
+
* A solid cell encodes as `128` (bit 7 set, neighbour bits clear) and an
|
|
57
|
+
* isolated fluid cell as `0`, so the SOR / PCG loops' `if ((mask & 63) === 0)
|
|
58
|
+
* continue` skip handles both "this cell is solid" and "this fluid cell is
|
|
59
|
+
* isolated" with a single comparison instead of one solid-self-check plus six
|
|
60
|
+
* solid-neighbour-checks plus six boundary checks. Bit 7 additionally lets
|
|
61
|
+
* {@link v3_grid_subtract_pressure_gradient} zero solid-cell velocity without
|
|
62
|
+
* consulting the {@link solid} array.
|
|
59
63
|
*
|
|
60
64
|
* Computed once by {@link recomputeSolidNeighbourMask} after every solid-mask
|
|
61
65
|
* mutation. The simulator runs that recompute at the top of every project()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FluidField.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/fluid/FluidField.js"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAQI;;;;OAIG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;OAEG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;OAEG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;;;;;OAMG;IACH,OAFU,UAAU,GAAC,IAAI,CAEZ;IAEb
|
|
1
|
+
{"version":3,"file":"FluidField.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/fluid/FluidField.js"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAQI;;;;OAIG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;OAEG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;OAEG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;;;;;OAMG;IACH,OAFU,UAAU,GAAC,IAAI,CAEZ;IAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,sBAFU,UAAU,GAAC,IAAI,CAEG;IAE5B;;;;;;;;;;;;;OAaG;IACH,UAFU,YAAY,kBAAc,IAAI,CAExB;IAEhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,yBAFU,OAAO,CAEe;IAShC;;;;;;OAMG;IACH,qBAJW,MAAM,SACN,MAAM,SACN,MAAM,QAWhB;IAED;;;OAGG;IACH,iBAHY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAKnC;IAED;;;OAGG;IACH,aAHY,MAAM,CAKjB;IAED;;;;;;;;;OASG;IACH,gBAHW,MAAM,GACL,MAAM,CAejB;IAED;;;OAGG;IACH,kBAHW,MAAM,GACL,MAAM,CAIjB;IAED;;;;OAIG;IACH,oBAJW,MAAM,GACL,YAAY,GAAC,IAAI,CAM5B;IAED;;OAEG;IACH,uBAFY;QAAE,MAAM,MAAM,CAAC;QAAC,IAAI,EAAE,YAAY,CAAA;KAAE,EAAE,CAIjD;IAED;;;OAGG;IACH,cAmBC;IAED;;;;;;OAMG;IACH,aALW,MAAM,KACN,MAAM,KACN,MAAM,GACL,MAAM,CAajB;IAED;;;;;;;;OAQG;IACH,iBAPW,MAAM,KACN,MAAM,KACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QAOhB;IAED;;;;;;;;OAQG;IACH,iBAPW,MAAM,KACN,MAAM,KACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,QAOhB;IAED;;;;;;OAMG;IACH,kCANW,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,SACN,MAAM,QAOhB;IAED;;;;;;OAMG;IACH,cALW,MAAM,KACN,MAAM,KACN,MAAM,YACN,OAAO,QAIjB;IAED;;;;;OAKG;IACH,aALW,MAAM,KACN,MAAM,KACN,MAAM,GACL,OAAO,CAIlB;IAED;;;;;;;;OAQG;IACH,oBANW,YAAY,GAAC,MAAM,EAAE,KACrB,MAAM,KACN,MAAM,KACN,MAAM,GACL,YAAY,GAAC,MAAM,EAAE,CAUhC;IAED;;;;;;;OAOG;IACH,mBANW,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,GACL,MAAM,CAMjB;IAED;;;;;;;;;;OAUG;IACH,cAHW,UAAU,GACT,OAAO,CAqDlB;IAED;;;;;;;;;;;OAWG;IACH,QAFY,MAAM,CAajB;IAED;;;;;;;;;OASG;IACH,oCASC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,eAJW,MAAM,WACN,MAAM,WACN,MAAM,QAyChB;IAED;;;;OAIG;IACH,cASC;;CACJ"}
|
|
@@ -65,17 +65,21 @@ export class FluidField {
|
|
|
65
65
|
* Per-cell pre-baked neighbourhood mask consumed by the pressure SOR loop.
|
|
66
66
|
* One byte per cell encoding which of the 6 cardinal neighbours are fluid:
|
|
67
67
|
*
|
|
68
|
-
* bit 0 (= 1)
|
|
69
|
-
* bit 1 (= 2)
|
|
70
|
-
* bit 2 (= 4)
|
|
71
|
-
* bit 3 (= 8)
|
|
72
|
-
* bit 4 (= 16)
|
|
73
|
-
* bit 5 (= 32)
|
|
68
|
+
* bit 0 (= 1) : -x neighbour is fluid (in-bounds AND non-solid)
|
|
69
|
+
* bit 1 (= 2) : +x neighbour is fluid
|
|
70
|
+
* bit 2 (= 4) : -y neighbour is fluid
|
|
71
|
+
* bit 3 (= 8) : +y neighbour is fluid
|
|
72
|
+
* bit 4 (= 16) : -z neighbour is fluid
|
|
73
|
+
* bit 5 (= 32) : +z neighbour is fluid
|
|
74
|
+
* bit 7 (= 128) : this cell is itself solid
|
|
74
75
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* "this
|
|
78
|
-
*
|
|
76
|
+
* A solid cell encodes as `128` (bit 7 set, neighbour bits clear) and an
|
|
77
|
+
* isolated fluid cell as `0`, so the SOR / PCG loops' `if ((mask & 63) === 0)
|
|
78
|
+
* continue` skip handles both "this cell is solid" and "this fluid cell is
|
|
79
|
+
* isolated" with a single comparison instead of one solid-self-check plus six
|
|
80
|
+
* solid-neighbour-checks plus six boundary checks. Bit 7 additionally lets
|
|
81
|
+
* {@link v3_grid_subtract_pressure_gradient} zero solid-cell velocity without
|
|
82
|
+
* consulting the {@link solid} array.
|
|
79
83
|
*
|
|
80
84
|
* Computed once by {@link recomputeSolidNeighbourMask} after every solid-mask
|
|
81
85
|
* mutation. The simulator runs that recompute at the top of every project()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FluidSimulator.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/fluid/FluidSimulator.js"],"names":[],"mappings":";;;;;;;;;;;;;;;6BAyBU,MAAM;;;;;AAOhB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;;;;;;;;;GASG;AACH,wCAFU,MAAM,CAE2B;AAE3C;IAEI;;;OAGG;IACH,yBAFU,MAAM,CAEY;IAE5B;;;;OAIG;IACH,+BAFU,MAAM,CAEkB;IAElC;;;;;OAKG;IACH,uBAFU,MAAM,CAEU;IAE1B;;OAEG;IACH,6BAFU,MAAM,CAEgB;IAEhC;;;;;;;;;;;;;;;OAeG;IACH,iBAFU,MAAM,CAEqB;IAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,0BAFU,OAAO,CAEe;IAEhC;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,0BAFU,MAAM,CAEgB;IA+EhC;;;;;;;;;;;;OAYG;IACH,iCALW,MAAM,SACN,MAAM,SACN,MAAM,GACL,MAAM,CAUjB;IAED;;;;;;OAMG;IACH,
|
|
1
|
+
{"version":3,"file":"FluidSimulator.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/fluid/FluidSimulator.js"],"names":[],"mappings":";;;;;;;;;;;;;;;6BAyBU,MAAM;;;;;AAOhB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;;;;;;;;;GASG;AACH,wCAFU,MAAM,CAE2B;AAE3C;IAEI;;;OAGG;IACH,yBAFU,MAAM,CAEY;IAE5B;;;;OAIG;IACH,+BAFU,MAAM,CAEkB;IAElC;;;;;OAKG;IACH,uBAFU,MAAM,CAEU;IAE1B;;OAEG;IACH,6BAFU,MAAM,CAEgB;IAEhC;;;;;;;;;;;;;;;OAeG;IACH,iBAFU,MAAM,CAEqB;IAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,0BAFU,OAAO,CAEe;IAEhC;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,0BAFU,MAAM,CAEgB;IA+EhC;;;;;;;;;;;;OAYG;IACH,iCALW,MAAM,SACN,MAAM,SACN,MAAM,GACL,MAAM,CAUjB;IAED;;;;;;OAMG;IACH,iCAkDC;IAqBD;;;;;;;;;;;;;OAaG;IACH,oCAVW,MAAM,aAEN,uBAAuB,iBACvB,YAAY,GAAC,MAAM,EAAE,QAyF/B;;CACJ"}
|
|
@@ -4,18 +4,25 @@
|
|
|
4
4
|
* {@link v3_grid_solve_pressure}, which uses the mask to skip boundary checks
|
|
5
5
|
* and solid-neighbour checks entirely.
|
|
6
6
|
*
|
|
7
|
-
* Encoding (one byte per cell
|
|
7
|
+
* Encoding (one byte per cell):
|
|
8
8
|
*
|
|
9
|
-
* bit 0 (= 1)
|
|
10
|
-
* bit 1 (= 2)
|
|
11
|
-
* bit 2 (= 4)
|
|
12
|
-
* bit 3 (= 8)
|
|
13
|
-
* bit 4 (= 16)
|
|
14
|
-
* bit 5 (= 32)
|
|
9
|
+
* bit 0 (= 1) : -x neighbour is fluid
|
|
10
|
+
* bit 1 (= 2) : +x neighbour is fluid
|
|
11
|
+
* bit 2 (= 4) : -y neighbour is fluid
|
|
12
|
+
* bit 3 (= 8) : +y neighbour is fluid
|
|
13
|
+
* bit 4 (= 16) : -z neighbour is fluid
|
|
14
|
+
* bit 5 (= 32) : +z neighbour is fluid
|
|
15
|
+
* bit 7 (= 128) : this cell is itself solid
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* The low 6 bits are the fluid-neighbour set; bit 7 flags the cell as solid.
|
|
18
|
+
* A solid cell encodes as exactly `128` — bit 7 set, every neighbour bit clear
|
|
19
|
+
* — and an isolated fluid cell (no fluid neighbours) as `0`. Both therefore
|
|
20
|
+
* satisfy `(mask & 63) === 0`, so the SOR / PCG loops' "no fluid neighbours →
|
|
21
|
+
* skip" branch is a single `(mask & 63) === 0` comparison that covers
|
|
22
|
+
* "self-solid" and "fluid but no fluid neighbours" alike. The dedicated bit 7
|
|
23
|
+
* additionally lets {@link v3_grid_subtract_pressure_gradient} tell solid cells
|
|
24
|
+
* (zero the velocity) apart from isolated fluid (leave it) without the original
|
|
25
|
+
* `solid` array.
|
|
19
26
|
*
|
|
20
27
|
* O(N) — one byte-write per cell, up to six byte-reads per cell. The output
|
|
21
28
|
* fully replaces whatever was in `mask` before; no need to zero-init.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v3_grid_compute_solid_neighbour_mask.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"v3_grid_compute_solid_neighbour_mask.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,2DANW,UAAU,SACV,UAAU,SACV,MAAM,SACN,MAAM,SACN,MAAM,QAqChB"}
|