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,524 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )
5
+ #define _CRT_SECURE_NO_WARNINGS
6
+ #endif
7
+
8
+ #include "broad_phase.h"
9
+
10
+ #include "aabb.h"
11
+ #include "array.h"
12
+ #include "atomic.h"
13
+ #include "body.h"
14
+ #include "contact.h"
15
+ #include "core.h"
16
+ #include "shape.h"
17
+ #include "arena_allocator.h"
18
+ #include "world.h"
19
+
20
+ #include <stdbool.h>
21
+ #include <string.h>
22
+
23
+ // #include <stdio.h>
24
+
25
+ // static FILE* s_file = NULL;
26
+
27
+ void b2CreateBroadPhase( b2BroadPhase* bp )
28
+ {
29
+ _Static_assert( b2_bodyTypeCount == 3, "must be three body types" );
30
+
31
+ // if (s_file == NULL)
32
+ //{
33
+ // s_file = fopen("pairs01.txt", "a");
34
+ // fprintf(s_file, "============\n\n");
35
+ // }
36
+
37
+ bp->proxyCount = 0;
38
+ bp->moveSet = b2CreateSet( 16 );
39
+ bp->moveArray = b2IntArray_Create( 16 );
40
+ bp->moveResults = NULL;
41
+ bp->movePairs = NULL;
42
+ bp->movePairCapacity = 0;
43
+ b2AtomicStoreInt(&bp->movePairIndex, 0);
44
+ bp->pairSet = b2CreateSet( 32 );
45
+
46
+ for ( int i = 0; i < b2_bodyTypeCount; ++i )
47
+ {
48
+ bp->trees[i] = b2DynamicTree_Create();
49
+ }
50
+ }
51
+
52
+ void b2DestroyBroadPhase( b2BroadPhase* bp )
53
+ {
54
+ for ( int i = 0; i < b2_bodyTypeCount; ++i )
55
+ {
56
+ b2DynamicTree_Destroy( bp->trees + i );
57
+ }
58
+
59
+ b2DestroySet( &bp->moveSet );
60
+ b2IntArray_Destroy( &bp->moveArray );
61
+ b2DestroySet( &bp->pairSet );
62
+
63
+ memset( bp, 0, sizeof( b2BroadPhase ) );
64
+
65
+ // if (s_file != NULL)
66
+ //{
67
+ // fclose(s_file);
68
+ // s_file = NULL;
69
+ // }
70
+ }
71
+
72
+ static inline void b2UnBufferMove( b2BroadPhase* bp, int proxyKey )
73
+ {
74
+ bool found = b2RemoveKey( &bp->moveSet, proxyKey + 1 );
75
+
76
+ if ( found )
77
+ {
78
+ // Purge from move buffer. Linear search.
79
+ // todo if I can iterate the move set then I don't need the moveArray
80
+ int count = bp->moveArray.count;
81
+ for ( int i = 0; i < count; ++i )
82
+ {
83
+ if ( bp->moveArray.data[i] == proxyKey )
84
+ {
85
+ b2IntArray_RemoveSwap( &bp->moveArray, i );
86
+ break;
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ int b2BroadPhase_CreateProxy( b2BroadPhase* bp, b2BodyType proxyType, b2AABB aabb, uint64_t categoryBits, int shapeIndex,
93
+ bool forcePairCreation )
94
+ {
95
+ B2_ASSERT( 0 <= proxyType && proxyType < b2_bodyTypeCount );
96
+ int proxyId = b2DynamicTree_CreateProxy( bp->trees + proxyType, aabb, categoryBits, shapeIndex );
97
+ int proxyKey = B2_PROXY_KEY( proxyId, proxyType );
98
+ if ( proxyType != b2_staticBody || forcePairCreation )
99
+ {
100
+ b2BufferMove( bp, proxyKey );
101
+ }
102
+ return proxyKey;
103
+ }
104
+
105
+ void b2BroadPhase_DestroyProxy( b2BroadPhase* bp, int proxyKey )
106
+ {
107
+ B2_ASSERT( bp->moveArray.count == (int)bp->moveSet.count );
108
+ b2UnBufferMove( bp, proxyKey );
109
+
110
+ --bp->proxyCount;
111
+
112
+ b2BodyType proxyType = B2_PROXY_TYPE( proxyKey );
113
+ int proxyId = B2_PROXY_ID( proxyKey );
114
+
115
+ B2_ASSERT( 0 <= proxyType && proxyType <= b2_bodyTypeCount );
116
+ b2DynamicTree_DestroyProxy( bp->trees + proxyType, proxyId );
117
+ }
118
+
119
+ void b2BroadPhase_MoveProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb )
120
+ {
121
+ b2BodyType proxyType = B2_PROXY_TYPE( proxyKey );
122
+ int proxyId = B2_PROXY_ID( proxyKey );
123
+
124
+ b2DynamicTree_MoveProxy( bp->trees + proxyType, proxyId, aabb );
125
+ b2BufferMove( bp, proxyKey );
126
+ }
127
+
128
+ void b2BroadPhase_EnlargeProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb )
129
+ {
130
+ B2_ASSERT( proxyKey != B2_NULL_INDEX );
131
+ int typeIndex = B2_PROXY_TYPE( proxyKey );
132
+ int proxyId = B2_PROXY_ID( proxyKey );
133
+
134
+ B2_ASSERT( typeIndex != b2_staticBody );
135
+
136
+ b2DynamicTree_EnlargeProxy( bp->trees + typeIndex, proxyId, aabb );
137
+ b2BufferMove( bp, proxyKey );
138
+ }
139
+
140
+ typedef struct b2MovePair
141
+ {
142
+ int shapeIndexA;
143
+ int shapeIndexB;
144
+ b2MovePair* next;
145
+ bool heap;
146
+ } b2MovePair;
147
+
148
+ typedef struct b2MoveResult
149
+ {
150
+ b2MovePair* pairList;
151
+ } b2MoveResult;
152
+
153
+ typedef struct b2QueryPairContext
154
+ {
155
+ b2World* world;
156
+ b2MoveResult* moveResult;
157
+ b2BodyType queryTreeType;
158
+ int queryProxyKey;
159
+ int queryShapeIndex;
160
+ } b2QueryPairContext;
161
+
162
+ // This is called from b2DynamicTree::Query when we are gathering pairs.
163
+ static bool b2PairQueryCallback( int proxyId, uint64_t userData, void* context )
164
+ {
165
+ int shapeId = (int)userData;
166
+
167
+ b2QueryPairContext* queryContext = context;
168
+ b2BroadPhase* broadPhase = &queryContext->world->broadPhase;
169
+
170
+ int proxyKey = B2_PROXY_KEY( proxyId, queryContext->queryTreeType );
171
+ int queryProxyKey = queryContext->queryProxyKey;
172
+
173
+ // A proxy cannot form a pair with itself.
174
+ if ( proxyKey == queryContext->queryProxyKey )
175
+ {
176
+ return true;
177
+ }
178
+
179
+ b2BodyType treeType = queryContext->queryTreeType;
180
+ b2BodyType queryProxyType = B2_PROXY_TYPE( queryProxyKey );
181
+
182
+ // De-duplication
183
+ // It is important to prevent duplicate contacts from being created. Ideally I can prevent duplicates
184
+ // early and in the worker. Most of the time the moveSet contains dynamic and kinematic proxies, but
185
+ // sometimes it has static proxies.
186
+
187
+ // I had an optimization here to skip checking the move set if this is a query into
188
+ // the static tree. The assumption is that the static proxies are never in the move set
189
+ // so there is no risk of duplication. However, this is not true with
190
+ // b2ShapeDef::forceContactCreation, b2ShapeDef::isSensor, or when a static shape is modified.
191
+ // There can easily be scenarios where the static proxy is in the moveSet but the dynamic proxy is not.
192
+ // I could have some flag to indicate that there are any static bodies in the moveSet.
193
+
194
+ // Is this proxy also moving?
195
+ if ( queryProxyType == b2_dynamicBody)
196
+ {
197
+ if ( treeType == b2_dynamicBody && proxyKey < queryProxyKey)
198
+ {
199
+ bool moved = b2ContainsKey( &broadPhase->moveSet, proxyKey + 1 );
200
+ if ( moved )
201
+ {
202
+ // Both proxies are moving. Avoid duplicate pairs.
203
+ return true;
204
+ }
205
+ }
206
+ }
207
+ else
208
+ {
209
+ B2_ASSERT( treeType == b2_dynamicBody );
210
+ bool moved = b2ContainsKey( &broadPhase->moveSet, proxyKey + 1 );
211
+ if ( moved )
212
+ {
213
+ // Both proxies are moving. Avoid duplicate pairs.
214
+ return true;
215
+ }
216
+ }
217
+
218
+ uint64_t pairKey = B2_SHAPE_PAIR_KEY( shapeId, queryContext->queryShapeIndex );
219
+ if ( b2ContainsKey( &broadPhase->pairSet, pairKey ) )
220
+ {
221
+ // contact exists
222
+ return true;
223
+ }
224
+
225
+ int shapeIdA, shapeIdB;
226
+ if ( proxyKey < queryProxyKey )
227
+ {
228
+ shapeIdA = shapeId;
229
+ shapeIdB = queryContext->queryShapeIndex;
230
+ }
231
+ else
232
+ {
233
+ shapeIdA = queryContext->queryShapeIndex;
234
+ shapeIdB = shapeId;
235
+ }
236
+
237
+ b2World* world = queryContext->world;
238
+
239
+ b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, shapeIdA );
240
+ b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, shapeIdB );
241
+
242
+ int bodyIdA = shapeA->bodyId;
243
+ int bodyIdB = shapeB->bodyId;
244
+
245
+ // Are the shapes on the same body?
246
+ if ( bodyIdA == bodyIdB )
247
+ {
248
+ return true;
249
+ }
250
+
251
+ // Sensors are handled elsewhere
252
+ if ( shapeA->sensorIndex != B2_NULL_INDEX || shapeB->sensorIndex != B2_NULL_INDEX )
253
+ {
254
+ return true;
255
+ }
256
+
257
+ if ( b2ShouldShapesCollide( shapeA->filter, shapeB->filter ) == false )
258
+ {
259
+ return true;
260
+ }
261
+
262
+ // Does a joint override collision?
263
+ b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );
264
+ b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );
265
+ if ( b2ShouldBodiesCollide( world, bodyA, bodyB ) == false )
266
+ {
267
+ return true;
268
+ }
269
+
270
+ // Custom user filter
271
+ b2CustomFilterFcn* customFilterFcn = queryContext->world->customFilterFcn;
272
+ if ( customFilterFcn != NULL )
273
+ {
274
+ b2ShapeId idA = { shapeIdA + 1, world->worldId, shapeA->generation };
275
+ b2ShapeId idB = { shapeIdB + 1, world->worldId, shapeB->generation };
276
+ bool shouldCollide = customFilterFcn( idA, idB, queryContext->world->customFilterContext );
277
+ if ( shouldCollide == false )
278
+ {
279
+ return true;
280
+ }
281
+ }
282
+
283
+ // todo per thread to eliminate atomic?
284
+ int pairIndex = b2AtomicFetchAddInt( &broadPhase->movePairIndex, 1 );
285
+
286
+ b2MovePair* pair;
287
+ if ( pairIndex < broadPhase->movePairCapacity )
288
+ {
289
+ pair = broadPhase->movePairs + pairIndex;
290
+ pair->heap = false;
291
+ }
292
+ else
293
+ {
294
+ pair = b2Alloc( sizeof( b2MovePair ) );
295
+ pair->heap = true;
296
+ }
297
+
298
+ pair->shapeIndexA = shapeIdA;
299
+ pair->shapeIndexB = shapeIdB;
300
+ pair->next = queryContext->moveResult->pairList;
301
+ queryContext->moveResult->pairList = pair;
302
+
303
+ // continue the query
304
+ return true;
305
+ }
306
+
307
+ // Warning: writing to these globals significantly slows multithreading performance
308
+ #if B2_SNOOP_PAIR_COUNTERS
309
+ b2TreeStats b2_dynamicStats;
310
+ b2TreeStats b2_kinematicStats;
311
+ b2TreeStats b2_staticStats;
312
+ #endif
313
+
314
+ static void b2FindPairsTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )
315
+ {
316
+ b2TracyCZoneNC( pair_task, "Pair", b2_colorMediumSlateBlue, true );
317
+
318
+ B2_UNUSED( threadIndex );
319
+
320
+ b2World* world = context;
321
+ b2BroadPhase* bp = &world->broadPhase;
322
+
323
+ b2QueryPairContext queryContext;
324
+ queryContext.world = world;
325
+
326
+ for ( int i = startIndex; i < endIndex; ++i )
327
+ {
328
+ // Initialize move result for this moved proxy
329
+ queryContext.moveResult = bp->moveResults + i;
330
+ queryContext.moveResult->pairList = NULL;
331
+
332
+ int proxyKey = bp->moveArray.data[i];
333
+ if ( proxyKey == B2_NULL_INDEX )
334
+ {
335
+ // proxy was destroyed after it moved
336
+ continue;
337
+ }
338
+
339
+ b2BodyType proxyType = B2_PROXY_TYPE( proxyKey );
340
+
341
+ int proxyId = B2_PROXY_ID( proxyKey );
342
+ queryContext.queryProxyKey = proxyKey;
343
+
344
+ const b2DynamicTree* baseTree = bp->trees + proxyType;
345
+
346
+ // We have to query the tree with the fat AABB so that
347
+ // we don't fail to create a contact that may touch later.
348
+ b2AABB fatAABB = b2DynamicTree_GetAABB( baseTree, proxyId );
349
+ queryContext.queryShapeIndex = (int)b2DynamicTree_GetUserData( baseTree, proxyId );
350
+
351
+ // Query trees. Only dynamic proxies collide with kinematic and static proxies.
352
+ // Using B2_DEFAULT_MASK_BITS so that b2Filter::groupIndex works.
353
+ b2TreeStats stats = { 0 };
354
+ if ( proxyType == b2_dynamicBody )
355
+ {
356
+ // consider using bits = groupIndex > 0 ? B2_DEFAULT_MASK_BITS : maskBits
357
+ queryContext.queryTreeType = b2_kinematicBody;
358
+ b2TreeStats statsKinematic = b2DynamicTree_Query( bp->trees + b2_kinematicBody, fatAABB, B2_DEFAULT_MASK_BITS, b2PairQueryCallback, &queryContext );
359
+ stats.nodeVisits += statsKinematic.nodeVisits;
360
+ stats.leafVisits += statsKinematic.leafVisits;
361
+
362
+ queryContext.queryTreeType = b2_staticBody;
363
+ b2TreeStats statsStatic = b2DynamicTree_Query( bp->trees + b2_staticBody, fatAABB, B2_DEFAULT_MASK_BITS, b2PairQueryCallback, &queryContext );
364
+ stats.nodeVisits += statsStatic.nodeVisits;
365
+ stats.leafVisits += statsStatic.leafVisits;
366
+ }
367
+
368
+ // All proxies collide with dynamic proxies
369
+ // Using B2_DEFAULT_MASK_BITS so that b2Filter::groupIndex works.
370
+ queryContext.queryTreeType = b2_dynamicBody;
371
+ b2TreeStats statsDynamic = b2DynamicTree_Query( bp->trees + b2_dynamicBody, fatAABB, B2_DEFAULT_MASK_BITS, b2PairQueryCallback, &queryContext );
372
+ stats.nodeVisits += statsDynamic.nodeVisits;
373
+ stats.leafVisits += statsDynamic.leafVisits;
374
+ }
375
+
376
+ b2TracyCZoneEnd( pair_task );
377
+ }
378
+
379
+ void b2UpdateBroadPhasePairs( b2World* world )
380
+ {
381
+ b2BroadPhase* bp = &world->broadPhase;
382
+
383
+ int moveCount = bp->moveArray.count;
384
+ B2_ASSERT( moveCount == (int)bp->moveSet.count );
385
+
386
+ if ( moveCount == 0 )
387
+ {
388
+ return;
389
+ }
390
+
391
+ b2TracyCZoneNC( update_pairs, "Find Pairs", b2_colorMediumSlateBlue, true );
392
+
393
+ b2ArenaAllocator* alloc = &world->arena;
394
+
395
+ // todo these could be in the step context
396
+ bp->moveResults = b2AllocateArenaItem( alloc, moveCount * sizeof( b2MoveResult ), "move results" );
397
+ bp->movePairCapacity = 16 * moveCount;
398
+ bp->movePairs = b2AllocateArenaItem( alloc, bp->movePairCapacity * sizeof( b2MovePair ), "move pairs" );
399
+ b2AtomicStoreInt(&bp->movePairIndex, 0);
400
+
401
+ #if B2_SNOOP_TABLE_COUNTERS
402
+ extern b2AtomicInt b2_probeCount;
403
+ b2AtomicStoreInt(&b2_probeCount, 0);
404
+ #endif
405
+
406
+ int minRange = 64;
407
+ void* userPairTask = world->enqueueTaskFcn( &b2FindPairsTask, moveCount, minRange, world, world->userTaskContext );
408
+ if (userPairTask != NULL)
409
+ {
410
+ world->finishTaskFcn( userPairTask, world->userTaskContext );
411
+ world->taskCount += 1;
412
+ }
413
+
414
+ // todo_erin could start tree rebuild here
415
+
416
+ b2TracyCZoneNC( create_contacts, "Create Contacts", b2_colorCoral, true );
417
+
418
+ // Single-threaded work
419
+ // - Clear move flags
420
+ // - Create contacts in deterministic order
421
+ for ( int i = 0; i < moveCount; ++i )
422
+ {
423
+ b2MoveResult* result = bp->moveResults + i;
424
+ b2MovePair* pair = result->pairList;
425
+ while ( pair != NULL )
426
+ {
427
+ int shapeIdA = pair->shapeIndexA;
428
+ int shapeIdB = pair->shapeIndexB;
429
+
430
+ // if (s_file != NULL)
431
+ //{
432
+ // fprintf(s_file, "%d %d\n", shapeIdA, shapeIdB);
433
+ // }
434
+
435
+ b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, shapeIdA );
436
+ b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, shapeIdB );
437
+
438
+ b2CreateContact( world, shapeA, shapeB );
439
+
440
+ if ( pair->heap )
441
+ {
442
+ b2MovePair* temp = pair;
443
+ pair = pair->next;
444
+ b2Free( temp, sizeof( b2MovePair ) );
445
+ }
446
+ else
447
+ {
448
+ pair = pair->next;
449
+ }
450
+ }
451
+
452
+ // if (s_file != NULL)
453
+ //{
454
+ // fprintf(s_file, "\n");
455
+ // }
456
+ }
457
+
458
+ // if (s_file != NULL)
459
+ //{
460
+ // fprintf(s_file, "count = %d\n\n", pairCount);
461
+ // }
462
+
463
+ // Reset move buffer
464
+ b2IntArray_Clear( &bp->moveArray );
465
+ b2ClearSet( &bp->moveSet );
466
+
467
+ b2FreeArenaItem( alloc, bp->movePairs );
468
+ bp->movePairs = NULL;
469
+ b2FreeArenaItem( alloc, bp->moveResults );
470
+ bp->moveResults = NULL;
471
+
472
+ b2ValidateSolverSets( world );
473
+
474
+ b2TracyCZoneEnd( create_contacts );
475
+
476
+ b2TracyCZoneEnd( update_pairs );
477
+ }
478
+
479
+ bool b2BroadPhase_TestOverlap( const b2BroadPhase* bp, int proxyKeyA, int proxyKeyB )
480
+ {
481
+ int typeIndexA = B2_PROXY_TYPE( proxyKeyA );
482
+ int proxyIdA = B2_PROXY_ID( proxyKeyA );
483
+ int typeIndexB = B2_PROXY_TYPE( proxyKeyB );
484
+ int proxyIdB = B2_PROXY_ID( proxyKeyB );
485
+
486
+ b2AABB aabbA = b2DynamicTree_GetAABB( bp->trees + typeIndexA, proxyIdA );
487
+ b2AABB aabbB = b2DynamicTree_GetAABB( bp->trees + typeIndexB, proxyIdB );
488
+ return b2AABB_Overlaps( aabbA, aabbB );
489
+ }
490
+
491
+ void b2BroadPhase_RebuildTrees( b2BroadPhase* bp )
492
+ {
493
+ b2DynamicTree_Rebuild( bp->trees + b2_dynamicBody, false );
494
+ b2DynamicTree_Rebuild( bp->trees + b2_kinematicBody, false );
495
+ }
496
+
497
+ int b2BroadPhase_GetShapeIndex( b2BroadPhase* bp, int proxyKey )
498
+ {
499
+ int typeIndex = B2_PROXY_TYPE( proxyKey );
500
+ int proxyId = B2_PROXY_ID( proxyKey );
501
+
502
+ return (int)b2DynamicTree_GetUserData( bp->trees + typeIndex, proxyId );
503
+ }
504
+
505
+ void b2ValidateBroadphase( const b2BroadPhase* bp )
506
+ {
507
+ b2DynamicTree_Validate( bp->trees + b2_dynamicBody );
508
+ b2DynamicTree_Validate( bp->trees + b2_kinematicBody );
509
+
510
+ // TODO_ERIN validate every shape AABB is contained in tree AABB
511
+ }
512
+
513
+ void b2ValidateNoEnlarged( const b2BroadPhase* bp )
514
+ {
515
+ #if B2_VALIDATE == 1
516
+ for ( int j = 0; j < b2_bodyTypeCount; ++j )
517
+ {
518
+ const b2DynamicTree* tree = bp->trees + j;
519
+ b2DynamicTree_ValidateNoEnlarged( tree );
520
+ }
521
+ #else
522
+ B2_UNUSED( bp );
523
+ #endif
524
+ }
@@ -0,0 +1,83 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #pragma once
5
+
6
+ #include "array.h"
7
+ #include "table.h"
8
+
9
+ #include "box2d/collision.h"
10
+ #include "box2d/types.h"
11
+
12
+ typedef struct b2Shape b2Shape;
13
+ typedef struct b2MovePair b2MovePair;
14
+ typedef struct b2MoveResult b2MoveResult;
15
+ typedef struct b2ArenaAllocator b2ArenaAllocator;
16
+ typedef struct b2World b2World;
17
+
18
+ // Store the proxy type in the lower 2 bits of the proxy key. This leaves 30 bits for the id.
19
+ #define B2_PROXY_TYPE( KEY ) ( (b2BodyType)( ( KEY ) & 3 ) )
20
+ #define B2_PROXY_ID( KEY ) ( ( KEY ) >> 2 )
21
+ #define B2_PROXY_KEY( ID, TYPE ) ( ( ( ID ) << 2 ) | ( TYPE ) )
22
+
23
+ /// The broad-phase is used for computing pairs and performing volume queries and ray casts.
24
+ /// This broad-phase does not persist pairs. Instead, this reports potentially new pairs.
25
+ /// It is up to the client to consume the new pairs and to track subsequent overlap.
26
+ typedef struct b2BroadPhase
27
+ {
28
+ b2DynamicTree trees[b2_bodyTypeCount];
29
+ int proxyCount;
30
+
31
+ // The move set and array are used to track shapes that have moved significantly
32
+ // and need a pair query for new contacts. The array has a deterministic order.
33
+ // todo perhaps just a move set?
34
+ // todo implement a 32bit hash set for faster lookup
35
+ // todo moveSet can grow quite large on the first time step and remain large
36
+ b2HashSet moveSet;
37
+ b2IntArray moveArray;
38
+
39
+ // These are the results from the pair query and are used to create new contacts
40
+ // in deterministic order.
41
+ // todo these could be in the step context
42
+ b2MoveResult* moveResults;
43
+ b2MovePair* movePairs;
44
+ int movePairCapacity;
45
+ b2AtomicInt movePairIndex;
46
+
47
+ // Tracks shape pairs that have a b2Contact
48
+ // todo pairSet can grow quite large on the first time step and remain large
49
+ b2HashSet pairSet;
50
+
51
+ } b2BroadPhase;
52
+
53
+ void b2CreateBroadPhase( b2BroadPhase* bp );
54
+ void b2DestroyBroadPhase( b2BroadPhase* bp );
55
+
56
+ int b2BroadPhase_CreateProxy( b2BroadPhase* bp, b2BodyType proxyType, b2AABB aabb, uint64_t categoryBits, int shapeIndex,
57
+ bool forcePairCreation );
58
+ void b2BroadPhase_DestroyProxy( b2BroadPhase* bp, int proxyKey );
59
+
60
+ void b2BroadPhase_MoveProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb );
61
+ void b2BroadPhase_EnlargeProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb );
62
+
63
+ void b2BroadPhase_RebuildTrees( b2BroadPhase* bp );
64
+
65
+ int b2BroadPhase_GetShapeIndex( b2BroadPhase* bp, int proxyKey );
66
+
67
+ void b2UpdateBroadPhasePairs( b2World* world );
68
+ bool b2BroadPhase_TestOverlap( const b2BroadPhase* bp, int proxyKeyA, int proxyKeyB );
69
+
70
+ void b2ValidateBroadphase( const b2BroadPhase* bp );
71
+ void b2ValidateNoEnlarged( const b2BroadPhase* bp );
72
+
73
+ // This is what triggers new contact pairs to be created
74
+ // Warning: this must be called in deterministic order
75
+ static inline void b2BufferMove( b2BroadPhase* bp, int queryProxy )
76
+ {
77
+ // Adding 1 because 0 is the sentinel
78
+ bool alreadyAdded = b2AddKey( &bp->moveSet, queryProxy + 1 );
79
+ if ( alreadyAdded == false )
80
+ {
81
+ b2IntArray_Push( &bp->moveArray, queryProxy );
82
+ }
83
+ }
@@ -0,0 +1,54 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #pragma once
5
+
6
+ extern float b2_lengthUnitsPerMeter;
7
+
8
+ // Used to detect bad values. Positions greater than about 16km will have precision
9
+ // problems, so 100km as a limit should be fine in all cases.
10
+ #define B2_HUGE ( 100000.0f * b2_lengthUnitsPerMeter )
11
+
12
+ // Maximum parallel workers. Used to size some static arrays.
13
+ #define B2_MAX_WORKERS 64
14
+
15
+ // Maximum number of colors in the constraint graph. Constraints that cannot
16
+ // find a color are added to the overflow set which are solved single-threaded.
17
+ #define B2_GRAPH_COLOR_COUNT 12
18
+
19
+ // A small length used as a collision and constraint tolerance. Usually it is
20
+ // chosen to be numerically significant, but visually insignificant. In meters.
21
+ // Normally this is 0.5cm.
22
+ // @warning modifying this can have a significant impact on stability
23
+ #define B2_LINEAR_SLOP ( 0.005f * b2_lengthUnitsPerMeter )
24
+
25
+ // Maximum number of simultaneous worlds that can be allocated
26
+ #ifndef B2_MAX_WORLDS
27
+ #define B2_MAX_WORLDS 128
28
+ #endif
29
+
30
+ // The maximum rotation of a body per time step. This limit is very large and is used
31
+ // to prevent numerical problems. You shouldn't need to adjust this.
32
+ // @warning increasing this to 0.5f * b2_pi or greater will break continuous collision.
33
+ #define B2_MAX_ROTATION ( 0.25f * B2_PI )
34
+
35
+ // Box2D uses limited speculative collision. This reduces jitter.
36
+ // Normally this is 2cm.
37
+ // @warning modifying this can have a significant impact on performance and stability
38
+ #define B2_SPECULATIVE_DISTANCE ( 4.0f * B2_LINEAR_SLOP )
39
+
40
+ // This is used to fatten AABBs in the dynamic tree. This allows proxies
41
+ // to move by a small amount without triggering a tree adjustment. This is in meters.
42
+ // Normally this is 5cm.
43
+ // @warning modifying this can have a significant impact on performance
44
+ #define B2_AABB_MARGIN ( 0.05f * b2_lengthUnitsPerMeter )
45
+
46
+ // The time that a body must be still before it will go to sleep. In seconds.
47
+ #define B2_TIME_TO_SLEEP 0.5f
48
+
49
+ enum b2TreeNodeFlags
50
+ {
51
+ b2_allocatedNode = 0x0001,
52
+ b2_enlargedNode = 0x0002,
53
+ b2_leafNode = 0x0004,
54
+ };