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.
Files changed (103) hide show
  1. package/README.md +1498 -1659
  2. package/dist/types/index.d.ts +4 -1
  3. package/dist/types/src/Animator.d.ts +1 -1
  4. package/dist/types/src/CollisionLayers.d.ts +2 -2
  5. package/dist/types/src/Color.d.ts +1 -0
  6. package/dist/types/src/Drawable.d.ts +1 -1
  7. package/dist/types/src/EmeraldDB.d.ts +2 -2
  8. package/dist/types/src/InstancedTexture.d.ts +17 -2
  9. package/dist/types/src/Material.d.ts +2 -2
  10. package/dist/types/src/MathUtils.d.ts +2 -1
  11. package/dist/types/src/ParticleEmitter.d.ts +1 -1
  12. package/dist/types/src/Physics.d.ts +148 -18
  13. package/dist/types/src/Scene.d.ts +1 -1
  14. package/dist/types/src/Shaders.d.ts +2 -2
  15. package/dist/types/src/Tilemap.d.ts +1 -1
  16. package/dist/types/src/UI.d.ts +1 -1
  17. package/dist/types/src/components/Behaviour.d.ts +2 -2
  18. package/dist/types/src/components/Collider.d.ts +7 -1
  19. package/dist/types/src/components/GameObject.d.ts +2 -2
  20. package/dist/types/src/components/PolygonCollider.d.ts +33 -0
  21. package/dist/types/src/components/RigidBody.d.ts +281 -8
  22. package/dist/types/src/importers/Aseprite.d.ts +2 -2
  23. package/dist/types/src/importers/ForgeLevel.d.ts +97 -0
  24. package/dist/types/src/importers/TiledMap.d.ts +1 -1
  25. package/dist/types/src/managers/EventManager.d.ts +1 -1
  26. package/dist/types/src/managers/Gamepad.d.ts +102 -0
  27. package/dist/types/src/managers/InputManager.d.ts +93 -2
  28. package/dist/types/src/managers/NetworkManager.d.ts +2 -2
  29. package/dist/types/src/managers/RenderStats.d.ts +1 -1
  30. package/dist/types/src/managers/TextureManager.d.ts +1 -1
  31. package/dist/types/src/physics/AABB.d.ts +92 -0
  32. package/dist/types/src/physics/Body.d.ts +435 -0
  33. package/dist/types/src/physics/BodyType.d.ts +6 -0
  34. package/dist/types/src/physics/BroadPhase.d.ts +210 -0
  35. package/dist/types/src/physics/Collision.d.ts +102 -0
  36. package/dist/types/src/physics/Contact.d.ts +206 -0
  37. package/dist/types/src/physics/ContactSolver.d.ts +108 -0
  38. package/dist/types/src/physics/Distance.d.ts +54 -0
  39. package/dist/types/src/physics/DistanceJoint.d.ts +90 -0
  40. package/dist/types/src/physics/Fixture.d.ts +221 -0
  41. package/dist/types/src/physics/Island.d.ts +52 -0
  42. package/dist/types/src/physics/Joint.d.ts +59 -0
  43. package/dist/types/src/physics/Math2D.d.ts +371 -0
  44. package/dist/types/src/physics/RevoluteJoint.d.ts +119 -0
  45. package/dist/types/src/physics/Settings.d.ts +22 -0
  46. package/dist/types/src/physics/Shapes.d.ts +207 -0
  47. package/dist/types/src/physics/TimeOfImpact.d.ts +22 -0
  48. package/dist/types/src/physics/World.d.ts +274 -0
  49. package/dist/types/src/physics/index.d.ts +34 -0
  50. package/index.js +6 -0
  51. package/package.json +2 -3
  52. package/src/Animator.js +1 -1
  53. package/src/CollisionLayers.js +3 -3
  54. package/src/Color.js +8 -0
  55. package/src/Drawable.js +1 -1
  56. package/src/Emerald.js +1 -1
  57. package/src/EmeraldDB.js +2 -2
  58. package/src/InstancedTexture.js +57 -9
  59. package/src/Material.js +2 -2
  60. package/src/MathUtils.js +2 -1
  61. package/src/ParticleEmitter.js +1 -1
  62. package/src/Physics.js +270 -60
  63. package/src/Scene.js +1 -1
  64. package/src/Shaders.js +20 -20
  65. package/src/Tilemap.js +1 -1
  66. package/src/UI.js +1 -1
  67. package/src/components/Behaviour.js +2 -2
  68. package/src/components/BoxCollider.js +7 -9
  69. package/src/components/BoxColliderDebug.js +3 -4
  70. package/src/components/CircleCollider.js +7 -9
  71. package/src/components/CircleColliderDebug.js +3 -2
  72. package/src/components/Collider.js +13 -3
  73. package/src/components/GameObject.js +2 -2
  74. package/src/components/PolygonCollider.js +55 -0
  75. package/src/components/RigidBody.js +441 -14
  76. package/src/importers/Aseprite.js +2 -2
  77. package/src/importers/ForgeLevel.js +581 -0
  78. package/src/importers/TiledMap.js +1 -1
  79. package/src/managers/EventManager.js +1 -1
  80. package/src/managers/Gamepad.js +126 -0
  81. package/src/managers/InputManager.js +129 -3
  82. package/src/managers/NetworkManager.js +2 -2
  83. package/src/managers/RenderStats.js +1 -1
  84. package/src/managers/TextureManager.js +1 -1
  85. package/src/physics/AABB.js +207 -0
  86. package/src/physics/Body.js +862 -0
  87. package/src/physics/BodyType.js +16 -0
  88. package/src/physics/BroadPhase.js +641 -0
  89. package/src/physics/Collision.js +534 -0
  90. package/src/physics/Contact.js +500 -0
  91. package/src/physics/ContactSolver.js +526 -0
  92. package/src/physics/Distance.js +403 -0
  93. package/src/physics/DistanceJoint.js +227 -0
  94. package/src/physics/Fixture.js +346 -0
  95. package/src/physics/Island.js +203 -0
  96. package/src/physics/Joint.js +78 -0
  97. package/src/physics/Math2D.js +573 -0
  98. package/src/physics/RevoluteJoint.js +278 -0
  99. package/src/physics/Settings.js +78 -0
  100. package/src/physics/Shapes.js +549 -0
  101. package/src/physics/TimeOfImpact.js +87 -0
  102. package/src/physics/World.js +731 -0
  103. package/src/physics/index.js +79 -0
@@ -0,0 +1,862 @@
1
+ import { Vec2, Rot, Transform2, Sweep } from "./Math2D.js";
2
+ import { Fixture } from "./Fixture.js";
3
+ import { BodyType } from "./BodyType.js";
4
+
5
+ /**
6
+ * @class Body
7
+ * @description A rigid body: a position, an orientation, and the velocity and
8
+ * mass that go with them. Collision geometry lives on its fixtures, added with
9
+ * {@link Body#createFixture}. Bodies are created through
10
+ * {@link World#createBody}, never with `new`.
11
+ *
12
+ * Everything here is in physics units (meters, radians, seconds). The engine's
13
+ * {@link RigidBody} component is the pixel-space wrapper around this class.
14
+ *
15
+ * @param {World} world - The owning world
16
+ * @param {Object} [def] - `{ type, position, angle, linearVelocity,
17
+ * angularVelocity, linearDamping, angularDamping, fixedRotation, bullet,
18
+ * allowSleep, awake, active, gravityScale, userData }`
19
+ */
20
+ class Body {
21
+ constructor(world, def = {}) {
22
+ this.world = world;
23
+ this.type = def.type || BodyType.STATIC;
24
+
25
+ this.xf = new Transform2();
26
+ this.xf.set(def.position || new Vec2(0, 0), def.angle || 0);
27
+
28
+ this.sweep = new Sweep();
29
+ this.sweep.localCenter.setZero();
30
+ this.sweep.c0.copy(this.xf.p);
31
+ this.sweep.c.copy(this.xf.p);
32
+ this.sweep.a0 = def.angle || 0;
33
+ this.sweep.a = def.angle || 0;
34
+ this.sweep.alpha0 = 0;
35
+
36
+ this.linearVelocity = new Vec2(def.linearVelocity || { x: 0, y: 0 });
37
+ this.angularVelocity = def.angularVelocity || 0;
38
+ this.force = new Vec2(0, 0);
39
+ this.torque = 0;
40
+
41
+ this.linearDamping = def.linearDamping ?? 0;
42
+ this.angularDamping = def.angularDamping ?? 0;
43
+ this.gravityScale = def.gravityScale ?? 1;
44
+
45
+ this.mass = 0;
46
+ this.invMass = 0;
47
+ this.I = 0;
48
+ this.invI = 0;
49
+
50
+ this.fixedRotation = !!def.fixedRotation;
51
+ this.bullet = !!def.bullet;
52
+ this.allowSleep = def.allowSleep !== false;
53
+ this.awake = def.awake !== false;
54
+ this.active = def.active !== false;
55
+ this.sleepTime = 0;
56
+
57
+ this.userData = def.userData ?? null;
58
+
59
+ /** Head of this body's fixture list. @private */
60
+ this.fixtureList = null;
61
+ /** @private */
62
+ this.fixtureCount = 0;
63
+ /** Contacts this body currently takes part in. @private */
64
+ this.contacts = [];
65
+ /** Joints this body currently takes part in. @private */
66
+ this.joints = [];
67
+
68
+ /** @private */
69
+ this.prev = null;
70
+ /** @private */
71
+ this.next = null;
72
+ /** Scratch flag used while building solver islands. @private */
73
+ this.islandFlag = false;
74
+ /** Index into the current island's body array. @private */
75
+ this.islandIndex = 0;
76
+
77
+ if (this.type === BodyType.STATIC) {
78
+ this.awake = false;
79
+ this.linearVelocity.setZero();
80
+ this.angularVelocity = 0;
81
+ } else if (this.type === BodyType.DYNAMIC) {
82
+ this.mass = 1;
83
+ this.invMass = 1;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * @method createFixture
89
+ * @description Attaches a shape to this body. The second argument may be a
90
+ * plain density number or a definition object.
91
+ * @param {Shape} shape - A CircleShape or PolygonShape
92
+ * @param {Object|number} [def] - `{ density, friction, restitution, isSensor,
93
+ * filterCategoryBits, filterMaskBits, filterGroupIndex, userData }`
94
+ * @returns {Fixture} - The new fixture
95
+ */
96
+ createFixture(shape, def = {}) {
97
+ const fixtureDef = typeof def === "number" ? { density: def } : def;
98
+ const fixture = new Fixture(this, shape, fixtureDef);
99
+
100
+ if (this.active) {
101
+ fixture.createProxies(this.world.broadPhase, this.xf);
102
+ }
103
+
104
+ fixture.next = this.fixtureList;
105
+ this.fixtureList = fixture;
106
+ this.fixtureCount++;
107
+
108
+ if (fixture.density > 0) this.resetMassData();
109
+
110
+ this.world.newContacts = true;
111
+ return fixture;
112
+ }
113
+
114
+ /**
115
+ * @method destroyFixture
116
+ * @description Removes a fixture and every contact it was part of.
117
+ * @param {Fixture} fixture
118
+ */
119
+ destroyFixture(fixture) {
120
+ if (!fixture || fixture.body !== this) return;
121
+
122
+ let node = this.fixtureList;
123
+ let prev = null;
124
+ let found = false;
125
+ while (node) {
126
+ if (node === fixture) {
127
+ if (prev) prev.next = node.next;
128
+ else this.fixtureList = node.next;
129
+ found = true;
130
+ break;
131
+ }
132
+ prev = node;
133
+ node = node.next;
134
+ }
135
+ if (!found) return;
136
+
137
+ for (let i = this.contacts.length - 1; i >= 0; i--) {
138
+ const contact = this.contacts[i];
139
+ if (contact.fixtureA === fixture || contact.fixtureB === fixture) {
140
+ this.world.contactManager.destroy(contact);
141
+ }
142
+ }
143
+
144
+ if (this.active) fixture.destroyProxies(this.world.broadPhase);
145
+
146
+ fixture.body = null;
147
+ fixture.next = null;
148
+ this.fixtureCount--;
149
+ this.resetMassData();
150
+ }
151
+
152
+ /**
153
+ * @method getFixtureList
154
+ * @description Returns the first fixture; walk the rest with
155
+ * {@link Fixture#getNext}.
156
+ * @returns {Fixture|null}
157
+ */
158
+ getFixtureList() {
159
+ return this.fixtureList;
160
+ }
161
+
162
+ /**
163
+ * @method getNext
164
+ * @description Returns the next body in the world's list.
165
+ * @returns {Body|null}
166
+ */
167
+ getNext() {
168
+ return this.next;
169
+ }
170
+
171
+ /**
172
+ * @method setUserData
173
+ * @description Attaches arbitrary data to the body. The engine stores the
174
+ * owning {@link RigidBody} here so contacts can be traced back to game
175
+ * objects.
176
+ * @param {*} data
177
+ */
178
+ setUserData(data) {
179
+ this.userData = data;
180
+ }
181
+
182
+ /**
183
+ * @method getUserData
184
+ * @description Returns the attached data.
185
+ * @returns {*}
186
+ */
187
+ getUserData() {
188
+ return this.userData;
189
+ }
190
+
191
+ /**
192
+ * @method getWorld
193
+ * @description Returns the owning world.
194
+ * @returns {World}
195
+ */
196
+ getWorld() {
197
+ return this.world;
198
+ }
199
+
200
+ /**
201
+ * @method getType
202
+ * @description Returns "static", "kinematic" or "dynamic".
203
+ * @returns {string}
204
+ */
205
+ getType() {
206
+ return this.type;
207
+ }
208
+
209
+ /**
210
+ * @method setType
211
+ * @description Changes the body type at runtime, resetting velocities and
212
+ * re-deriving mass. Existing contacts are dropped so they rebuild against the
213
+ * new type.
214
+ * @param {string} type - "static", "kinematic" or "dynamic"
215
+ */
216
+ setType(type) {
217
+ if (this.type === type) return;
218
+ this.type = type;
219
+
220
+ this.resetMassData();
221
+
222
+ if (this.type === BodyType.STATIC) {
223
+ this.linearVelocity.setZero();
224
+ this.angularVelocity = 0;
225
+ this.sweep.a0 = this.sweep.a;
226
+ this.sweep.c0.copy(this.sweep.c);
227
+ this.awake = false;
228
+ this.synchronizeFixtures();
229
+ } else {
230
+ this.setAwake(true);
231
+ }
232
+
233
+ this.force.setZero();
234
+ this.torque = 0;
235
+
236
+ for (let i = this.contacts.length - 1; i >= 0; i--) {
237
+ this.world.contactManager.destroy(this.contacts[i]);
238
+ }
239
+
240
+ for (let f = this.fixtureList; f; f = f.next) {
241
+ for (const proxy of f.proxies) {
242
+ if (proxy.proxyId !== -1)
243
+ this.world.broadPhase.touchProxy(proxy.proxyId);
244
+ }
245
+ }
246
+ this.world.newContacts = true;
247
+ }
248
+
249
+ /**
250
+ * @method setTransform
251
+ * @description Teleports the body: sets position and angle directly, without
252
+ * any velocity implied by the move.
253
+ *
254
+ * A teleport wakes the body. Contacts are only re-evaluated for bodies that
255
+ * are awake, so a sleeping body dropped into a new spot would otherwise sit
256
+ * there without noticing what it now overlaps: respawning a player onto a
257
+ * pickup, for instance, would silently miss it.
258
+ *
259
+ * @param {Object} position - `{ x, y }` in physics units
260
+ * @param {number} angle - Radians
261
+ */
262
+ setTransform(position, angle) {
263
+ this.xf.q.set(angle);
264
+ this.xf.p.copy(position);
265
+
266
+ this.sweep.c.copy(Transform2.mulVec(this.xf, this.sweep.localCenter));
267
+ this.sweep.a = angle;
268
+ this.sweep.c0.copy(this.sweep.c);
269
+ this.sweep.a0 = angle;
270
+
271
+ this.setAwake(true);
272
+ this.synchronizeFixtures(this.xf);
273
+ this.world.newContacts = true;
274
+ }
275
+
276
+ /**
277
+ * @method setPosition
278
+ * @description Moves the body, keeping its angle.
279
+ * @param {Object} position - `{ x, y }` in physics units
280
+ */
281
+ setPosition(position) {
282
+ this.setTransform(position, this.sweep.a);
283
+ }
284
+
285
+ /**
286
+ * @method setAngle
287
+ * @description Rotates the body, keeping its position.
288
+ * @param {number} angle - Radians
289
+ */
290
+ setAngle(angle) {
291
+ this.setTransform(this.xf.p, angle);
292
+ }
293
+
294
+ /**
295
+ * @method getTransform
296
+ * @description Returns the body's transform (live reference).
297
+ * @returns {Transform2}
298
+ */
299
+ getTransform() {
300
+ return this.xf;
301
+ }
302
+
303
+ /**
304
+ * @method getPosition
305
+ * @description Returns the body origin in world space (live reference).
306
+ * @returns {Vec2}
307
+ */
308
+ getPosition() {
309
+ return this.xf.p;
310
+ }
311
+
312
+ /**
313
+ * @method getAngle
314
+ * @description Returns the body's angle in radians.
315
+ * @returns {number}
316
+ */
317
+ getAngle() {
318
+ return this.sweep.a;
319
+ }
320
+
321
+ /**
322
+ * @method getWorldCenter
323
+ * @description Returns the center of mass in world space.
324
+ * @returns {Vec2}
325
+ */
326
+ getWorldCenter() {
327
+ return this.sweep.c;
328
+ }
329
+
330
+ /**
331
+ * @method getLocalCenter
332
+ * @description Returns the center of mass in the body's frame.
333
+ * @returns {Vec2}
334
+ */
335
+ getLocalCenter() {
336
+ return this.sweep.localCenter;
337
+ }
338
+
339
+ /**
340
+ * @method getWorldPoint
341
+ * @description Converts a local point to world space.
342
+ * @param {Object} localPoint
343
+ * @returns {Vec2}
344
+ */
345
+ getWorldPoint(localPoint) {
346
+ return Transform2.mulVec(this.xf, localPoint);
347
+ }
348
+
349
+ /**
350
+ * @method getLocalPoint
351
+ * @description Converts a world point to the body's frame.
352
+ * @param {Object} worldPoint
353
+ * @returns {Vec2}
354
+ */
355
+ getLocalPoint(worldPoint) {
356
+ return Transform2.mulTVec(this.xf, worldPoint);
357
+ }
358
+
359
+ /**
360
+ * @method getWorldVector
361
+ * @description Rotates a local direction into world space.
362
+ * @param {Object} localVector
363
+ * @returns {Vec2}
364
+ */
365
+ getWorldVector(localVector) {
366
+ return Rot.mulVec(this.xf.q, localVector);
367
+ }
368
+
369
+ /**
370
+ * @method getLocalVector
371
+ * @description Rotates a world direction into the body's frame.
372
+ * @param {Object} worldVector
373
+ * @returns {Vec2}
374
+ */
375
+ getLocalVector(worldVector) {
376
+ return Rot.mulTVec(this.xf.q, worldVector);
377
+ }
378
+
379
+ /**
380
+ * @method setLinearVelocity
381
+ * @description Sets the velocity of the center of mass.
382
+ * @param {Object} velocity - `{ x, y }` in physics units per second
383
+ */
384
+ setLinearVelocity(velocity) {
385
+ if (this.type === BodyType.STATIC) return;
386
+ if (velocity.x * velocity.x + velocity.y * velocity.y > 0) {
387
+ this.setAwake(true);
388
+ }
389
+ this.linearVelocity.set(velocity.x, velocity.y);
390
+ }
391
+
392
+ /**
393
+ * @method getLinearVelocity
394
+ * @description Returns the velocity of the center of mass (live reference).
395
+ * @returns {Vec2}
396
+ */
397
+ getLinearVelocity() {
398
+ return this.linearVelocity;
399
+ }
400
+
401
+ /**
402
+ * @method getLinearVelocityFromWorldPoint
403
+ * @description Velocity of the material point of this body that currently
404
+ * coincides with a world point (linear plus the spin contribution).
405
+ * @param {Object} worldPoint
406
+ * @returns {Vec2}
407
+ */
408
+ getLinearVelocityFromWorldPoint(worldPoint) {
409
+ return Vec2.add(
410
+ this.linearVelocity,
411
+ Vec2.crossSV(this.angularVelocity, Vec2.sub(worldPoint, this.sweep.c))
412
+ );
413
+ }
414
+
415
+ /**
416
+ * @method setAngularVelocity
417
+ * @description Sets the spin in radians per second.
418
+ * @param {number} omega
419
+ */
420
+ setAngularVelocity(omega) {
421
+ if (this.type === BodyType.STATIC) return;
422
+ if (omega * omega > 0) this.setAwake(true);
423
+ this.angularVelocity = omega;
424
+ }
425
+
426
+ /**
427
+ * @method getAngularVelocity
428
+ * @description Returns the spin in radians per second.
429
+ * @returns {number}
430
+ */
431
+ getAngularVelocity() {
432
+ return this.angularVelocity;
433
+ }
434
+
435
+ /**
436
+ * @method applyForce
437
+ * @description Applies a force at a world point. Forces are cleared at the
438
+ * end of every step, so this belongs in your update loop.
439
+ * @param {Object} force
440
+ * @param {Object} [point] - Defaults to the center of mass
441
+ * @param {boolean} [wake=true]
442
+ */
443
+ applyForce(force, point = null, wake = true) {
444
+ if (this.type !== BodyType.DYNAMIC) return;
445
+ if (wake && !this.awake) this.setAwake(true);
446
+ if (!this.awake) return;
447
+
448
+ this.force.add(force);
449
+ if (point) {
450
+ this.torque += Vec2.cross(Vec2.sub(point, this.sweep.c), force);
451
+ }
452
+ }
453
+
454
+ /**
455
+ * @method applyForceToCenter
456
+ * @description Applies a force at the center of mass (no torque).
457
+ * @param {Object} force
458
+ * @param {boolean} [wake=true]
459
+ */
460
+ applyForceToCenter(force, wake = true) {
461
+ this.applyForce(force, null, wake);
462
+ }
463
+
464
+ /**
465
+ * @method applyTorque
466
+ * @description Applies a torque about the center of mass.
467
+ * @param {number} torque
468
+ * @param {boolean} [wake=true]
469
+ */
470
+ applyTorque(torque, wake = true) {
471
+ if (this.type !== BodyType.DYNAMIC) return;
472
+ if (wake && !this.awake) this.setAwake(true);
473
+ if (!this.awake) return;
474
+ this.torque += torque;
475
+ }
476
+
477
+ /**
478
+ * @method applyLinearImpulse
479
+ * @description Applies an instantaneous change in momentum at a world point.
480
+ * Unlike a force, an impulse takes effect immediately: this is what a jump
481
+ * or a knockback should use.
482
+ * @param {Object} impulse
483
+ * @param {Object} [point] - Defaults to the center of mass
484
+ * @param {boolean} [wake=true]
485
+ */
486
+ applyLinearImpulse(impulse, point = null, wake = true) {
487
+ if (this.type !== BodyType.DYNAMIC) return;
488
+ if (wake && !this.awake) this.setAwake(true);
489
+ if (!this.awake) return;
490
+
491
+ this.linearVelocity.addMul(this.invMass, impulse);
492
+ if (point) {
493
+ this.angularVelocity +=
494
+ this.invI * Vec2.cross(Vec2.sub(point, this.sweep.c), impulse);
495
+ }
496
+ }
497
+
498
+ /**
499
+ * @method applyAngularImpulse
500
+ * @description Applies an instantaneous change in angular momentum.
501
+ * @param {number} impulse
502
+ * @param {boolean} [wake=true]
503
+ */
504
+ applyAngularImpulse(impulse, wake = true) {
505
+ if (this.type !== BodyType.DYNAMIC) return;
506
+ if (wake && !this.awake) this.setAwake(true);
507
+ if (!this.awake) return;
508
+ this.angularVelocity += this.invI * impulse;
509
+ }
510
+
511
+ /**
512
+ * @method getMass
513
+ * @description Returns the body's mass.
514
+ * @returns {number}
515
+ */
516
+ getMass() {
517
+ return this.mass;
518
+ }
519
+
520
+ /**
521
+ * @method getInertia
522
+ * @description Returns the rotational inertia about the center of mass.
523
+ * @returns {number}
524
+ */
525
+ getInertia() {
526
+ return this.I;
527
+ }
528
+
529
+ /**
530
+ * @method resetMassData
531
+ * @description Recomputes mass, center of mass and inertia from the
532
+ * fixtures' densities. Called automatically when fixtures change.
533
+ */
534
+ resetMassData() {
535
+ this.mass = 0;
536
+ this.invMass = 0;
537
+ this.I = 0;
538
+ this.invI = 0;
539
+ this.sweep.localCenter.setZero();
540
+
541
+ if (this.type === BodyType.STATIC || this.type === BodyType.KINEMATIC) {
542
+ this.sweep.c0.copy(this.xf.p);
543
+ this.sweep.c.copy(this.xf.p);
544
+ this.sweep.a0 = this.sweep.a;
545
+ return;
546
+ }
547
+
548
+ const localCenter = new Vec2(0, 0);
549
+ for (let f = this.fixtureList; f; f = f.next) {
550
+ if (f.density === 0) continue;
551
+ const massData = f.getMassData();
552
+ this.mass += massData.mass;
553
+ localCenter.addMul(massData.mass, massData.center);
554
+ this.I += massData.I;
555
+ }
556
+
557
+ if (this.mass > 0) {
558
+ this.invMass = 1 / this.mass;
559
+ localCenter.mul(this.invMass);
560
+ } else {
561
+ this.mass = 1;
562
+ this.invMass = 1;
563
+ }
564
+
565
+ if (this.I > 0 && !this.fixedRotation) {
566
+ this.I -= this.mass * Vec2.dot(localCenter, localCenter);
567
+ this.invI = 1 / this.I;
568
+ } else {
569
+ this.I = 0;
570
+ this.invI = 0;
571
+ }
572
+
573
+ const oldCenter = this.sweep.c.clone();
574
+ this.sweep.localCenter.copy(localCenter);
575
+ this.sweep.c.copy(Transform2.mulVec(this.xf, this.sweep.localCenter));
576
+ this.sweep.c0.copy(this.sweep.c);
577
+
578
+ this.linearVelocity.add(
579
+ Vec2.crossSV(this.angularVelocity, Vec2.sub(this.sweep.c, oldCenter))
580
+ );
581
+ }
582
+
583
+ /**
584
+ * @method setMassData
585
+ * @description Overrides the computed mass properties.
586
+ * @param {Object} massData - `{ mass, center, I }`
587
+ */
588
+ setMassData(massData) {
589
+ if (this.type !== BodyType.DYNAMIC) return;
590
+
591
+ this.invMass = 0;
592
+ this.I = 0;
593
+ this.invI = 0;
594
+
595
+ this.mass = massData.mass;
596
+ if (this.mass <= 0) this.mass = 1;
597
+ this.invMass = 1 / this.mass;
598
+
599
+ if (massData.I > 0 && !this.fixedRotation) {
600
+ this.I =
601
+ massData.I - this.mass * Vec2.dot(massData.center, massData.center);
602
+ this.invI = 1 / this.I;
603
+ }
604
+
605
+ const oldCenter = this.sweep.c.clone();
606
+ this.sweep.localCenter.copy(massData.center);
607
+ this.sweep.c.copy(Transform2.mulVec(this.xf, this.sweep.localCenter));
608
+ this.sweep.c0.copy(this.sweep.c);
609
+ this.linearVelocity.add(
610
+ Vec2.crossSV(this.angularVelocity, Vec2.sub(this.sweep.c, oldCenter))
611
+ );
612
+ }
613
+
614
+ /**
615
+ * @method setAwake
616
+ * @description Wakes the body or puts it to sleep. A sleeping body is skipped
617
+ * by the solver until something touches it, which is what keeps a settled
618
+ * pile of crates free.
619
+ * @param {boolean} flag
620
+ */
621
+ setAwake(flag) {
622
+ if (this.type === BodyType.STATIC) return;
623
+ if (flag) {
624
+ this.awake = true;
625
+ this.sleepTime = 0;
626
+ } else {
627
+ this.awake = false;
628
+ this.sleepTime = 0;
629
+ this.linearVelocity.setZero();
630
+ this.angularVelocity = 0;
631
+ this.force.setZero();
632
+ this.torque = 0;
633
+ }
634
+ }
635
+
636
+ /**
637
+ * @method isAwake
638
+ * @description Whether the body is currently simulated.
639
+ * @returns {boolean}
640
+ */
641
+ isAwake() {
642
+ return this.awake;
643
+ }
644
+
645
+ /**
646
+ * @method setSleepingAllowed
647
+ * @description Allows or forbids this body from ever sleeping.
648
+ * @param {boolean} flag
649
+ */
650
+ setSleepingAllowed(flag) {
651
+ this.allowSleep = !!flag;
652
+ if (!flag) this.setAwake(true);
653
+ }
654
+
655
+ /**
656
+ * @method isSleepingAllowed
657
+ * @description Whether this body may sleep.
658
+ * @returns {boolean}
659
+ */
660
+ isSleepingAllowed() {
661
+ return this.allowSleep;
662
+ }
663
+
664
+ /**
665
+ * @method setActive
666
+ * @description Adds or removes the body from collision detection without
667
+ * destroying it.
668
+ * @param {boolean} flag
669
+ */
670
+ setActive(flag) {
671
+ if (flag === this.active) return;
672
+ this.active = !!flag;
673
+
674
+ if (this.active) {
675
+ for (let f = this.fixtureList; f; f = f.next) {
676
+ f.createProxies(this.world.broadPhase, this.xf);
677
+ }
678
+ this.world.newContacts = true;
679
+ } else {
680
+ for (let f = this.fixtureList; f; f = f.next) {
681
+ f.destroyProxies(this.world.broadPhase);
682
+ }
683
+ for (let i = this.contacts.length - 1; i >= 0; i--) {
684
+ this.world.contactManager.destroy(this.contacts[i]);
685
+ }
686
+ }
687
+ }
688
+
689
+ /**
690
+ * @method isActive
691
+ * @description Whether the body takes part in collision detection.
692
+ * @returns {boolean}
693
+ */
694
+ isActive() {
695
+ return this.active;
696
+ }
697
+
698
+ /**
699
+ * @method setFixedRotation
700
+ * @description Locks or unlocks the body's rotation. Locking is the usual
701
+ * choice for player characters, which should never topple over.
702
+ * @param {boolean} flag
703
+ */
704
+ setFixedRotation(flag) {
705
+ if (this.fixedRotation === !!flag) return;
706
+ this.fixedRotation = !!flag;
707
+ this.angularVelocity = 0;
708
+ this.resetMassData();
709
+ }
710
+
711
+ /**
712
+ * @method isFixedRotation
713
+ * @description Whether rotation is locked.
714
+ * @returns {boolean}
715
+ */
716
+ isFixedRotation() {
717
+ return this.fixedRotation;
718
+ }
719
+
720
+ /**
721
+ * @method setBullet
722
+ * @description Turns continuous collision detection on for this body, so it
723
+ * is swept against static geometry instead of teleporting between steps.
724
+ * @param {boolean} flag
725
+ */
726
+ setBullet(flag) {
727
+ this.bullet = !!flag;
728
+ }
729
+
730
+ /**
731
+ * @method isBullet
732
+ * @description Whether continuous collision detection is enabled.
733
+ * @returns {boolean}
734
+ */
735
+ isBullet() {
736
+ return this.bullet;
737
+ }
738
+
739
+ /**
740
+ * @method setLinearDamping
741
+ * @description Sets the drag applied to linear motion each step.
742
+ * @param {number} damping
743
+ */
744
+ setLinearDamping(damping) {
745
+ this.linearDamping = damping;
746
+ }
747
+
748
+ /**
749
+ * @method getLinearDamping
750
+ * @description Returns the linear damping.
751
+ * @returns {number}
752
+ */
753
+ getLinearDamping() {
754
+ return this.linearDamping;
755
+ }
756
+
757
+ /**
758
+ * @method setAngularDamping
759
+ * @description Sets the drag applied to rotation each step.
760
+ * @param {number} damping
761
+ */
762
+ setAngularDamping(damping) {
763
+ this.angularDamping = damping;
764
+ }
765
+
766
+ /**
767
+ * @method getAngularDamping
768
+ * @description Returns the angular damping.
769
+ * @returns {number}
770
+ */
771
+ getAngularDamping() {
772
+ return this.angularDamping;
773
+ }
774
+
775
+ /**
776
+ * @method setGravityScale
777
+ * @description Scales how strongly gravity pulls on this body (0 disables it,
778
+ * 2 makes it twice as heavy-feeling).
779
+ * @param {number} scale
780
+ */
781
+ setGravityScale(scale) {
782
+ this.gravityScale = scale;
783
+ }
784
+
785
+ /**
786
+ * @method getGravityScale
787
+ * @description Returns the gravity multiplier.
788
+ * @returns {number}
789
+ */
790
+ getGravityScale() {
791
+ return this.gravityScale;
792
+ }
793
+
794
+ /**
795
+ * @method getContactList
796
+ * @description Returns the contacts this body is part of.
797
+ * @returns {Array<Contact>}
798
+ */
799
+ getContactList() {
800
+ return this.contacts;
801
+ }
802
+
803
+ /**
804
+ * @method synchronizeTransform
805
+ * @description Rebuilds the body transform from the sweep's current state.
806
+ * @private
807
+ */
808
+ synchronizeTransform() {
809
+ this.xf.q.set(this.sweep.a);
810
+ this.xf.p.set(
811
+ this.sweep.c.x -
812
+ (this.xf.q.c * this.sweep.localCenter.x -
813
+ this.xf.q.s * this.sweep.localCenter.y),
814
+ this.sweep.c.y -
815
+ (this.xf.q.s * this.sweep.localCenter.x +
816
+ this.xf.q.c * this.sweep.localCenter.y)
817
+ );
818
+ }
819
+
820
+ /**
821
+ * @method synchronizeFixtures
822
+ * @description Updates the broadphase proxies to span where the body was at
823
+ * the start of the step and where it is now.
824
+ * @param {Transform2} [xf1] - Optional explicit "before" transform
825
+ * @private
826
+ */
827
+ synchronizeFixtures(xf1 = null) {
828
+ if (!this.active) return;
829
+ let from = xf1;
830
+ if (!from) {
831
+ from = new Transform2();
832
+ from.q.set(this.sweep.a0);
833
+ from.p.set(
834
+ this.sweep.c0.x -
835
+ (from.q.c * this.sweep.localCenter.x -
836
+ from.q.s * this.sweep.localCenter.y),
837
+ this.sweep.c0.y -
838
+ (from.q.s * this.sweep.localCenter.x +
839
+ from.q.c * this.sweep.localCenter.y)
840
+ );
841
+ }
842
+ for (let f = this.fixtureList; f; f = f.next) {
843
+ f.synchronize(this.world.broadPhase, from, this.xf);
844
+ }
845
+ }
846
+
847
+ /**
848
+ * @method advance
849
+ * @description Moves the body to a fraction of its sweep, used by continuous
850
+ * collision detection when an impact is found mid-step.
851
+ * @param {number} alpha
852
+ * @private
853
+ */
854
+ advance(alpha) {
855
+ this.sweep.advance(alpha);
856
+ this.sweep.c.copy(this.sweep.c0);
857
+ this.sweep.a = this.sweep.a0;
858
+ this.synchronizeTransform();
859
+ }
860
+ }
861
+
862
+ export { Body, BodyType };