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,1714 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #include "shape.h"
5
+
6
+ #include "body.h"
7
+ #include "broad_phase.h"
8
+ #include "contact.h"
9
+ #include "sensor.h"
10
+ #include "world.h"
11
+
12
+ // needed for dll export
13
+ #include "box2d/box2d.h"
14
+
15
+ #include <stddef.h>
16
+
17
+ B2_ARRAY_SOURCE( b2ChainShape, b2ChainShape )
18
+ B2_ARRAY_SOURCE( b2Shape, b2Shape )
19
+
20
+ static b2Shape* b2GetShape( b2World* world, b2ShapeId shapeId )
21
+ {
22
+ int id = shapeId.index1 - 1;
23
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, id );
24
+ B2_ASSERT( shape->id == id && shape->generation == shapeId.generation );
25
+ return shape;
26
+ }
27
+
28
+ static b2ChainShape* b2GetChainShape( b2World* world, b2ChainId chainId )
29
+ {
30
+ int id = chainId.index1 - 1;
31
+ b2ChainShape* chain = b2ChainShapeArray_Get( &world->chainShapes, id );
32
+ B2_ASSERT( chain->id == id && chain->generation == chainId.generation );
33
+ return chain;
34
+ }
35
+
36
+ static void b2UpdateShapeAABBs( b2Shape* shape, b2Transform transform, b2BodyType proxyType )
37
+ {
38
+ // Compute a bounding box with a speculative margin
39
+ const float speculativeDistance = B2_SPECULATIVE_DISTANCE;
40
+ const float aabbMargin = B2_AABB_MARGIN;
41
+
42
+ b2AABB aabb = b2ComputeShapeAABB( shape, transform );
43
+ aabb.lowerBound.x -= speculativeDistance;
44
+ aabb.lowerBound.y -= speculativeDistance;
45
+ aabb.upperBound.x += speculativeDistance;
46
+ aabb.upperBound.y += speculativeDistance;
47
+ shape->aabb = aabb;
48
+
49
+ // Smaller margin for static bodies. Cannot be zero due to TOI tolerance.
50
+ float margin = proxyType == b2_staticBody ? speculativeDistance : aabbMargin;
51
+ b2AABB fatAABB;
52
+ fatAABB.lowerBound.x = aabb.lowerBound.x - margin;
53
+ fatAABB.lowerBound.y = aabb.lowerBound.y - margin;
54
+ fatAABB.upperBound.x = aabb.upperBound.x + margin;
55
+ fatAABB.upperBound.y = aabb.upperBound.y + margin;
56
+ shape->fatAABB = fatAABB;
57
+ }
58
+
59
+ static b2Shape* b2CreateShapeInternal( b2World* world, b2Body* body, b2Transform transform, const b2ShapeDef* def,
60
+ const void* geometry, b2ShapeType shapeType )
61
+ {
62
+ int shapeId = b2AllocId( &world->shapeIdPool );
63
+
64
+ if ( shapeId == world->shapes.count )
65
+ {
66
+ b2ShapeArray_Push( &world->shapes, ( b2Shape ){ 0 } );
67
+ }
68
+ else
69
+ {
70
+ B2_ASSERT( world->shapes.data[shapeId].id == B2_NULL_INDEX );
71
+ }
72
+
73
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
74
+
75
+ switch ( shapeType )
76
+ {
77
+ case b2_capsuleShape:
78
+ shape->capsule = *(const b2Capsule*)geometry;
79
+ break;
80
+
81
+ case b2_circleShape:
82
+ shape->circle = *(const b2Circle*)geometry;
83
+ break;
84
+
85
+ case b2_polygonShape:
86
+ shape->polygon = *(const b2Polygon*)geometry;
87
+ break;
88
+
89
+ case b2_segmentShape:
90
+ shape->segment = *(const b2Segment*)geometry;
91
+ break;
92
+
93
+ case b2_chainSegmentShape:
94
+ shape->chainSegment = *(const b2ChainSegment*)geometry;
95
+ break;
96
+
97
+ default:
98
+ B2_ASSERT( false );
99
+ break;
100
+ }
101
+
102
+ shape->id = shapeId;
103
+ shape->bodyId = body->id;
104
+ shape->type = shapeType;
105
+ shape->density = def->density;
106
+ shape->friction = def->material.friction;
107
+ shape->restitution = def->material.restitution;
108
+ shape->rollingResistance = def->material.rollingResistance;
109
+ shape->tangentSpeed = def->material.tangentSpeed;
110
+ shape->userMaterialId = def->material.userMaterialId;
111
+ shape->filter = def->filter;
112
+ shape->userData = def->userData;
113
+ shape->customColor = def->material.customColor;
114
+ shape->enlargedAABB = false;
115
+ shape->enableSensorEvents = def->enableSensorEvents;
116
+ shape->enableContactEvents = def->enableContactEvents;
117
+ shape->enableHitEvents = def->enableHitEvents;
118
+ shape->enablePreSolveEvents = def->enablePreSolveEvents;
119
+ shape->proxyKey = B2_NULL_INDEX;
120
+ shape->localCentroid = b2GetShapeCentroid( shape );
121
+ shape->aabb = ( b2AABB ){ b2Vec2_zero, b2Vec2_zero };
122
+ shape->fatAABB = ( b2AABB ){ b2Vec2_zero, b2Vec2_zero };
123
+ shape->generation += 1;
124
+
125
+ if ( body->setIndex != b2_disabledSet )
126
+ {
127
+ b2BodyType proxyType = body->type;
128
+ b2CreateShapeProxy( shape, &world->broadPhase, proxyType, transform, def->invokeContactCreation || def->isSensor );
129
+ }
130
+
131
+ // Add to shape doubly linked list
132
+ if ( body->headShapeId != B2_NULL_INDEX )
133
+ {
134
+ b2Shape* headShape = b2ShapeArray_Get( &world->shapes, body->headShapeId );
135
+ headShape->prevShapeId = shapeId;
136
+ }
137
+
138
+ shape->prevShapeId = B2_NULL_INDEX;
139
+ shape->nextShapeId = body->headShapeId;
140
+ body->headShapeId = shapeId;
141
+ body->shapeCount += 1;
142
+
143
+ if ( def->isSensor )
144
+ {
145
+ shape->sensorIndex = world->sensors.count;
146
+ b2Sensor sensor = {
147
+ .overlaps1 = b2ShapeRefArray_Create( 16 ),
148
+ .overlaps2 = b2ShapeRefArray_Create( 16 ),
149
+ .shapeId = shapeId,
150
+ };
151
+ b2SensorArray_Push( &world->sensors, sensor );
152
+ }
153
+ else
154
+ {
155
+ shape->sensorIndex = B2_NULL_INDEX;
156
+ }
157
+
158
+ b2ValidateSolverSets( world );
159
+
160
+ return shape;
161
+ }
162
+
163
+ static b2ShapeId b2CreateShape( b2BodyId bodyId, const b2ShapeDef* def, const void* geometry, b2ShapeType shapeType )
164
+ {
165
+ B2_CHECK_DEF( def );
166
+ B2_ASSERT( b2IsValidFloat( def->density ) && def->density >= 0.0f );
167
+ B2_ASSERT( b2IsValidFloat( def->material.friction ) && def->material.friction >= 0.0f );
168
+ B2_ASSERT( b2IsValidFloat( def->material.restitution ) && def->material.restitution >= 0.0f );
169
+ B2_ASSERT( b2IsValidFloat( def->material.rollingResistance ) && def->material.rollingResistance >= 0.0f );
170
+ B2_ASSERT( b2IsValidFloat( def->material.tangentSpeed ) );
171
+
172
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
173
+ if ( world == NULL )
174
+ {
175
+ return ( b2ShapeId ){ 0 };
176
+ }
177
+
178
+ b2Body* body = b2GetBodyFullId( world, bodyId );
179
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
180
+
181
+ b2Shape* shape = b2CreateShapeInternal( world, body, transform, def, geometry, shapeType );
182
+
183
+ if ( def->updateBodyMass == true )
184
+ {
185
+ b2UpdateBodyMassData( world, body );
186
+ }
187
+
188
+ b2ValidateSolverSets( world );
189
+
190
+ b2ShapeId id = { shape->id + 1, bodyId.world0, shape->generation };
191
+ return id;
192
+ }
193
+
194
+ b2ShapeId b2CreateCircleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Circle* circle )
195
+ {
196
+ return b2CreateShape( bodyId, def, circle, b2_circleShape );
197
+ }
198
+
199
+ b2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Capsule* capsule )
200
+ {
201
+ float lengthSqr = b2DistanceSquared( capsule->center1, capsule->center2 );
202
+ if ( lengthSqr <= B2_LINEAR_SLOP * B2_LINEAR_SLOP )
203
+ {
204
+ b2Circle circle = { b2Lerp( capsule->center1, capsule->center2, 0.5f ), capsule->radius };
205
+ return b2CreateShape( bodyId, def, &circle, b2_circleShape );
206
+ }
207
+
208
+ return b2CreateShape( bodyId, def, capsule, b2_capsuleShape );
209
+ }
210
+
211
+ b2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Polygon* polygon )
212
+ {
213
+ B2_ASSERT( b2IsValidFloat( polygon->radius ) && polygon->radius >= 0.0f );
214
+ return b2CreateShape( bodyId, def, polygon, b2_polygonShape );
215
+ }
216
+
217
+ b2ShapeId b2CreateSegmentShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Segment* segment )
218
+ {
219
+ float lengthSqr = b2DistanceSquared( segment->point1, segment->point2 );
220
+ if ( lengthSqr <= B2_LINEAR_SLOP * B2_LINEAR_SLOP )
221
+ {
222
+ B2_ASSERT( false );
223
+ return b2_nullShapeId;
224
+ }
225
+
226
+ return b2CreateShape( bodyId, def, segment, b2_segmentShape );
227
+ }
228
+
229
+ // Destroy a shape on a body. This doesn't need to be called when destroying a body.
230
+ static void b2DestroyShapeInternal( b2World* world, b2Shape* shape, b2Body* body, bool wakeBodies )
231
+ {
232
+ int shapeId = shape->id;
233
+
234
+ // Remove the shape from the body's doubly linked list.
235
+ if ( shape->prevShapeId != B2_NULL_INDEX )
236
+ {
237
+ b2Shape* prevShape = b2ShapeArray_Get( &world->shapes, shape->prevShapeId );
238
+ prevShape->nextShapeId = shape->nextShapeId;
239
+ }
240
+
241
+ if ( shape->nextShapeId != B2_NULL_INDEX )
242
+ {
243
+ b2Shape* nextShape = b2ShapeArray_Get( &world->shapes, shape->nextShapeId );
244
+ nextShape->prevShapeId = shape->prevShapeId;
245
+ }
246
+
247
+ if ( shapeId == body->headShapeId )
248
+ {
249
+ body->headShapeId = shape->nextShapeId;
250
+ }
251
+
252
+ body->shapeCount -= 1;
253
+
254
+ // Remove from broad-phase.
255
+ b2DestroyShapeProxy( shape, &world->broadPhase );
256
+
257
+ // Destroy any contacts associated with the shape.
258
+ int contactKey = body->headContactKey;
259
+ while ( contactKey != B2_NULL_INDEX )
260
+ {
261
+ int contactId = contactKey >> 1;
262
+ int edgeIndex = contactKey & 1;
263
+
264
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
265
+ contactKey = contact->edges[edgeIndex].nextKey;
266
+
267
+ if ( contact->shapeIdA == shapeId || contact->shapeIdB == shapeId )
268
+ {
269
+ b2DestroyContact( world, contact, wakeBodies );
270
+ }
271
+ }
272
+
273
+ if ( shape->sensorIndex != B2_NULL_INDEX )
274
+ {
275
+ b2Sensor* sensor = b2SensorArray_Get( &world->sensors, shape->sensorIndex );
276
+ for ( int i = 0; i < sensor->overlaps2.count; ++i )
277
+ {
278
+ b2ShapeRef* ref = sensor->overlaps2.data + i;
279
+ b2SensorEndTouchEvent event = {
280
+ .sensorShapeId =
281
+ {
282
+ .index1 = shapeId + 1,
283
+ .generation = shape->generation,
284
+ .world0 = world->worldId,
285
+ },
286
+ .visitorShapeId =
287
+ {
288
+ .index1 = ref->shapeId + 1,
289
+ .generation = ref->generation,
290
+ .world0 = world->worldId,
291
+ },
292
+ };
293
+
294
+ b2SensorEndTouchEventArray_Push( world->sensorEndEvents + world->endEventArrayIndex, event );
295
+ }
296
+
297
+ // Destroy sensor
298
+ b2ShapeRefArray_Destroy( &sensor->overlaps1 );
299
+ b2ShapeRefArray_Destroy( &sensor->overlaps2 );
300
+
301
+ int movedIndex = b2SensorArray_RemoveSwap( &world->sensors, shape->sensorIndex );
302
+ if ( movedIndex != B2_NULL_INDEX )
303
+ {
304
+ // Fixup moved sensor
305
+ b2Sensor* movedSensor = b2SensorArray_Get( &world->sensors, shape->sensorIndex );
306
+ b2Shape* otherSensorShape = b2ShapeArray_Get( &world->shapes, movedSensor->shapeId );
307
+ otherSensorShape->sensorIndex = shape->sensorIndex;
308
+ }
309
+ }
310
+
311
+ // Return shape to free list.
312
+ b2FreeId( &world->shapeIdPool, shapeId );
313
+ shape->id = B2_NULL_INDEX;
314
+
315
+ b2ValidateSolverSets( world );
316
+ }
317
+
318
+ void b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass )
319
+ {
320
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
321
+ if ( world == NULL )
322
+ {
323
+ return;
324
+ }
325
+
326
+ b2Shape* shape = b2GetShape( world, shapeId );
327
+
328
+ // need to wake bodies because this might be a static body
329
+ bool wakeBodies = true;
330
+ b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
331
+ b2DestroyShapeInternal( world, shape, body, wakeBodies );
332
+
333
+ if ( updateBodyMass == true )
334
+ {
335
+ b2UpdateBodyMassData( world, body );
336
+ }
337
+ }
338
+
339
+ b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def )
340
+ {
341
+ B2_CHECK_DEF( def );
342
+ B2_ASSERT( def->count >= 4 );
343
+ B2_ASSERT( def->materialCount == 1 || def->materialCount == def->count );
344
+
345
+ b2World* world = b2GetWorldLocked( bodyId.world0 );
346
+ if ( world == NULL )
347
+ {
348
+ return ( b2ChainId ){ 0 };
349
+ }
350
+
351
+ b2Body* body = b2GetBodyFullId( world, bodyId );
352
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
353
+
354
+ int chainId = b2AllocId( &world->chainIdPool );
355
+
356
+ if ( chainId == world->chainShapes.count )
357
+ {
358
+ b2ChainShapeArray_Push( &world->chainShapes, ( b2ChainShape ){ 0 } );
359
+ }
360
+ else
361
+ {
362
+ B2_ASSERT( world->chainShapes.data[chainId].id == B2_NULL_INDEX );
363
+ }
364
+
365
+ b2ChainShape* chainShape = b2ChainShapeArray_Get( &world->chainShapes, chainId );
366
+
367
+ chainShape->id = chainId;
368
+ chainShape->bodyId = body->id;
369
+ chainShape->nextChainId = body->headChainId;
370
+ chainShape->generation += 1;
371
+
372
+ int materialCount = def->materialCount;
373
+ chainShape->materialCount = materialCount;
374
+ chainShape->materials = b2Alloc( materialCount * sizeof( b2SurfaceMaterial ) );
375
+
376
+ for ( int i = 0; i < materialCount; ++i )
377
+ {
378
+ const b2SurfaceMaterial* material = def->materials + i;
379
+ B2_ASSERT( b2IsValidFloat( material->friction ) && material->friction >= 0.0f );
380
+ B2_ASSERT( b2IsValidFloat( material->restitution ) && material->restitution >= 0.0f );
381
+ B2_ASSERT( b2IsValidFloat( material->rollingResistance ) && material->rollingResistance >= 0.0f );
382
+ B2_ASSERT( b2IsValidFloat( material->tangentSpeed ) );
383
+
384
+ chainShape->materials[i] = *material;
385
+ }
386
+
387
+ body->headChainId = chainId;
388
+
389
+ b2ShapeDef shapeDef = b2DefaultShapeDef();
390
+ shapeDef.userData = def->userData;
391
+ shapeDef.filter = def->filter;
392
+ shapeDef.enableSensorEvents = def->enableSensorEvents;
393
+ shapeDef.enableContactEvents = false;
394
+ shapeDef.enableHitEvents = false;
395
+
396
+ const b2Vec2* points = def->points;
397
+ int n = def->count;
398
+
399
+ if ( def->isLoop )
400
+ {
401
+ chainShape->count = n;
402
+ chainShape->shapeIndices = b2Alloc( chainShape->count * sizeof( int ) );
403
+
404
+ b2ChainSegment chainSegment;
405
+
406
+ int prevIndex = n - 1;
407
+ for ( int i = 0; i < n - 2; ++i )
408
+ {
409
+ chainSegment.ghost1 = points[prevIndex];
410
+ chainSegment.segment.point1 = points[i];
411
+ chainSegment.segment.point2 = points[i + 1];
412
+ chainSegment.ghost2 = points[i + 2];
413
+ chainSegment.chainId = chainId;
414
+ prevIndex = i;
415
+
416
+ int materialIndex = materialCount == 1 ? 0 : i;
417
+ shapeDef.material = def->materials[materialIndex];
418
+
419
+ b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape );
420
+ chainShape->shapeIndices[i] = shape->id;
421
+ }
422
+
423
+ {
424
+ chainSegment.ghost1 = points[n - 3];
425
+ chainSegment.segment.point1 = points[n - 2];
426
+ chainSegment.segment.point2 = points[n - 1];
427
+ chainSegment.ghost2 = points[0];
428
+ chainSegment.chainId = chainId;
429
+
430
+ int materialIndex = materialCount == 1 ? 0 : n - 2;
431
+ shapeDef.material = def->materials[materialIndex];
432
+
433
+ b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape );
434
+ chainShape->shapeIndices[n - 2] = shape->id;
435
+ }
436
+
437
+ {
438
+ chainSegment.ghost1 = points[n - 2];
439
+ chainSegment.segment.point1 = points[n - 1];
440
+ chainSegment.segment.point2 = points[0];
441
+ chainSegment.ghost2 = points[1];
442
+ chainSegment.chainId = chainId;
443
+
444
+ int materialIndex = materialCount == 1 ? 0 : n - 1;
445
+ shapeDef.material = def->materials[materialIndex];
446
+
447
+ b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape );
448
+ chainShape->shapeIndices[n - 1] = shape->id;
449
+ }
450
+ }
451
+ else
452
+ {
453
+ chainShape->count = n - 3;
454
+ chainShape->shapeIndices = b2Alloc( chainShape->count * sizeof( int ) );
455
+
456
+ b2ChainSegment chainSegment;
457
+
458
+ for ( int i = 0; i < n - 3; ++i )
459
+ {
460
+ chainSegment.ghost1 = points[i];
461
+ chainSegment.segment.point1 = points[i + 1];
462
+ chainSegment.segment.point2 = points[i + 2];
463
+ chainSegment.ghost2 = points[i + 3];
464
+ chainSegment.chainId = chainId;
465
+
466
+ // Material is associated with leading point of solid segment
467
+ int materialIndex = materialCount == 1 ? 0 : i + 1;
468
+ shapeDef.material = def->materials[materialIndex];
469
+
470
+ b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape );
471
+ chainShape->shapeIndices[i] = shape->id;
472
+ }
473
+ }
474
+
475
+ b2ChainId id = { chainId + 1, world->worldId, chainShape->generation };
476
+ return id;
477
+ }
478
+
479
+ void b2FreeChainData(b2ChainShape* chain)
480
+ {
481
+ b2Free( chain->shapeIndices, chain->count * sizeof( int ) );
482
+ chain->shapeIndices = NULL;
483
+
484
+ b2Free( chain->materials, chain->materialCount * sizeof( b2SurfaceMaterial ) );
485
+ chain->materials = NULL;
486
+ }
487
+
488
+ void b2DestroyChain( b2ChainId chainId )
489
+ {
490
+ b2World* world = b2GetWorldLocked( chainId.world0 );
491
+ if ( world == NULL )
492
+ {
493
+ return;
494
+ }
495
+
496
+ b2ChainShape* chain = b2GetChainShape( world, chainId );
497
+
498
+ b2Body* body = b2BodyArray_Get( &world->bodies, chain->bodyId );
499
+
500
+ // Remove the chain from the body's singly linked list.
501
+ int* chainIdPtr = &body->headChainId;
502
+ bool found = false;
503
+ while ( *chainIdPtr != B2_NULL_INDEX )
504
+ {
505
+ if ( *chainIdPtr == chain->id )
506
+ {
507
+ *chainIdPtr = chain->nextChainId;
508
+ found = true;
509
+ break;
510
+ }
511
+
512
+ chainIdPtr = &( world->chainShapes.data[*chainIdPtr].nextChainId );
513
+ }
514
+
515
+ B2_ASSERT( found == true );
516
+ if ( found == false )
517
+ {
518
+ return;
519
+ }
520
+
521
+ int count = chain->count;
522
+ for ( int i = 0; i < count; ++i )
523
+ {
524
+ int shapeId = chain->shapeIndices[i];
525
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
526
+ bool wakeBodies = true;
527
+ b2DestroyShapeInternal( world, shape, body, wakeBodies );
528
+ }
529
+
530
+ b2FreeChainData( chain );
531
+
532
+ // Return chain to free list.
533
+ b2FreeId( &world->chainIdPool, chain->id );
534
+ chain->id = B2_NULL_INDEX;
535
+
536
+ b2ValidateSolverSets( world );
537
+ }
538
+
539
+ b2WorldId b2Chain_GetWorld( b2ChainId chainId )
540
+ {
541
+ b2World* world = b2GetWorld( chainId.world0 );
542
+ return ( b2WorldId ){ chainId.world0 + 1, world->generation };
543
+ }
544
+
545
+ int b2Chain_GetSegmentCount( b2ChainId chainId )
546
+ {
547
+ b2World* world = b2GetWorldLocked( chainId.world0 );
548
+ if ( world == NULL )
549
+ {
550
+ return 0;
551
+ }
552
+
553
+ b2ChainShape* chain = b2GetChainShape( world, chainId );
554
+ return chain->count;
555
+ }
556
+
557
+ int b2Chain_GetSegments( b2ChainId chainId, b2ShapeId* segmentArray, int capacity )
558
+ {
559
+ b2World* world = b2GetWorldLocked( chainId.world0 );
560
+ if ( world == NULL )
561
+ {
562
+ return 0;
563
+ }
564
+
565
+ b2ChainShape* chain = b2GetChainShape( world, chainId );
566
+
567
+ int count = b2MinInt( chain->count, capacity );
568
+ for ( int i = 0; i < count; ++i )
569
+ {
570
+ int shapeId = chain->shapeIndices[i];
571
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
572
+ segmentArray[i] = ( b2ShapeId ){ shapeId + 1, chainId.world0, shape->generation };
573
+ }
574
+
575
+ return count;
576
+ }
577
+
578
+ b2AABB b2ComputeShapeAABB( const b2Shape* shape, b2Transform xf )
579
+ {
580
+ switch ( shape->type )
581
+ {
582
+ case b2_capsuleShape:
583
+ return b2ComputeCapsuleAABB( &shape->capsule, xf );
584
+ case b2_circleShape:
585
+ return b2ComputeCircleAABB( &shape->circle, xf );
586
+ case b2_polygonShape:
587
+ return b2ComputePolygonAABB( &shape->polygon, xf );
588
+ case b2_segmentShape:
589
+ return b2ComputeSegmentAABB( &shape->segment, xf );
590
+ case b2_chainSegmentShape:
591
+ return b2ComputeSegmentAABB( &shape->chainSegment.segment, xf );
592
+ default:
593
+ {
594
+ B2_ASSERT( false );
595
+ b2AABB empty = { xf.p, xf.p };
596
+ return empty;
597
+ }
598
+ }
599
+ }
600
+
601
+ b2Vec2 b2GetShapeCentroid( const b2Shape* shape )
602
+ {
603
+ switch ( shape->type )
604
+ {
605
+ case b2_capsuleShape:
606
+ return b2Lerp( shape->capsule.center1, shape->capsule.center2, 0.5f );
607
+ case b2_circleShape:
608
+ return shape->circle.center;
609
+ case b2_polygonShape:
610
+ return shape->polygon.centroid;
611
+ case b2_segmentShape:
612
+ return b2Lerp( shape->segment.point1, shape->segment.point2, 0.5f );
613
+ case b2_chainSegmentShape:
614
+ return b2Lerp( shape->chainSegment.segment.point1, shape->chainSegment.segment.point2, 0.5f );
615
+ default:
616
+ return b2Vec2_zero;
617
+ }
618
+ }
619
+
620
+ // todo_erin maybe compute this on shape creation
621
+ float b2GetShapePerimeter( const b2Shape* shape )
622
+ {
623
+ switch ( shape->type )
624
+ {
625
+ case b2_capsuleShape:
626
+ return 2.0f * b2Length( b2Sub( shape->capsule.center1, shape->capsule.center2 ) ) +
627
+ 2.0f * B2_PI * shape->capsule.radius;
628
+ case b2_circleShape:
629
+ return 2.0f * B2_PI * shape->circle.radius;
630
+ case b2_polygonShape:
631
+ {
632
+ const b2Vec2* points = shape->polygon.vertices;
633
+ int count = shape->polygon.count;
634
+ float perimeter = 2.0f * B2_PI * shape->polygon.radius;
635
+ B2_ASSERT( count > 0 );
636
+ b2Vec2 prev = points[count - 1];
637
+ for ( int i = 0; i < count; ++i )
638
+ {
639
+ b2Vec2 next = points[i];
640
+ perimeter += b2Length( b2Sub( next, prev ) );
641
+ prev = next;
642
+ }
643
+
644
+ return perimeter;
645
+ }
646
+ case b2_segmentShape:
647
+ return 2.0f * b2Length( b2Sub( shape->segment.point1, shape->segment.point2 ) );
648
+ case b2_chainSegmentShape:
649
+ return 2.0f * b2Length( b2Sub( shape->chainSegment.segment.point1, shape->chainSegment.segment.point2 ) );
650
+ default:
651
+ return 0.0f;
652
+ }
653
+ }
654
+
655
+ // This projects the shape perimeter onto an infinite line
656
+ float b2GetShapeProjectedPerimeter( const b2Shape* shape, b2Vec2 line )
657
+ {
658
+ switch ( shape->type )
659
+ {
660
+ case b2_capsuleShape:
661
+ {
662
+ b2Vec2 axis = b2Sub( shape->capsule.center2, shape->capsule.center1 );
663
+ float projectedLength = b2AbsFloat( b2Dot( axis, line ) );
664
+ return projectedLength + 2.0f * shape->capsule.radius;
665
+ }
666
+
667
+ case b2_circleShape:
668
+ return 2.0f * shape->circle.radius;
669
+
670
+ case b2_polygonShape:
671
+ {
672
+ const b2Vec2* points = shape->polygon.vertices;
673
+ int count = shape->polygon.count;
674
+ B2_ASSERT( count > 0 );
675
+ float value = b2Dot( points[0], line );
676
+ float lower = value;
677
+ float upper = value;
678
+ for ( int i = 1; i < count; ++i )
679
+ {
680
+ value = b2Dot( points[i], line );
681
+ lower = b2MinFloat( lower, value );
682
+ upper = b2MaxFloat( upper, value );
683
+ }
684
+
685
+ return ( upper - lower ) + 2.0f * shape->polygon.radius;
686
+ }
687
+
688
+ case b2_segmentShape:
689
+ {
690
+ float value1 = b2Dot( shape->segment.point1, line );
691
+ float value2 = b2Dot( shape->segment.point2, line );
692
+ return b2AbsFloat( value2 - value1 );
693
+ }
694
+
695
+ case b2_chainSegmentShape:
696
+ {
697
+ float value1 = b2Dot( shape->chainSegment.segment.point1, line );
698
+ float value2 = b2Dot( shape->chainSegment.segment.point2, line );
699
+ return b2AbsFloat( value2 - value1 );
700
+ }
701
+
702
+ default:
703
+ return 0.0f;
704
+ }
705
+ }
706
+
707
+ b2MassData b2ComputeShapeMass( const b2Shape* shape )
708
+ {
709
+ switch ( shape->type )
710
+ {
711
+ case b2_capsuleShape:
712
+ return b2ComputeCapsuleMass( &shape->capsule, shape->density );
713
+ case b2_circleShape:
714
+ return b2ComputeCircleMass( &shape->circle, shape->density );
715
+ case b2_polygonShape:
716
+ return b2ComputePolygonMass( &shape->polygon, shape->density );
717
+ default:
718
+ return ( b2MassData ){ 0 };
719
+ }
720
+ }
721
+
722
+ b2ShapeExtent b2ComputeShapeExtent( const b2Shape* shape, b2Vec2 localCenter )
723
+ {
724
+ b2ShapeExtent extent = { 0 };
725
+
726
+ switch ( shape->type )
727
+ {
728
+ case b2_capsuleShape:
729
+ {
730
+ float radius = shape->capsule.radius;
731
+ extent.minExtent = radius;
732
+ b2Vec2 c1 = b2Sub( shape->capsule.center1, localCenter );
733
+ b2Vec2 c2 = b2Sub( shape->capsule.center2, localCenter );
734
+ extent.maxExtent = sqrtf( b2MaxFloat( b2LengthSquared( c1 ), b2LengthSquared( c2 ) ) ) + radius;
735
+ }
736
+ break;
737
+
738
+ case b2_circleShape:
739
+ {
740
+ float radius = shape->circle.radius;
741
+ extent.minExtent = radius;
742
+ extent.maxExtent = b2Length( b2Sub( shape->circle.center, localCenter ) ) + radius;
743
+ }
744
+ break;
745
+
746
+ case b2_polygonShape:
747
+ {
748
+ const b2Polygon* poly = &shape->polygon;
749
+ float minExtent = B2_HUGE;
750
+ float maxExtentSqr = 0.0f;
751
+ int count = poly->count;
752
+ for ( int i = 0; i < count; ++i )
753
+ {
754
+ b2Vec2 v = poly->vertices[i];
755
+ float planeOffset = b2Dot( poly->normals[i], b2Sub( v, poly->centroid ) );
756
+ minExtent = b2MinFloat( minExtent, planeOffset );
757
+
758
+ float distanceSqr = b2LengthSquared( b2Sub( v, localCenter ) );
759
+ maxExtentSqr = b2MaxFloat( maxExtentSqr, distanceSqr );
760
+ }
761
+
762
+ extent.minExtent = minExtent + poly->radius;
763
+ extent.maxExtent = sqrtf( maxExtentSqr ) + poly->radius;
764
+ }
765
+ break;
766
+
767
+ case b2_segmentShape:
768
+ {
769
+ extent.minExtent = 0.0f;
770
+ b2Vec2 c1 = b2Sub( shape->segment.point1, localCenter );
771
+ b2Vec2 c2 = b2Sub( shape->segment.point2, localCenter );
772
+ extent.maxExtent = sqrtf( b2MaxFloat( b2LengthSquared( c1 ), b2LengthSquared( c2 ) ) );
773
+ }
774
+ break;
775
+
776
+ case b2_chainSegmentShape:
777
+ {
778
+ extent.minExtent = 0.0f;
779
+ b2Vec2 c1 = b2Sub( shape->chainSegment.segment.point1, localCenter );
780
+ b2Vec2 c2 = b2Sub( shape->chainSegment.segment.point2, localCenter );
781
+ extent.maxExtent = sqrtf( b2MaxFloat( b2LengthSquared( c1 ), b2LengthSquared( c2 ) ) );
782
+ }
783
+ break;
784
+
785
+ default:
786
+ break;
787
+ }
788
+
789
+ return extent;
790
+ }
791
+
792
+ b2CastOutput b2RayCastShape( const b2RayCastInput* input, const b2Shape* shape, b2Transform transform )
793
+ {
794
+ b2RayCastInput localInput = *input;
795
+ localInput.origin = b2InvTransformPoint( transform, input->origin );
796
+ localInput.translation = b2InvRotateVector( transform.q, input->translation );
797
+
798
+ b2CastOutput output = { 0 };
799
+ switch ( shape->type )
800
+ {
801
+ case b2_capsuleShape:
802
+ output = b2RayCastCapsule( &localInput, &shape->capsule );
803
+ break;
804
+ case b2_circleShape:
805
+ output = b2RayCastCircle( &localInput, &shape->circle );
806
+ break;
807
+ case b2_polygonShape:
808
+ output = b2RayCastPolygon( &localInput, &shape->polygon );
809
+ break;
810
+ case b2_segmentShape:
811
+ output = b2RayCastSegment( &localInput, &shape->segment, false );
812
+ break;
813
+ case b2_chainSegmentShape:
814
+ output = b2RayCastSegment( &localInput, &shape->chainSegment.segment, true );
815
+ break;
816
+ default:
817
+ return output;
818
+ }
819
+
820
+ output.point = b2TransformPoint( transform, output.point );
821
+ output.normal = b2RotateVector( transform.q, output.normal );
822
+ return output;
823
+ }
824
+
825
+ b2CastOutput b2ShapeCastShape( const b2ShapeCastInput* input, const b2Shape* shape, b2Transform transform )
826
+ {
827
+ b2ShapeCastInput localInput = *input;
828
+
829
+ for ( int i = 0; i < localInput.proxy.count; ++i )
830
+ {
831
+ localInput.proxy.points[i] = b2InvTransformPoint( transform, input->proxy.points[i] );
832
+ }
833
+
834
+ localInput.translation = b2InvRotateVector( transform.q, input->translation );
835
+
836
+ b2CastOutput output = { 0 };
837
+ switch ( shape->type )
838
+ {
839
+ case b2_capsuleShape:
840
+ output = b2ShapeCastCapsule( &localInput, &shape->capsule );
841
+ break;
842
+ case b2_circleShape:
843
+ output = b2ShapeCastCircle( &localInput, &shape->circle );
844
+ break;
845
+ case b2_polygonShape:
846
+ output = b2ShapeCastPolygon( &localInput, &shape->polygon );
847
+ break;
848
+ case b2_segmentShape:
849
+ output = b2ShapeCastSegment( &localInput, &shape->segment );
850
+ break;
851
+ case b2_chainSegmentShape:
852
+ output = b2ShapeCastSegment( &localInput, &shape->chainSegment.segment );
853
+ break;
854
+ default:
855
+ return output;
856
+ }
857
+
858
+ output.point = b2TransformPoint( transform, output.point );
859
+ output.normal = b2RotateVector( transform.q, output.normal );
860
+ return output;
861
+ }
862
+
863
+ b2PlaneResult b2CollideMover( const b2Shape* shape, b2Transform transform, const b2Capsule* mover )
864
+ {
865
+ b2Capsule localMover;
866
+ localMover.center1 = b2InvTransformPoint( transform, mover->center1 );
867
+ localMover.center2 = b2InvTransformPoint( transform, mover->center2 );
868
+ localMover.radius = mover->radius;
869
+
870
+ b2PlaneResult result = { 0 };
871
+ switch ( shape->type )
872
+ {
873
+ case b2_capsuleShape:
874
+ result = b2CollideMoverAndCapsule( &shape->capsule, &localMover );
875
+ break;
876
+ case b2_circleShape:
877
+ result = b2CollideMoverAndCircle( &shape->circle, &localMover );
878
+ break;
879
+ case b2_polygonShape:
880
+ result = b2CollideMoverAndPolygon( &shape->polygon, &localMover );
881
+ break;
882
+ case b2_segmentShape:
883
+ result = b2CollideMoverAndSegment( &shape->segment, &localMover );
884
+ break;
885
+ case b2_chainSegmentShape:
886
+ result = b2CollideMoverAndSegment( &shape->chainSegment.segment, &localMover );
887
+ break;
888
+ default:
889
+ return result;
890
+ }
891
+
892
+ if (result.hit == false)
893
+ {
894
+ return result;
895
+ }
896
+
897
+ result.plane.normal = b2RotateVector( transform.q, result.plane.normal );
898
+ return result;
899
+ }
900
+
901
+ void b2CreateShapeProxy( b2Shape* shape, b2BroadPhase* bp, b2BodyType type, b2Transform transform, bool forcePairCreation )
902
+ {
903
+ B2_ASSERT( shape->proxyKey == B2_NULL_INDEX );
904
+
905
+ b2UpdateShapeAABBs( shape, transform, type );
906
+
907
+ // Create proxies in the broad-phase.
908
+ shape->proxyKey =
909
+ b2BroadPhase_CreateProxy( bp, type, shape->fatAABB, shape->filter.categoryBits, shape->id, forcePairCreation );
910
+ B2_ASSERT( B2_PROXY_TYPE( shape->proxyKey ) < b2_bodyTypeCount );
911
+ }
912
+
913
+ void b2DestroyShapeProxy( b2Shape* shape, b2BroadPhase* bp )
914
+ {
915
+ if ( shape->proxyKey != B2_NULL_INDEX )
916
+ {
917
+ b2BroadPhase_DestroyProxy( bp, shape->proxyKey );
918
+ shape->proxyKey = B2_NULL_INDEX;
919
+ }
920
+ }
921
+
922
+ b2ShapeProxy b2MakeShapeDistanceProxy( const b2Shape* shape )
923
+ {
924
+ switch ( shape->type )
925
+ {
926
+ case b2_capsuleShape:
927
+ return b2MakeProxy( &shape->capsule.center1, 2, shape->capsule.radius );
928
+ case b2_circleShape:
929
+ return b2MakeProxy( &shape->circle.center, 1, shape->circle.radius );
930
+ case b2_polygonShape:
931
+ return b2MakeProxy( shape->polygon.vertices, shape->polygon.count, shape->polygon.radius );
932
+ case b2_segmentShape:
933
+ return b2MakeProxy( &shape->segment.point1, 2, 0.0f );
934
+ case b2_chainSegmentShape:
935
+ return b2MakeProxy( &shape->chainSegment.segment.point1, 2, 0.0f );
936
+ default:
937
+ {
938
+ B2_ASSERT( false );
939
+ b2ShapeProxy empty = { 0 };
940
+ return empty;
941
+ }
942
+ }
943
+ }
944
+
945
+ b2BodyId b2Shape_GetBody( b2ShapeId shapeId )
946
+ {
947
+ b2World* world = b2GetWorld( shapeId.world0 );
948
+ b2Shape* shape = b2GetShape( world, shapeId );
949
+ return b2MakeBodyId( world, shape->bodyId );
950
+ }
951
+
952
+ b2WorldId b2Shape_GetWorld( b2ShapeId shapeId )
953
+ {
954
+ b2World* world = b2GetWorld( shapeId.world0 );
955
+ return ( b2WorldId ){ shapeId.world0 + 1, world->generation };
956
+ }
957
+
958
+ void b2Shape_SetUserData( b2ShapeId shapeId, void* userData )
959
+ {
960
+ b2World* world = b2GetWorld( shapeId.world0 );
961
+ b2Shape* shape = b2GetShape( world, shapeId );
962
+ shape->userData = userData;
963
+ }
964
+
965
+ void* b2Shape_GetUserData( b2ShapeId shapeId )
966
+ {
967
+ b2World* world = b2GetWorld( shapeId.world0 );
968
+ b2Shape* shape = b2GetShape( world, shapeId );
969
+ return shape->userData;
970
+ }
971
+
972
+ bool b2Shape_IsSensor( b2ShapeId shapeId )
973
+ {
974
+ b2World* world = b2GetWorld( shapeId.world0 );
975
+ b2Shape* shape = b2GetShape( world, shapeId );
976
+ return shape->sensorIndex != B2_NULL_INDEX;
977
+ }
978
+
979
+ bool b2Shape_TestPoint( b2ShapeId shapeId, b2Vec2 point )
980
+ {
981
+ b2World* world = b2GetWorld( shapeId.world0 );
982
+ b2Shape* shape = b2GetShape( world, shapeId );
983
+
984
+ b2Transform transform = b2GetBodyTransform( world, shape->bodyId );
985
+ b2Vec2 localPoint = b2InvTransformPoint( transform, point );
986
+
987
+ switch ( shape->type )
988
+ {
989
+ case b2_capsuleShape:
990
+ return b2PointInCapsule( localPoint, &shape->capsule );
991
+
992
+ case b2_circleShape:
993
+ return b2PointInCircle( localPoint, &shape->circle );
994
+
995
+ case b2_polygonShape:
996
+ return b2PointInPolygon( localPoint, &shape->polygon );
997
+
998
+ default:
999
+ return false;
1000
+ }
1001
+ }
1002
+
1003
+ // todo_erin untested
1004
+ b2CastOutput b2Shape_RayCast( b2ShapeId shapeId, const b2RayCastInput* input )
1005
+ {
1006
+ b2World* world = b2GetWorld( shapeId.world0 );
1007
+ b2Shape* shape = b2GetShape( world, shapeId );
1008
+
1009
+ b2Transform transform = b2GetBodyTransform( world, shape->bodyId );
1010
+
1011
+ // input in local coordinates
1012
+ b2RayCastInput localInput;
1013
+ localInput.origin = b2InvTransformPoint( transform, input->origin );
1014
+ localInput.translation = b2InvRotateVector( transform.q, input->translation );
1015
+ localInput.maxFraction = input->maxFraction;
1016
+
1017
+ b2CastOutput output = { 0 };
1018
+ switch ( shape->type )
1019
+ {
1020
+ case b2_capsuleShape:
1021
+ output = b2RayCastCapsule( &localInput, &shape->capsule );
1022
+ break;
1023
+
1024
+ case b2_circleShape:
1025
+ output = b2RayCastCircle( &localInput, &shape->circle );
1026
+ break;
1027
+
1028
+ case b2_segmentShape:
1029
+ output = b2RayCastSegment( &localInput, &shape->segment, false );
1030
+ break;
1031
+
1032
+ case b2_polygonShape:
1033
+ output = b2RayCastPolygon( &localInput, &shape->polygon );
1034
+ break;
1035
+
1036
+ case b2_chainSegmentShape:
1037
+ output = b2RayCastSegment( &localInput, &shape->chainSegment.segment, true );
1038
+ break;
1039
+
1040
+ default:
1041
+ B2_ASSERT( false );
1042
+ return output;
1043
+ }
1044
+
1045
+ if ( output.hit )
1046
+ {
1047
+ // convert to world coordinates
1048
+ output.normal = b2RotateVector( transform.q, output.normal );
1049
+ output.point = b2TransformPoint( transform, output.point );
1050
+ }
1051
+
1052
+ return output;
1053
+ }
1054
+
1055
+ void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass )
1056
+ {
1057
+ B2_ASSERT( b2IsValidFloat( density ) && density >= 0.0f );
1058
+
1059
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1060
+ if ( world == NULL )
1061
+ {
1062
+ return;
1063
+ }
1064
+
1065
+ b2Shape* shape = b2GetShape( world, shapeId );
1066
+ if ( density == shape->density )
1067
+ {
1068
+ // early return to avoid expensive function
1069
+ return;
1070
+ }
1071
+
1072
+ shape->density = density;
1073
+
1074
+ if ( updateBodyMass == true )
1075
+ {
1076
+ b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
1077
+ b2UpdateBodyMassData( world, body );
1078
+ }
1079
+ }
1080
+
1081
+ float b2Shape_GetDensity( b2ShapeId shapeId )
1082
+ {
1083
+ b2World* world = b2GetWorld( shapeId.world0 );
1084
+ b2Shape* shape = b2GetShape( world, shapeId );
1085
+ return shape->density;
1086
+ }
1087
+
1088
+ void b2Shape_SetFriction( b2ShapeId shapeId, float friction )
1089
+ {
1090
+ B2_ASSERT( b2IsValidFloat( friction ) && friction >= 0.0f );
1091
+
1092
+ b2World* world = b2GetWorld( shapeId.world0 );
1093
+ B2_ASSERT( world->locked == false );
1094
+ if ( world->locked )
1095
+ {
1096
+ return;
1097
+ }
1098
+
1099
+ b2Shape* shape = b2GetShape( world, shapeId );
1100
+ shape->friction = friction;
1101
+ }
1102
+
1103
+ float b2Shape_GetFriction( b2ShapeId shapeId )
1104
+ {
1105
+ b2World* world = b2GetWorld( shapeId.world0 );
1106
+ b2Shape* shape = b2GetShape( world, shapeId );
1107
+ return shape->friction;
1108
+ }
1109
+
1110
+ void b2Shape_SetRestitution( b2ShapeId shapeId, float restitution )
1111
+ {
1112
+ B2_ASSERT( b2IsValidFloat( restitution ) && restitution >= 0.0f );
1113
+
1114
+ b2World* world = b2GetWorld( shapeId.world0 );
1115
+ B2_ASSERT( world->locked == false );
1116
+ if ( world->locked )
1117
+ {
1118
+ return;
1119
+ }
1120
+
1121
+ b2Shape* shape = b2GetShape( world, shapeId );
1122
+ shape->restitution = restitution;
1123
+ }
1124
+
1125
+ float b2Shape_GetRestitution( b2ShapeId shapeId )
1126
+ {
1127
+ b2World* world = b2GetWorld( shapeId.world0 );
1128
+ b2Shape* shape = b2GetShape( world, shapeId );
1129
+ return shape->restitution;
1130
+ }
1131
+
1132
+ void b2Shape_SetMaterial( b2ShapeId shapeId, int material )
1133
+ {
1134
+ b2World* world = b2GetWorld( shapeId.world0 );
1135
+ B2_ASSERT( world->locked == false );
1136
+ if ( world->locked )
1137
+ {
1138
+ return;
1139
+ }
1140
+
1141
+ b2Shape* shape = b2GetShape( world, shapeId );
1142
+ shape->userMaterialId = material;
1143
+ }
1144
+
1145
+ int b2Shape_GetMaterial( b2ShapeId shapeId )
1146
+ {
1147
+ b2World* world = b2GetWorld( shapeId.world0 );
1148
+ b2Shape* shape = b2GetShape( world, shapeId );
1149
+ return shape->userMaterialId;
1150
+ }
1151
+
1152
+ b2Filter b2Shape_GetFilter( b2ShapeId shapeId )
1153
+ {
1154
+ b2World* world = b2GetWorld( shapeId.world0 );
1155
+ b2Shape* shape = b2GetShape( world, shapeId );
1156
+ return shape->filter;
1157
+ }
1158
+
1159
+ static void b2ResetProxy( b2World* world, b2Shape* shape, bool wakeBodies, bool destroyProxy )
1160
+ {
1161
+ b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
1162
+
1163
+ int shapeId = shape->id;
1164
+
1165
+ // destroy all contacts associated with this shape
1166
+ int contactKey = body->headContactKey;
1167
+ while ( contactKey != B2_NULL_INDEX )
1168
+ {
1169
+ int contactId = contactKey >> 1;
1170
+ int edgeIndex = contactKey & 1;
1171
+
1172
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
1173
+ contactKey = contact->edges[edgeIndex].nextKey;
1174
+
1175
+ if ( contact->shapeIdA == shapeId || contact->shapeIdB == shapeId )
1176
+ {
1177
+ b2DestroyContact( world, contact, wakeBodies );
1178
+ }
1179
+ }
1180
+
1181
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
1182
+ if ( shape->proxyKey != B2_NULL_INDEX )
1183
+ {
1184
+ b2BodyType proxyType = B2_PROXY_TYPE( shape->proxyKey );
1185
+ b2UpdateShapeAABBs( shape, transform, proxyType );
1186
+
1187
+ if ( destroyProxy )
1188
+ {
1189
+ b2BroadPhase_DestroyProxy( &world->broadPhase, shape->proxyKey );
1190
+
1191
+ bool forcePairCreation = true;
1192
+ shape->proxyKey = b2BroadPhase_CreateProxy( &world->broadPhase, proxyType, shape->fatAABB, shape->filter.categoryBits,
1193
+ shapeId, forcePairCreation );
1194
+ }
1195
+ else
1196
+ {
1197
+ b2BroadPhase_MoveProxy( &world->broadPhase, shape->proxyKey, shape->fatAABB );
1198
+ }
1199
+ }
1200
+ else
1201
+ {
1202
+ b2BodyType proxyType = body->type;
1203
+ b2UpdateShapeAABBs( shape, transform, proxyType );
1204
+ }
1205
+
1206
+ b2ValidateSolverSets( world );
1207
+ }
1208
+
1209
+ void b2Shape_SetFilter( b2ShapeId shapeId, b2Filter filter )
1210
+ {
1211
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1212
+ if ( world == NULL )
1213
+ {
1214
+ return;
1215
+ }
1216
+
1217
+ b2Shape* shape = b2GetShape( world, shapeId );
1218
+ if ( filter.maskBits == shape->filter.maskBits && filter.categoryBits == shape->filter.categoryBits &&
1219
+ filter.groupIndex == shape->filter.groupIndex )
1220
+ {
1221
+ return;
1222
+ }
1223
+
1224
+ // If the category bits change, I need to destroy the proxy because it affects the tree sorting.
1225
+ bool destroyProxy = filter.categoryBits != shape->filter.categoryBits;
1226
+
1227
+ shape->filter = filter;
1228
+
1229
+ // need to wake bodies because a filter change may destroy contacts
1230
+ bool wakeBodies = true;
1231
+ b2ResetProxy( world, shape, wakeBodies, destroyProxy );
1232
+
1233
+ // note: this does not immediately update sensor overlaps. Instead sensor
1234
+ // overlaps are updated the next time step
1235
+ }
1236
+
1237
+ void b2Shape_EnableSensorEvents( b2ShapeId shapeId, bool flag )
1238
+ {
1239
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1240
+ if ( world == NULL )
1241
+ {
1242
+ return;
1243
+ }
1244
+
1245
+ b2Shape* shape = b2GetShape( world, shapeId );
1246
+ shape->enableSensorEvents = flag;
1247
+ }
1248
+
1249
+ bool b2Shape_AreSensorEventsEnabled( b2ShapeId shapeId )
1250
+ {
1251
+ b2World* world = b2GetWorld( shapeId.world0 );
1252
+ b2Shape* shape = b2GetShape( world, shapeId );
1253
+ return shape->enableSensorEvents;
1254
+ }
1255
+
1256
+ void b2Shape_EnableContactEvents( b2ShapeId shapeId, bool flag )
1257
+ {
1258
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1259
+ if ( world == NULL )
1260
+ {
1261
+ return;
1262
+ }
1263
+
1264
+ b2Shape* shape = b2GetShape( world, shapeId );
1265
+ shape->enableContactEvents = flag;
1266
+ }
1267
+
1268
+ bool b2Shape_AreContactEventsEnabled( b2ShapeId shapeId )
1269
+ {
1270
+ b2World* world = b2GetWorld( shapeId.world0 );
1271
+ b2Shape* shape = b2GetShape( world, shapeId );
1272
+ return shape->enableContactEvents;
1273
+ }
1274
+
1275
+ void b2Shape_EnablePreSolveEvents( b2ShapeId shapeId, bool flag )
1276
+ {
1277
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1278
+ if ( world == NULL )
1279
+ {
1280
+ return;
1281
+ }
1282
+
1283
+ b2Shape* shape = b2GetShape( world, shapeId );
1284
+ shape->enablePreSolveEvents = flag;
1285
+ }
1286
+
1287
+ bool b2Shape_ArePreSolveEventsEnabled( b2ShapeId shapeId )
1288
+ {
1289
+ b2World* world = b2GetWorld( shapeId.world0 );
1290
+ b2Shape* shape = b2GetShape( world, shapeId );
1291
+ return shape->enablePreSolveEvents;
1292
+ }
1293
+
1294
+ void b2Shape_EnableHitEvents( b2ShapeId shapeId, bool flag )
1295
+ {
1296
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1297
+ if ( world == NULL )
1298
+ {
1299
+ return;
1300
+ }
1301
+
1302
+ b2Shape* shape = b2GetShape( world, shapeId );
1303
+ shape->enableHitEvents = flag;
1304
+ }
1305
+
1306
+ bool b2Shape_AreHitEventsEnabled( b2ShapeId shapeId )
1307
+ {
1308
+ b2World* world = b2GetWorld( shapeId.world0 );
1309
+ b2Shape* shape = b2GetShape( world, shapeId );
1310
+ return shape->enableHitEvents;
1311
+ }
1312
+
1313
+ b2ShapeType b2Shape_GetType( b2ShapeId shapeId )
1314
+ {
1315
+ b2World* world = b2GetWorld( shapeId.world0 );
1316
+ b2Shape* shape = b2GetShape( world, shapeId );
1317
+ return shape->type;
1318
+ }
1319
+
1320
+ b2Circle b2Shape_GetCircle( b2ShapeId shapeId )
1321
+ {
1322
+ b2World* world = b2GetWorld( shapeId.world0 );
1323
+ b2Shape* shape = b2GetShape( world, shapeId );
1324
+ B2_ASSERT( shape->type == b2_circleShape );
1325
+ return shape->circle;
1326
+ }
1327
+
1328
+ b2Segment b2Shape_GetSegment( b2ShapeId shapeId )
1329
+ {
1330
+ b2World* world = b2GetWorld( shapeId.world0 );
1331
+ b2Shape* shape = b2GetShape( world, shapeId );
1332
+ B2_ASSERT( shape->type == b2_segmentShape );
1333
+ return shape->segment;
1334
+ }
1335
+
1336
+ b2ChainSegment b2Shape_GetChainSegment( b2ShapeId shapeId )
1337
+ {
1338
+ b2World* world = b2GetWorld( shapeId.world0 );
1339
+ b2Shape* shape = b2GetShape( world, shapeId );
1340
+ B2_ASSERT( shape->type == b2_chainSegmentShape );
1341
+ return shape->chainSegment;
1342
+ }
1343
+
1344
+ b2Capsule b2Shape_GetCapsule( b2ShapeId shapeId )
1345
+ {
1346
+ b2World* world = b2GetWorld( shapeId.world0 );
1347
+ b2Shape* shape = b2GetShape( world, shapeId );
1348
+ B2_ASSERT( shape->type == b2_capsuleShape );
1349
+ return shape->capsule;
1350
+ }
1351
+
1352
+ b2Polygon b2Shape_GetPolygon( b2ShapeId shapeId )
1353
+ {
1354
+ b2World* world = b2GetWorld( shapeId.world0 );
1355
+ b2Shape* shape = b2GetShape( world, shapeId );
1356
+ B2_ASSERT( shape->type == b2_polygonShape );
1357
+ return shape->polygon;
1358
+ }
1359
+
1360
+ void b2Shape_SetCircle( b2ShapeId shapeId, const b2Circle* circle )
1361
+ {
1362
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1363
+ if ( world == NULL )
1364
+ {
1365
+ return;
1366
+ }
1367
+
1368
+ b2Shape* shape = b2GetShape( world, shapeId );
1369
+ shape->circle = *circle;
1370
+ shape->type = b2_circleShape;
1371
+
1372
+ // need to wake bodies so they can react to the shape change
1373
+ bool wakeBodies = true;
1374
+ bool destroyProxy = true;
1375
+ b2ResetProxy( world, shape, wakeBodies, destroyProxy );
1376
+ }
1377
+
1378
+ void b2Shape_SetCapsule( b2ShapeId shapeId, const b2Capsule* capsule )
1379
+ {
1380
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1381
+ if ( world == NULL )
1382
+ {
1383
+ return;
1384
+ }
1385
+
1386
+ b2Shape* shape = b2GetShape( world, shapeId );
1387
+ shape->capsule = *capsule;
1388
+ shape->type = b2_capsuleShape;
1389
+
1390
+ // need to wake bodies so they can react to the shape change
1391
+ bool wakeBodies = true;
1392
+ bool destroyProxy = true;
1393
+ b2ResetProxy( world, shape, wakeBodies, destroyProxy );
1394
+ }
1395
+
1396
+ void b2Shape_SetSegment( b2ShapeId shapeId, const b2Segment* segment )
1397
+ {
1398
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1399
+ if ( world == NULL )
1400
+ {
1401
+ return;
1402
+ }
1403
+
1404
+ b2Shape* shape = b2GetShape( world, shapeId );
1405
+ shape->segment = *segment;
1406
+ shape->type = b2_segmentShape;
1407
+
1408
+ // need to wake bodies so they can react to the shape change
1409
+ bool wakeBodies = true;
1410
+ bool destroyProxy = true;
1411
+ b2ResetProxy( world, shape, wakeBodies, destroyProxy );
1412
+ }
1413
+
1414
+ void b2Shape_SetPolygon( b2ShapeId shapeId, const b2Polygon* polygon )
1415
+ {
1416
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1417
+ if ( world == NULL )
1418
+ {
1419
+ return;
1420
+ }
1421
+
1422
+ b2Shape* shape = b2GetShape( world, shapeId );
1423
+ shape->polygon = *polygon;
1424
+ shape->type = b2_polygonShape;
1425
+
1426
+ // need to wake bodies so they can react to the shape change
1427
+ bool wakeBodies = true;
1428
+ bool destroyProxy = true;
1429
+ b2ResetProxy( world, shape, wakeBodies, destroyProxy );
1430
+ }
1431
+
1432
+ b2ChainId b2Shape_GetParentChain( b2ShapeId shapeId )
1433
+ {
1434
+ b2World* world = b2GetWorld( shapeId.world0 );
1435
+ b2Shape* shape = b2GetShape( world, shapeId );
1436
+ if ( shape->type == b2_chainSegmentShape )
1437
+ {
1438
+ int chainId = shape->chainSegment.chainId;
1439
+ if ( chainId != B2_NULL_INDEX )
1440
+ {
1441
+ b2ChainShape* chain = b2ChainShapeArray_Get( &world->chainShapes, chainId );
1442
+ b2ChainId id = { chainId + 1, shapeId.world0, chain->generation };
1443
+ return id;
1444
+ }
1445
+ }
1446
+
1447
+ return ( b2ChainId ){ 0 };
1448
+ }
1449
+
1450
+ void b2Chain_SetFriction( b2ChainId chainId, float friction )
1451
+ {
1452
+ B2_ASSERT( b2IsValidFloat( friction ) && friction >= 0.0f );
1453
+
1454
+ b2World* world = b2GetWorldLocked( chainId.world0 );
1455
+ if ( world == NULL )
1456
+ {
1457
+ return;
1458
+ }
1459
+
1460
+ b2ChainShape* chainShape = b2GetChainShape( world, chainId );
1461
+
1462
+ int materialCount = chainShape->materialCount;
1463
+ for ( int i = 0; i < materialCount; ++i )
1464
+ {
1465
+ chainShape->materials[i].friction = friction;
1466
+ }
1467
+
1468
+ int count = chainShape->count;
1469
+
1470
+ for ( int i = 0; i < count; ++i )
1471
+ {
1472
+ int shapeId = chainShape->shapeIndices[i];
1473
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1474
+ shape->friction = friction;
1475
+ }
1476
+ }
1477
+
1478
+ float b2Chain_GetFriction( b2ChainId chainId )
1479
+ {
1480
+ b2World* world = b2GetWorld( chainId.world0 );
1481
+ b2ChainShape* chainShape = b2GetChainShape( world, chainId );
1482
+ return chainShape->materials[0].friction;
1483
+ }
1484
+
1485
+ void b2Chain_SetRestitution( b2ChainId chainId, float restitution )
1486
+ {
1487
+ B2_ASSERT( b2IsValidFloat( restitution ) );
1488
+
1489
+ b2World* world = b2GetWorldLocked( chainId.world0 );
1490
+ if ( world == NULL )
1491
+ {
1492
+ return;
1493
+ }
1494
+
1495
+ b2ChainShape* chainShape = b2GetChainShape( world, chainId );
1496
+
1497
+ int materialCount = chainShape->materialCount;
1498
+ for ( int i = 0; i < materialCount; ++i )
1499
+ {
1500
+ chainShape->materials[i].restitution = restitution;
1501
+ }
1502
+
1503
+ int count = chainShape->count;
1504
+
1505
+ for ( int i = 0; i < count; ++i )
1506
+ {
1507
+ int shapeId = chainShape->shapeIndices[i];
1508
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1509
+ shape->restitution = restitution;
1510
+ }
1511
+ }
1512
+
1513
+ float b2Chain_GetRestitution( b2ChainId chainId )
1514
+ {
1515
+ b2World* world = b2GetWorld( chainId.world0 );
1516
+ b2ChainShape* chainShape = b2GetChainShape( world, chainId );
1517
+ return chainShape->materials[0].restitution;
1518
+ }
1519
+
1520
+ void b2Chain_SetMaterial( b2ChainId chainId, int material )
1521
+ {
1522
+ b2World* world = b2GetWorldLocked( chainId.world0 );
1523
+ if ( world == NULL )
1524
+ {
1525
+ return;
1526
+ }
1527
+
1528
+ b2ChainShape* chainShape = b2GetChainShape( world, chainId );
1529
+ int materialCount = chainShape->materialCount;
1530
+ for ( int i = 0; i < materialCount; ++i )
1531
+ {
1532
+ chainShape->materials[i].userMaterialId = material;
1533
+ }
1534
+
1535
+ int count = chainShape->count;
1536
+
1537
+ for ( int i = 0; i < count; ++i )
1538
+ {
1539
+ int shapeId = chainShape->shapeIndices[i];
1540
+ b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
1541
+ shape->userMaterialId = material;
1542
+ }
1543
+ }
1544
+
1545
+ int b2Chain_GetMaterial( b2ChainId chainId )
1546
+ {
1547
+ b2World* world = b2GetWorld( chainId.world0 );
1548
+ b2ChainShape* chainShape = b2GetChainShape( world, chainId );
1549
+ return chainShape->materials[0].userMaterialId;
1550
+ }
1551
+
1552
+ int b2Shape_GetContactCapacity( b2ShapeId shapeId )
1553
+ {
1554
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1555
+ if ( world == NULL )
1556
+ {
1557
+ return 0;
1558
+ }
1559
+
1560
+ b2Shape* shape = b2GetShape( world, shapeId );
1561
+ if ( shape->sensorIndex != B2_NULL_INDEX )
1562
+ {
1563
+ return 0;
1564
+ }
1565
+
1566
+ b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
1567
+
1568
+ // Conservative and fast
1569
+ return body->contactCount;
1570
+ }
1571
+
1572
+ int b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int capacity )
1573
+ {
1574
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1575
+ if ( world == NULL )
1576
+ {
1577
+ return 0;
1578
+ }
1579
+
1580
+ b2Shape* shape = b2GetShape( world, shapeId );
1581
+ if ( shape->sensorIndex != B2_NULL_INDEX )
1582
+ {
1583
+ return 0;
1584
+ }
1585
+
1586
+ b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
1587
+ int contactKey = body->headContactKey;
1588
+ int index = 0;
1589
+ while ( contactKey != B2_NULL_INDEX && index < capacity )
1590
+ {
1591
+ int contactId = contactKey >> 1;
1592
+ int edgeIndex = contactKey & 1;
1593
+
1594
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
1595
+
1596
+ // Does contact involve this shape and is it touching?
1597
+ if ( ( contact->shapeIdA == shapeId.index1 - 1 || contact->shapeIdB == shapeId.index1 - 1 ) &&
1598
+ ( contact->flags & b2_contactTouchingFlag ) != 0 )
1599
+ {
1600
+ b2Shape* shapeA = world->shapes.data + contact->shapeIdA;
1601
+ b2Shape* shapeB = world->shapes.data + contact->shapeIdB;
1602
+
1603
+ contactData[index].shapeIdA = ( b2ShapeId ){ shapeA->id + 1, shapeId.world0, shapeA->generation };
1604
+ contactData[index].shapeIdB = ( b2ShapeId ){ shapeB->id + 1, shapeId.world0, shapeB->generation };
1605
+
1606
+ b2ContactSim* contactSim = b2GetContactSim( world, contact );
1607
+ contactData[index].manifold = contactSim->manifold;
1608
+ index += 1;
1609
+ }
1610
+
1611
+ contactKey = contact->edges[edgeIndex].nextKey;
1612
+ }
1613
+
1614
+ B2_ASSERT( index <= capacity );
1615
+
1616
+ return index;
1617
+ }
1618
+
1619
+ int b2Shape_GetSensorCapacity( b2ShapeId shapeId )
1620
+ {
1621
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1622
+ if ( world == NULL )
1623
+ {
1624
+ return 0;
1625
+ }
1626
+
1627
+ b2Shape* shape = b2GetShape( world, shapeId );
1628
+ if ( shape->sensorIndex == B2_NULL_INDEX )
1629
+ {
1630
+ return 0;
1631
+ }
1632
+
1633
+ b2Sensor* sensor = b2SensorArray_Get( &world->sensors, shape->sensorIndex );
1634
+ return sensor->overlaps2.count;
1635
+ }
1636
+
1637
+ int b2Shape_GetSensorOverlaps( b2ShapeId shapeId, b2ShapeId* overlaps, int capacity )
1638
+ {
1639
+ b2World* world = b2GetWorldLocked( shapeId.world0 );
1640
+ if ( world == NULL )
1641
+ {
1642
+ return 0;
1643
+ }
1644
+
1645
+ b2Shape* shape = b2GetShape( world, shapeId );
1646
+ if ( shape->sensorIndex == B2_NULL_INDEX )
1647
+ {
1648
+ return 0;
1649
+ }
1650
+
1651
+ b2Sensor* sensor = b2SensorArray_Get( &world->sensors, shape->sensorIndex );
1652
+
1653
+ int count = b2MinInt( sensor->overlaps2.count, capacity );
1654
+ b2ShapeRef* refs = sensor->overlaps2.data;
1655
+ for ( int i = 0; i < count; ++i )
1656
+ {
1657
+ overlaps[i] = ( b2ShapeId ){
1658
+ .index1 = refs[i].shapeId + 1,
1659
+ .generation = refs[i].generation,
1660
+ .world0 = shapeId.world0,
1661
+ };
1662
+ }
1663
+
1664
+ return count;
1665
+ }
1666
+
1667
+ b2AABB b2Shape_GetAABB( b2ShapeId shapeId )
1668
+ {
1669
+ b2World* world = b2GetWorld( shapeId.world0 );
1670
+ if ( world == NULL )
1671
+ {
1672
+ return ( b2AABB ){ 0 };
1673
+ }
1674
+
1675
+ b2Shape* shape = b2GetShape( world, shapeId );
1676
+ return shape->aabb;
1677
+ }
1678
+
1679
+ b2MassData b2Shape_GetMassData( b2ShapeId shapeId )
1680
+ {
1681
+ b2World* world = b2GetWorld( shapeId.world0 );
1682
+ if ( world == NULL )
1683
+ {
1684
+ return ( b2MassData ){ 0 };
1685
+ }
1686
+
1687
+ b2Shape* shape = b2GetShape( world, shapeId );
1688
+ return b2ComputeShapeMass( shape );
1689
+ }
1690
+
1691
+ b2Vec2 b2Shape_GetClosestPoint( b2ShapeId shapeId, b2Vec2 target )
1692
+ {
1693
+ b2World* world = b2GetWorld( shapeId.world0 );
1694
+ if ( world == NULL )
1695
+ {
1696
+ return ( b2Vec2 ){ 0 };
1697
+ }
1698
+
1699
+ b2Shape* shape = b2GetShape( world, shapeId );
1700
+ b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
1701
+ b2Transform transform = b2GetBodyTransformQuick( world, body );
1702
+
1703
+ b2DistanceInput input;
1704
+ input.proxyA = b2MakeShapeDistanceProxy( shape );
1705
+ input.proxyB = b2MakeProxy( &target, 1, 0.0f );
1706
+ input.transformA = transform;
1707
+ input.transformB = b2Transform_identity;
1708
+ input.useRadii = true;
1709
+
1710
+ b2SimplexCache cache = { 0 };
1711
+ b2DistanceOutput output = b2ShapeDistance(&input, &cache, NULL, 0 );
1712
+
1713
+ return output.pointA;
1714
+ }