box2d-ruby 1.0.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 (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +336 -0
  4. data/examples/contact_events.rb +22 -0
  5. data/examples/debug_draw.rb +15 -0
  6. data/examples/falling_ball.rb +19 -0
  7. data/examples/rugl_debug_draw.rb +68 -0
  8. data/examples/stagecraft_debug_draw.rb +64 -0
  9. data/examples/support/box2d_debug_lines.rb +125 -0
  10. data/ext/box2d/CMakeLists.txt +35 -0
  11. data/ext/box2d/extconf.rb +42 -0
  12. data/ext/box2d/native.c +9 -0
  13. data/ext/box2d/vendor/box2d/LICENSE +21 -0
  14. data/ext/box2d/vendor/box2d/VERSION +1 -0
  15. data/ext/box2d/vendor/box2d/include/box2d/base.h +131 -0
  16. data/ext/box2d/vendor/box2d/include/box2d/box2d.h +1222 -0
  17. data/ext/box2d/vendor/box2d/include/box2d/collision.h +830 -0
  18. data/ext/box2d/vendor/box2d/include/box2d/id.h +144 -0
  19. data/ext/box2d/vendor/box2d/include/box2d/math_functions.h +761 -0
  20. data/ext/box2d/vendor/box2d/include/box2d/types.h +1457 -0
  21. data/ext/box2d/vendor/box2d/src/CMakeLists.txt +223 -0
  22. data/ext/box2d/vendor/box2d/src/aabb.c +132 -0
  23. data/ext/box2d/vendor/box2d/src/aabb.h +56 -0
  24. data/ext/box2d/vendor/box2d/src/arena_allocator.c +112 -0
  25. data/ext/box2d/vendor/box2d/src/arena_allocator.h +48 -0
  26. data/ext/box2d/vendor/box2d/src/array.c +8 -0
  27. data/ext/box2d/vendor/box2d/src/array.h +179 -0
  28. data/ext/box2d/vendor/box2d/src/atomic.h +79 -0
  29. data/ext/box2d/vendor/box2d/src/bitset.c +67 -0
  30. data/ext/box2d/vendor/box2d/src/bitset.h +65 -0
  31. data/ext/box2d/vendor/box2d/src/body.c +1884 -0
  32. data/ext/box2d/vendor/box2d/src/body.h +194 -0
  33. data/ext/box2d/vendor/box2d/src/box2d.natvis +41 -0
  34. data/ext/box2d/vendor/box2d/src/broad_phase.c +524 -0
  35. data/ext/box2d/vendor/box2d/src/broad_phase.h +83 -0
  36. data/ext/box2d/vendor/box2d/src/constants.h +54 -0
  37. data/ext/box2d/vendor/box2d/src/constraint_graph.c +322 -0
  38. data/ext/box2d/vendor/box2d/src/constraint_graph.h +58 -0
  39. data/ext/box2d/vendor/box2d/src/contact.c +650 -0
  40. data/ext/box2d/vendor/box2d/src/contact.h +148 -0
  41. data/ext/box2d/vendor/box2d/src/contact_solver.c +2120 -0
  42. data/ext/box2d/vendor/box2d/src/contact_solver.h +54 -0
  43. data/ext/box2d/vendor/box2d/src/core.c +178 -0
  44. data/ext/box2d/vendor/box2d/src/core.h +149 -0
  45. data/ext/box2d/vendor/box2d/src/ctz.h +112 -0
  46. data/ext/box2d/vendor/box2d/src/distance.c +1415 -0
  47. data/ext/box2d/vendor/box2d/src/distance_joint.c +556 -0
  48. data/ext/box2d/vendor/box2d/src/dynamic_tree.c +1989 -0
  49. data/ext/box2d/vendor/box2d/src/geometry.c +1028 -0
  50. data/ext/box2d/vendor/box2d/src/hull.c +328 -0
  51. data/ext/box2d/vendor/box2d/src/id_pool.c +79 -0
  52. data/ext/box2d/vendor/box2d/src/id_pool.h +35 -0
  53. data/ext/box2d/vendor/box2d/src/island.c +977 -0
  54. data/ext/box2d/vendor/box2d/src/island.h +89 -0
  55. data/ext/box2d/vendor/box2d/src/joint.c +1272 -0
  56. data/ext/box2d/vendor/box2d/src/joint.h +335 -0
  57. data/ext/box2d/vendor/box2d/src/manifold.c +1726 -0
  58. data/ext/box2d/vendor/box2d/src/math_functions.c +159 -0
  59. data/ext/box2d/vendor/box2d/src/motor_joint.c +283 -0
  60. data/ext/box2d/vendor/box2d/src/mouse_joint.c +214 -0
  61. data/ext/box2d/vendor/box2d/src/mover.c +73 -0
  62. data/ext/box2d/vendor/box2d/src/prismatic_joint.c +656 -0
  63. data/ext/box2d/vendor/box2d/src/revolute_joint.c +534 -0
  64. data/ext/box2d/vendor/box2d/src/sensor.c +389 -0
  65. data/ext/box2d/vendor/box2d/src/sensor.h +36 -0
  66. data/ext/box2d/vendor/box2d/src/shape.c +1714 -0
  67. data/ext/box2d/vendor/box2d/src/shape.h +123 -0
  68. data/ext/box2d/vendor/box2d/src/solver.c +2038 -0
  69. data/ext/box2d/vendor/box2d/src/solver.h +155 -0
  70. data/ext/box2d/vendor/box2d/src/solver_set.c +613 -0
  71. data/ext/box2d/vendor/box2d/src/solver_set.h +57 -0
  72. data/ext/box2d/vendor/box2d/src/table.c +238 -0
  73. data/ext/box2d/vendor/box2d/src/table.h +37 -0
  74. data/ext/box2d/vendor/box2d/src/timer.c +185 -0
  75. data/ext/box2d/vendor/box2d/src/types.c +151 -0
  76. data/ext/box2d/vendor/box2d/src/weld_joint.c +310 -0
  77. data/ext/box2d/vendor/box2d/src/wheel_joint.c +551 -0
  78. data/ext/box2d/vendor/box2d/src/world.c +3301 -0
  79. data/ext/box2d/vendor/box2d/src/world.h +192 -0
  80. data/generator/generate.rb +316 -0
  81. data/generator/layout_probe.c +507 -0
  82. data/generator/verify_layouts.rb +32 -0
  83. data/lib/box2d/body.rb +172 -0
  84. data/lib/box2d/body_definition.rb +38 -0
  85. data/lib/box2d/body_shapes.rb +135 -0
  86. data/lib/box2d/chain.rb +61 -0
  87. data/lib/box2d/debug_draw.rb +118 -0
  88. data/lib/box2d/events.rb +103 -0
  89. data/lib/box2d/fixed_stepper.rb +39 -0
  90. data/lib/box2d/handle.rb +46 -0
  91. data/lib/box2d/hit.rb +5 -0
  92. data/lib/box2d/joint.rb +197 -0
  93. data/lib/box2d/native.rb +1462 -0
  94. data/lib/box2d/native_loader.rb +45 -0
  95. data/lib/box2d/pixel_scale.rb +29 -0
  96. data/lib/box2d/shape.rb +109 -0
  97. data/lib/box2d/shape_definition.rb +44 -0
  98. data/lib/box2d/value_conversion.rb +80 -0
  99. data/lib/box2d/version.rb +7 -0
  100. data/lib/box2d/world.rb +135 -0
  101. data/lib/box2d/world_joints.rb +156 -0
  102. data/lib/box2d/world_queries.rb +122 -0
  103. data/lib/box2d/world_registry.rb +95 -0
  104. data/lib/box2d.rb +31 -0
  105. data/script/build_platform_gem.rb +24 -0
  106. data/script/deterministic_scene.rb +56 -0
  107. data/script/update_deterministic_snapshot.rb +11 -0
  108. data/script/verify_platform_gem.rb +24 -0
  109. metadata +164 -0
@@ -0,0 +1,1884 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #include "body.h"
5
+
6
+ #include "aabb.h"
7
+ #include "array.h"
8
+ #include "contact.h"
9
+ #include "core.h"
10
+ #include "id_pool.h"
11
+ #include "island.h"
12
+ #include "joint.h"
13
+ #include "shape.h"
14
+ #include "solver_set.h"
15
+ #include "world.h"
16
+
17
+ // needed for dll export
18
+ #include "sensor.h"
19
+
20
+ #include "box2d/box2d.h"
21
+ #include "box2d/id.h"
22
+
23
+ #include <string.h>
24
+
25
+ // Implement functions for b2BodyArray
26
+ B2_ARRAY_SOURCE( b2Body, b2Body )
27
+ B2_ARRAY_SOURCE( b2BodySim, b2BodySim )
28
+ B2_ARRAY_SOURCE( b2BodyState, b2BodyState )
29
+
30
+ // Get a validated body from a world using an id.
31
+ b2Body* b2GetBodyFullId( b2World* world, b2BodyId bodyId )
32
+ {
33
+ B2_ASSERT( b2Body_IsValid( bodyId ) );
34
+
35
+ // id index starts at one so that zero can represent null
36
+ return b2BodyArray_Get( &world->bodies, bodyId.index1 - 1 );
37
+ }
38
+
39
+ b2Transform b2GetBodyTransformQuick( b2World* world, b2Body* body )
40
+ {
41
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );
42
+ b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, body->localIndex );
43
+ return bodySim->transform;
44
+ }
45
+
46
+ b2Transform b2GetBodyTransform( b2World* world, int bodyId )
47
+ {
48
+ b2Body* body = b2BodyArray_Get( &world->bodies, bodyId );
49
+ return b2GetBodyTransformQuick( world, body );
50
+ }
51
+
52
+ // Create a b2BodyId from a raw id.
53
+ b2BodyId b2MakeBodyId( b2World* world, int bodyId )
54
+ {
55
+ b2Body* body = b2BodyArray_Get( &world->bodies, bodyId );
56
+ return (b2BodyId){ bodyId + 1, world->worldId, body->generation };
57
+ }
58
+
59
+ b2BodySim* b2GetBodySim( b2World* world, b2Body* body )
60
+ {
61
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );
62
+ b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, body->localIndex );
63
+ return bodySim;
64
+ }
65
+
66
+ b2BodyState* b2GetBodyState( b2World* world, b2Body* body )
67
+ {
68
+ if ( body->setIndex == b2_awakeSet )
69
+ {
70
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
71
+ return b2BodyStateArray_Get( &set->bodyStates, body->localIndex );
72
+ }
73
+
74
+ return NULL;
75
+ }
76
+
77
+ static void b2CreateIslandForBody( b2World* world, int setIndex, b2Body* body )
78
+ {
79
+ B2_ASSERT( body->islandId == B2_NULL_INDEX );
80
+ B2_ASSERT( body->islandPrev == B2_NULL_INDEX );
81
+ B2_ASSERT( body->islandNext == B2_NULL_INDEX );
82
+ B2_ASSERT( setIndex != b2_disabledSet );
83
+
84
+ b2Island* island = b2CreateIsland( world, setIndex );
85
+
86
+ body->islandId = island->islandId;
87
+ island->headBody = body->id;
88
+ island->tailBody = body->id;
89
+ island->bodyCount = 1;
90
+ }
91
+
92
+ static void b2RemoveBodyFromIsland( b2World* world, b2Body* body )
93
+ {
94
+ if ( body->islandId == B2_NULL_INDEX )
95
+ {
96
+ B2_ASSERT( body->islandPrev == B2_NULL_INDEX );
97
+ B2_ASSERT( body->islandNext == B2_NULL_INDEX );
98
+ return;
99
+ }
100
+
101
+ int islandId = body->islandId;
102
+ b2Island* island = b2IslandArray_Get( &world->islands, islandId );
103
+
104
+ // Fix the island's linked list of sims
105
+ if ( body->islandPrev != B2_NULL_INDEX )
106
+ {
107
+ b2Body* prevBody = b2BodyArray_Get( &world->bodies, body->islandPrev );
108
+ prevBody->islandNext = body->islandNext;
109
+ }
110
+
111
+ if ( body->islandNext != B2_NULL_INDEX )
112
+ {
113
+ b2Body* nextBody = b2BodyArray_Get( &world->bodies, body->islandNext );
114
+ nextBody->islandPrev = body->islandPrev;
115
+ }
116
+
117
+ B2_ASSERT( island->bodyCount > 0 );
118
+ island->bodyCount -= 1;
119
+ bool islandDestroyed = false;
120
+
121
+ if ( island->headBody == body->id )
122
+ {
123
+ island->headBody = body->islandNext;
124
+
125
+ if ( island->headBody == B2_NULL_INDEX )
126
+ {
127
+ // Destroy empty island
128
+ B2_ASSERT( island->tailBody == body->id );
129
+ B2_ASSERT( island->bodyCount == 0 );
130
+ B2_ASSERT( island->contactCount == 0 );
131
+ B2_ASSERT( island->jointCount == 0 );
132
+
133
+ // Free the island
134
+ b2DestroyIsland( world, island->islandId );
135
+ islandDestroyed = true;
136
+ }
137
+ }
138
+ else if ( island->tailBody == body->id )
139
+ {
140
+ island->tailBody = body->islandPrev;
141
+ }
142
+
143
+ if ( islandDestroyed == false )
144
+ {
145
+ b2ValidateIsland( world, islandId );
146
+ }
147
+
148
+ body->islandId = B2_NULL_INDEX;
149
+ body->islandPrev = B2_NULL_INDEX;
150
+ body->islandNext = B2_NULL_INDEX;
151
+ }
152
+
153
+ static void b2DestroyBodyContacts( b2World* world, b2Body* body, bool wakeBodies )
154
+ {
155
+ // Destroy the attached contacts
156
+ int edgeKey = body->headContactKey;
157
+ while ( edgeKey != B2_NULL_INDEX )
158
+ {
159
+ int contactId = edgeKey >> 1;
160
+ int edgeIndex = edgeKey & 1;
161
+
162
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
163
+ edgeKey = contact->edges[edgeIndex].nextKey;
164
+ b2DestroyContact( world, contact, wakeBodies );
165
+ }
166
+
167
+ b2ValidateSolverSets( world );
168
+ }
169
+
170
+ b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def )
171
+ {
172
+ B2_CHECK_DEF( def );
173
+ B2_ASSERT( b2IsValidVec2( def->position ) );
174
+ B2_ASSERT( b2IsValidRotation( def->rotation ) );
175
+ B2_ASSERT( b2IsValidVec2( def->linearVelocity ) );
176
+ B2_ASSERT( b2IsValidFloat( def->angularVelocity ) );
177
+ B2_ASSERT( b2IsValidFloat( def->linearDamping ) && def->linearDamping >= 0.0f );
178
+ B2_ASSERT( b2IsValidFloat( def->angularDamping ) && def->angularDamping >= 0.0f );
179
+ B2_ASSERT( b2IsValidFloat( def->sleepThreshold ) && def->sleepThreshold >= 0.0f );
180
+ B2_ASSERT( b2IsValidFloat( def->gravityScale ) );
181
+
182
+ b2World* world = b2GetWorldFromId( worldId );
183
+ B2_ASSERT( world->locked == false );
184
+
185
+ if ( world->locked )
186
+ {
187
+ return b2_nullBodyId;
188
+ }
189
+
190
+ bool isAwake = ( def->isAwake || def->enableSleep == false ) && def->isEnabled;
191
+
192
+ // determine the solver set
193
+ int setId;
194
+ if ( def->isEnabled == false )
195
+ {
196
+ // any body type can be disabled
197
+ setId = b2_disabledSet;
198
+ }
199
+ else if ( def->type == b2_staticBody )
200
+ {
201
+ setId = b2_staticSet;
202
+ }
203
+ else if ( isAwake == true )
204
+ {
205
+ setId = b2_awakeSet;
206
+ }
207
+ else
208
+ {
209
+ // new set for a sleeping body in its own island
210
+ setId = b2AllocId( &world->solverSetIdPool );
211
+ if ( setId == world->solverSets.count )
212
+ {
213
+ // Create a zero initialized solver set. All sub-arrays are also zero initialized.
214
+ b2SolverSetArray_Push( &world->solverSets, (b2SolverSet){ 0 } );
215
+ }
216
+ else
217
+ {
218
+ B2_ASSERT( world->solverSets.data[setId].setIndex == B2_NULL_INDEX );
219
+ }
220
+
221
+ world->solverSets.data[setId].setIndex = setId;
222
+ }
223
+
224
+ B2_ASSERT( 0 <= setId && setId < world->solverSets.count );
225
+
226
+ int bodyId = b2AllocId( &world->bodyIdPool );
227
+
228
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setId );
229
+ b2BodySim* bodySim = b2BodySimArray_Add( &set->bodySims );
230
+ *bodySim = (b2BodySim){ 0 };
231
+ bodySim->transform.p = def->position;
232
+ bodySim->transform.q = def->rotation;
233
+ bodySim->center = def->position;
234
+ bodySim->rotation0 = bodySim->transform.q;
235
+ bodySim->center0 = bodySim->center;
236
+ bodySim->minExtent = B2_HUGE;
237
+ bodySim->maxExtent = 0.0f;
238
+ bodySim->linearDamping = def->linearDamping;
239
+ bodySim->angularDamping = def->angularDamping;
240
+ bodySim->gravityScale = def->gravityScale;
241
+ bodySim->bodyId = bodyId;
242
+ bodySim->isBullet = def->isBullet;
243
+ bodySim->allowFastRotation = def->allowFastRotation;
244
+
245
+ if ( setId == b2_awakeSet )
246
+ {
247
+ b2BodyState* bodyState = b2BodyStateArray_Add( &set->bodyStates );
248
+ B2_ASSERT( ( (uintptr_t)bodyState & 0x1F ) == 0 );
249
+
250
+ *bodyState = (b2BodyState){ 0 };
251
+ bodyState->linearVelocity = def->linearVelocity;
252
+ bodyState->angularVelocity = def->angularVelocity;
253
+ bodyState->deltaRotation = b2Rot_identity;
254
+ }
255
+
256
+ if ( bodyId == world->bodies.count )
257
+ {
258
+ b2BodyArray_Push( &world->bodies, (b2Body){ 0 } );
259
+ }
260
+ else
261
+ {
262
+ B2_ASSERT( world->bodies.data[bodyId].id == B2_NULL_INDEX );
263
+ }
264
+
265
+ b2Body* body = b2BodyArray_Get( &world->bodies, bodyId );
266
+
267
+ if ( def->name )
268
+ {
269
+ int i = 0;
270
+ while ( i < 31 && def->name[i] != 0 )
271
+ {
272
+ body->name[i] = def->name[i];
273
+ i += 1;
274
+ }
275
+
276
+ while ( i < 32)
277
+ {
278
+ body->name[i] = 0;
279
+ i += 1;
280
+ }
281
+ }
282
+ else
283
+ {
284
+ memset( body->name, 0, 32 * sizeof( char ) );
285
+ }
286
+
287
+ body->userData = def->userData;
288
+ body->setIndex = setId;
289
+ body->localIndex = set->bodySims.count - 1;
290
+ body->generation += 1;
291
+ body->headShapeId = B2_NULL_INDEX;
292
+ body->shapeCount = 0;
293
+ body->headChainId = B2_NULL_INDEX;
294
+ body->headContactKey = B2_NULL_INDEX;
295
+ body->contactCount = 0;
296
+ body->headJointKey = B2_NULL_INDEX;
297
+ body->jointCount = 0;
298
+ body->islandId = B2_NULL_INDEX;
299
+ body->islandPrev = B2_NULL_INDEX;
300
+ body->islandNext = B2_NULL_INDEX;
301
+ body->bodyMoveIndex = B2_NULL_INDEX;
302
+ body->id = bodyId;
303
+ body->mass = 0.0f;
304
+ body->inertia = 0.0f;
305
+ body->sleepThreshold = def->sleepThreshold;
306
+ body->sleepTime = 0.0f;
307
+ body->type = def->type;
308
+ body->enableSleep = def->enableSleep;
309
+ body->fixedRotation = def->fixedRotation;
310
+ body->isSpeedCapped = false;
311
+ body->isMarked = false;
312
+
313
+ // dynamic and kinematic bodies that are enabled need a island
314
+ if ( setId >= b2_awakeSet )
315
+ {
316
+ b2CreateIslandForBody( world, setId, body );
317
+ }
318
+
319
+ b2ValidateSolverSets( world );
320
+
321
+ b2BodyId id = { bodyId + 1, world->worldId, body->generation };
322
+ return id;
323
+ }
324
+
325
+ bool b2WakeBody( b2World* world, b2Body* body )
326
+ {
327
+ if ( body->setIndex >= b2_firstSleepingSet )
328
+ {
329
+ b2WakeSolverSet( world, body->setIndex );
330
+ return true;
331
+ }
332
+
333
+ return false;
334
+ }
335
+
336
+ void b2DestroyBody( b2BodyId bodyId )
337
+ {
338
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
339
+ if ( world == NULL )
340
+ {
341
+ return;
342
+ }
343
+
344
+ b2Body* body = b2GetBodyFullId( world, bodyId );
345
+
346
+ // Wake bodies attached to this body, even if this body is static.
347
+ bool wakeBodies = true;
348
+
349
+ // Destroy the attached joints
350
+ int edgeKey = body->headJointKey;
351
+ while ( edgeKey != B2_NULL_INDEX )
352
+ {
353
+ int jointId = edgeKey >> 1;
354
+ int edgeIndex = edgeKey & 1;
355
+
356
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
357
+ edgeKey = joint->edges[edgeIndex].nextKey;
358
+
359
+ // Careful because this modifies the list being traversed
360
+ b2DestroyJointInternal( world, joint, wakeBodies );
361
+ }
362
+
363
+ // Destroy all contacts attached to this body.
364
+ b2DestroyBodyContacts( world, body, wakeBodies );
365
+
366
+ // Destroy the attached shapes and their broad-phase proxies.
367
+ int shapeId = body->headShapeId;
368
+ while ( shapeId != B2_NULL_INDEX )
369
+ {
370
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
371
+
372
+ if ( shape->sensorIndex != B2_NULL_INDEX )
373
+ {
374
+ b2DestroySensor( world, shape );
375
+ }
376
+
377
+ b2DestroyShapeProxy( shape, &world->broadPhase );
378
+
379
+ // Return shape to free list.
380
+ b2FreeId( &world->shapeIdPool, shapeId );
381
+ shape->id = B2_NULL_INDEX;
382
+
383
+ shapeId = shape->nextShapeId;
384
+ }
385
+
386
+ // Destroy the attached chains. The associated shapes have already been destroyed above.
387
+ int chainId = body->headChainId;
388
+ while ( chainId != B2_NULL_INDEX )
389
+ {
390
+ b2ChainShape* chain = b2ChainShapeArray_Get( &world->chainShapes, chainId );
391
+
392
+ b2FreeChainData( chain );
393
+
394
+ // Return chain to free list.
395
+ b2FreeId( &world->chainIdPool, chainId );
396
+ chain->id = B2_NULL_INDEX;
397
+
398
+ chainId = chain->nextChainId;
399
+ }
400
+
401
+ b2RemoveBodyFromIsland( world, body );
402
+
403
+ // Remove body sim from solver set that owns it
404
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );
405
+ int movedIndex = b2BodySimArray_RemoveSwap( &set->bodySims, body->localIndex );
406
+ if ( movedIndex != B2_NULL_INDEX )
407
+ {
408
+ // Fix moved body index
409
+ b2BodySim* movedSim = set->bodySims.data + body->localIndex;
410
+ int movedId = movedSim->bodyId;
411
+ b2Body* movedBody = b2BodyArray_Get( &world->bodies, movedId );
412
+ B2_ASSERT( movedBody->localIndex == movedIndex );
413
+ movedBody->localIndex = body->localIndex;
414
+ }
415
+
416
+ // Remove body state from awake set
417
+ if ( body->setIndex == b2_awakeSet )
418
+ {
419
+ int result = b2BodyStateArray_RemoveSwap( &set->bodyStates, body->localIndex );
420
+ B2_ASSERT( result == movedIndex );
421
+ B2_UNUSED( result );
422
+ }
423
+ else if ( set->setIndex >= b2_firstSleepingSet && set->bodySims.count == 0 )
424
+ {
425
+ // Remove solver set if it's now an orphan.
426
+ b2DestroySolverSet( world, set->setIndex );
427
+ }
428
+
429
+ // Free body and id (preserve body generation)
430
+ b2FreeId( &world->bodyIdPool, body->id );
431
+
432
+ body->setIndex = B2_NULL_INDEX;
433
+ body->localIndex = B2_NULL_INDEX;
434
+ body->id = B2_NULL_INDEX;
435
+
436
+ b2ValidateSolverSets( world );
437
+ }
438
+
439
+ int b2Body_GetContactCapacity( b2BodyId bodyId )
440
+ {
441
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
442
+ if ( world == NULL )
443
+ {
444
+ return 0;
445
+ }
446
+
447
+ b2Body* body = b2GetBodyFullId( world, bodyId );
448
+
449
+ // Conservative and fast
450
+ return body->contactCount;
451
+ }
452
+
453
+ int b2Body_GetContactData( b2BodyId bodyId, b2ContactData* contactData, int capacity )
454
+ {
455
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
456
+ if ( world == NULL )
457
+ {
458
+ return 0;
459
+ }
460
+
461
+ b2Body* body = b2GetBodyFullId( world, bodyId );
462
+
463
+ int contactKey = body->headContactKey;
464
+ int index = 0;
465
+ while ( contactKey != B2_NULL_INDEX && index < capacity )
466
+ {
467
+ int contactId = contactKey >> 1;
468
+ int edgeIndex = contactKey & 1;
469
+
470
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
471
+
472
+ // Is contact touching?
473
+ if ( contact->flags & b2_contactTouchingFlag )
474
+ {
475
+ b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, contact->shapeIdA );
476
+ b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, contact->shapeIdB );
477
+
478
+ contactData[index].shapeIdA = (b2ShapeId){ shapeA->id + 1, bodyId.world0, shapeA->generation };
479
+ contactData[index].shapeIdB = (b2ShapeId){ shapeB->id + 1, bodyId.world0, shapeB->generation };
480
+
481
+ b2ContactSim* contactSim = b2GetContactSim( world, contact );
482
+ contactData[index].manifold = contactSim->manifold;
483
+
484
+ index += 1;
485
+ }
486
+
487
+ contactKey = contact->edges[edgeIndex].nextKey;
488
+ }
489
+
490
+ B2_ASSERT( index <= capacity );
491
+
492
+ return index;
493
+ }
494
+
495
+ b2AABB b2Body_ComputeAABB( b2BodyId bodyId )
496
+ {
497
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
498
+ if ( world == NULL )
499
+ {
500
+ return (b2AABB){ 0 };
501
+ }
502
+
503
+ b2Body* body = b2GetBodyFullId( world, bodyId );
504
+ if ( body->headShapeId == B2_NULL_INDEX )
505
+ {
506
+ b2Transform transform = b2GetBodyTransform( world, body->id );
507
+ return (b2AABB){ transform.p, transform.p };
508
+ }
509
+
510
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, body->headShapeId );
511
+ b2AABB aabb = shape->aabb;
512
+ while ( shape->nextShapeId != B2_NULL_INDEX )
513
+ {
514
+ shape = b2ShapeArray_Get( &world->shapes, shape->nextShapeId );
515
+ aabb = b2AABB_Union( aabb, shape->aabb );
516
+ }
517
+
518
+ return aabb;
519
+ }
520
+
521
+ void b2UpdateBodyMassData( b2World* world, b2Body* body )
522
+ {
523
+ b2BodySim* bodySim = b2GetBodySim( world, body );
524
+
525
+ // Compute mass data from shapes. Each shape has its own density.
526
+ body->mass = 0.0f;
527
+ body->inertia = 0.0f;
528
+
529
+ bodySim->invMass = 0.0f;
530
+ bodySim->invInertia = 0.0f;
531
+ bodySim->localCenter = b2Vec2_zero;
532
+ bodySim->minExtent = B2_HUGE;
533
+ bodySim->maxExtent = 0.0f;
534
+
535
+ // Static and kinematic sims have zero mass.
536
+ if ( body->type != b2_dynamicBody )
537
+ {
538
+ bodySim->center = bodySim->transform.p;
539
+
540
+ // Need extents for kinematic bodies for sleeping to work correctly.
541
+ if ( body->type == b2_kinematicBody )
542
+ {
543
+ int shapeId = body->headShapeId;
544
+ while ( shapeId != B2_NULL_INDEX )
545
+ {
546
+ const b2Shape* s = b2ShapeArray_Get( &world->shapes, shapeId );
547
+
548
+ b2ShapeExtent extent = b2ComputeShapeExtent( s, b2Vec2_zero );
549
+ bodySim->minExtent = b2MinFloat( bodySim->minExtent, extent.minExtent );
550
+ bodySim->maxExtent = b2MaxFloat( bodySim->maxExtent, extent.maxExtent );
551
+
552
+ shapeId = s->nextShapeId;
553
+ }
554
+ }
555
+
556
+ return;
557
+ }
558
+
559
+ // Accumulate mass over all shapes.
560
+ b2Vec2 localCenter = b2Vec2_zero;
561
+ int shapeId = body->headShapeId;
562
+ while ( shapeId != B2_NULL_INDEX )
563
+ {
564
+ const b2Shape* s = b2ShapeArray_Get( &world->shapes, shapeId );
565
+ shapeId = s->nextShapeId;
566
+
567
+ if ( s->density == 0.0f )
568
+ {
569
+ continue;
570
+ }
571
+
572
+ b2MassData massData = b2ComputeShapeMass( s );
573
+ body->mass += massData.mass;
574
+ localCenter = b2MulAdd( localCenter, massData.mass, massData.center );
575
+ body->inertia += massData.rotationalInertia;
576
+ }
577
+
578
+ // Compute center of mass.
579
+ if ( body->mass > 0.0f )
580
+ {
581
+ bodySim->invMass = 1.0f / body->mass;
582
+ localCenter = b2MulSV( bodySim->invMass, localCenter );
583
+ }
584
+
585
+ if ( body->inertia > 0.0f && body->fixedRotation == false )
586
+ {
587
+ // Center the inertia about the center of mass.
588
+ body->inertia -= body->mass * b2Dot( localCenter, localCenter );
589
+ B2_ASSERT( body->inertia > 0.0f );
590
+ bodySim->invInertia = 1.0f / body->inertia;
591
+ }
592
+ else
593
+ {
594
+ body->inertia = 0.0f;
595
+ bodySim->invInertia = 0.0f;
596
+ }
597
+
598
+ // Move center of mass.
599
+ b2Vec2 oldCenter = bodySim->center;
600
+ bodySim->localCenter = localCenter;
601
+ bodySim->center = b2TransformPoint( bodySim->transform, bodySim->localCenter );
602
+ bodySim->center0 = bodySim->center;
603
+
604
+ // Update center of mass velocity
605
+ b2BodyState* state = b2GetBodyState( world, body );
606
+ if ( state != NULL )
607
+ {
608
+ b2Vec2 deltaLinear = b2CrossSV( state->angularVelocity, b2Sub( bodySim->center, oldCenter ) );
609
+ state->linearVelocity = b2Add( state->linearVelocity, deltaLinear );
610
+ }
611
+
612
+ // Compute body extents relative to center of mass
613
+ shapeId = body->headShapeId;
614
+ while ( shapeId != B2_NULL_INDEX )
615
+ {
616
+ const b2Shape* s = b2ShapeArray_Get( &world->shapes, shapeId );
617
+
618
+ b2ShapeExtent extent = b2ComputeShapeExtent( s, localCenter );
619
+ bodySim->minExtent = b2MinFloat( bodySim->minExtent, extent.minExtent );
620
+ bodySim->maxExtent = b2MaxFloat( bodySim->maxExtent, extent.maxExtent );
621
+
622
+ shapeId = s->nextShapeId;
623
+ }
624
+ }
625
+
626
+ b2Vec2 b2Body_GetPosition( b2BodyId bodyId )
627
+ {
628
+ b2World* world = b2GetWorld( bodyId.world0 );
629
+ b2Body* body = b2GetBodyFullId( world, bodyId );
630
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
631
+ return transform.p;
632
+ }
633
+
634
+ b2Rot b2Body_GetRotation( b2BodyId bodyId )
635
+ {
636
+ b2World* world = b2GetWorld( bodyId.world0 );
637
+ b2Body* body = b2GetBodyFullId( world, bodyId );
638
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
639
+ return transform.q;
640
+ }
641
+
642
+ b2Transform b2Body_GetTransform( b2BodyId bodyId )
643
+ {
644
+ b2World* world = b2GetWorld( bodyId.world0 );
645
+ b2Body* body = b2GetBodyFullId( world, bodyId );
646
+ return b2GetBodyTransformQuick( world, body );
647
+ }
648
+
649
+ b2Vec2 b2Body_GetLocalPoint( b2BodyId bodyId, b2Vec2 worldPoint )
650
+ {
651
+ b2World* world = b2GetWorld( bodyId.world0 );
652
+ b2Body* body = b2GetBodyFullId( world, bodyId );
653
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
654
+ return b2InvTransformPoint( transform, worldPoint );
655
+ }
656
+
657
+ b2Vec2 b2Body_GetWorldPoint( b2BodyId bodyId, b2Vec2 localPoint )
658
+ {
659
+ b2World* world = b2GetWorld( bodyId.world0 );
660
+ b2Body* body = b2GetBodyFullId( world, bodyId );
661
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
662
+ return b2TransformPoint( transform, localPoint );
663
+ }
664
+
665
+ b2Vec2 b2Body_GetLocalVector( b2BodyId bodyId, b2Vec2 worldVector )
666
+ {
667
+ b2World* world = b2GetWorld( bodyId.world0 );
668
+ b2Body* body = b2GetBodyFullId( world, bodyId );
669
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
670
+ return b2InvRotateVector( transform.q, worldVector );
671
+ }
672
+
673
+ b2Vec2 b2Body_GetWorldVector( b2BodyId bodyId, b2Vec2 localVector )
674
+ {
675
+ b2World* world = b2GetWorld( bodyId.world0 );
676
+ b2Body* body = b2GetBodyFullId( world, bodyId );
677
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
678
+ return b2RotateVector( transform.q, localVector );
679
+ }
680
+
681
+ void b2Body_SetTransform( b2BodyId bodyId, b2Vec2 position, b2Rot rotation )
682
+ {
683
+ B2_ASSERT( b2IsValidVec2( position ) );
684
+ B2_ASSERT( b2IsValidRotation( rotation ) );
685
+ B2_ASSERT( b2Body_IsValid( bodyId ) );
686
+ b2World* world = b2GetWorld( bodyId.world0 );
687
+ B2_ASSERT( world->locked == false );
688
+
689
+ b2Body* body = b2GetBodyFullId( world, bodyId );
690
+ b2BodySim* bodySim = b2GetBodySim( world, body );
691
+
692
+ bodySim->transform.p = position;
693
+ bodySim->transform.q = rotation;
694
+ bodySim->center = b2TransformPoint( bodySim->transform, bodySim->localCenter );
695
+
696
+ bodySim->rotation0 = bodySim->transform.q;
697
+ bodySim->center0 = bodySim->center;
698
+
699
+ b2BroadPhase* broadPhase = &world->broadPhase;
700
+
701
+ b2Transform transform = bodySim->transform;
702
+ const float margin = B2_AABB_MARGIN;
703
+ const float speculativeDistance = B2_SPECULATIVE_DISTANCE;
704
+
705
+ int shapeId = body->headShapeId;
706
+ while ( shapeId != B2_NULL_INDEX )
707
+ {
708
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
709
+ b2AABB aabb = b2ComputeShapeAABB( shape, transform );
710
+ aabb.lowerBound.x -= speculativeDistance;
711
+ aabb.lowerBound.y -= speculativeDistance;
712
+ aabb.upperBound.x += speculativeDistance;
713
+ aabb.upperBound.y += speculativeDistance;
714
+ shape->aabb = aabb;
715
+
716
+ if ( b2AABB_Contains( shape->fatAABB, aabb ) == false )
717
+ {
718
+ b2AABB fatAABB;
719
+ fatAABB.lowerBound.x = aabb.lowerBound.x - margin;
720
+ fatAABB.lowerBound.y = aabb.lowerBound.y - margin;
721
+ fatAABB.upperBound.x = aabb.upperBound.x + margin;
722
+ fatAABB.upperBound.y = aabb.upperBound.y + margin;
723
+ shape->fatAABB = fatAABB;
724
+
725
+ // They body could be disabled
726
+ if ( shape->proxyKey != B2_NULL_INDEX )
727
+ {
728
+ b2BroadPhase_MoveProxy( broadPhase, shape->proxyKey, fatAABB );
729
+ }
730
+ }
731
+
732
+ shapeId = shape->nextShapeId;
733
+ }
734
+ }
735
+
736
+ b2Vec2 b2Body_GetLinearVelocity( b2BodyId bodyId )
737
+ {
738
+ b2World* world = b2GetWorld( bodyId.world0 );
739
+ b2Body* body = b2GetBodyFullId( world, bodyId );
740
+ b2BodyState* state = b2GetBodyState( world, body );
741
+ if ( state != NULL )
742
+ {
743
+ return state->linearVelocity;
744
+ }
745
+ return b2Vec2_zero;
746
+ }
747
+
748
+ float b2Body_GetAngularVelocity( b2BodyId bodyId )
749
+ {
750
+ b2World* world = b2GetWorld( bodyId.world0 );
751
+ b2Body* body = b2GetBodyFullId( world, bodyId );
752
+ b2BodyState* state = b2GetBodyState( world, body );
753
+ if ( state != NULL )
754
+ {
755
+ return state->angularVelocity;
756
+ }
757
+ return 0.0;
758
+ }
759
+
760
+ void b2Body_SetLinearVelocity( b2BodyId bodyId, b2Vec2 linearVelocity )
761
+ {
762
+ b2World* world = b2GetWorld( bodyId.world0 );
763
+ b2Body* body = b2GetBodyFullId( world, bodyId );
764
+
765
+ if ( body->type == b2_staticBody )
766
+ {
767
+ return;
768
+ }
769
+
770
+ if ( b2LengthSquared( linearVelocity ) > 0.0f )
771
+ {
772
+ b2WakeBody( world, body );
773
+ }
774
+
775
+ b2BodyState* state = b2GetBodyState( world, body );
776
+ if ( state == NULL )
777
+ {
778
+ return;
779
+ }
780
+
781
+ state->linearVelocity = linearVelocity;
782
+ }
783
+
784
+ void b2Body_SetAngularVelocity( b2BodyId bodyId, float angularVelocity )
785
+ {
786
+ b2World* world = b2GetWorld( bodyId.world0 );
787
+ b2Body* body = b2GetBodyFullId( world, bodyId );
788
+
789
+ if ( body->type == b2_staticBody || body->fixedRotation )
790
+ {
791
+ return;
792
+ }
793
+
794
+ if ( angularVelocity != 0.0f )
795
+ {
796
+ b2WakeBody( world, body );
797
+ }
798
+
799
+ b2BodyState* state = b2GetBodyState( world, body );
800
+ if ( state == NULL )
801
+ {
802
+ return;
803
+ }
804
+
805
+ state->angularVelocity = angularVelocity;
806
+ }
807
+
808
+ void b2Body_SetTargetTransform( b2BodyId bodyId, b2Transform target, float timeStep )
809
+ {
810
+ b2World* world = b2GetWorld( bodyId.world0 );
811
+ b2Body* body = b2GetBodyFullId( world, bodyId );
812
+
813
+ if ( body->type == b2_staticBody || timeStep <= 0.0f )
814
+ {
815
+ return;
816
+ }
817
+
818
+ b2BodySim* sim = b2GetBodySim( world, body );
819
+
820
+ // Compute linear velocity
821
+ b2Vec2 center1 = sim->center;
822
+ b2Vec2 center2 = b2TransformPoint( target, sim->localCenter );
823
+ float invTimeStep = 1.0f / timeStep;
824
+ b2Vec2 linearVelocity = b2MulSV( invTimeStep, b2Sub( center2, center1 ) );
825
+
826
+ // Compute angular velocity
827
+ float angularVelocity = 0.0f;
828
+ if ( body->fixedRotation == false )
829
+ {
830
+ b2Rot q1 = sim->transform.q;
831
+ b2Rot q2 = target.q;
832
+ float deltaAngle = b2RelativeAngle( q2, q1 );
833
+ angularVelocity = invTimeStep * deltaAngle;
834
+ }
835
+
836
+ // Return if velocity would be zero
837
+ if ( b2LengthSquared( linearVelocity ) == 0.0f && b2AbsFloat( angularVelocity ) == 0.0f )
838
+ {
839
+ return;
840
+ }
841
+
842
+ // Must wake for state to exist
843
+ b2WakeBody( world, body );
844
+
845
+ b2BodyState* state = b2GetBodyState( world, body );
846
+ if ( state == NULL )
847
+ {
848
+ return;
849
+ }
850
+
851
+ state->linearVelocity = linearVelocity;
852
+ state->angularVelocity = angularVelocity;
853
+ }
854
+
855
+ b2Vec2 b2Body_GetLocalPointVelocity( b2BodyId bodyId, b2Vec2 localPoint )
856
+ {
857
+ b2World* world = b2GetWorld( bodyId.world0 );
858
+ b2Body* body = b2GetBodyFullId( world, bodyId );
859
+ b2BodyState* state = b2GetBodyState( world, body );
860
+ if ( state == NULL )
861
+ {
862
+ return b2Vec2_zero;
863
+ }
864
+
865
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );
866
+ b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, body->localIndex );
867
+
868
+ b2Vec2 r = b2RotateVector( bodySim->transform.q, b2Sub( localPoint, bodySim->localCenter ) );
869
+ b2Vec2 v = b2Add( state->linearVelocity, b2CrossSV( state->angularVelocity, r ) );
870
+ return v;
871
+ }
872
+
873
+ b2Vec2 b2Body_GetWorldPointVelocity( b2BodyId bodyId, b2Vec2 worldPoint )
874
+ {
875
+ b2World* world = b2GetWorld( bodyId.world0 );
876
+ b2Body* body = b2GetBodyFullId( world, bodyId );
877
+ b2BodyState* state = b2GetBodyState( world, body );
878
+ if ( state == NULL )
879
+ {
880
+ return b2Vec2_zero;
881
+ }
882
+
883
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );
884
+ b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, body->localIndex );
885
+
886
+ b2Vec2 r = b2Sub( worldPoint, bodySim->center );
887
+ b2Vec2 v = b2Add( state->linearVelocity, b2CrossSV( state->angularVelocity, r ) );
888
+ return v;
889
+ }
890
+
891
+ void b2Body_ApplyForce( b2BodyId bodyId, b2Vec2 force, b2Vec2 point, bool wake )
892
+ {
893
+ b2World* world = b2GetWorld( bodyId.world0 );
894
+ b2Body* body = b2GetBodyFullId( world, bodyId );
895
+
896
+ if ( wake && body->setIndex >= b2_firstSleepingSet )
897
+ {
898
+ b2WakeBody( world, body );
899
+ }
900
+
901
+ if ( body->setIndex == b2_awakeSet )
902
+ {
903
+ b2BodySim* bodySim = b2GetBodySim( world, body );
904
+ bodySim->force = b2Add( bodySim->force, force );
905
+ bodySim->torque += b2Cross( b2Sub( point, bodySim->center ), force );
906
+ }
907
+ }
908
+
909
+ void b2Body_ApplyForceToCenter( b2BodyId bodyId, b2Vec2 force, bool wake )
910
+ {
911
+ b2World* world = b2GetWorld( bodyId.world0 );
912
+ b2Body* body = b2GetBodyFullId( world, bodyId );
913
+
914
+ if ( wake && body->setIndex >= b2_firstSleepingSet )
915
+ {
916
+ b2WakeBody( world, body );
917
+ }
918
+
919
+ if ( body->setIndex == b2_awakeSet )
920
+ {
921
+ b2BodySim* bodySim = b2GetBodySim( world, body );
922
+ bodySim->force = b2Add( bodySim->force, force );
923
+ }
924
+ }
925
+
926
+ void b2Body_ApplyTorque( b2BodyId bodyId, float torque, bool wake )
927
+ {
928
+ b2World* world = b2GetWorld( bodyId.world0 );
929
+ b2Body* body = b2GetBodyFullId( world, bodyId );
930
+
931
+ if ( wake && body->setIndex >= b2_firstSleepingSet )
932
+ {
933
+ b2WakeBody( world, body );
934
+ }
935
+
936
+ if ( body->setIndex == b2_awakeSet )
937
+ {
938
+ b2BodySim* bodySim = b2GetBodySim( world, body );
939
+ bodySim->torque += torque;
940
+ }
941
+ }
942
+
943
+ void b2Body_ApplyLinearImpulse( b2BodyId bodyId, b2Vec2 impulse, b2Vec2 point, bool wake )
944
+ {
945
+ b2World* world = b2GetWorld( bodyId.world0 );
946
+ b2Body* body = b2GetBodyFullId( world, bodyId );
947
+
948
+ if ( wake && body->setIndex >= b2_firstSleepingSet )
949
+ {
950
+ b2WakeBody( world, body );
951
+ }
952
+
953
+ if ( body->setIndex == b2_awakeSet )
954
+ {
955
+ int localIndex = body->localIndex;
956
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
957
+ b2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex );
958
+ b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex );
959
+ state->linearVelocity = b2MulAdd( state->linearVelocity, bodySim->invMass, impulse );
960
+ state->angularVelocity += bodySim->invInertia * b2Cross( b2Sub( point, bodySim->center ), impulse );
961
+ }
962
+ }
963
+
964
+ void b2Body_ApplyLinearImpulseToCenter( b2BodyId bodyId, b2Vec2 impulse, bool wake )
965
+ {
966
+ b2World* world = b2GetWorld( bodyId.world0 );
967
+ b2Body* body = b2GetBodyFullId( world, bodyId );
968
+
969
+ if ( wake && body->setIndex >= b2_firstSleepingSet )
970
+ {
971
+ b2WakeBody( world, body );
972
+ }
973
+
974
+ if ( body->setIndex == b2_awakeSet )
975
+ {
976
+ int localIndex = body->localIndex;
977
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
978
+ b2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex );
979
+ b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex );
980
+ state->linearVelocity = b2MulAdd( state->linearVelocity, bodySim->invMass, impulse );
981
+ }
982
+ }
983
+
984
+ void b2Body_ApplyAngularImpulse( b2BodyId bodyId, float impulse, bool wake )
985
+ {
986
+ B2_ASSERT( b2Body_IsValid( bodyId ) );
987
+ b2World* world = b2GetWorld( bodyId.world0 );
988
+
989
+ int id = bodyId.index1 - 1;
990
+ b2Body* body = b2BodyArray_Get( &world->bodies, id );
991
+ B2_ASSERT( body->generation == bodyId.generation );
992
+
993
+ if ( wake && body->setIndex >= b2_firstSleepingSet )
994
+ {
995
+ // this will not invalidate body pointer
996
+ b2WakeBody( world, body );
997
+ }
998
+
999
+ if ( body->setIndex == b2_awakeSet )
1000
+ {
1001
+ int localIndex = body->localIndex;
1002
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
1003
+ b2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex );
1004
+ b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex );
1005
+ state->angularVelocity += bodySim->invInertia * impulse;
1006
+ }
1007
+ }
1008
+
1009
+ b2BodyType b2Body_GetType( b2BodyId bodyId )
1010
+ {
1011
+ b2World* world = b2GetWorld( bodyId.world0 );
1012
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1013
+ return body->type;
1014
+ }
1015
+
1016
+ // Changing the body type is quite complex mainly due to joints.
1017
+ // Considerations:
1018
+ // - body and joints must be moved to the correct set
1019
+ // - islands must be updated
1020
+ // - graph coloring must be correct
1021
+ // - any body connected to a joint may be disabled
1022
+ // - joints between static bodies must go into the static set
1023
+ void b2Body_SetType( b2BodyId bodyId, b2BodyType type )
1024
+ {
1025
+ b2World* world = b2GetWorld( bodyId.world0 );
1026
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1027
+
1028
+ b2BodyType originalType = body->type;
1029
+ if ( originalType == type )
1030
+ {
1031
+ return;
1032
+ }
1033
+
1034
+ if ( body->setIndex == b2_disabledSet )
1035
+ {
1036
+ // Disabled bodies don't change solver sets or islands when they change type.
1037
+ body->type = type;
1038
+
1039
+ // Body type affects the mass
1040
+ b2UpdateBodyMassData( world, body );
1041
+ return;
1042
+ }
1043
+
1044
+ // Destroy all contacts but don't wake bodies.
1045
+ bool wakeBodies = false;
1046
+ b2DestroyBodyContacts( world, body, wakeBodies );
1047
+
1048
+ // Wake this body because we assume below that it is awake or static.
1049
+ b2WakeBody( world, body );
1050
+
1051
+ // Unlink all joints and wake attached bodies.
1052
+ {
1053
+ int jointKey = body->headJointKey;
1054
+ while ( jointKey != B2_NULL_INDEX )
1055
+ {
1056
+ int jointId = jointKey >> 1;
1057
+ int edgeIndex = jointKey & 1;
1058
+
1059
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
1060
+ if ( joint->islandId != B2_NULL_INDEX )
1061
+ {
1062
+ b2UnlinkJoint( world, joint );
1063
+ }
1064
+
1065
+ // A body going from static to dynamic or kinematic goes to the awake set
1066
+ // and other attached bodies must be awake as well. For consistency, this is
1067
+ // done for all cases.
1068
+ b2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId );
1069
+ b2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId );
1070
+ b2WakeBody( world, bodyA );
1071
+ b2WakeBody( world, bodyB );
1072
+
1073
+ jointKey = joint->edges[edgeIndex].nextKey;
1074
+ }
1075
+ }
1076
+
1077
+ body->type = type;
1078
+
1079
+ if ( originalType == b2_staticBody )
1080
+ {
1081
+ // Body is going from static to dynamic or kinematic. It only makes sense to move it to the awake set.
1082
+ B2_ASSERT( body->setIndex == b2_staticSet );
1083
+
1084
+ b2SolverSet* staticSet = b2SolverSetArray_Get( &world->solverSets, b2_staticSet );
1085
+ b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
1086
+
1087
+ // Transfer body to awake set
1088
+ b2TransferBody( world, awakeSet, staticSet, body );
1089
+
1090
+ // Create island for body
1091
+ b2CreateIslandForBody( world, b2_awakeSet, body );
1092
+
1093
+ // Transfer static joints to awake set
1094
+ int jointKey = body->headJointKey;
1095
+ while ( jointKey != B2_NULL_INDEX )
1096
+ {
1097
+ int jointId = jointKey >> 1;
1098
+ int edgeIndex = jointKey & 1;
1099
+
1100
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
1101
+
1102
+ // Transfer the joint if it is in the static set
1103
+ if ( joint->setIndex == b2_staticSet )
1104
+ {
1105
+ b2TransferJoint( world, awakeSet, staticSet, joint );
1106
+ }
1107
+ else if ( joint->setIndex == b2_awakeSet )
1108
+ {
1109
+ // In this case the joint must be re-inserted into the constraint graph to ensure the correct
1110
+ // graph color.
1111
+
1112
+ // First transfer to the static set.
1113
+ b2TransferJoint( world, staticSet, awakeSet, joint );
1114
+
1115
+ // Now transfer it back to the awake set and into the graph coloring.
1116
+ b2TransferJoint( world, awakeSet, staticSet, joint );
1117
+ }
1118
+ else
1119
+ {
1120
+ // Otherwise the joint must be disabled.
1121
+ B2_ASSERT( joint->setIndex == b2_disabledSet );
1122
+ }
1123
+
1124
+ jointKey = joint->edges[edgeIndex].nextKey;
1125
+ }
1126
+
1127
+ // Recreate shape proxies in movable tree.
1128
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
1129
+ int shapeId = body->headShapeId;
1130
+ while ( shapeId != B2_NULL_INDEX )
1131
+ {
1132
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1133
+ shapeId = shape->nextShapeId;
1134
+ b2DestroyShapeProxy( shape, &world->broadPhase );
1135
+ bool forcePairCreation = true;
1136
+ b2BodyType proxyType = type;
1137
+ b2CreateShapeProxy( shape, &world->broadPhase, proxyType, transform, forcePairCreation );
1138
+ }
1139
+ }
1140
+ else if ( type == b2_staticBody )
1141
+ {
1142
+ // The body is going from dynamic/kinematic to static. It should be awake.
1143
+ B2_ASSERT( body->setIndex == b2_awakeSet );
1144
+
1145
+ b2SolverSet* staticSet = b2SolverSetArray_Get( &world->solverSets, b2_staticSet );
1146
+ b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
1147
+
1148
+ // Transfer body to static set
1149
+ b2TransferBody( world, staticSet, awakeSet, body );
1150
+
1151
+ // Remove body from island.
1152
+ b2RemoveBodyFromIsland( world, body );
1153
+
1154
+ b2BodySim* bodySim = b2BodySimArray_Get( &staticSet->bodySims, body->localIndex );
1155
+ bodySim->isFast = false;
1156
+
1157
+ // Maybe transfer joints to static set.
1158
+ int jointKey = body->headJointKey;
1159
+ while ( jointKey != B2_NULL_INDEX )
1160
+ {
1161
+ int jointId = jointKey >> 1;
1162
+ int edgeIndex = jointKey & 1;
1163
+
1164
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
1165
+ jointKey = joint->edges[edgeIndex].nextKey;
1166
+
1167
+ int otherEdgeIndex = edgeIndex ^ 1;
1168
+ b2Body* otherBody = b2BodyArray_Get( &world->bodies, joint->edges[otherEdgeIndex].bodyId );
1169
+
1170
+ // Skip disabled joint
1171
+ if ( joint->setIndex == b2_disabledSet )
1172
+ {
1173
+ // Joint is disable, should be connected to a disabled body
1174
+ B2_ASSERT( otherBody->setIndex == b2_disabledSet );
1175
+ continue;
1176
+ }
1177
+
1178
+ // Since the body was not static, the joint must be awake.
1179
+ B2_ASSERT( joint->setIndex == b2_awakeSet );
1180
+
1181
+ // Only transfer joint to static set if both bodies are static.
1182
+ if ( otherBody->setIndex == b2_staticSet )
1183
+ {
1184
+ b2TransferJoint( world, staticSet, awakeSet, joint );
1185
+ }
1186
+ else
1187
+ {
1188
+ // The other body must be awake.
1189
+ B2_ASSERT( otherBody->setIndex == b2_awakeSet );
1190
+
1191
+ // The joint must live in a graph color.
1192
+ B2_ASSERT( 0 <= joint->colorIndex && joint->colorIndex < B2_GRAPH_COLOR_COUNT );
1193
+
1194
+ // In this case the joint must be re-inserted into the constraint graph to ensure the correct
1195
+ // graph color.
1196
+
1197
+ // First transfer to the static set.
1198
+ b2TransferJoint( world, staticSet, awakeSet, joint );
1199
+
1200
+ // Now transfer it back to the awake set and into the graph coloring.
1201
+ b2TransferJoint( world, awakeSet, staticSet, joint );
1202
+ }
1203
+ }
1204
+
1205
+ // Recreate shape proxies in static tree.
1206
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
1207
+ int shapeId = body->headShapeId;
1208
+ while ( shapeId != B2_NULL_INDEX )
1209
+ {
1210
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1211
+ shapeId = shape->nextShapeId;
1212
+ b2DestroyShapeProxy( shape, &world->broadPhase );
1213
+ bool forcePairCreation = true;
1214
+ b2CreateShapeProxy( shape, &world->broadPhase, b2_staticBody, transform, forcePairCreation );
1215
+ }
1216
+ }
1217
+ else
1218
+ {
1219
+ B2_ASSERT( originalType == b2_dynamicBody || originalType == b2_kinematicBody );
1220
+ B2_ASSERT( type == b2_dynamicBody || type == b2_kinematicBody );
1221
+
1222
+ // Recreate shape proxies in static tree.
1223
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
1224
+ int shapeId = body->headShapeId;
1225
+ while ( shapeId != B2_NULL_INDEX )
1226
+ {
1227
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1228
+ shapeId = shape->nextShapeId;
1229
+ b2DestroyShapeProxy( shape, &world->broadPhase );
1230
+ b2BodyType proxyType = type;
1231
+ bool forcePairCreation = true;
1232
+ b2CreateShapeProxy( shape, &world->broadPhase, proxyType, transform, forcePairCreation );
1233
+ }
1234
+ }
1235
+
1236
+ // Relink all joints
1237
+ {
1238
+ int jointKey = body->headJointKey;
1239
+ while ( jointKey != B2_NULL_INDEX )
1240
+ {
1241
+ int jointId = jointKey >> 1;
1242
+ int edgeIndex = jointKey & 1;
1243
+
1244
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
1245
+ jointKey = joint->edges[edgeIndex].nextKey;
1246
+
1247
+ int otherEdgeIndex = edgeIndex ^ 1;
1248
+ int otherBodyId = joint->edges[otherEdgeIndex].bodyId;
1249
+ b2Body* otherBody = b2BodyArray_Get( &world->bodies, otherBodyId );
1250
+
1251
+ if ( otherBody->setIndex == b2_disabledSet )
1252
+ {
1253
+ continue;
1254
+ }
1255
+
1256
+ if ( body->type == b2_staticBody && otherBody->type == b2_staticBody )
1257
+ {
1258
+ continue;
1259
+ }
1260
+
1261
+ b2LinkJoint( world, joint, false );
1262
+ }
1263
+
1264
+ b2MergeAwakeIslands( world );
1265
+ }
1266
+
1267
+ // Body type affects the mass
1268
+ b2UpdateBodyMassData( world, body );
1269
+
1270
+ b2ValidateSolverSets( world );
1271
+ }
1272
+
1273
+ void b2Body_SetName( b2BodyId bodyId, const char* name )
1274
+ {
1275
+ b2World* world = b2GetWorld( bodyId.world0 );
1276
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1277
+
1278
+ if ( name != NULL )
1279
+ {
1280
+ for ( int i = 0; i < 31; ++i )
1281
+ {
1282
+ body->name[i] = name[i];
1283
+ }
1284
+
1285
+ body->name[31] = 0;
1286
+ }
1287
+ else
1288
+ {
1289
+ memset( body->name, 0, 32 * sizeof( char ) );
1290
+ }
1291
+ }
1292
+
1293
+ const char* b2Body_GetName( b2BodyId bodyId )
1294
+ {
1295
+ b2World* world = b2GetWorld( bodyId.world0 );
1296
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1297
+ return body->name;
1298
+ }
1299
+
1300
+ void b2Body_SetUserData( b2BodyId bodyId, void* userData )
1301
+ {
1302
+ b2World* world = b2GetWorld( bodyId.world0 );
1303
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1304
+ body->userData = userData;
1305
+ }
1306
+
1307
+ void* b2Body_GetUserData( b2BodyId bodyId )
1308
+ {
1309
+ b2World* world = b2GetWorld( bodyId.world0 );
1310
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1311
+ return body->userData;
1312
+ }
1313
+
1314
+ float b2Body_GetMass( b2BodyId bodyId )
1315
+ {
1316
+ b2World* world = b2GetWorld( bodyId.world0 );
1317
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1318
+ return body->mass;
1319
+ }
1320
+
1321
+ float b2Body_GetRotationalInertia( b2BodyId bodyId )
1322
+ {
1323
+ b2World* world = b2GetWorld( bodyId.world0 );
1324
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1325
+ return body->inertia;
1326
+ }
1327
+
1328
+ b2Vec2 b2Body_GetLocalCenterOfMass( b2BodyId bodyId )
1329
+ {
1330
+ b2World* world = b2GetWorld( bodyId.world0 );
1331
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1332
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1333
+ return bodySim->localCenter;
1334
+ }
1335
+
1336
+ b2Vec2 b2Body_GetWorldCenterOfMass( b2BodyId bodyId )
1337
+ {
1338
+ b2World* world = b2GetWorld( bodyId.world0 );
1339
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1340
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1341
+ return bodySim->center;
1342
+ }
1343
+
1344
+ void b2Body_SetMassData( b2BodyId bodyId, b2MassData massData )
1345
+ {
1346
+ B2_ASSERT( b2IsValidFloat( massData.mass ) && massData.mass >= 0.0f );
1347
+ B2_ASSERT( b2IsValidFloat( massData.rotationalInertia ) && massData.rotationalInertia >= 0.0f );
1348
+ B2_ASSERT( b2IsValidVec2( massData.center ) );
1349
+
1350
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1351
+ if ( world == NULL )
1352
+ {
1353
+ return;
1354
+ }
1355
+
1356
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1357
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1358
+
1359
+ body->mass = massData.mass;
1360
+ body->inertia = massData.rotationalInertia;
1361
+ bodySim->localCenter = massData.center;
1362
+
1363
+ b2Vec2 center = b2TransformPoint( bodySim->transform, massData.center );
1364
+ bodySim->center = center;
1365
+ bodySim->center0 = center;
1366
+
1367
+ bodySim->invMass = body->mass > 0.0f ? 1.0f / body->mass : 0.0f;
1368
+ bodySim->invInertia = body->inertia > 0.0f ? 1.0f / body->inertia : 0.0f;
1369
+ }
1370
+
1371
+ b2MassData b2Body_GetMassData( b2BodyId bodyId )
1372
+ {
1373
+ b2World* world = b2GetWorld( bodyId.world0 );
1374
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1375
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1376
+ b2MassData massData = { body->mass, bodySim->localCenter, body->inertia };
1377
+ return massData;
1378
+ }
1379
+
1380
+ void b2Body_ApplyMassFromShapes( b2BodyId bodyId )
1381
+ {
1382
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1383
+ if ( world == NULL )
1384
+ {
1385
+ return;
1386
+ }
1387
+
1388
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1389
+ b2UpdateBodyMassData( world, body );
1390
+ }
1391
+
1392
+ void b2Body_SetLinearDamping( b2BodyId bodyId, float linearDamping )
1393
+ {
1394
+ B2_ASSERT( b2IsValidFloat( linearDamping ) && linearDamping >= 0.0f );
1395
+
1396
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1397
+ if ( world == NULL )
1398
+ {
1399
+ return;
1400
+ }
1401
+
1402
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1403
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1404
+ bodySim->linearDamping = linearDamping;
1405
+ }
1406
+
1407
+ float b2Body_GetLinearDamping( b2BodyId bodyId )
1408
+ {
1409
+ b2World* world = b2GetWorld( bodyId.world0 );
1410
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1411
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1412
+ return bodySim->linearDamping;
1413
+ }
1414
+
1415
+ void b2Body_SetAngularDamping( b2BodyId bodyId, float angularDamping )
1416
+ {
1417
+ B2_ASSERT( b2IsValidFloat( angularDamping ) && angularDamping >= 0.0f );
1418
+
1419
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1420
+ if ( world == NULL )
1421
+ {
1422
+ return;
1423
+ }
1424
+
1425
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1426
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1427
+ bodySim->angularDamping = angularDamping;
1428
+ }
1429
+
1430
+ float b2Body_GetAngularDamping( b2BodyId bodyId )
1431
+ {
1432
+ b2World* world = b2GetWorld( bodyId.world0 );
1433
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1434
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1435
+ return bodySim->angularDamping;
1436
+ }
1437
+
1438
+ void b2Body_SetGravityScale( b2BodyId bodyId, float gravityScale )
1439
+ {
1440
+ B2_ASSERT( b2Body_IsValid( bodyId ) );
1441
+ B2_ASSERT( b2IsValidFloat( gravityScale ) );
1442
+
1443
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1444
+ if ( world == NULL )
1445
+ {
1446
+ return;
1447
+ }
1448
+
1449
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1450
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1451
+ bodySim->gravityScale = gravityScale;
1452
+ }
1453
+
1454
+ float b2Body_GetGravityScale( b2BodyId bodyId )
1455
+ {
1456
+ B2_ASSERT( b2Body_IsValid( bodyId ) );
1457
+ b2World* world = b2GetWorld( bodyId.world0 );
1458
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1459
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1460
+ return bodySim->gravityScale;
1461
+ }
1462
+
1463
+ bool b2Body_IsAwake( b2BodyId bodyId )
1464
+ {
1465
+ b2World* world = b2GetWorld( bodyId.world0 );
1466
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1467
+ return body->setIndex == b2_awakeSet;
1468
+ }
1469
+
1470
+ void b2Body_SetAwake( b2BodyId bodyId, bool awake )
1471
+ {
1472
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1473
+ if ( world == NULL )
1474
+ {
1475
+ return;
1476
+ }
1477
+
1478
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1479
+
1480
+ if ( awake && body->setIndex >= b2_firstSleepingSet )
1481
+ {
1482
+ b2WakeBody( world, body );
1483
+ }
1484
+ else if ( awake == false && body->setIndex == b2_awakeSet )
1485
+ {
1486
+ b2Island* island = b2IslandArray_Get( &world->islands, body->islandId );
1487
+ if ( island->constraintRemoveCount > 0 )
1488
+ {
1489
+ // Must split the island before sleeping. This is expensive.
1490
+ b2SplitIsland( world, body->islandId );
1491
+ }
1492
+
1493
+ b2TrySleepIsland( world, body->islandId );
1494
+ }
1495
+ }
1496
+
1497
+ bool b2Body_IsEnabled( b2BodyId bodyId )
1498
+ {
1499
+ b2World* world = b2GetWorld( bodyId.world0 );
1500
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1501
+ return body->setIndex != b2_disabledSet;
1502
+ }
1503
+
1504
+ bool b2Body_IsSleepEnabled( b2BodyId bodyId )
1505
+ {
1506
+ b2World* world = b2GetWorld( bodyId.world0 );
1507
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1508
+ return body->enableSleep;
1509
+ }
1510
+
1511
+ void b2Body_SetSleepThreshold( b2BodyId bodyId, float sleepThreshold )
1512
+ {
1513
+ b2World* world = b2GetWorld( bodyId.world0 );
1514
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1515
+ body->sleepThreshold = sleepThreshold;
1516
+ }
1517
+
1518
+ float b2Body_GetSleepThreshold( b2BodyId bodyId )
1519
+ {
1520
+ b2World* world = b2GetWorld( bodyId.world0 );
1521
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1522
+ return body->sleepThreshold;
1523
+ }
1524
+
1525
+ void b2Body_EnableSleep( b2BodyId bodyId, bool enableSleep )
1526
+ {
1527
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1528
+ if ( world == NULL )
1529
+ {
1530
+ return;
1531
+ }
1532
+
1533
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1534
+ body->enableSleep = enableSleep;
1535
+
1536
+ if ( enableSleep == false )
1537
+ {
1538
+ b2WakeBody( world, body );
1539
+ }
1540
+ }
1541
+
1542
+ // Disabling a body requires a lot of detailed bookkeeping, but it is a valuable feature.
1543
+ // The most challenging aspect that joints may connect to bodies that are not disabled.
1544
+ void b2Body_Disable( b2BodyId bodyId )
1545
+ {
1546
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1547
+ if ( world == NULL )
1548
+ {
1549
+ return;
1550
+ }
1551
+
1552
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1553
+ if ( body->setIndex == b2_disabledSet )
1554
+ {
1555
+ return;
1556
+ }
1557
+
1558
+ // Destroy contacts and wake bodies touching this body. This avoid floating bodies.
1559
+ // This is necessary even for static bodies.
1560
+ bool wakeBodies = true;
1561
+ b2DestroyBodyContacts( world, body, wakeBodies );
1562
+
1563
+ // Disabled bodies are not in an island.
1564
+ b2RemoveBodyFromIsland( world, body );
1565
+
1566
+ // Remove shapes from broad-phase
1567
+ int shapeId = body->headShapeId;
1568
+ while ( shapeId != B2_NULL_INDEX )
1569
+ {
1570
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1571
+ shapeId = shape->nextShapeId;
1572
+ b2DestroyShapeProxy( shape, &world->broadPhase );
1573
+ }
1574
+
1575
+ // Transfer simulation data to disabled set
1576
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex );
1577
+ b2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet );
1578
+
1579
+ // Transfer body sim
1580
+ b2TransferBody( world, disabledSet, set, body );
1581
+
1582
+ // Unlink joints and transfer
1583
+ int jointKey = body->headJointKey;
1584
+ while ( jointKey != B2_NULL_INDEX )
1585
+ {
1586
+ int jointId = jointKey >> 1;
1587
+ int edgeIndex = jointKey & 1;
1588
+
1589
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
1590
+ jointKey = joint->edges[edgeIndex].nextKey;
1591
+
1592
+ // joint may already be disabled by other body
1593
+ if ( joint->setIndex == b2_disabledSet )
1594
+ {
1595
+ continue;
1596
+ }
1597
+
1598
+ B2_ASSERT( joint->setIndex == set->setIndex || set->setIndex == b2_staticSet );
1599
+
1600
+ // Remove joint from island
1601
+ if ( joint->islandId != B2_NULL_INDEX )
1602
+ {
1603
+ b2UnlinkJoint( world, joint );
1604
+ }
1605
+
1606
+ // Transfer joint to disabled set
1607
+ b2SolverSet* jointSet = b2SolverSetArray_Get( &world->solverSets, joint->setIndex );
1608
+ b2TransferJoint( world, disabledSet, jointSet, joint );
1609
+ }
1610
+
1611
+ b2ValidateConnectivity( world );
1612
+ b2ValidateSolverSets( world );
1613
+ }
1614
+
1615
+ void b2Body_Enable( b2BodyId bodyId )
1616
+ {
1617
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1618
+ if ( world == NULL )
1619
+ {
1620
+ return;
1621
+ }
1622
+
1623
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1624
+ if ( body->setIndex != b2_disabledSet )
1625
+ {
1626
+ return;
1627
+ }
1628
+
1629
+ b2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet );
1630
+ int setId = body->type == b2_staticBody ? b2_staticSet : b2_awakeSet;
1631
+ b2SolverSet* targetSet = b2SolverSetArray_Get( &world->solverSets, setId );
1632
+
1633
+ b2TransferBody( world, targetSet, disabledSet, body );
1634
+
1635
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
1636
+
1637
+ // Add shapes to broad-phase
1638
+ b2BodyType proxyType = body->type;
1639
+ bool forcePairCreation = true;
1640
+ int shapeId = body->headShapeId;
1641
+ while ( shapeId != B2_NULL_INDEX )
1642
+ {
1643
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1644
+ shapeId = shape->nextShapeId;
1645
+
1646
+ b2CreateShapeProxy( shape, &world->broadPhase, proxyType, transform, forcePairCreation );
1647
+ }
1648
+
1649
+ if ( setId != b2_staticSet )
1650
+ {
1651
+ b2CreateIslandForBody( world, setId, body );
1652
+ }
1653
+
1654
+ // Transfer joints. If the other body is disabled, don't transfer.
1655
+ // If the other body is sleeping, wake it.
1656
+ bool mergeIslands = false;
1657
+ int jointKey = body->headJointKey;
1658
+ while ( jointKey != B2_NULL_INDEX )
1659
+ {
1660
+ int jointId = jointKey >> 1;
1661
+ int edgeIndex = jointKey & 1;
1662
+
1663
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
1664
+ B2_ASSERT( joint->setIndex == b2_disabledSet );
1665
+ B2_ASSERT( joint->islandId == B2_NULL_INDEX );
1666
+
1667
+ jointKey = joint->edges[edgeIndex].nextKey;
1668
+
1669
+ b2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId );
1670
+ b2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId );
1671
+
1672
+ if ( bodyA->setIndex == b2_disabledSet || bodyB->setIndex == b2_disabledSet )
1673
+ {
1674
+ // one body is still disabled
1675
+ continue;
1676
+ }
1677
+
1678
+ // Transfer joint first
1679
+ int jointSetId;
1680
+ if ( bodyA->setIndex == b2_staticSet && bodyB->setIndex == b2_staticSet )
1681
+ {
1682
+ jointSetId = b2_staticSet;
1683
+ }
1684
+ else if ( bodyA->setIndex == b2_staticSet )
1685
+ {
1686
+ jointSetId = bodyB->setIndex;
1687
+ }
1688
+ else
1689
+ {
1690
+ jointSetId = bodyA->setIndex;
1691
+ }
1692
+
1693
+ b2SolverSet* jointSet = b2SolverSetArray_Get( &world->solverSets, jointSetId );
1694
+ b2TransferJoint( world, jointSet, disabledSet, joint );
1695
+
1696
+ // Now that the joint is in the correct set, I can link the joint in the island.
1697
+ if ( jointSetId != b2_staticSet )
1698
+ {
1699
+ b2LinkJoint( world, joint, mergeIslands );
1700
+ }
1701
+ }
1702
+
1703
+ // Now merge islands
1704
+ b2MergeAwakeIslands( world );
1705
+
1706
+ b2ValidateSolverSets( world );
1707
+ }
1708
+
1709
+ void b2Body_SetFixedRotation( b2BodyId bodyId, bool flag )
1710
+ {
1711
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1712
+ if ( world == NULL )
1713
+ {
1714
+ return;
1715
+ }
1716
+
1717
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1718
+ if ( body->fixedRotation != flag )
1719
+ {
1720
+ body->fixedRotation = flag;
1721
+
1722
+ b2BodyState* state = b2GetBodyState( world, body );
1723
+ if ( state != NULL )
1724
+ {
1725
+ state->angularVelocity = 0.0f;
1726
+ }
1727
+ b2UpdateBodyMassData( world, body );
1728
+ }
1729
+ }
1730
+
1731
+ bool b2Body_IsFixedRotation( b2BodyId bodyId )
1732
+ {
1733
+ b2World* world = b2GetWorld( bodyId.world0 );
1734
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1735
+ return body->fixedRotation;
1736
+ }
1737
+
1738
+ void b2Body_SetBullet( b2BodyId bodyId, bool flag )
1739
+ {
1740
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
1741
+ if ( world == NULL )
1742
+ {
1743
+ return;
1744
+ }
1745
+
1746
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1747
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1748
+ bodySim->isBullet = flag;
1749
+ }
1750
+
1751
+ bool b2Body_IsBullet( b2BodyId bodyId )
1752
+ {
1753
+ b2World* world = b2GetWorld( bodyId.world0 );
1754
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1755
+ b2BodySim* bodySim = b2GetBodySim( world, body );
1756
+ return bodySim->isBullet;
1757
+ }
1758
+
1759
+ void b2Body_EnableContactEvents( b2BodyId bodyId, bool flag )
1760
+ {
1761
+ b2World* world = b2GetWorld( bodyId.world0 );
1762
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1763
+ int shapeId = body->headShapeId;
1764
+ while ( shapeId != B2_NULL_INDEX )
1765
+ {
1766
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1767
+ shape->enableContactEvents = flag;
1768
+ shapeId = shape->nextShapeId;
1769
+ }
1770
+ }
1771
+
1772
+ void b2Body_EnableHitEvents( b2BodyId bodyId, bool flag )
1773
+ {
1774
+ b2World* world = b2GetWorld( bodyId.world0 );
1775
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1776
+ int shapeId = body->headShapeId;
1777
+ while ( shapeId != B2_NULL_INDEX )
1778
+ {
1779
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1780
+ shape->enableHitEvents = flag;
1781
+ shapeId = shape->nextShapeId;
1782
+ }
1783
+ }
1784
+
1785
+ b2WorldId b2Body_GetWorld( b2BodyId bodyId )
1786
+ {
1787
+ b2World* world = b2GetWorld( bodyId.world0 );
1788
+ return (b2WorldId){ bodyId.world0 + 1, world->generation };
1789
+ }
1790
+
1791
+ int b2Body_GetShapeCount( b2BodyId bodyId )
1792
+ {
1793
+ b2World* world = b2GetWorld( bodyId.world0 );
1794
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1795
+ return body->shapeCount;
1796
+ }
1797
+
1798
+ int b2Body_GetShapes( b2BodyId bodyId, b2ShapeId* shapeArray, int capacity )
1799
+ {
1800
+ b2World* world = b2GetWorld( bodyId.world0 );
1801
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1802
+ int shapeId = body->headShapeId;
1803
+ int shapeCount = 0;
1804
+ while ( shapeId != B2_NULL_INDEX && shapeCount < capacity )
1805
+ {
1806
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1807
+ b2ShapeId id = { shape->id + 1, bodyId.world0, shape->generation };
1808
+ shapeArray[shapeCount] = id;
1809
+ shapeCount += 1;
1810
+
1811
+ shapeId = shape->nextShapeId;
1812
+ }
1813
+
1814
+ return shapeCount;
1815
+ }
1816
+
1817
+ int b2Body_GetJointCount( b2BodyId bodyId )
1818
+ {
1819
+ b2World* world = b2GetWorld( bodyId.world0 );
1820
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1821
+ return body->jointCount;
1822
+ }
1823
+
1824
+ int b2Body_GetJoints( b2BodyId bodyId, b2JointId* jointArray, int capacity )
1825
+ {
1826
+ b2World* world = b2GetWorld( bodyId.world0 );
1827
+ b2Body* body = b2GetBodyFullId( world, bodyId );
1828
+ int jointKey = body->headJointKey;
1829
+
1830
+ int jointCount = 0;
1831
+ while ( jointKey != B2_NULL_INDEX && jointCount < capacity )
1832
+ {
1833
+ int jointId = jointKey >> 1;
1834
+ int edgeIndex = jointKey & 1;
1835
+
1836
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
1837
+
1838
+ b2JointId id = { jointId + 1, bodyId.world0, joint->generation };
1839
+ jointArray[jointCount] = id;
1840
+ jointCount += 1;
1841
+
1842
+ jointKey = joint->edges[edgeIndex].nextKey;
1843
+ }
1844
+
1845
+ return jointCount;
1846
+ }
1847
+
1848
+ bool b2ShouldBodiesCollide( b2World* world, b2Body* bodyA, b2Body* bodyB )
1849
+ {
1850
+ if ( bodyA->type != b2_dynamicBody && bodyB->type != b2_dynamicBody )
1851
+ {
1852
+ return false;
1853
+ }
1854
+
1855
+ int jointKey;
1856
+ int otherBodyId;
1857
+ if ( bodyA->jointCount < bodyB->jointCount )
1858
+ {
1859
+ jointKey = bodyA->headJointKey;
1860
+ otherBodyId = bodyB->id;
1861
+ }
1862
+ else
1863
+ {
1864
+ jointKey = bodyB->headJointKey;
1865
+ otherBodyId = bodyA->id;
1866
+ }
1867
+
1868
+ while ( jointKey != B2_NULL_INDEX )
1869
+ {
1870
+ int jointId = jointKey >> 1;
1871
+ int edgeIndex = jointKey & 1;
1872
+ int otherEdgeIndex = edgeIndex ^ 1;
1873
+
1874
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
1875
+ if ( joint->collideConnected == false && joint->edges[otherEdgeIndex].bodyId == otherBodyId )
1876
+ {
1877
+ return false;
1878
+ }
1879
+
1880
+ jointKey = joint->edges[edgeIndex].nextKey;
1881
+ }
1882
+
1883
+ return true;
1884
+ }