emeraldengine 3.0.0 → 3.1.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 +1498 -1659
- package/dist/types/index.d.ts +4 -1
- package/dist/types/src/Animator.d.ts +1 -1
- package/dist/types/src/CollisionLayers.d.ts +2 -2
- package/dist/types/src/Color.d.ts +1 -0
- package/dist/types/src/Drawable.d.ts +1 -1
- package/dist/types/src/EmeraldDB.d.ts +2 -2
- package/dist/types/src/InstancedTexture.d.ts +17 -2
- package/dist/types/src/Material.d.ts +2 -2
- package/dist/types/src/MathUtils.d.ts +2 -1
- package/dist/types/src/ParticleEmitter.d.ts +1 -1
- package/dist/types/src/Physics.d.ts +148 -18
- package/dist/types/src/Scene.d.ts +1 -1
- package/dist/types/src/Shaders.d.ts +2 -2
- package/dist/types/src/Tilemap.d.ts +1 -1
- package/dist/types/src/UI.d.ts +1 -1
- package/dist/types/src/components/Behaviour.d.ts +2 -2
- package/dist/types/src/components/Collider.d.ts +7 -1
- package/dist/types/src/components/GameObject.d.ts +2 -2
- package/dist/types/src/components/PolygonCollider.d.ts +33 -0
- package/dist/types/src/components/RigidBody.d.ts +281 -8
- package/dist/types/src/importers/Aseprite.d.ts +2 -2
- package/dist/types/src/importers/ForgeLevel.d.ts +97 -0
- package/dist/types/src/importers/TiledMap.d.ts +1 -1
- package/dist/types/src/managers/EventManager.d.ts +1 -1
- package/dist/types/src/managers/Gamepad.d.ts +102 -0
- package/dist/types/src/managers/InputManager.d.ts +93 -2
- package/dist/types/src/managers/NetworkManager.d.ts +2 -2
- package/dist/types/src/managers/RenderStats.d.ts +1 -1
- package/dist/types/src/managers/TextureManager.d.ts +1 -1
- package/dist/types/src/physics/AABB.d.ts +92 -0
- package/dist/types/src/physics/Body.d.ts +435 -0
- package/dist/types/src/physics/BodyType.d.ts +6 -0
- package/dist/types/src/physics/BroadPhase.d.ts +210 -0
- package/dist/types/src/physics/Collision.d.ts +102 -0
- package/dist/types/src/physics/Contact.d.ts +206 -0
- package/dist/types/src/physics/ContactSolver.d.ts +108 -0
- package/dist/types/src/physics/Distance.d.ts +54 -0
- package/dist/types/src/physics/DistanceJoint.d.ts +90 -0
- package/dist/types/src/physics/Fixture.d.ts +221 -0
- package/dist/types/src/physics/Island.d.ts +52 -0
- package/dist/types/src/physics/Joint.d.ts +59 -0
- package/dist/types/src/physics/Math2D.d.ts +371 -0
- package/dist/types/src/physics/RevoluteJoint.d.ts +119 -0
- package/dist/types/src/physics/Settings.d.ts +22 -0
- package/dist/types/src/physics/Shapes.d.ts +207 -0
- package/dist/types/src/physics/TimeOfImpact.d.ts +22 -0
- package/dist/types/src/physics/World.d.ts +274 -0
- package/dist/types/src/physics/index.d.ts +34 -0
- package/index.js +6 -0
- package/package.json +2 -3
- package/src/Animator.js +1 -1
- package/src/CollisionLayers.js +3 -3
- package/src/Color.js +8 -0
- package/src/Drawable.js +1 -1
- package/src/Emerald.js +1 -1
- package/src/EmeraldDB.js +2 -2
- package/src/InstancedTexture.js +57 -9
- package/src/Material.js +2 -2
- package/src/MathUtils.js +2 -1
- package/src/ParticleEmitter.js +1 -1
- package/src/Physics.js +270 -60
- package/src/Scene.js +1 -1
- package/src/Shaders.js +20 -20
- package/src/Tilemap.js +1 -1
- package/src/UI.js +1 -1
- package/src/components/Behaviour.js +2 -2
- package/src/components/BoxCollider.js +7 -9
- package/src/components/BoxColliderDebug.js +3 -4
- package/src/components/CircleCollider.js +7 -9
- package/src/components/CircleColliderDebug.js +3 -2
- package/src/components/Collider.js +13 -3
- package/src/components/GameObject.js +2 -2
- package/src/components/PolygonCollider.js +55 -0
- package/src/components/RigidBody.js +441 -14
- package/src/importers/Aseprite.js +2 -2
- package/src/importers/ForgeLevel.js +581 -0
- package/src/importers/TiledMap.js +1 -1
- package/src/managers/EventManager.js +1 -1
- package/src/managers/Gamepad.js +126 -0
- package/src/managers/InputManager.js +129 -3
- package/src/managers/NetworkManager.js +2 -2
- package/src/managers/RenderStats.js +1 -1
- package/src/managers/TextureManager.js +1 -1
- package/src/physics/AABB.js +207 -0
- package/src/physics/Body.js +862 -0
- package/src/physics/BodyType.js +16 -0
- package/src/physics/BroadPhase.js +641 -0
- package/src/physics/Collision.js +534 -0
- package/src/physics/Contact.js +500 -0
- package/src/physics/ContactSolver.js +526 -0
- package/src/physics/Distance.js +403 -0
- package/src/physics/DistanceJoint.js +227 -0
- package/src/physics/Fixture.js +346 -0
- package/src/physics/Island.js +203 -0
- package/src/physics/Joint.js +78 -0
- package/src/physics/Math2D.js +573 -0
- package/src/physics/RevoluteJoint.js +278 -0
- package/src/physics/Settings.js +78 -0
- package/src/physics/Shapes.js +549 -0
- package/src/physics/TimeOfImpact.js +87 -0
- package/src/physics/World.js +731 -0
- package/src/physics/index.js +79 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Manifold,
|
|
3
|
+
WorldManifold,
|
|
4
|
+
collideCircles,
|
|
5
|
+
collidePolygonAndCircle,
|
|
6
|
+
collidePolygons,
|
|
7
|
+
} from "./Collision.js";
|
|
8
|
+
import { ShapeType } from "./Shapes.js";
|
|
9
|
+
import { testOverlap } from "./Distance.js";
|
|
10
|
+
import { BodyType } from "./BodyType.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @function mixFriction
|
|
14
|
+
* @description Combines two friction coefficients (geometric mean), so a slick
|
|
15
|
+
* body sliding on a rough one lands somewhere in between.
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
function mixFriction(a, b) {
|
|
19
|
+
return Math.sqrt(a * b);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @function mixRestitution
|
|
24
|
+
* @description Combines two restitutions: the bouncier surface wins, which is
|
|
25
|
+
* what players expect from a trampoline on concrete.
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
function mixRestitution(a, b) {
|
|
29
|
+
return a > b ? a : b;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @function shouldCollide
|
|
34
|
+
* @description Applies the collision filter to a fixture pair. A positive
|
|
35
|
+
* shared group index always collides, a negative one never does; otherwise each
|
|
36
|
+
* fixture's category must appear in the other's mask.
|
|
37
|
+
* @param {Fixture} fixtureA
|
|
38
|
+
* @param {Fixture} fixtureB
|
|
39
|
+
* @returns {boolean}
|
|
40
|
+
*/
|
|
41
|
+
function shouldCollide(fixtureA, fixtureB) {
|
|
42
|
+
const filterA = fixtureA.filter;
|
|
43
|
+
const filterB = fixtureB.filter;
|
|
44
|
+
|
|
45
|
+
if (filterA.groupIndex === filterB.groupIndex && filterA.groupIndex !== 0) {
|
|
46
|
+
return filterA.groupIndex > 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
(filterA.maskBits & filterB.categoryBits) !== 0 &&
|
|
51
|
+
(filterB.maskBits & filterA.categoryBits) !== 0
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @class Contact
|
|
57
|
+
* @description A potential or actual touch between two fixtures. The broadphase
|
|
58
|
+
* creates one as soon as two AABBs overlap; `touching` only becomes true once
|
|
59
|
+
* the narrowphase finds real contact points. Contacts persist across steps,
|
|
60
|
+
* which is what lets the solver warm-start from last frame's impulses.
|
|
61
|
+
*/
|
|
62
|
+
class Contact {
|
|
63
|
+
constructor(fixtureA, fixtureB) {
|
|
64
|
+
this.fixtureA = fixtureA;
|
|
65
|
+
this.fixtureB = fixtureB;
|
|
66
|
+
this.manifold = new Manifold();
|
|
67
|
+
this.friction = mixFriction(fixtureA.friction, fixtureB.friction);
|
|
68
|
+
this.restitution = mixRestitution(
|
|
69
|
+
fixtureA.restitution,
|
|
70
|
+
fixtureB.restitution
|
|
71
|
+
);
|
|
72
|
+
this.tangentSpeed = 0;
|
|
73
|
+
|
|
74
|
+
/** @private */
|
|
75
|
+
this.touching = false;
|
|
76
|
+
/** @private */
|
|
77
|
+
this.enabled = true;
|
|
78
|
+
/** @private */
|
|
79
|
+
this.filterFlag = false;
|
|
80
|
+
/** @private */
|
|
81
|
+
this.islandFlag = false;
|
|
82
|
+
/** @private */
|
|
83
|
+
this.toiFlag = false;
|
|
84
|
+
/** @private */
|
|
85
|
+
this.toi = 1;
|
|
86
|
+
/** @private */
|
|
87
|
+
this._index = -1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @method create
|
|
92
|
+
* @description Builds a contact for a fixture pair, normalizing the order so
|
|
93
|
+
* mixed polygon/circle pairs always arrive as (polygon, circle): the one
|
|
94
|
+
* ordering the narrowphase implements.
|
|
95
|
+
* @param {Fixture} fixtureA
|
|
96
|
+
* @param {Fixture} fixtureB
|
|
97
|
+
* @returns {Contact}
|
|
98
|
+
*/
|
|
99
|
+
static create(fixtureA, fixtureB) {
|
|
100
|
+
if (
|
|
101
|
+
fixtureA.shape.type === ShapeType.CIRCLE &&
|
|
102
|
+
fixtureB.shape.type === ShapeType.POLYGON
|
|
103
|
+
) {
|
|
104
|
+
return new Contact(fixtureB, fixtureA);
|
|
105
|
+
}
|
|
106
|
+
return new Contact(fixtureA, fixtureB);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @method getManifold
|
|
111
|
+
* @description Returns the local-space manifold.
|
|
112
|
+
* @returns {Manifold}
|
|
113
|
+
*/
|
|
114
|
+
getManifold() {
|
|
115
|
+
return this.manifold;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @method getWorldManifold
|
|
120
|
+
* @description Returns the contact in world space: the normal (pointing from
|
|
121
|
+
* fixture A's body towards fixture B's), the contact points, and the
|
|
122
|
+
* separation at each point.
|
|
123
|
+
* @param {WorldManifold} [worldManifold] - Optional object to fill
|
|
124
|
+
* @returns {WorldManifold}
|
|
125
|
+
*/
|
|
126
|
+
getWorldManifold(worldManifold = new WorldManifold()) {
|
|
127
|
+
return worldManifold.initialize(
|
|
128
|
+
this.manifold,
|
|
129
|
+
this.fixtureA.body.xf,
|
|
130
|
+
this.fixtureA.shape.radius,
|
|
131
|
+
this.fixtureB.body.xf,
|
|
132
|
+
this.fixtureB.shape.radius
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @method isTouching
|
|
138
|
+
* @description Whether the fixtures are actually in contact this step.
|
|
139
|
+
* @returns {boolean}
|
|
140
|
+
*/
|
|
141
|
+
isTouching() {
|
|
142
|
+
return this.touching;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @method setEnabled
|
|
147
|
+
* @description Enables or disables the collision response for this contact.
|
|
148
|
+
* Disabling only lasts for the current step (useful from a pre-solve
|
|
149
|
+
* callback to build one-way platforms).
|
|
150
|
+
* @param {boolean} value
|
|
151
|
+
*/
|
|
152
|
+
setEnabled(value) {
|
|
153
|
+
this.enabled = !!value;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @method isEnabled
|
|
158
|
+
* @description Whether the collision response is enabled.
|
|
159
|
+
* @returns {boolean}
|
|
160
|
+
*/
|
|
161
|
+
isEnabled() {
|
|
162
|
+
return this.enabled;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @method getFixtureA
|
|
167
|
+
* @description Returns the first fixture.
|
|
168
|
+
* @returns {Fixture}
|
|
169
|
+
*/
|
|
170
|
+
getFixtureA() {
|
|
171
|
+
return this.fixtureA;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @method getFixtureB
|
|
176
|
+
* @description Returns the second fixture.
|
|
177
|
+
* @returns {Fixture}
|
|
178
|
+
*/
|
|
179
|
+
getFixtureB() {
|
|
180
|
+
return this.fixtureB;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @method getFriction
|
|
185
|
+
* @description Returns the mixed friction used by the solver.
|
|
186
|
+
* @returns {number}
|
|
187
|
+
*/
|
|
188
|
+
getFriction() {
|
|
189
|
+
return this.friction;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @method setFriction
|
|
194
|
+
* @description Overrides the mixed friction for this contact.
|
|
195
|
+
* @param {number} friction
|
|
196
|
+
*/
|
|
197
|
+
setFriction(friction) {
|
|
198
|
+
this.friction = friction;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @method resetFriction
|
|
203
|
+
* @description Restores the friction mixed from the two fixtures.
|
|
204
|
+
*/
|
|
205
|
+
resetFriction() {
|
|
206
|
+
this.friction = mixFriction(this.fixtureA.friction, this.fixtureB.friction);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @method getRestitution
|
|
211
|
+
* @description Returns the mixed restitution used by the solver.
|
|
212
|
+
* @returns {number}
|
|
213
|
+
*/
|
|
214
|
+
getRestitution() {
|
|
215
|
+
return this.restitution;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @method setRestitution
|
|
220
|
+
* @description Overrides the mixed restitution for this contact.
|
|
221
|
+
* @param {number} restitution
|
|
222
|
+
*/
|
|
223
|
+
setRestitution(restitution) {
|
|
224
|
+
this.restitution = restitution;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @method resetRestitution
|
|
229
|
+
* @description Restores the restitution mixed from the two fixtures.
|
|
230
|
+
*/
|
|
231
|
+
resetRestitution() {
|
|
232
|
+
this.restitution = mixRestitution(
|
|
233
|
+
this.fixtureA.restitution,
|
|
234
|
+
this.fixtureB.restitution
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @method flagForFiltering
|
|
240
|
+
* @description Marks the contact so the next step re-checks whether these two
|
|
241
|
+
* fixtures are still allowed to collide.
|
|
242
|
+
*/
|
|
243
|
+
flagForFiltering() {
|
|
244
|
+
this.filterFlag = true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @method evaluate
|
|
249
|
+
* @description Runs the narrowphase for this fixture pair into `manifold`.
|
|
250
|
+
* @param {Manifold} manifold
|
|
251
|
+
* @param {Transform2} xfA
|
|
252
|
+
* @param {Transform2} xfB
|
|
253
|
+
* @private
|
|
254
|
+
*/
|
|
255
|
+
evaluate(manifold, xfA, xfB) {
|
|
256
|
+
const shapeA = this.fixtureA.shape;
|
|
257
|
+
const shapeB = this.fixtureB.shape;
|
|
258
|
+
|
|
259
|
+
if (shapeA.type === ShapeType.CIRCLE) {
|
|
260
|
+
collideCircles(manifold, shapeA, xfA, shapeB, xfB);
|
|
261
|
+
} else if (shapeB.type === ShapeType.CIRCLE) {
|
|
262
|
+
collidePolygonAndCircle(manifold, shapeA, xfA, shapeB, xfB);
|
|
263
|
+
} else {
|
|
264
|
+
collidePolygons(manifold, shapeA, xfA, shapeB, xfB);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @method update
|
|
270
|
+
* @description Re-runs the narrowphase, carries last step's impulses over to
|
|
271
|
+
* matching contact points (warm starting), and fires begin/end contact
|
|
272
|
+
* events on the world when the touching state flips.
|
|
273
|
+
* @param {World} world - The owning world, used to dispatch events
|
|
274
|
+
*/
|
|
275
|
+
update(world) {
|
|
276
|
+
const oldPoints = [];
|
|
277
|
+
for (let i = 0; i < this.manifold.pointCount; i++) {
|
|
278
|
+
oldPoints.push({
|
|
279
|
+
id: this.manifold.points[i].id,
|
|
280
|
+
normalImpulse: this.manifold.points[i].normalImpulse,
|
|
281
|
+
tangentImpulse: this.manifold.points[i].tangentImpulse,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const wasTouching = this.touching;
|
|
286
|
+
let touching = false;
|
|
287
|
+
this.enabled = true;
|
|
288
|
+
|
|
289
|
+
const bodyA = this.fixtureA.body;
|
|
290
|
+
const bodyB = this.fixtureB.body;
|
|
291
|
+
const sensor = this.fixtureA.sensor || this.fixtureB.sensor;
|
|
292
|
+
|
|
293
|
+
if (sensor) {
|
|
294
|
+
touching = testOverlap(
|
|
295
|
+
this.fixtureA.shape,
|
|
296
|
+
bodyA.xf,
|
|
297
|
+
this.fixtureB.shape,
|
|
298
|
+
bodyB.xf
|
|
299
|
+
);
|
|
300
|
+
this.manifold.pointCount = 0;
|
|
301
|
+
} else {
|
|
302
|
+
this.evaluate(this.manifold, bodyA.xf, bodyB.xf);
|
|
303
|
+
touching = this.manifold.pointCount > 0;
|
|
304
|
+
|
|
305
|
+
for (let i = 0; i < this.manifold.pointCount; i++) {
|
|
306
|
+
const point = this.manifold.points[i];
|
|
307
|
+
point.normalImpulse = 0;
|
|
308
|
+
point.tangentImpulse = 0;
|
|
309
|
+
for (const old of oldPoints) {
|
|
310
|
+
if (old.id === point.id) {
|
|
311
|
+
point.normalImpulse = old.normalImpulse;
|
|
312
|
+
point.tangentImpulse = old.tangentImpulse;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (touching !== wasTouching) {
|
|
319
|
+
bodyA.setAwake(true);
|
|
320
|
+
bodyB.setAwake(true);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
this.touching = touching;
|
|
325
|
+
|
|
326
|
+
if (!wasTouching && touching) world.dispatch("begin-contact", this);
|
|
327
|
+
if (wasTouching && !touching) world.dispatch("end-contact", this);
|
|
328
|
+
if (!sensor && touching) world.dispatch("pre-solve", this, this.manifold);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* @class ContactManager
|
|
334
|
+
* @description Owns the world's contact list: creates contacts from broadphase
|
|
335
|
+
* pairs, drops them once the AABBs stop overlapping, and refreshes the ones
|
|
336
|
+
* that remain each step.
|
|
337
|
+
*/
|
|
338
|
+
class ContactManager {
|
|
339
|
+
constructor(world) {
|
|
340
|
+
this.world = world;
|
|
341
|
+
this.contacts = [];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @method addPair
|
|
346
|
+
* @description Creates a contact for a new broadphase pair, unless the two
|
|
347
|
+
* fixtures share a body, already have a contact, or the filter rejects them.
|
|
348
|
+
* @param {FixtureProxy} proxyA
|
|
349
|
+
* @param {FixtureProxy} proxyB
|
|
350
|
+
*/
|
|
351
|
+
addPair(proxyA, proxyB) {
|
|
352
|
+
const fixtureA = proxyA.fixture;
|
|
353
|
+
const fixtureB = proxyB.fixture;
|
|
354
|
+
const bodyA = fixtureA.body;
|
|
355
|
+
const bodyB = fixtureB.body;
|
|
356
|
+
|
|
357
|
+
if (bodyA === bodyB) return;
|
|
358
|
+
|
|
359
|
+
if (bodyA.type !== BodyType.DYNAMIC && bodyB.type !== BodyType.DYNAMIC) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
for (const contact of bodyB.contacts) {
|
|
364
|
+
if (
|
|
365
|
+
(contact.fixtureA === fixtureA && contact.fixtureB === fixtureB) ||
|
|
366
|
+
(contact.fixtureA === fixtureB && contact.fixtureB === fixtureA)
|
|
367
|
+
) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
for (const joint of bodyA.joints) {
|
|
373
|
+
if (
|
|
374
|
+
!joint.collideConnected &&
|
|
375
|
+
(joint.bodyA === bodyB || joint.bodyB === bodyB)
|
|
376
|
+
) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (
|
|
382
|
+
this.world.contactFilter &&
|
|
383
|
+
!this.world.contactFilter(fixtureA, fixtureB)
|
|
384
|
+
) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (!shouldCollide(fixtureA, fixtureB)) return;
|
|
388
|
+
|
|
389
|
+
const contact = Contact.create(fixtureA, fixtureB);
|
|
390
|
+
contact._index = this.contacts.length;
|
|
391
|
+
this.contacts.push(contact);
|
|
392
|
+
bodyA.contacts.push(contact);
|
|
393
|
+
bodyB.contacts.push(contact);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* @method findNewContacts
|
|
398
|
+
* @description Turns this step's broadphase movement into new contacts.
|
|
399
|
+
*/
|
|
400
|
+
findNewContacts() {
|
|
401
|
+
this.world.broadPhase.updatePairs((proxyA, proxyB) =>
|
|
402
|
+
this.addPair(proxyA, proxyB)
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @method destroy
|
|
408
|
+
* @description Removes a contact, firing end-contact if it was touching.
|
|
409
|
+
* @param {Contact} contact
|
|
410
|
+
*/
|
|
411
|
+
destroy(contact) {
|
|
412
|
+
const bodyA = contact.fixtureA.body;
|
|
413
|
+
const bodyB = contact.fixtureB.body;
|
|
414
|
+
|
|
415
|
+
if (contact.touching) {
|
|
416
|
+
this.world.dispatch("end-contact", contact);
|
|
417
|
+
bodyA.setAwake(true);
|
|
418
|
+
bodyB.setAwake(true);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const index = contact._index;
|
|
422
|
+
if (index >= 0 && this.contacts[index] === contact) {
|
|
423
|
+
const last = this.contacts.pop();
|
|
424
|
+
if (last !== contact) {
|
|
425
|
+
this.contacts[index] = last;
|
|
426
|
+
last._index = index;
|
|
427
|
+
}
|
|
428
|
+
contact._index = -1;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
removeFrom(bodyA.contacts, contact);
|
|
432
|
+
removeFrom(bodyB.contacts, contact);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* @method collide
|
|
437
|
+
* @description Refreshes every contact: drops the ones whose AABBs no longer
|
|
438
|
+
* overlap or whose filters changed, and re-runs the narrowphase on the rest.
|
|
439
|
+
*/
|
|
440
|
+
collide() {
|
|
441
|
+
for (let i = this.contacts.length - 1; i >= 0; i--) {
|
|
442
|
+
const contact = this.contacts[i];
|
|
443
|
+
const fixtureA = contact.fixtureA;
|
|
444
|
+
const fixtureB = contact.fixtureB;
|
|
445
|
+
const bodyA = fixtureA.body;
|
|
446
|
+
const bodyB = fixtureB.body;
|
|
447
|
+
|
|
448
|
+
if (contact.filterFlag) {
|
|
449
|
+
if (
|
|
450
|
+
bodyA.type !== BodyType.DYNAMIC &&
|
|
451
|
+
bodyB.type !== BodyType.DYNAMIC
|
|
452
|
+
) {
|
|
453
|
+
this.destroy(contact);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (
|
|
457
|
+
this.world.contactFilter &&
|
|
458
|
+
!this.world.contactFilter(fixtureA, fixtureB)
|
|
459
|
+
) {
|
|
460
|
+
this.destroy(contact);
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (!shouldCollide(fixtureA, fixtureB)) {
|
|
464
|
+
this.destroy(contact);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
contact.filterFlag = false;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const activeA = bodyA.awake && bodyA.type !== BodyType.STATIC;
|
|
471
|
+
const activeB = bodyB.awake && bodyB.type !== BodyType.STATIC;
|
|
472
|
+
if (!activeA && !activeB) continue;
|
|
473
|
+
|
|
474
|
+
const proxyIdA = fixtureA.proxies[0].proxyId;
|
|
475
|
+
const proxyIdB = fixtureB.proxies[0].proxyId;
|
|
476
|
+
if (!this.world.broadPhase.testOverlap(proxyIdA, proxyIdB)) {
|
|
477
|
+
this.destroy(contact);
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
contact.update(this.world);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* @function removeFrom
|
|
488
|
+
* @description Removes an item from an array in place (order is irrelevant for
|
|
489
|
+
* contact lists).
|
|
490
|
+
* @private
|
|
491
|
+
*/
|
|
492
|
+
function removeFrom(array, item) {
|
|
493
|
+
const index = array.indexOf(item);
|
|
494
|
+
if (index >= 0) {
|
|
495
|
+
const last = array.pop();
|
|
496
|
+
if (index < array.length) array[index] = last;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export { Contact, ContactManager, shouldCollide, mixFriction, mixRestitution };
|