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,650 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #include "contact.h"
5
+
6
+ #include "array.h"
7
+ #include "body.h"
8
+ #include "core.h"
9
+ #include "island.h"
10
+ #include "shape.h"
11
+ #include "solver_set.h"
12
+ #include "table.h"
13
+ #include "world.h"
14
+
15
+ #include "box2d/collision.h"
16
+
17
+ #include <float.h>
18
+ #include <math.h>
19
+ #include <stddef.h>
20
+
21
+ B2_ARRAY_SOURCE( b2Contact, b2Contact )
22
+ B2_ARRAY_SOURCE( b2ContactSim, b2ContactSim )
23
+
24
+ // Contacts and determinism
25
+ // A deterministic simulation requires contacts to exist in the same order in b2Island no matter the thread count.
26
+ // The order must reproduce from run to run. This is necessary because the Gauss-Seidel constraint solver is order dependent.
27
+ //
28
+ // Creation:
29
+ // - Contacts are created using results from b2UpdateBroadPhasePairs
30
+ // - These results are ordered according to the order of the broad-phase move array
31
+ // - The move array is ordered according to the shape creation order using a bitset.
32
+ // - The island/shape/body order is determined by creation order
33
+ // - Logically contacts are only created for awake bodies, so they are immediately added to the awake contact array (serially)
34
+ //
35
+ // Island linking:
36
+ // - The awake contact array is built from the body-contact graph for all awake bodies in awake islands.
37
+ // - Awake contacts are solved in parallel and they generate contact state changes.
38
+ // - These state changes may link islands together using union find.
39
+ // - The state changes are ordered using a bit array that encompasses all contacts
40
+ // - As long as contacts are created in deterministic order, island link order is deterministic.
41
+ // - This keeps the order of contacts in islands deterministic
42
+
43
+ // Manifold functions should compute important results in local space to improve precision. However, this
44
+ // interface function takes two world transforms instead of a relative transform for these reasons:
45
+ //
46
+ // First:
47
+ // The anchors need to be computed relative to the shape origin in world space. This is necessary so the
48
+ // solver does not need to access static body transforms. Not even in constraint preparation. This approach
49
+ // has world space vectors yet retains precision.
50
+ //
51
+ // Second:
52
+ // b2ManifoldPoint::point is very useful for debugging and it is in world space.
53
+ //
54
+ // Third:
55
+ // The user may call the manifold functions directly and they should be easy to use and have easy to use
56
+ // results.
57
+ typedef b2Manifold b2ManifoldFcn( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
58
+ b2SimplexCache* cache );
59
+
60
+ struct b2ContactRegister
61
+ {
62
+ b2ManifoldFcn* fcn;
63
+ bool primary;
64
+ };
65
+
66
+ static struct b2ContactRegister s_registers[b2_shapeTypeCount][b2_shapeTypeCount];
67
+ static bool s_initialized = false;
68
+
69
+ static b2Manifold b2CircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
70
+ b2SimplexCache* cache )
71
+ {
72
+ B2_UNUSED( cache );
73
+ return b2CollideCircles( &shapeA->circle, xfA, &shapeB->circle, xfB );
74
+ }
75
+
76
+ static b2Manifold b2CapsuleAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
77
+ b2SimplexCache* cache )
78
+ {
79
+ B2_UNUSED( cache );
80
+ return b2CollideCapsuleAndCircle( &shapeA->capsule, xfA, &shapeB->circle, xfB );
81
+ }
82
+
83
+ static b2Manifold b2CapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
84
+ b2SimplexCache* cache )
85
+ {
86
+ B2_UNUSED( cache );
87
+ return b2CollideCapsules( &shapeA->capsule, xfA, &shapeB->capsule, xfB );
88
+ }
89
+
90
+ static b2Manifold b2PolygonAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
91
+ b2SimplexCache* cache )
92
+ {
93
+ B2_UNUSED( cache );
94
+ return b2CollidePolygonAndCircle( &shapeA->polygon, xfA, &shapeB->circle, xfB );
95
+ }
96
+
97
+ static b2Manifold b2PolygonAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
98
+ b2SimplexCache* cache )
99
+ {
100
+ B2_UNUSED( cache );
101
+ return b2CollidePolygonAndCapsule( &shapeA->polygon, xfA, &shapeB->capsule, xfB );
102
+ }
103
+
104
+ static b2Manifold b2PolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
105
+ b2SimplexCache* cache )
106
+ {
107
+ B2_UNUSED( cache );
108
+ return b2CollidePolygons( &shapeA->polygon, xfA, &shapeB->polygon, xfB );
109
+ }
110
+
111
+ static b2Manifold b2SegmentAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
112
+ b2SimplexCache* cache )
113
+ {
114
+ B2_UNUSED( cache );
115
+ return b2CollideSegmentAndCircle( &shapeA->segment, xfA, &shapeB->circle, xfB );
116
+ }
117
+
118
+ static b2Manifold b2SegmentAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
119
+ b2SimplexCache* cache )
120
+ {
121
+ B2_UNUSED( cache );
122
+ return b2CollideSegmentAndCapsule( &shapeA->segment, xfA, &shapeB->capsule, xfB );
123
+ }
124
+
125
+ static b2Manifold b2SegmentAndPolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
126
+ b2SimplexCache* cache )
127
+ {
128
+ B2_UNUSED( cache );
129
+ return b2CollideSegmentAndPolygon( &shapeA->segment, xfA, &shapeB->polygon, xfB );
130
+ }
131
+
132
+ static b2Manifold b2ChainSegmentAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB,
133
+ b2SimplexCache* cache )
134
+ {
135
+ B2_UNUSED( cache );
136
+ return b2CollideChainSegmentAndCircle( &shapeA->chainSegment, xfA, &shapeB->circle, xfB );
137
+ }
138
+
139
+ static b2Manifold b2ChainSegmentAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB,
140
+ b2Transform xfB, b2SimplexCache* cache )
141
+ {
142
+ return b2CollideChainSegmentAndCapsule( &shapeA->chainSegment, xfA, &shapeB->capsule, xfB, cache );
143
+ }
144
+
145
+ static b2Manifold b2ChainSegmentAndPolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB,
146
+ b2Transform xfB, b2SimplexCache* cache )
147
+ {
148
+ return b2CollideChainSegmentAndPolygon( &shapeA->chainSegment, xfA, &shapeB->polygon, xfB, cache );
149
+ }
150
+
151
+ static void b2AddType( b2ManifoldFcn* fcn, b2ShapeType type1, b2ShapeType type2 )
152
+ {
153
+ B2_ASSERT( 0 <= type1 && type1 < b2_shapeTypeCount );
154
+ B2_ASSERT( 0 <= type2 && type2 < b2_shapeTypeCount );
155
+
156
+ s_registers[type1][type2].fcn = fcn;
157
+ s_registers[type1][type2].primary = true;
158
+
159
+ if ( type1 != type2 )
160
+ {
161
+ s_registers[type2][type1].fcn = fcn;
162
+ s_registers[type2][type1].primary = false;
163
+ }
164
+ }
165
+
166
+ void b2InitializeContactRegisters( void )
167
+ {
168
+ if ( s_initialized == false )
169
+ {
170
+ b2AddType( b2CircleManifold, b2_circleShape, b2_circleShape );
171
+ b2AddType( b2CapsuleAndCircleManifold, b2_capsuleShape, b2_circleShape );
172
+ b2AddType( b2CapsuleManifold, b2_capsuleShape, b2_capsuleShape );
173
+ b2AddType( b2PolygonAndCircleManifold, b2_polygonShape, b2_circleShape );
174
+ b2AddType( b2PolygonAndCapsuleManifold, b2_polygonShape, b2_capsuleShape );
175
+ b2AddType( b2PolygonManifold, b2_polygonShape, b2_polygonShape );
176
+ b2AddType( b2SegmentAndCircleManifold, b2_segmentShape, b2_circleShape );
177
+ b2AddType( b2SegmentAndCapsuleManifold, b2_segmentShape, b2_capsuleShape );
178
+ b2AddType( b2SegmentAndPolygonManifold, b2_segmentShape, b2_polygonShape );
179
+ b2AddType( b2ChainSegmentAndCircleManifold, b2_chainSegmentShape, b2_circleShape );
180
+ b2AddType( b2ChainSegmentAndCapsuleManifold, b2_chainSegmentShape, b2_capsuleShape );
181
+ b2AddType( b2ChainSegmentAndPolygonManifold, b2_chainSegmentShape, b2_polygonShape );
182
+ s_initialized = true;
183
+ }
184
+ }
185
+
186
+ void b2CreateContact( b2World* world, b2Shape* shapeA, b2Shape* shapeB )
187
+ {
188
+ b2ShapeType type1 = shapeA->type;
189
+ b2ShapeType type2 = shapeB->type;
190
+
191
+ B2_ASSERT( 0 <= type1 && type1 < b2_shapeTypeCount );
192
+ B2_ASSERT( 0 <= type2 && type2 < b2_shapeTypeCount );
193
+
194
+ if ( s_registers[type1][type2].fcn == NULL )
195
+ {
196
+ // For example, no segment vs segment collision
197
+ return;
198
+ }
199
+
200
+ if ( s_registers[type1][type2].primary == false )
201
+ {
202
+ // flip order
203
+ b2CreateContact( world, shapeB, shapeA );
204
+ return;
205
+ }
206
+
207
+ b2Body* bodyA = b2BodyArray_Get( &world->bodies, shapeA->bodyId );
208
+ b2Body* bodyB = b2BodyArray_Get( &world->bodies, shapeB->bodyId );
209
+
210
+ B2_ASSERT( bodyA->setIndex != b2_disabledSet && bodyB->setIndex != b2_disabledSet );
211
+ B2_ASSERT( bodyA->setIndex != b2_staticSet || bodyB->setIndex != b2_staticSet );
212
+
213
+ int setIndex;
214
+ if ( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet )
215
+ {
216
+ setIndex = b2_awakeSet;
217
+ }
218
+ else
219
+ {
220
+ // sleeping and non-touching contacts live in the disabled set
221
+ // later if this set is found to be touching then the sleeping
222
+ // islands will be linked and the contact moved to the merged island
223
+ setIndex = b2_disabledSet;
224
+ }
225
+
226
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );
227
+
228
+ // Create contact key and contact
229
+ int contactId = b2AllocId( &world->contactIdPool );
230
+ if ( contactId == world->contacts.count )
231
+ {
232
+ b2ContactArray_Push( &world->contacts, ( b2Contact ){ 0 } );
233
+ }
234
+
235
+ int shapeIdA = shapeA->id;
236
+ int shapeIdB = shapeB->id;
237
+
238
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
239
+ contact->contactId = contactId;
240
+ contact->setIndex = setIndex;
241
+ contact->colorIndex = B2_NULL_INDEX;
242
+ contact->localIndex = set->contactSims.count;
243
+ contact->islandId = B2_NULL_INDEX;
244
+ contact->islandPrev = B2_NULL_INDEX;
245
+ contact->islandNext = B2_NULL_INDEX;
246
+ contact->shapeIdA = shapeIdA;
247
+ contact->shapeIdB = shapeIdB;
248
+ contact->isMarked = false;
249
+ contact->flags = 0;
250
+
251
+ B2_ASSERT( shapeA->sensorIndex == B2_NULL_INDEX && shapeB->sensorIndex == B2_NULL_INDEX );
252
+
253
+ if ( shapeA->enableContactEvents || shapeB->enableContactEvents )
254
+ {
255
+ contact->flags |= b2_contactEnableContactEvents;
256
+ }
257
+
258
+ // Connect to body A
259
+ {
260
+ contact->edges[0].bodyId = shapeA->bodyId;
261
+ contact->edges[0].prevKey = B2_NULL_INDEX;
262
+ contact->edges[0].nextKey = bodyA->headContactKey;
263
+
264
+ int keyA = ( contactId << 1 ) | 0;
265
+ int headContactKey = bodyA->headContactKey;
266
+ if ( headContactKey != B2_NULL_INDEX )
267
+ {
268
+ b2Contact* headContact = b2ContactArray_Get( &world->contacts, headContactKey >> 1 );
269
+ headContact->edges[headContactKey & 1].prevKey = keyA;
270
+ }
271
+ bodyA->headContactKey = keyA;
272
+ bodyA->contactCount += 1;
273
+ }
274
+
275
+ // Connect to body B
276
+ {
277
+ contact->edges[1].bodyId = shapeB->bodyId;
278
+ contact->edges[1].prevKey = B2_NULL_INDEX;
279
+ contact->edges[1].nextKey = bodyB->headContactKey;
280
+
281
+ int keyB = ( contactId << 1 ) | 1;
282
+ int headContactKey = bodyB->headContactKey;
283
+ if ( bodyB->headContactKey != B2_NULL_INDEX )
284
+ {
285
+ b2Contact* headContact = b2ContactArray_Get( &world->contacts, headContactKey >> 1 );
286
+ headContact->edges[headContactKey & 1].prevKey = keyB;
287
+ }
288
+ bodyB->headContactKey = keyB;
289
+ bodyB->contactCount += 1;
290
+ }
291
+
292
+ // Add to pair set for fast lookup
293
+ uint64_t pairKey = B2_SHAPE_PAIR_KEY( shapeIdA, shapeIdB );
294
+ b2AddKey( &world->broadPhase.pairSet, pairKey );
295
+
296
+ // Contacts are created as non-touching. Later if they are found to be touching
297
+ // they will link islands and be moved into the constraint graph.
298
+ b2ContactSim* contactSim = b2ContactSimArray_Add( &set->contactSims );
299
+ contactSim->contactId = contactId;
300
+
301
+ #if B2_VALIDATE
302
+ contactSim->bodyIdA = shapeA->bodyId;
303
+ contactSim->bodyIdB = shapeB->bodyId;
304
+ #endif
305
+
306
+ contactSim->bodySimIndexA = B2_NULL_INDEX;
307
+ contactSim->bodySimIndexB = B2_NULL_INDEX;
308
+ contactSim->invMassA = 0.0f;
309
+ contactSim->invIA = 0.0f;
310
+ contactSim->invMassB = 0.0f;
311
+ contactSim->invIB = 0.0f;
312
+ contactSim->shapeIdA = shapeIdA;
313
+ contactSim->shapeIdB = shapeIdB;
314
+ contactSim->cache = b2_emptySimplexCache;
315
+ contactSim->manifold = ( b2Manifold ){ 0 };
316
+
317
+ // These also get updated in the narrow phase
318
+ contactSim->friction = world->frictionCallback(shapeA->friction, shapeA->userMaterialId, shapeB->friction, shapeB->userMaterialId);
319
+ contactSim->restitution = world->restitutionCallback(shapeA->restitution, shapeA->userMaterialId, shapeB->restitution, shapeB->userMaterialId);
320
+
321
+ contactSim->tangentSpeed = 0.0f;
322
+ contactSim->simFlags = 0;
323
+
324
+ if ( shapeA->enablePreSolveEvents || shapeB->enablePreSolveEvents )
325
+ {
326
+ contactSim->simFlags |= b2_simEnablePreSolveEvents;
327
+ }
328
+ }
329
+
330
+ // A contact is destroyed when:
331
+ // - broad-phase proxies stop overlapping
332
+ // - a body is destroyed
333
+ // - a body is disabled
334
+ // - a body changes type from dynamic to kinematic or static
335
+ // - a shape is destroyed
336
+ // - contact filtering is modified
337
+ void b2DestroyContact( b2World* world, b2Contact* contact, bool wakeBodies )
338
+ {
339
+ // Remove pair from set
340
+ uint64_t pairKey = B2_SHAPE_PAIR_KEY( contact->shapeIdA, contact->shapeIdB );
341
+ b2RemoveKey( &world->broadPhase.pairSet, pairKey );
342
+
343
+ b2ContactEdge* edgeA = contact->edges + 0;
344
+ b2ContactEdge* edgeB = contact->edges + 1;
345
+
346
+ int bodyIdA = edgeA->bodyId;
347
+ int bodyIdB = edgeB->bodyId;
348
+ b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );
349
+ b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );
350
+
351
+ uint32_t flags = contact->flags;
352
+ bool touching = ( flags & b2_contactTouchingFlag ) != 0;
353
+
354
+ // End touch event
355
+ if ( touching && ( flags & b2_contactEnableContactEvents ) != 0 )
356
+ {
357
+ uint16_t worldId = world->worldId;
358
+ const b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, contact->shapeIdA );
359
+ const b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, contact->shapeIdB );
360
+ b2ShapeId shapeIdA = { shapeA->id + 1, worldId, shapeA->generation };
361
+ b2ShapeId shapeIdB = { shapeB->id + 1, worldId, shapeB->generation };
362
+
363
+ b2ContactEndTouchEvent event = { shapeIdA, shapeIdB };
364
+ b2ContactEndTouchEventArray_Push( world->contactEndEvents + world->endEventArrayIndex, event );
365
+ }
366
+
367
+ // Remove from body A
368
+ if ( edgeA->prevKey != B2_NULL_INDEX )
369
+ {
370
+ b2Contact* prevContact = b2ContactArray_Get( &world->contacts, edgeA->prevKey >> 1 );
371
+ b2ContactEdge* prevEdge = prevContact->edges + ( edgeA->prevKey & 1 );
372
+ prevEdge->nextKey = edgeA->nextKey;
373
+ }
374
+
375
+ if ( edgeA->nextKey != B2_NULL_INDEX )
376
+ {
377
+ b2Contact* nextContact = b2ContactArray_Get( &world->contacts, edgeA->nextKey >> 1 );
378
+ b2ContactEdge* nextEdge = nextContact->edges + ( edgeA->nextKey & 1 );
379
+ nextEdge->prevKey = edgeA->prevKey;
380
+ }
381
+
382
+ int contactId = contact->contactId;
383
+
384
+ int edgeKeyA = ( contactId << 1 ) | 0;
385
+ if ( bodyA->headContactKey == edgeKeyA )
386
+ {
387
+ bodyA->headContactKey = edgeA->nextKey;
388
+ }
389
+
390
+ bodyA->contactCount -= 1;
391
+
392
+ // Remove from body B
393
+ if ( edgeB->prevKey != B2_NULL_INDEX )
394
+ {
395
+ b2Contact* prevContact = b2ContactArray_Get( &world->contacts, edgeB->prevKey >> 1 );
396
+ b2ContactEdge* prevEdge = prevContact->edges + ( edgeB->prevKey & 1 );
397
+ prevEdge->nextKey = edgeB->nextKey;
398
+ }
399
+
400
+ if ( edgeB->nextKey != B2_NULL_INDEX )
401
+ {
402
+ b2Contact* nextContact = b2ContactArray_Get( &world->contacts, edgeB->nextKey >> 1 );
403
+ b2ContactEdge* nextEdge = nextContact->edges + ( edgeB->nextKey & 1 );
404
+ nextEdge->prevKey = edgeB->prevKey;
405
+ }
406
+
407
+ int edgeKeyB = ( contactId << 1 ) | 1;
408
+ if ( bodyB->headContactKey == edgeKeyB )
409
+ {
410
+ bodyB->headContactKey = edgeB->nextKey;
411
+ }
412
+
413
+ bodyB->contactCount -= 1;
414
+
415
+ // Remove contact from the array that owns it
416
+ if ( contact->islandId != B2_NULL_INDEX )
417
+ {
418
+ b2UnlinkContact( world, contact );
419
+ }
420
+
421
+ if ( contact->colorIndex != B2_NULL_INDEX )
422
+ {
423
+ // contact is an active constraint
424
+ B2_ASSERT( contact->setIndex == b2_awakeSet );
425
+ b2RemoveContactFromGraph( world, bodyIdA, bodyIdB, contact->colorIndex, contact->localIndex );
426
+ }
427
+ else
428
+ {
429
+ // contact is non-touching or is sleeping or is a sensor
430
+ B2_ASSERT( contact->setIndex != b2_awakeSet || ( contact->flags & b2_contactTouchingFlag ) == 0 );
431
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, contact->setIndex );
432
+ int movedIndex = b2ContactSimArray_RemoveSwap( &set->contactSims, contact->localIndex );
433
+ if ( movedIndex != B2_NULL_INDEX )
434
+ {
435
+ b2ContactSim* movedContactSim = set->contactSims.data + contact->localIndex;
436
+ b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId );
437
+ movedContact->localIndex = contact->localIndex;
438
+ }
439
+ }
440
+
441
+ contact->contactId = B2_NULL_INDEX;
442
+ contact->setIndex = B2_NULL_INDEX;
443
+ contact->colorIndex = B2_NULL_INDEX;
444
+ contact->localIndex = B2_NULL_INDEX;
445
+
446
+ b2FreeId( &world->contactIdPool, contactId );
447
+
448
+ if ( wakeBodies && touching )
449
+ {
450
+ b2WakeBody( world, bodyA );
451
+ b2WakeBody( world, bodyB );
452
+ }
453
+ }
454
+
455
+ b2ContactSim* b2GetContactSim( b2World* world, b2Contact* contact )
456
+ {
457
+ if ( contact->setIndex == b2_awakeSet && contact->colorIndex != B2_NULL_INDEX )
458
+ {
459
+ // contact lives in constraint graph
460
+ B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < B2_GRAPH_COLOR_COUNT );
461
+ b2GraphColor* color = world->constraintGraph.colors + contact->colorIndex;
462
+ return b2ContactSimArray_Get( &color->contactSims, contact->localIndex );
463
+ }
464
+
465
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, contact->setIndex );
466
+ return b2ContactSimArray_Get( &set->contactSims, contact->localIndex );
467
+ }
468
+
469
+ bool b2ShouldShapesCollide( b2Filter filterA, b2Filter filterB )
470
+ {
471
+ if ( filterA.groupIndex == filterB.groupIndex && filterA.groupIndex != 0 )
472
+ {
473
+ return filterA.groupIndex > 0;
474
+ }
475
+
476
+ bool collide = ( filterA.maskBits & filterB.categoryBits ) != 0 && ( filterA.categoryBits & filterB.maskBits ) != 0;
477
+ return collide;
478
+ }
479
+
480
+ // Update the contact manifold and touching status. Also updates sensor overlap.
481
+ // Note: do not assume the shape AABBs are overlapping or are valid.
482
+ bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, b2Transform transformA, b2Vec2 centerOffsetA,
483
+ b2Shape* shapeB, b2Transform transformB, b2Vec2 centerOffsetB )
484
+ {
485
+ // Save old manifold
486
+ b2Manifold oldManifold = contactSim->manifold;
487
+
488
+ // Compute new manifold
489
+ b2ManifoldFcn* fcn = s_registers[shapeA->type][shapeB->type].fcn;
490
+ contactSim->manifold = fcn( shapeA, transformA, shapeB, transformB, &contactSim->cache );
491
+
492
+ // Keep these updated in case the values on the shapes are modified
493
+ contactSim->friction = world->frictionCallback( shapeA->friction, shapeA->userMaterialId, shapeB->friction, shapeB->userMaterialId );
494
+ contactSim->restitution = world->restitutionCallback( shapeA->restitution, shapeA->userMaterialId, shapeB->restitution, shapeB->userMaterialId );
495
+
496
+ // todo branch improves perf?
497
+ if (shapeA->rollingResistance > 0.0f || shapeB->rollingResistance > 0.0f)
498
+ {
499
+ float radiusA = b2GetShapeRadius( shapeA );
500
+ float radiusB = b2GetShapeRadius( shapeB );
501
+ float maxRadius = b2MaxFloat( radiusA, radiusB );
502
+ contactSim->rollingResistance = b2MaxFloat( shapeA->rollingResistance, shapeB->rollingResistance ) * maxRadius;
503
+ }
504
+ else
505
+ {
506
+ contactSim->rollingResistance = 0.0f;
507
+ }
508
+
509
+ contactSim->tangentSpeed = shapeA->tangentSpeed + shapeB->tangentSpeed;
510
+
511
+ int pointCount = contactSim->manifold.pointCount;
512
+ bool touching = pointCount > 0;
513
+
514
+ if ( touching && world->preSolveFcn && ( contactSim->simFlags & b2_simEnablePreSolveEvents ) != 0 )
515
+ {
516
+ b2ShapeId shapeIdA = { shapeA->id + 1, world->worldId, shapeA->generation };
517
+ b2ShapeId shapeIdB = { shapeB->id + 1, world->worldId, shapeB->generation };
518
+
519
+ // this call assumes thread safety
520
+ touching = world->preSolveFcn( shapeIdA, shapeIdB, &contactSim->manifold, world->preSolveContext );
521
+ if ( touching == false )
522
+ {
523
+ // disable contact
524
+ pointCount = 0;
525
+ contactSim->manifold.pointCount = 0;
526
+ }
527
+ }
528
+
529
+ // This flag is for testing
530
+ if ( world->enableSpeculative == false && pointCount == 2 )
531
+ {
532
+ if ( contactSim->manifold.points[0].separation > 1.5f * B2_LINEAR_SLOP )
533
+ {
534
+ contactSim->manifold.points[0] = contactSim->manifold.points[1];
535
+ contactSim->manifold.pointCount = 1;
536
+ }
537
+ else if ( contactSim->manifold.points[0].separation > 1.5f * B2_LINEAR_SLOP )
538
+ {
539
+ contactSim->manifold.pointCount = 1;
540
+ }
541
+
542
+ pointCount = contactSim->manifold.pointCount;
543
+ }
544
+
545
+ if ( touching && ( shapeA->enableHitEvents || shapeB->enableHitEvents ) )
546
+ {
547
+ contactSim->simFlags |= b2_simEnableHitEvent;
548
+ }
549
+ else
550
+ {
551
+ contactSim->simFlags &= ~b2_simEnableHitEvent;
552
+ }
553
+
554
+ if (pointCount > 0)
555
+ {
556
+ contactSim->manifold.rollingImpulse = oldManifold.rollingImpulse;
557
+ }
558
+
559
+ // Match old contact ids to new contact ids and copy the
560
+ // stored impulses to warm start the solver.
561
+ int unmatchedCount = 0;
562
+ for ( int i = 0; i < pointCount; ++i )
563
+ {
564
+ b2ManifoldPoint* mp2 = contactSim->manifold.points + i;
565
+
566
+ // shift anchors to be center of mass relative
567
+ mp2->anchorA = b2Sub( mp2->anchorA, centerOffsetA );
568
+ mp2->anchorB = b2Sub( mp2->anchorB, centerOffsetB );
569
+
570
+ mp2->normalImpulse = 0.0f;
571
+ mp2->tangentImpulse = 0.0f;
572
+ mp2->totalNormalImpulse = 0.0f;
573
+ mp2->normalVelocity = 0.0f;
574
+ mp2->persisted = false;
575
+
576
+ uint16_t id2 = mp2->id;
577
+
578
+ for ( int j = 0; j < oldManifold.pointCount; ++j )
579
+ {
580
+ b2ManifoldPoint* mp1 = oldManifold.points + j;
581
+
582
+ if ( mp1->id == id2 )
583
+ {
584
+ mp2->normalImpulse = mp1->normalImpulse;
585
+ mp2->tangentImpulse = mp1->tangentImpulse;
586
+ mp2->persisted = true;
587
+
588
+ // clear old impulse
589
+ mp1->normalImpulse = 0.0f;
590
+ mp1->tangentImpulse = 0.0f;
591
+ break;
592
+ }
593
+ }
594
+
595
+ unmatchedCount += mp2->persisted ? 0 : 1;
596
+ }
597
+
598
+ B2_UNUSED( unmatchedCount );
599
+
600
+ #if 0
601
+ // todo I haven't found an improvement from this yet
602
+ // If there are unmatched new contact points, apply any left over old impulse.
603
+ if (unmatchedCount > 0)
604
+ {
605
+ float unmatchedNormalImpulse = 0.0f;
606
+ float unmatchedTangentImpulse = 0.0f;
607
+ for (int i = 0; i < oldManifold.pointCount; ++i)
608
+ {
609
+ b2ManifoldPoint* mp = oldManifold.points + i;
610
+ unmatchedNormalImpulse += mp->normalImpulse;
611
+ unmatchedTangentImpulse += mp->tangentImpulse;
612
+ }
613
+
614
+ float inverse = 1.0f / unmatchedCount;
615
+ unmatchedNormalImpulse *= inverse;
616
+ unmatchedTangentImpulse *= inverse;
617
+
618
+ for ( int i = 0; i < pointCount; ++i )
619
+ {
620
+ b2ManifoldPoint* mp2 = contactSim->manifold.points + i;
621
+
622
+ if (mp2->persisted)
623
+ {
624
+ continue;
625
+ }
626
+
627
+ mp2->normalImpulse = unmatchedNormalImpulse;
628
+ mp2->tangentImpulse = unmatchedTangentImpulse;
629
+ }
630
+ }
631
+ #endif
632
+
633
+ if ( touching )
634
+ {
635
+ contactSim->simFlags |= b2_simTouchingFlag;
636
+ }
637
+ else
638
+ {
639
+ contactSim->simFlags &= ~b2_simTouchingFlag;
640
+ }
641
+
642
+ return touching;
643
+ }
644
+
645
+ b2Manifold b2ComputeManifold( b2Shape* shapeA, b2Transform transformA, b2Shape* shapeB, b2Transform transformB )
646
+ {
647
+ b2ManifoldFcn* fcn = s_registers[shapeA->type][shapeB->type].fcn;
648
+ b2SimplexCache cache = { 0 };
649
+ return fcn( shapeA, transformA, shapeB, transformB, &cache );
650
+ }