@codexo/exojs-physics 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -10
- package/dist/esm/Collider.d.ts +17 -6
- package/dist/esm/Collider.js +28 -6
- package/dist/esm/Collider.js.map +1 -1
- package/dist/esm/ContactGraph.d.ts +49 -3
- package/dist/esm/ContactGraph.js +132 -44
- package/dist/esm/ContactGraph.js.map +1 -1
- package/dist/esm/PhysicsBody.d.ts +113 -15
- package/dist/esm/PhysicsBody.js +224 -21
- package/dist/esm/PhysicsBody.js.map +1 -1
- package/dist/esm/PhysicsWorld.d.ts +107 -35
- package/dist/esm/PhysicsWorld.js +136 -31
- package/dist/esm/PhysicsWorld.js.map +1 -1
- package/dist/esm/backend/NativePhysicsBackend.d.ts +5 -0
- package/dist/esm/backend/NativePhysicsBackend.js +14 -0
- package/dist/esm/backend/NativePhysicsBackend.js.map +1 -1
- package/dist/esm/backend/PhysicsBackend.d.ts +9 -1
- package/dist/esm/binding/PhysicsBinding.d.ts +6 -6
- package/dist/esm/binding/PhysicsBinding.js +8 -5
- package/dist/esm/binding/PhysicsBinding.js.map +1 -1
- package/dist/esm/broadphase/SweepAndPrune.d.ts +1 -0
- package/dist/esm/broadphase/SweepAndPrune.js +32 -3
- package/dist/esm/broadphase/SweepAndPrune.js.map +1 -1
- package/dist/esm/collision/CollisionProxy.d.ts +2 -2
- package/dist/esm/collision/narrowphase.js +91 -38
- package/dist/esm/collision/narrowphase.js.map +1 -1
- package/dist/esm/debug/PhysicsDebugDraw.d.ts +2 -1
- package/dist/esm/debug/PhysicsDebugDraw.js +2 -1
- package/dist/esm/debug/PhysicsDebugDraw.js.map +1 -1
- package/dist/esm/physicsBuildInfo.js +2 -2
- package/dist/esm/public.d.ts +2 -1
- package/dist/esm/query/QueryEngine.d.ts +2 -2
- package/dist/esm/query/QueryEngine.js +13 -4
- package/dist/esm/query/QueryEngine.js.map +1 -1
- package/dist/esm/shapes/AnyShape.d.ts +9 -0
- package/dist/esm/shapes/CircleShape.d.ts +1 -2
- package/dist/esm/shapes/CircleShape.js.map +1 -1
- package/dist/esm/shapes/PolygonShape.d.ts +1 -2
- package/dist/esm/shapes/PolygonShape.js +45 -17
- package/dist/esm/shapes/PolygonShape.js.map +1 -1
- package/dist/esm/shapes/index.d.ts +1 -0
- package/dist/esm/solver/ContactSolver.d.ts +87 -0
- package/dist/esm/solver/ContactSolver.js +483 -0
- package/dist/esm/solver/ContactSolver.js.map +1 -0
- package/dist/esm/sort.d.ts +17 -0
- package/dist/esm/sort.js +54 -0
- package/dist/esm/sort.js.map +1 -0
- package/package.json +4 -4
|
@@ -10,14 +10,16 @@ export interface BodyOptions {
|
|
|
10
10
|
position?: VectorLike;
|
|
11
11
|
/** Initial rotation in radians. Default `0`. */
|
|
12
12
|
angle?: number;
|
|
13
|
-
/** Linear damping
|
|
13
|
+
/** Linear damping applied to the velocity each sub-step. Default `0`. */
|
|
14
14
|
linearDamping?: number;
|
|
15
|
-
/** Angular damping
|
|
15
|
+
/** Angular damping applied to the angular velocity each sub-step. Default `0`. */
|
|
16
16
|
angularDamping?: number;
|
|
17
|
-
/** Per-body multiplier on world gravity (
|
|
17
|
+
/** Per-body multiplier on world gravity (e.g. `0` to ignore gravity). Default `1`. */
|
|
18
18
|
gravityScale?: number;
|
|
19
19
|
/** When `true`, the body never rotates under contacts (infinite rotational inertia). Default `false`. */
|
|
20
20
|
fixedRotation?: boolean;
|
|
21
|
+
/** Colliders to attach up-front. Each may be a {@link Collider} instance or its {@link ColliderOptions}. */
|
|
22
|
+
colliders?: Array<Collider | ColliderOptions>;
|
|
21
23
|
}
|
|
22
24
|
/**
|
|
23
25
|
* Internal hook the world hands to each body so colliders can be id-allocated
|
|
@@ -29,19 +31,20 @@ export interface BodyOwner {
|
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* A rigid body: a world transform plus mass properties aggregated from its
|
|
32
|
-
* colliders.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* colliders. Dynamic bodies integrate under gravity, accumulated forces/torque
|
|
35
|
+
* and contact impulses each fixed sub-step; static bodies never move; kinematic
|
|
36
|
+
* bodies move by their velocity only and stay immovable under contacts. Drive a
|
|
37
|
+
* body with {@link applyForce}, {@link applyTorque} and {@link applyImpulse}, or
|
|
38
|
+
* set {@link linearVelocityX}/{@link linearVelocityY}/{@link angularVelocity}
|
|
39
|
+
* directly; reposition it (teleport, no velocity) with {@link setTransform}.
|
|
36
40
|
*/
|
|
37
41
|
export declare class PhysicsBody {
|
|
38
|
-
readonly id: number;
|
|
39
42
|
readonly type: BodyType;
|
|
40
|
-
/** Linear damping
|
|
43
|
+
/** Linear damping applied to the velocity each sub-step. */
|
|
41
44
|
linearDamping: number;
|
|
42
|
-
/** Angular damping
|
|
45
|
+
/** Angular damping applied to the angular velocity each sub-step. */
|
|
43
46
|
angularDamping: number;
|
|
44
|
-
/** Per-body gravity
|
|
47
|
+
/** Per-body multiplier on world gravity (e.g. `0` to ignore gravity). */
|
|
45
48
|
gravityScale: number;
|
|
46
49
|
/** When `true`, rotational inertia is treated as infinite. */
|
|
47
50
|
fixedRotation: boolean;
|
|
@@ -53,14 +56,39 @@ export declare class PhysicsBody {
|
|
|
53
56
|
inertia: number;
|
|
54
57
|
/** Inverse rotational inertia (`0` = no angular response). */
|
|
55
58
|
invInertia: number;
|
|
56
|
-
|
|
59
|
+
/** Linear velocity X in px/s. */
|
|
60
|
+
linearVelocityX: number;
|
|
61
|
+
/** Linear velocity Y in px/s. */
|
|
62
|
+
linearVelocityY: number;
|
|
63
|
+
/** Angular velocity in rad/s. */
|
|
64
|
+
angularVelocity: number;
|
|
65
|
+
/** @internal — Delta position X accumulated across the frame's sub-steps by the TGS integrator; written into the transform once per frame by {@link _finalizePosition}. */
|
|
66
|
+
_deltaPosX: number;
|
|
67
|
+
/** @internal — Delta position Y accumulated across the frame's sub-steps by the TGS integrator. */
|
|
68
|
+
_deltaPosY: number;
|
|
69
|
+
/** @internal — Delta rotation (radians) accumulated across the frame's sub-steps by the TGS integrator. */
|
|
70
|
+
_deltaAngle: number;
|
|
71
|
+
/** @internal — `cos(_deltaAngle)`, cached so the solver can rotate the contact anchors by the live sub-step rotation without a per-contact trig call. */
|
|
72
|
+
_deltaCos: number;
|
|
73
|
+
/** @internal — `sin(_deltaAngle)`, cached alongside {@link _deltaCos}. */
|
|
74
|
+
_deltaSin: number;
|
|
75
|
+
private _forceX;
|
|
76
|
+
private _forceY;
|
|
77
|
+
private _torque;
|
|
78
|
+
private _id;
|
|
79
|
+
private _owner;
|
|
80
|
+
private _attached;
|
|
57
81
|
private readonly _transform;
|
|
58
82
|
private readonly _colliders;
|
|
59
83
|
private _comX;
|
|
60
84
|
private _comY;
|
|
61
85
|
private _massReady;
|
|
62
86
|
private _destroyed;
|
|
63
|
-
constructor(
|
|
87
|
+
constructor(options?: BodyOptions);
|
|
88
|
+
/** Stable id, assigned when the body joins a world via `world.add()`; `-1` until then. */
|
|
89
|
+
get id(): number;
|
|
90
|
+
/** `true` once the body has been added to a world (guards against double-add). */
|
|
91
|
+
get attached(): boolean;
|
|
64
92
|
/** World X. */
|
|
65
93
|
get x(): number;
|
|
66
94
|
/** World Y. */
|
|
@@ -85,8 +113,15 @@ export declare class PhysicsBody {
|
|
|
85
113
|
get isMassReady(): boolean;
|
|
86
114
|
/** `true` after the owning world has destroyed this body. */
|
|
87
115
|
get destroyed(): boolean;
|
|
88
|
-
/**
|
|
89
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Attach a collider to this body. Accepts a {@link Collider} instance or its
|
|
118
|
+
* {@link ColliderOptions} (a convenience that constructs the collider for you).
|
|
119
|
+
* When the body is already in a world, the collider is id-allocated, registered,
|
|
120
|
+
* the mass model is recomputed and the world geometry synchronised immediately;
|
|
121
|
+
* on a free-standing body the collider is simply held until `world.add()`.
|
|
122
|
+
* Returns the collider.
|
|
123
|
+
*/
|
|
124
|
+
addCollider(collider: Collider | ColliderOptions): Collider;
|
|
90
125
|
/**
|
|
91
126
|
* Set the body's world transform. Resets the body's cached collider geometry
|
|
92
127
|
* immediately so subsequent queries see the new placement. Returns `this`.
|
|
@@ -94,8 +129,71 @@ export declare class PhysicsBody {
|
|
|
94
129
|
setTransform(position: VectorLike, angle?: number): this;
|
|
95
130
|
/** Refresh every collider's world geometry from the current transform. */
|
|
96
131
|
synchronizeColliders(): void;
|
|
132
|
+
/** World-space centre of mass X (body origin + rotated local centre of mass). */
|
|
133
|
+
get worldCenterOfMassX(): number;
|
|
134
|
+
/** World-space centre of mass Y. */
|
|
135
|
+
get worldCenterOfMassY(): number;
|
|
136
|
+
/**
|
|
137
|
+
* Accumulate a world-space force at the centre of mass; integrated on the next
|
|
138
|
+
* sub-step and then cleared. No-op on static/kinematic bodies. Returns `this`.
|
|
139
|
+
*/
|
|
140
|
+
applyForce(forceX: number, forceY: number): this;
|
|
141
|
+
/**
|
|
142
|
+
* Accumulate a torque; integrated on the next sub-step and then cleared. No-op
|
|
143
|
+
* on static/kinematic bodies (and fixed-rotation bodies). Returns `this`.
|
|
144
|
+
*/
|
|
145
|
+
applyTorque(torque: number): this;
|
|
146
|
+
/**
|
|
147
|
+
* Apply an instantaneous world-space impulse at `(pointX, pointY)` (world space;
|
|
148
|
+
* defaults to the centre of mass), changing velocity immediately. No-op on
|
|
149
|
+
* static/kinematic bodies (infinite mass). Returns `this`.
|
|
150
|
+
*/
|
|
151
|
+
applyImpulse(impulseX: number, impulseY: number, pointX?: number, pointY?: number): this;
|
|
152
|
+
/**
|
|
153
|
+
* @internal — integrate velocity from gravity, accumulated forces/torque and
|
|
154
|
+
* damping over the sub-step `h`. No-op for static/kinematic (infinite mass
|
|
155
|
+
* keeps their velocity solver-driven only). Called once per TGS sub-step, so
|
|
156
|
+
* gravity/forces are applied with `h` each sub-step (`N·h = dt`, same total
|
|
157
|
+
* impulse, but the small-step position error scales with `h²`). The
|
|
158
|
+
* force/torque accumulators are **not** cleared here — they are consumed by
|
|
159
|
+
* every sub-step and cleared once per frame by {@link _finalizePosition}.
|
|
160
|
+
*/
|
|
161
|
+
_integrateVelocity(h: number, gravityX: number, gravityY: number): void;
|
|
162
|
+
/**
|
|
163
|
+
* @internal — accumulate this sub-step's velocity into the per-frame delta
|
|
164
|
+
* position/rotation (TGS sub-stepping). The transform itself is **not** moved
|
|
165
|
+
* here; {@link _finalizePosition} writes the accumulated delta once per frame.
|
|
166
|
+
* Tracking the delta (rather than moving the transform each sub-step) lets the
|
|
167
|
+
* solver recompute contact separation from it without re-running detection or
|
|
168
|
+
* re-syncing collider geometry per sub-step. Static bodies never move.
|
|
169
|
+
*/
|
|
170
|
+
_integratePosition(h: number): void;
|
|
171
|
+
/**
|
|
172
|
+
* @internal — apply the frame's accumulated delta position/rotation to the
|
|
173
|
+
* transform (rotating about the centre of mass), re-sync collider geometry and
|
|
174
|
+
* clear the force/torque accumulators. Called once per frame after the
|
|
175
|
+
* sub-step loop. Static bodies never move; the force clear still runs (forces
|
|
176
|
+
* are a no-op on infinite mass but the accumulator is reset for consistency).
|
|
177
|
+
*/
|
|
178
|
+
_finalizePosition(): void;
|
|
179
|
+
/** Clear the per-frame TGS delta accumulators (position, rotation and its cached cos/sin). */
|
|
180
|
+
private _resetDelta;
|
|
97
181
|
/** Internal: detach a collider (called by the world during destruction). */
|
|
98
182
|
_removeCollider(collider: Collider): void;
|
|
183
|
+
/**
|
|
184
|
+
* @internal — join `owner`'s world with the allocated `id`. Allocates ids for
|
|
185
|
+
* and registers every held collider, then aggregates the mass model and
|
|
186
|
+
* synchronises world geometry. Called once by {@link PhysicsWorld.add}.
|
|
187
|
+
*
|
|
188
|
+
* Ordering matters and mirrors the per-collider create path exactly: ids and
|
|
189
|
+
* the body link go on first, then a single mass recompute over the full
|
|
190
|
+
* collider set (so the centre of mass / inertia aggregate is correct), then
|
|
191
|
+
* `synchronize` over every collider (the cached world geometry the broad/narrow
|
|
192
|
+
* phase reads), then world registration last. Recomputing mass before all
|
|
193
|
+
* colliders are linked, or syncing before the mass model exists, would feed the
|
|
194
|
+
* solver a stale CoM and produce subtle integration bugs.
|
|
195
|
+
*/
|
|
196
|
+
_attachToWorld(owner: BodyOwner, id: number): void;
|
|
99
197
|
/** Internal: mark destroyed (called by the world). */
|
|
100
198
|
_markDestroyed(): void;
|
|
101
199
|
/** Recompute mass, centre of mass and rotational inertia from the colliders. */
|
package/dist/esm/PhysicsBody.js
CHANGED
|
@@ -4,19 +4,20 @@ import { createTransform, setTransform, applyTransform } from './math.js';
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A rigid body: a world transform plus mass properties aggregated from its
|
|
7
|
-
* colliders.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* colliders. Dynamic bodies integrate under gravity, accumulated forces/torque
|
|
8
|
+
* and contact impulses each fixed sub-step; static bodies never move; kinematic
|
|
9
|
+
* bodies move by their velocity only and stay immovable under contacts. Drive a
|
|
10
|
+
* body with {@link applyForce}, {@link applyTorque} and {@link applyImpulse}, or
|
|
11
|
+
* set {@link linearVelocityX}/{@link linearVelocityY}/{@link angularVelocity}
|
|
12
|
+
* directly; reposition it (teleport, no velocity) with {@link setTransform}.
|
|
11
13
|
*/
|
|
12
14
|
class PhysicsBody {
|
|
13
|
-
id;
|
|
14
15
|
type;
|
|
15
|
-
/** Linear damping
|
|
16
|
+
/** Linear damping applied to the velocity each sub-step. */
|
|
16
17
|
linearDamping;
|
|
17
|
-
/** Angular damping
|
|
18
|
+
/** Angular damping applied to the angular velocity each sub-step. */
|
|
18
19
|
angularDamping;
|
|
19
|
-
/** Per-body gravity
|
|
20
|
+
/** Per-body multiplier on world gravity (e.g. `0` to ignore gravity). */
|
|
20
21
|
gravityScale;
|
|
21
22
|
/** When `true`, rotational inertia is treated as infinite. */
|
|
22
23
|
fixedRotation;
|
|
@@ -28,22 +29,57 @@ class PhysicsBody {
|
|
|
28
29
|
inertia = 0;
|
|
29
30
|
/** Inverse rotational inertia (`0` = no angular response). */
|
|
30
31
|
invInertia = 0;
|
|
31
|
-
|
|
32
|
+
/** Linear velocity X in px/s. */
|
|
33
|
+
linearVelocityX = 0;
|
|
34
|
+
/** Linear velocity Y in px/s. */
|
|
35
|
+
linearVelocityY = 0;
|
|
36
|
+
/** Angular velocity in rad/s. */
|
|
37
|
+
angularVelocity = 0;
|
|
38
|
+
/** @internal — Delta position X accumulated across the frame's sub-steps by the TGS integrator; written into the transform once per frame by {@link _finalizePosition}. */
|
|
39
|
+
_deltaPosX = 0;
|
|
40
|
+
/** @internal — Delta position Y accumulated across the frame's sub-steps by the TGS integrator. */
|
|
41
|
+
_deltaPosY = 0;
|
|
42
|
+
/** @internal — Delta rotation (radians) accumulated across the frame's sub-steps by the TGS integrator. */
|
|
43
|
+
_deltaAngle = 0;
|
|
44
|
+
/** @internal — `cos(_deltaAngle)`, cached so the solver can rotate the contact anchors by the live sub-step rotation without a per-contact trig call. */
|
|
45
|
+
_deltaCos = 1;
|
|
46
|
+
/** @internal — `sin(_deltaAngle)`, cached alongside {@link _deltaCos}. */
|
|
47
|
+
_deltaSin = 0;
|
|
48
|
+
_forceX = 0;
|
|
49
|
+
_forceY = 0;
|
|
50
|
+
_torque = 0;
|
|
51
|
+
_id = -1;
|
|
52
|
+
_owner = null;
|
|
53
|
+
_attached = false;
|
|
32
54
|
_transform;
|
|
33
55
|
_colliders = [];
|
|
34
56
|
_comX = 0;
|
|
35
57
|
_comY = 0;
|
|
36
58
|
_massReady = false;
|
|
37
59
|
_destroyed = false;
|
|
38
|
-
constructor(
|
|
39
|
-
this.id = id;
|
|
60
|
+
constructor(options = {}) {
|
|
40
61
|
this.type = options.type ?? 'dynamic';
|
|
41
|
-
this._owner = owner;
|
|
42
62
|
this._transform = createTransform(options.position?.x ?? 0, options.position?.y ?? 0, options.angle ?? 0);
|
|
43
63
|
this.linearDamping = options.linearDamping ?? 0;
|
|
44
64
|
this.angularDamping = options.angularDamping ?? 0;
|
|
45
65
|
this.gravityScale = options.gravityScale ?? 1;
|
|
46
66
|
this.fixedRotation = options.fixedRotation ?? false;
|
|
67
|
+
// Build up-front colliders now (no id/world registration until the body
|
|
68
|
+
// joins a world via `world.add()`). They sit in `_colliders` unsynchronised;
|
|
69
|
+
// `_attachToWorld` assigns ids, registers them and computes the mass model.
|
|
70
|
+
if (options.colliders) {
|
|
71
|
+
for (const entry of options.colliders) {
|
|
72
|
+
this._colliders.push(entry instanceof Collider ? entry : new Collider(entry));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Stable id, assigned when the body joins a world via `world.add()`; `-1` until then. */
|
|
77
|
+
get id() {
|
|
78
|
+
return this._id;
|
|
79
|
+
}
|
|
80
|
+
/** `true` once the body has been added to a world (guards against double-add). */
|
|
81
|
+
get attached() {
|
|
82
|
+
return this._attached;
|
|
47
83
|
}
|
|
48
84
|
/** World X. */
|
|
49
85
|
get x() {
|
|
@@ -89,19 +125,32 @@ class PhysicsBody {
|
|
|
89
125
|
get destroyed() {
|
|
90
126
|
return this._destroyed;
|
|
91
127
|
}
|
|
92
|
-
/**
|
|
93
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Attach a collider to this body. Accepts a {@link Collider} instance or its
|
|
130
|
+
* {@link ColliderOptions} (a convenience that constructs the collider for you).
|
|
131
|
+
* When the body is already in a world, the collider is id-allocated, registered,
|
|
132
|
+
* the mass model is recomputed and the world geometry synchronised immediately;
|
|
133
|
+
* on a free-standing body the collider is simply held until `world.add()`.
|
|
134
|
+
* Returns the collider.
|
|
135
|
+
*/
|
|
136
|
+
addCollider(collider) {
|
|
94
137
|
if (this._destroyed) {
|
|
95
|
-
throw new Error('PhysicsBody: cannot
|
|
138
|
+
throw new Error('PhysicsBody: cannot add a collider to a destroyed body.');
|
|
96
139
|
}
|
|
97
140
|
// Collider imports PhysicsBody type-only (erased), so this value import is
|
|
98
141
|
// one-directional — no runtime cycle.
|
|
99
|
-
const
|
|
100
|
-
this._colliders.push(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
142
|
+
const instance = collider instanceof Collider ? collider : new Collider(collider);
|
|
143
|
+
this._colliders.push(instance);
|
|
144
|
+
// Before the body joins a world there is no owner to allocate ids / register
|
|
145
|
+
// with; `_attachToWorld` does that (and the mass/sync pass) for every held
|
|
146
|
+
// collider. Once attached, mirror the world's create order exactly.
|
|
147
|
+
if (this._attached && this._owner) {
|
|
148
|
+
instance._attach(this, this._owner._allocateColliderId());
|
|
149
|
+
this._recomputeMass();
|
|
150
|
+
instance.synchronize(this._transform);
|
|
151
|
+
this._owner._registerCollider(instance);
|
|
152
|
+
}
|
|
153
|
+
return instance;
|
|
105
154
|
}
|
|
106
155
|
/**
|
|
107
156
|
* Set the body's world transform. Resets the body's cached collider geometry
|
|
@@ -123,6 +172,129 @@ class PhysicsBody {
|
|
|
123
172
|
collider.synchronize(this._transform);
|
|
124
173
|
}
|
|
125
174
|
}
|
|
175
|
+
/** World-space centre of mass X (body origin + rotated local centre of mass). */
|
|
176
|
+
get worldCenterOfMassX() {
|
|
177
|
+
return this._transform.x + this._transform.cos * this._comX - this._transform.sin * this._comY;
|
|
178
|
+
}
|
|
179
|
+
/** World-space centre of mass Y. */
|
|
180
|
+
get worldCenterOfMassY() {
|
|
181
|
+
return this._transform.y + this._transform.sin * this._comX + this._transform.cos * this._comY;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Accumulate a world-space force at the centre of mass; integrated on the next
|
|
185
|
+
* sub-step and then cleared. No-op on static/kinematic bodies. Returns `this`.
|
|
186
|
+
*/
|
|
187
|
+
applyForce(forceX, forceY) {
|
|
188
|
+
this._forceX += forceX;
|
|
189
|
+
this._forceY += forceY;
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Accumulate a torque; integrated on the next sub-step and then cleared. No-op
|
|
194
|
+
* on static/kinematic bodies (and fixed-rotation bodies). Returns `this`.
|
|
195
|
+
*/
|
|
196
|
+
applyTorque(torque) {
|
|
197
|
+
this._torque += torque;
|
|
198
|
+
return this;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Apply an instantaneous world-space impulse at `(pointX, pointY)` (world space;
|
|
202
|
+
* defaults to the centre of mass), changing velocity immediately. No-op on
|
|
203
|
+
* static/kinematic bodies (infinite mass). Returns `this`.
|
|
204
|
+
*/
|
|
205
|
+
applyImpulse(impulseX, impulseY, pointX, pointY) {
|
|
206
|
+
if (this.invMass === 0) {
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
this.linearVelocityX += impulseX * this.invMass;
|
|
210
|
+
this.linearVelocityY += impulseY * this.invMass;
|
|
211
|
+
if (pointX !== undefined && pointY !== undefined && this.invInertia !== 0) {
|
|
212
|
+
const rx = pointX - this.worldCenterOfMassX;
|
|
213
|
+
const ry = pointY - this.worldCenterOfMassY;
|
|
214
|
+
this.angularVelocity += (rx * impulseY - ry * impulseX) * this.invInertia;
|
|
215
|
+
}
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* @internal — integrate velocity from gravity, accumulated forces/torque and
|
|
220
|
+
* damping over the sub-step `h`. No-op for static/kinematic (infinite mass
|
|
221
|
+
* keeps their velocity solver-driven only). Called once per TGS sub-step, so
|
|
222
|
+
* gravity/forces are applied with `h` each sub-step (`N·h = dt`, same total
|
|
223
|
+
* impulse, but the small-step position error scales with `h²`). The
|
|
224
|
+
* force/torque accumulators are **not** cleared here — they are consumed by
|
|
225
|
+
* every sub-step and cleared once per frame by {@link _finalizePosition}.
|
|
226
|
+
*/
|
|
227
|
+
_integrateVelocity(h, gravityX, gravityY) {
|
|
228
|
+
if (this.invMass === 0) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
this.linearVelocityX += (gravityX * this.gravityScale + this._forceX * this.invMass) * h;
|
|
232
|
+
this.linearVelocityY += (gravityY * this.gravityScale + this._forceY * this.invMass) * h;
|
|
233
|
+
this.angularVelocity += this._torque * this.invInertia * h;
|
|
234
|
+
// Exponential damping (no-op when damping is 0).
|
|
235
|
+
this.linearVelocityX /= 1 + h * this.linearDamping;
|
|
236
|
+
this.linearVelocityY /= 1 + h * this.linearDamping;
|
|
237
|
+
this.angularVelocity /= 1 + h * this.angularDamping;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* @internal — accumulate this sub-step's velocity into the per-frame delta
|
|
241
|
+
* position/rotation (TGS sub-stepping). The transform itself is **not** moved
|
|
242
|
+
* here; {@link _finalizePosition} writes the accumulated delta once per frame.
|
|
243
|
+
* Tracking the delta (rather than moving the transform each sub-step) lets the
|
|
244
|
+
* solver recompute contact separation from it without re-running detection or
|
|
245
|
+
* re-syncing collider geometry per sub-step. Static bodies never move.
|
|
246
|
+
*/
|
|
247
|
+
_integratePosition(h) {
|
|
248
|
+
if (this.type === 'static') {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
this._deltaPosX += this.linearVelocityX * h;
|
|
252
|
+
this._deltaPosY += this.linearVelocityY * h;
|
|
253
|
+
this._deltaAngle += this.angularVelocity * h;
|
|
254
|
+
// Cache the rotation so the solver can rotate the contact anchors by the
|
|
255
|
+
// live sub-step rotation (TGS): without it the anchors stay frozen at frame
|
|
256
|
+
// start and a tilting tower's restoring torque is mis-computed, so the tilt
|
|
257
|
+
// grows instead of being corrected. Only non-zero rotation pays the trig.
|
|
258
|
+
if (this._deltaAngle !== 0) {
|
|
259
|
+
this._deltaCos = Math.cos(this._deltaAngle);
|
|
260
|
+
this._deltaSin = Math.sin(this._deltaAngle);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* @internal — apply the frame's accumulated delta position/rotation to the
|
|
265
|
+
* transform (rotating about the centre of mass), re-sync collider geometry and
|
|
266
|
+
* clear the force/torque accumulators. Called once per frame after the
|
|
267
|
+
* sub-step loop. Static bodies never move; the force clear still runs (forces
|
|
268
|
+
* are a no-op on infinite mass but the accumulator is reset for consistency).
|
|
269
|
+
*/
|
|
270
|
+
_finalizePosition() {
|
|
271
|
+
this._forceX = 0;
|
|
272
|
+
this._forceY = 0;
|
|
273
|
+
this._torque = 0;
|
|
274
|
+
if (this.type === 'static' || (this._deltaPosX === 0 && this._deltaPosY === 0 && this._deltaAngle === 0)) {
|
|
275
|
+
this._resetDelta();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const newComX = this.worldCenterOfMassX + this._deltaPosX;
|
|
279
|
+
const newComY = this.worldCenterOfMassY + this._deltaPosY;
|
|
280
|
+
const newAngle = this._transform.angle + this._deltaAngle;
|
|
281
|
+
const cos = Math.cos(newAngle);
|
|
282
|
+
const sin = Math.sin(newAngle);
|
|
283
|
+
// origin = newCoM − R(newAngle)·comLocal (so rotation pivots about the CoM).
|
|
284
|
+
setTransform(this._transform, newComX - (cos * this._comX - sin * this._comY), newComY - (sin * this._comX + cos * this._comY), newAngle);
|
|
285
|
+
this._resetDelta();
|
|
286
|
+
for (const collider of this._colliders) {
|
|
287
|
+
collider.synchronize(this._transform);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/** Clear the per-frame TGS delta accumulators (position, rotation and its cached cos/sin). */
|
|
291
|
+
_resetDelta() {
|
|
292
|
+
this._deltaPosX = 0;
|
|
293
|
+
this._deltaPosY = 0;
|
|
294
|
+
this._deltaAngle = 0;
|
|
295
|
+
this._deltaCos = 1;
|
|
296
|
+
this._deltaSin = 0;
|
|
297
|
+
}
|
|
126
298
|
/** Internal: detach a collider (called by the world during destruction). */
|
|
127
299
|
_removeCollider(collider) {
|
|
128
300
|
const index = this._colliders.indexOf(collider);
|
|
@@ -131,6 +303,37 @@ class PhysicsBody {
|
|
|
131
303
|
this._recomputeMass();
|
|
132
304
|
}
|
|
133
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* @internal — join `owner`'s world with the allocated `id`. Allocates ids for
|
|
308
|
+
* and registers every held collider, then aggregates the mass model and
|
|
309
|
+
* synchronises world geometry. Called once by {@link PhysicsWorld.add}.
|
|
310
|
+
*
|
|
311
|
+
* Ordering matters and mirrors the per-collider create path exactly: ids and
|
|
312
|
+
* the body link go on first, then a single mass recompute over the full
|
|
313
|
+
* collider set (so the centre of mass / inertia aggregate is correct), then
|
|
314
|
+
* `synchronize` over every collider (the cached world geometry the broad/narrow
|
|
315
|
+
* phase reads), then world registration last. Recomputing mass before all
|
|
316
|
+
* colliders are linked, or syncing before the mass model exists, would feed the
|
|
317
|
+
* solver a stale CoM and produce subtle integration bugs.
|
|
318
|
+
*/
|
|
319
|
+
_attachToWorld(owner, id) {
|
|
320
|
+
if (this._destroyed) {
|
|
321
|
+
throw new Error('PhysicsBody: cannot add a destroyed body to a world.');
|
|
322
|
+
}
|
|
323
|
+
this._owner = owner;
|
|
324
|
+
this._id = id;
|
|
325
|
+
this._attached = true;
|
|
326
|
+
for (const collider of this._colliders) {
|
|
327
|
+
collider._attach(this, owner._allocateColliderId());
|
|
328
|
+
}
|
|
329
|
+
this._recomputeMass();
|
|
330
|
+
for (const collider of this._colliders) {
|
|
331
|
+
collider.synchronize(this._transform);
|
|
332
|
+
}
|
|
333
|
+
for (const collider of this._colliders) {
|
|
334
|
+
owner._registerCollider(collider);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
134
337
|
/** Internal: mark destroyed (called by the world). */
|
|
135
338
|
_markDestroyed() {
|
|
136
339
|
this._destroyed = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PhysicsBody.js","sources":["../../../src/PhysicsBody.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"PhysicsBody.js","sources":["../../../src/PhysicsBody.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|