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,613 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #include "solver_set.h"
5
+
6
+ #include "body.h"
7
+ #include "constraint_graph.h"
8
+ #include "contact.h"
9
+ #include "core.h"
10
+ #include "island.h"
11
+ #include "joint.h"
12
+ #include "world.h"
13
+
14
+ #include <string.h>
15
+
16
+ B2_ARRAY_SOURCE( b2SolverSet, b2SolverSet )
17
+
18
+ void b2DestroySolverSet( b2World* world, int setIndex )
19
+ {
20
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );
21
+ b2BodySimArray_Destroy( &set->bodySims );
22
+ b2BodyStateArray_Destroy( &set->bodyStates );
23
+ b2ContactSimArray_Destroy( &set->contactSims );
24
+ b2JointSimArray_Destroy( &set->jointSims );
25
+ b2IslandSimArray_Destroy( &set->islandSims );
26
+ b2FreeId( &world->solverSetIdPool, setIndex );
27
+ *set = ( b2SolverSet ){ 0 };
28
+ set->setIndex = B2_NULL_INDEX;
29
+ }
30
+
31
+ // Wake a solver set. Does not merge islands.
32
+ // Contacts can be in several places:
33
+ // 1. non-touching contacts in the disabled set
34
+ // 2. non-touching contacts already in the awake set
35
+ // 3. touching contacts in the sleeping set
36
+ // This handles contact types 1 and 3. Type 2 doesn't need any action.
37
+ void b2WakeSolverSet( b2World* world, int setIndex )
38
+ {
39
+ B2_ASSERT( setIndex >= b2_firstSleepingSet );
40
+ b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );
41
+ b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
42
+ b2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet );
43
+
44
+ b2Body* bodies = world->bodies.data;
45
+
46
+ int bodyCount = set->bodySims.count;
47
+ for ( int i = 0; i < bodyCount; ++i )
48
+ {
49
+ b2BodySim* simSrc = set->bodySims.data + i;
50
+
51
+ b2Body* body = bodies + simSrc->bodyId;
52
+ B2_ASSERT( body->setIndex == setIndex );
53
+ body->setIndex = b2_awakeSet;
54
+ body->localIndex = awakeSet->bodySims.count;
55
+
56
+ // Reset sleep timer
57
+ body->sleepTime = 0.0f;
58
+
59
+ b2BodySim* simDst = b2BodySimArray_Add( &awakeSet->bodySims );
60
+ memcpy( simDst, simSrc, sizeof( b2BodySim ) );
61
+
62
+ b2BodyState* state = b2BodyStateArray_Add( &awakeSet->bodyStates );
63
+ *state = b2_identityBodyState;
64
+
65
+ // move non-touching contacts from disabled set to awake set
66
+ int contactKey = body->headContactKey;
67
+ while ( contactKey != B2_NULL_INDEX )
68
+ {
69
+ int edgeIndex = contactKey & 1;
70
+ int contactId = contactKey >> 1;
71
+
72
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
73
+
74
+ contactKey = contact->edges[edgeIndex].nextKey;
75
+
76
+ if ( contact->setIndex != b2_disabledSet )
77
+ {
78
+ B2_ASSERT( contact->setIndex == b2_awakeSet || contact->setIndex == setIndex );
79
+ continue;
80
+ }
81
+
82
+ int localIndex = contact->localIndex;
83
+ b2ContactSim* contactSim = b2ContactSimArray_Get( &disabledSet->contactSims, localIndex );
84
+
85
+ B2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) == 0 && contactSim->manifold.pointCount == 0 );
86
+
87
+ contact->setIndex = b2_awakeSet;
88
+ contact->localIndex = awakeSet->contactSims.count;
89
+ b2ContactSim* awakeContactSim = b2ContactSimArray_Add( &awakeSet->contactSims );
90
+ memcpy( awakeContactSim, contactSim, sizeof( b2ContactSim ) );
91
+
92
+ int movedLocalIndex = b2ContactSimArray_RemoveSwap( &disabledSet->contactSims, localIndex );
93
+ if ( movedLocalIndex != B2_NULL_INDEX )
94
+ {
95
+ // fix moved element
96
+ b2ContactSim* movedContactSim = disabledSet->contactSims.data + localIndex;
97
+ b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId );
98
+ B2_ASSERT( movedContact->localIndex == movedLocalIndex );
99
+ movedContact->localIndex = localIndex;
100
+ }
101
+ }
102
+ }
103
+
104
+ // transfer touching contacts from sleeping set to contact graph
105
+ {
106
+ int contactCount = set->contactSims.count;
107
+ for ( int i = 0; i < contactCount; ++i )
108
+ {
109
+ b2ContactSim* contactSim = set->contactSims.data + i;
110
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactSim->contactId );
111
+ B2_ASSERT( contact->flags & b2_contactTouchingFlag );
112
+ B2_ASSERT( contactSim->simFlags & b2_simTouchingFlag );
113
+ B2_ASSERT( contactSim->manifold.pointCount > 0 );
114
+ B2_ASSERT( contact->setIndex == setIndex );
115
+ b2AddContactToGraph( world, contactSim, contact );
116
+ contact->setIndex = b2_awakeSet;
117
+ }
118
+ }
119
+
120
+ // transfer joints from sleeping set to awake set
121
+ {
122
+ int jointCount = set->jointSims.count;
123
+ for ( int i = 0; i < jointCount; ++i )
124
+ {
125
+ b2JointSim* jointSim = set->jointSims.data + i;
126
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointSim->jointId );
127
+ B2_ASSERT( joint->setIndex == setIndex );
128
+ b2AddJointToGraph( world, jointSim, joint );
129
+ joint->setIndex = b2_awakeSet;
130
+ }
131
+ }
132
+
133
+ // transfer island from sleeping set to awake set
134
+ // Usually a sleeping set has only one island, but it is possible
135
+ // that joints are created between sleeping islands and they
136
+ // are moved to the same sleeping set.
137
+ {
138
+ int islandCount = set->islandSims.count;
139
+ for ( int i = 0; i < islandCount; ++i )
140
+ {
141
+ b2IslandSim* islandSrc = set->islandSims.data + i;
142
+ b2Island* island = b2IslandArray_Get( &world->islands, islandSrc->islandId );
143
+ island->setIndex = b2_awakeSet;
144
+ island->localIndex = awakeSet->islandSims.count;
145
+ b2IslandSim* islandDst = b2IslandSimArray_Add( &awakeSet->islandSims );
146
+ memcpy( islandDst, islandSrc, sizeof( b2IslandSim ) );
147
+ }
148
+ }
149
+
150
+ // destroy the sleeping set
151
+ b2DestroySolverSet( world, setIndex );
152
+
153
+ b2ValidateSolverSets( world );
154
+ }
155
+
156
+ void b2TrySleepIsland( b2World* world, int islandId )
157
+ {
158
+ b2Island* island = b2IslandArray_Get( &world->islands, islandId );
159
+ B2_ASSERT( island->setIndex == b2_awakeSet );
160
+
161
+ // cannot put an island to sleep while it has a pending split
162
+ if ( island->constraintRemoveCount > 0 )
163
+ {
164
+ return;
165
+ }
166
+
167
+ // island is sleeping
168
+ // - create new sleeping solver set
169
+ // - move island to sleeping solver set
170
+ // - identify non-touching contacts that should move to sleeping solver set or disabled set
171
+ // - remove old island
172
+ // - fix island
173
+ int sleepSetId = b2AllocId( &world->solverSetIdPool );
174
+ if ( sleepSetId == world->solverSets.count )
175
+ {
176
+ b2SolverSet set = { 0 };
177
+ set.setIndex = B2_NULL_INDEX;
178
+ b2SolverSetArray_Push( &world->solverSets, set );
179
+ }
180
+
181
+ b2SolverSet* sleepSet = b2SolverSetArray_Get( &world->solverSets, sleepSetId );
182
+ *sleepSet = ( b2SolverSet ){ 0 };
183
+
184
+ // grab awake set after creating the sleep set because the solver set array may have been resized
185
+ b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
186
+ B2_ASSERT( 0 <= island->localIndex && island->localIndex < awakeSet->islandSims.count );
187
+
188
+ sleepSet->setIndex = sleepSetId;
189
+ sleepSet->bodySims = b2BodySimArray_Create( island->bodyCount );
190
+ sleepSet->contactSims = b2ContactSimArray_Create( island->contactCount );
191
+ sleepSet->jointSims = b2JointSimArray_Create( island->jointCount );
192
+
193
+ // move awake bodies to sleeping set
194
+ // this shuffles around bodies in the awake set
195
+ {
196
+ b2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet );
197
+ int bodyId = island->headBody;
198
+ while ( bodyId != B2_NULL_INDEX )
199
+ {
200
+ b2Body* body = b2BodyArray_Get( &world->bodies, bodyId );
201
+ B2_ASSERT( body->setIndex == b2_awakeSet );
202
+ B2_ASSERT( body->islandId == islandId );
203
+
204
+ // Update the body move event to indicate this body fell asleep
205
+ // It could happen the body is forced asleep before it ever moves.
206
+ if ( body->bodyMoveIndex != B2_NULL_INDEX )
207
+ {
208
+ b2BodyMoveEvent* moveEvent = b2BodyMoveEventArray_Get( &world->bodyMoveEvents, body->bodyMoveIndex );
209
+ B2_ASSERT( moveEvent->bodyId.index1 - 1 == bodyId );
210
+ B2_ASSERT( moveEvent->bodyId.generation == body->generation );
211
+ moveEvent->fellAsleep = true;
212
+ body->bodyMoveIndex = B2_NULL_INDEX;
213
+ }
214
+
215
+ int awakeBodyIndex = body->localIndex;
216
+ b2BodySim* awakeSim = b2BodySimArray_Get( &awakeSet->bodySims, awakeBodyIndex );
217
+
218
+ // move body sim to sleep set
219
+ int sleepBodyIndex = sleepSet->bodySims.count;
220
+ b2BodySim* sleepBodySim = b2BodySimArray_Add( &sleepSet->bodySims );
221
+ memcpy( sleepBodySim, awakeSim, sizeof( b2BodySim ) );
222
+
223
+ int movedIndex = b2BodySimArray_RemoveSwap( &awakeSet->bodySims, awakeBodyIndex );
224
+ if ( movedIndex != B2_NULL_INDEX )
225
+ {
226
+ // fix local index on moved element
227
+ b2BodySim* movedSim = awakeSet->bodySims.data + awakeBodyIndex;
228
+ int movedId = movedSim->bodyId;
229
+ b2Body* movedBody = b2BodyArray_Get( &world->bodies, movedId );
230
+ B2_ASSERT( movedBody->localIndex == movedIndex );
231
+ movedBody->localIndex = awakeBodyIndex;
232
+ }
233
+
234
+ // destroy state, no need to clone
235
+ b2BodyStateArray_RemoveSwap( &awakeSet->bodyStates, awakeBodyIndex );
236
+
237
+ body->setIndex = sleepSetId;
238
+ body->localIndex = sleepBodyIndex;
239
+
240
+ // Move non-touching contacts to the disabled set.
241
+ // Non-touching contacts may exist between sleeping islands and there is no clear ownership.
242
+ int contactKey = body->headContactKey;
243
+ while ( contactKey != B2_NULL_INDEX )
244
+ {
245
+ int contactId = contactKey >> 1;
246
+ int edgeIndex = contactKey & 1;
247
+
248
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
249
+
250
+ B2_ASSERT( contact->setIndex == b2_awakeSet || contact->setIndex == b2_disabledSet );
251
+ contactKey = contact->edges[edgeIndex].nextKey;
252
+
253
+ if ( contact->setIndex == b2_disabledSet )
254
+ {
255
+ // already moved to disabled set by another body in the island
256
+ continue;
257
+ }
258
+
259
+ if ( contact->colorIndex != B2_NULL_INDEX )
260
+ {
261
+ // contact is touching and will be moved separately
262
+ B2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) != 0 );
263
+ continue;
264
+ }
265
+
266
+ // the other body may still be awake, it still may go to sleep and then it will be responsible
267
+ // for moving this contact to the disabled set.
268
+ int otherEdgeIndex = edgeIndex ^ 1;
269
+ int otherBodyId = contact->edges[otherEdgeIndex].bodyId;
270
+ b2Body* otherBody = b2BodyArray_Get( &world->bodies, otherBodyId );
271
+ if ( otherBody->setIndex == b2_awakeSet )
272
+ {
273
+ continue;
274
+ }
275
+
276
+ int localIndex = contact->localIndex;
277
+ b2ContactSim* contactSim = b2ContactSimArray_Get( &awakeSet->contactSims, localIndex );
278
+
279
+ B2_ASSERT( contactSim->manifold.pointCount == 0 );
280
+ B2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) == 0 );
281
+
282
+ // move the non-touching contact to the disabled set
283
+ contact->setIndex = b2_disabledSet;
284
+ contact->localIndex = disabledSet->contactSims.count;
285
+ b2ContactSim* disabledContactSim = b2ContactSimArray_Add( &disabledSet->contactSims );
286
+ memcpy( disabledContactSim, contactSim, sizeof( b2ContactSim ) );
287
+
288
+ int movedLocalIndex = b2ContactSimArray_RemoveSwap( &awakeSet->contactSims, localIndex );
289
+ if ( movedLocalIndex != B2_NULL_INDEX )
290
+ {
291
+ // fix moved element
292
+ b2ContactSim* movedContactSim = awakeSet->contactSims.data + localIndex;
293
+ b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId );
294
+ B2_ASSERT( movedContact->localIndex == movedLocalIndex );
295
+ movedContact->localIndex = localIndex;
296
+ }
297
+ }
298
+
299
+ bodyId = body->islandNext;
300
+ }
301
+ }
302
+
303
+ // move touching contacts
304
+ // this shuffles contacts in the awake set
305
+ {
306
+ int contactId = island->headContact;
307
+ while ( contactId != B2_NULL_INDEX )
308
+ {
309
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
310
+ B2_ASSERT( contact->setIndex == b2_awakeSet );
311
+ B2_ASSERT( contact->islandId == islandId );
312
+ int colorIndex = contact->colorIndex;
313
+ B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );
314
+
315
+ b2GraphColor* color = world->constraintGraph.colors + colorIndex;
316
+
317
+ // Remove bodies from graph coloring associated with this constraint
318
+ if ( colorIndex != B2_OVERFLOW_INDEX )
319
+ {
320
+ // might clear a bit for a static body, but this has no effect
321
+ b2ClearBit( &color->bodySet, contact->edges[0].bodyId );
322
+ b2ClearBit( &color->bodySet, contact->edges[1].bodyId );
323
+ }
324
+
325
+ int localIndex = contact->localIndex;
326
+ b2ContactSim* awakeContactSim = b2ContactSimArray_Get( &color->contactSims, localIndex );
327
+
328
+ int sleepContactIndex = sleepSet->contactSims.count;
329
+ b2ContactSim* sleepContactSim = b2ContactSimArray_Add( &sleepSet->contactSims );
330
+ memcpy( sleepContactSim, awakeContactSim, sizeof( b2ContactSim ) );
331
+
332
+ int movedLocalIndex = b2ContactSimArray_RemoveSwap( &color->contactSims, localIndex );
333
+ if ( movedLocalIndex != B2_NULL_INDEX )
334
+ {
335
+ // fix moved element
336
+ b2ContactSim* movedContactSim = color->contactSims.data + localIndex;
337
+ b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId );
338
+ B2_ASSERT( movedContact->localIndex == movedLocalIndex );
339
+ movedContact->localIndex = localIndex;
340
+ }
341
+
342
+ contact->setIndex = sleepSetId;
343
+ contact->colorIndex = B2_NULL_INDEX;
344
+ contact->localIndex = sleepContactIndex;
345
+
346
+ contactId = contact->islandNext;
347
+ }
348
+ }
349
+
350
+ // move joints
351
+ // this shuffles joints in the awake set
352
+ {
353
+ int jointId = island->headJoint;
354
+ while ( jointId != B2_NULL_INDEX )
355
+ {
356
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
357
+ B2_ASSERT( joint->setIndex == b2_awakeSet );
358
+ B2_ASSERT( joint->islandId == islandId );
359
+ int colorIndex = joint->colorIndex;
360
+ int localIndex = joint->localIndex;
361
+
362
+ B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );
363
+
364
+ b2GraphColor* color = world->constraintGraph.colors + colorIndex;
365
+
366
+ b2JointSim* awakeJointSim = b2JointSimArray_Get( &color->jointSims, localIndex );
367
+
368
+ if ( colorIndex != B2_OVERFLOW_INDEX )
369
+ {
370
+ // might clear a bit for a static body, but this has no effect
371
+ b2ClearBit( &color->bodySet, joint->edges[0].bodyId );
372
+ b2ClearBit( &color->bodySet, joint->edges[1].bodyId );
373
+ }
374
+
375
+ int sleepJointIndex = sleepSet->jointSims.count;
376
+ b2JointSim* sleepJointSim = b2JointSimArray_Add( &sleepSet->jointSims );
377
+ memcpy( sleepJointSim, awakeJointSim, sizeof( b2JointSim ) );
378
+
379
+ int movedIndex = b2JointSimArray_RemoveSwap( &color->jointSims, localIndex );
380
+ if ( movedIndex != B2_NULL_INDEX )
381
+ {
382
+ // fix moved element
383
+ b2JointSim* movedJointSim = color->jointSims.data + localIndex;
384
+ int movedId = movedJointSim->jointId;
385
+ b2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId );
386
+ B2_ASSERT( movedJoint->localIndex == movedIndex );
387
+ movedJoint->localIndex = localIndex;
388
+ }
389
+
390
+ joint->setIndex = sleepSetId;
391
+ joint->colorIndex = B2_NULL_INDEX;
392
+ joint->localIndex = sleepJointIndex;
393
+
394
+ jointId = joint->islandNext;
395
+ }
396
+ }
397
+
398
+ // move island struct
399
+ {
400
+ B2_ASSERT( island->setIndex == b2_awakeSet );
401
+
402
+ int islandIndex = island->localIndex;
403
+ b2IslandSim* sleepIsland = b2IslandSimArray_Add( &sleepSet->islandSims );
404
+ sleepIsland->islandId = islandId;
405
+
406
+ int movedIslandIndex = b2IslandSimArray_RemoveSwap( &awakeSet->islandSims, islandIndex );
407
+ if ( movedIslandIndex != B2_NULL_INDEX )
408
+ {
409
+ // fix index on moved element
410
+ b2IslandSim* movedIslandSim = awakeSet->islandSims.data + islandIndex;
411
+ int movedIslandId = movedIslandSim->islandId;
412
+ b2Island* movedIsland = b2IslandArray_Get( &world->islands, movedIslandId );
413
+ B2_ASSERT( movedIsland->localIndex == movedIslandIndex );
414
+ movedIsland->localIndex = islandIndex;
415
+ }
416
+
417
+ island->setIndex = sleepSetId;
418
+ island->localIndex = 0;
419
+ }
420
+
421
+ b2ValidateSolverSets( world );
422
+ }
423
+
424
+ // This is called when joints are created between sets. I want to allow the sets
425
+ // to continue sleeping if both are asleep. Otherwise one set is waked.
426
+ // Islands will get merge when the set is waked.
427
+ void b2MergeSolverSets( b2World* world, int setId1, int setId2 )
428
+ {
429
+ B2_ASSERT( setId1 >= b2_firstSleepingSet );
430
+ B2_ASSERT( setId2 >= b2_firstSleepingSet );
431
+ b2SolverSet* set1 = b2SolverSetArray_Get( &world->solverSets, setId1 );
432
+ b2SolverSet* set2 = b2SolverSetArray_Get( &world->solverSets, setId2 );
433
+
434
+ // Move the fewest number of bodies
435
+ if ( set1->bodySims.count < set2->bodySims.count )
436
+ {
437
+ b2SolverSet* tempSet = set1;
438
+ set1 = set2;
439
+ set2 = tempSet;
440
+
441
+ int tempId = setId1;
442
+ setId1 = setId2;
443
+ setId2 = tempId;
444
+ }
445
+
446
+ // transfer bodies
447
+ {
448
+ b2Body* bodies = world->bodies.data;
449
+ int bodyCount = set2->bodySims.count;
450
+ for ( int i = 0; i < bodyCount; ++i )
451
+ {
452
+ b2BodySim* simSrc = set2->bodySims.data + i;
453
+
454
+ b2Body* body = bodies + simSrc->bodyId;
455
+ B2_ASSERT( body->setIndex == setId2 );
456
+ body->setIndex = setId1;
457
+ body->localIndex = set1->bodySims.count;
458
+
459
+ b2BodySim* simDst = b2BodySimArray_Add( &set1->bodySims );
460
+ memcpy( simDst, simSrc, sizeof( b2BodySim ) );
461
+ }
462
+ }
463
+
464
+ // transfer contacts
465
+ {
466
+ int contactCount = set2->contactSims.count;
467
+ for ( int i = 0; i < contactCount; ++i )
468
+ {
469
+ b2ContactSim* contactSrc = set2->contactSims.data + i;
470
+
471
+ b2Contact* contact = b2ContactArray_Get( &world->contacts, contactSrc->contactId );
472
+ B2_ASSERT( contact->setIndex == setId2 );
473
+ contact->setIndex = setId1;
474
+ contact->localIndex = set1->contactSims.count;
475
+
476
+ b2ContactSim* contactDst = b2ContactSimArray_Add( &set1->contactSims );
477
+ memcpy( contactDst, contactSrc, sizeof( b2ContactSim ) );
478
+ }
479
+ }
480
+
481
+ // transfer joints
482
+ {
483
+ int jointCount = set2->jointSims.count;
484
+ for ( int i = 0; i < jointCount; ++i )
485
+ {
486
+ b2JointSim* jointSrc = set2->jointSims.data + i;
487
+
488
+ b2Joint* joint = b2JointArray_Get( &world->joints, jointSrc->jointId );
489
+ B2_ASSERT( joint->setIndex == setId2 );
490
+ joint->setIndex = setId1;
491
+ joint->localIndex = set1->jointSims.count;
492
+
493
+ b2JointSim* jointDst = b2JointSimArray_Add( &set1->jointSims );
494
+ memcpy( jointDst, jointSrc, sizeof( b2JointSim ) );
495
+ }
496
+ }
497
+
498
+ // transfer islands
499
+ {
500
+ int islandCount = set2->islandSims.count;
501
+ for ( int i = 0; i < islandCount; ++i )
502
+ {
503
+ b2IslandSim* islandSrc = set2->islandSims.data + i;
504
+ int islandId = islandSrc->islandId;
505
+
506
+ b2Island* island = b2IslandArray_Get( &world->islands, islandId );
507
+ island->setIndex = setId1;
508
+ island->localIndex = set1->islandSims.count;
509
+
510
+ b2IslandSim* islandDst = b2IslandSimArray_Add( &set1->islandSims );
511
+ memcpy( islandDst, islandSrc, sizeof( b2IslandSim ) );
512
+ }
513
+ }
514
+
515
+ // destroy the merged set
516
+ b2DestroySolverSet( world, setId2 );
517
+
518
+ b2ValidateSolverSets( world );
519
+ }
520
+
521
+ void b2TransferBody( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Body* body )
522
+ {
523
+ B2_ASSERT( targetSet != sourceSet );
524
+
525
+ int sourceIndex = body->localIndex;
526
+ b2BodySim* sourceSim = b2BodySimArray_Get( &sourceSet->bodySims, sourceIndex );
527
+
528
+ int targetIndex = targetSet->bodySims.count;
529
+ b2BodySim* targetSim = b2BodySimArray_Add( &targetSet->bodySims );
530
+ memcpy( targetSim, sourceSim, sizeof( b2BodySim ) );
531
+
532
+ // Remove body sim from solver set that owns it
533
+ int movedIndex = b2BodySimArray_RemoveSwap( &sourceSet->bodySims, sourceIndex );
534
+ if ( movedIndex != B2_NULL_INDEX )
535
+ {
536
+ // Fix moved body index
537
+ b2BodySim* movedSim = sourceSet->bodySims.data + sourceIndex;
538
+ int movedId = movedSim->bodyId;
539
+ b2Body* movedBody = b2BodyArray_Get( &world->bodies, movedId );
540
+ B2_ASSERT( movedBody->localIndex == movedIndex );
541
+ movedBody->localIndex = sourceIndex;
542
+ }
543
+
544
+ if ( sourceSet->setIndex == b2_awakeSet )
545
+ {
546
+ b2BodyStateArray_RemoveSwap( &sourceSet->bodyStates, sourceIndex );
547
+ }
548
+ else if ( targetSet->setIndex == b2_awakeSet )
549
+ {
550
+ b2BodyState* state = b2BodyStateArray_Add( &targetSet->bodyStates );
551
+ *state = b2_identityBodyState;
552
+ }
553
+
554
+ body->setIndex = targetSet->setIndex;
555
+ body->localIndex = targetIndex;
556
+ }
557
+
558
+ void b2TransferJoint( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Joint* joint )
559
+ {
560
+ B2_ASSERT( targetSet != sourceSet );
561
+
562
+ int localIndex = joint->localIndex;
563
+ int colorIndex = joint->colorIndex;
564
+
565
+ // Retrieve source.
566
+ b2JointSim* sourceSim;
567
+ if ( sourceSet->setIndex == b2_awakeSet )
568
+ {
569
+ B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );
570
+ b2GraphColor* color = world->constraintGraph.colors + colorIndex;
571
+
572
+ sourceSim = b2JointSimArray_Get( &color->jointSims, localIndex );
573
+ }
574
+ else
575
+ {
576
+ B2_ASSERT( colorIndex == B2_NULL_INDEX );
577
+ sourceSim = b2JointSimArray_Get( &sourceSet->jointSims, localIndex );
578
+ }
579
+
580
+ // Create target and copy. Fix joint.
581
+ if ( targetSet->setIndex == b2_awakeSet )
582
+ {
583
+ b2AddJointToGraph( world, sourceSim, joint );
584
+ joint->setIndex = b2_awakeSet;
585
+ }
586
+ else
587
+ {
588
+ joint->setIndex = targetSet->setIndex;
589
+ joint->localIndex = targetSet->jointSims.count;
590
+ joint->colorIndex = B2_NULL_INDEX;
591
+
592
+ b2JointSim* targetSim = b2JointSimArray_Add( &targetSet->jointSims );
593
+ memcpy( targetSim, sourceSim, sizeof( b2JointSim ) );
594
+ }
595
+
596
+ // Destroy source.
597
+ if ( sourceSet->setIndex == b2_awakeSet )
598
+ {
599
+ b2RemoveJointFromGraph( world, joint->edges[0].bodyId, joint->edges[1].bodyId, colorIndex, localIndex );
600
+ }
601
+ else
602
+ {
603
+ int movedIndex = b2JointSimArray_RemoveSwap( &sourceSet->jointSims, localIndex );
604
+ if ( movedIndex != B2_NULL_INDEX )
605
+ {
606
+ // fix swapped element
607
+ b2JointSim* movedJointSim = sourceSet->jointSims.data + localIndex;
608
+ int movedId = movedJointSim->jointId;
609
+ b2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId );
610
+ movedJoint->localIndex = localIndex;
611
+ }
612
+ }
613
+ }
@@ -0,0 +1,57 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #pragma once
5
+
6
+ #include "array.h"
7
+
8
+ typedef struct b2Body b2Body;
9
+ typedef struct b2Joint b2Joint;
10
+ typedef struct b2World b2World;
11
+
12
+ // This holds solver set data. The following sets are used:
13
+ // - static set for all static bodies (no contacts or joints)
14
+ // - active set for all active bodies with body states (no contacts or joints)
15
+ // - disabled set for disabled bodies and their joints
16
+ // - all further sets are sleeping island sets along with their contacts and joints
17
+ // The purpose of solver sets is to achieve high memory locality.
18
+ // https://www.youtube.com/watch?v=nZNd5FjSquk
19
+ typedef struct b2SolverSet
20
+ {
21
+ // Body array. Empty for unused set.
22
+ b2BodySimArray bodySims;
23
+
24
+ // Body state only exists for active set
25
+ b2BodyStateArray bodyStates;
26
+
27
+ // This holds sleeping/disabled joints. Empty for static/active set.
28
+ b2JointSimArray jointSims;
29
+
30
+ // This holds all contacts for sleeping sets.
31
+ // This holds non-touching contacts for the awake set.
32
+ b2ContactSimArray contactSims;
33
+
34
+ // The awake set has an array of islands. Sleeping sets normally have a single islands. However, joints
35
+ // created between sleeping sets causes the sets to merge, leaving them with multiple islands. These sleeping
36
+ // islands will be naturally merged with the set is woken.
37
+ // The static and disabled sets have no islands.
38
+ // Islands live in the solver sets to limit the number of islands that need to be considered for sleeping.
39
+ b2IslandSimArray islandSims;
40
+
41
+ // Aligns with b2World::solverSetIdPool. Used to create a stable id for body/contact/joint/islands.
42
+ int setIndex;
43
+ } b2SolverSet;
44
+
45
+ void b2DestroySolverSet( b2World* world, int setIndex );
46
+
47
+ void b2WakeSolverSet( b2World* world, int setIndex );
48
+ void b2TrySleepIsland( b2World* world, int islandId );
49
+
50
+ // Merge set 2 into set 1 then destroy set 2.
51
+ // Warning: any pointers into these sets will be orphaned.
52
+ void b2MergeSolverSets( b2World* world, int setIndex1, int setIndex2 );
53
+
54
+ void b2TransferBody( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Body* body );
55
+ void b2TransferJoint( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Joint* joint );
56
+
57
+ B2_ARRAY_INLINE( b2SolverSet, b2SolverSet )