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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +336 -0
- data/examples/contact_events.rb +22 -0
- data/examples/debug_draw.rb +15 -0
- data/examples/falling_ball.rb +19 -0
- data/examples/rugl_debug_draw.rb +68 -0
- data/examples/stagecraft_debug_draw.rb +64 -0
- data/examples/support/box2d_debug_lines.rb +125 -0
- data/ext/box2d/CMakeLists.txt +35 -0
- data/ext/box2d/extconf.rb +42 -0
- data/ext/box2d/native.c +9 -0
- data/ext/box2d/vendor/box2d/LICENSE +21 -0
- data/ext/box2d/vendor/box2d/VERSION +1 -0
- data/ext/box2d/vendor/box2d/include/box2d/base.h +131 -0
- data/ext/box2d/vendor/box2d/include/box2d/box2d.h +1222 -0
- data/ext/box2d/vendor/box2d/include/box2d/collision.h +830 -0
- data/ext/box2d/vendor/box2d/include/box2d/id.h +144 -0
- data/ext/box2d/vendor/box2d/include/box2d/math_functions.h +761 -0
- data/ext/box2d/vendor/box2d/include/box2d/types.h +1457 -0
- data/ext/box2d/vendor/box2d/src/CMakeLists.txt +223 -0
- data/ext/box2d/vendor/box2d/src/aabb.c +132 -0
- data/ext/box2d/vendor/box2d/src/aabb.h +56 -0
- data/ext/box2d/vendor/box2d/src/arena_allocator.c +112 -0
- data/ext/box2d/vendor/box2d/src/arena_allocator.h +48 -0
- data/ext/box2d/vendor/box2d/src/array.c +8 -0
- data/ext/box2d/vendor/box2d/src/array.h +179 -0
- data/ext/box2d/vendor/box2d/src/atomic.h +79 -0
- data/ext/box2d/vendor/box2d/src/bitset.c +67 -0
- data/ext/box2d/vendor/box2d/src/bitset.h +65 -0
- data/ext/box2d/vendor/box2d/src/body.c +1884 -0
- data/ext/box2d/vendor/box2d/src/body.h +194 -0
- data/ext/box2d/vendor/box2d/src/box2d.natvis +41 -0
- data/ext/box2d/vendor/box2d/src/broad_phase.c +524 -0
- data/ext/box2d/vendor/box2d/src/broad_phase.h +83 -0
- data/ext/box2d/vendor/box2d/src/constants.h +54 -0
- data/ext/box2d/vendor/box2d/src/constraint_graph.c +322 -0
- data/ext/box2d/vendor/box2d/src/constraint_graph.h +58 -0
- data/ext/box2d/vendor/box2d/src/contact.c +650 -0
- data/ext/box2d/vendor/box2d/src/contact.h +148 -0
- data/ext/box2d/vendor/box2d/src/contact_solver.c +2120 -0
- data/ext/box2d/vendor/box2d/src/contact_solver.h +54 -0
- data/ext/box2d/vendor/box2d/src/core.c +178 -0
- data/ext/box2d/vendor/box2d/src/core.h +149 -0
- data/ext/box2d/vendor/box2d/src/ctz.h +112 -0
- data/ext/box2d/vendor/box2d/src/distance.c +1415 -0
- data/ext/box2d/vendor/box2d/src/distance_joint.c +556 -0
- data/ext/box2d/vendor/box2d/src/dynamic_tree.c +1989 -0
- data/ext/box2d/vendor/box2d/src/geometry.c +1028 -0
- data/ext/box2d/vendor/box2d/src/hull.c +328 -0
- data/ext/box2d/vendor/box2d/src/id_pool.c +79 -0
- data/ext/box2d/vendor/box2d/src/id_pool.h +35 -0
- data/ext/box2d/vendor/box2d/src/island.c +977 -0
- data/ext/box2d/vendor/box2d/src/island.h +89 -0
- data/ext/box2d/vendor/box2d/src/joint.c +1272 -0
- data/ext/box2d/vendor/box2d/src/joint.h +335 -0
- data/ext/box2d/vendor/box2d/src/manifold.c +1726 -0
- data/ext/box2d/vendor/box2d/src/math_functions.c +159 -0
- data/ext/box2d/vendor/box2d/src/motor_joint.c +283 -0
- data/ext/box2d/vendor/box2d/src/mouse_joint.c +214 -0
- data/ext/box2d/vendor/box2d/src/mover.c +73 -0
- data/ext/box2d/vendor/box2d/src/prismatic_joint.c +656 -0
- data/ext/box2d/vendor/box2d/src/revolute_joint.c +534 -0
- data/ext/box2d/vendor/box2d/src/sensor.c +389 -0
- data/ext/box2d/vendor/box2d/src/sensor.h +36 -0
- data/ext/box2d/vendor/box2d/src/shape.c +1714 -0
- data/ext/box2d/vendor/box2d/src/shape.h +123 -0
- data/ext/box2d/vendor/box2d/src/solver.c +2038 -0
- data/ext/box2d/vendor/box2d/src/solver.h +155 -0
- data/ext/box2d/vendor/box2d/src/solver_set.c +613 -0
- data/ext/box2d/vendor/box2d/src/solver_set.h +57 -0
- data/ext/box2d/vendor/box2d/src/table.c +238 -0
- data/ext/box2d/vendor/box2d/src/table.h +37 -0
- data/ext/box2d/vendor/box2d/src/timer.c +185 -0
- data/ext/box2d/vendor/box2d/src/types.c +151 -0
- data/ext/box2d/vendor/box2d/src/weld_joint.c +310 -0
- data/ext/box2d/vendor/box2d/src/wheel_joint.c +551 -0
- data/ext/box2d/vendor/box2d/src/world.c +3301 -0
- data/ext/box2d/vendor/box2d/src/world.h +192 -0
- data/generator/generate.rb +316 -0
- data/generator/layout_probe.c +507 -0
- data/generator/verify_layouts.rb +32 -0
- data/lib/box2d/body.rb +172 -0
- data/lib/box2d/body_definition.rb +38 -0
- data/lib/box2d/body_shapes.rb +135 -0
- data/lib/box2d/chain.rb +61 -0
- data/lib/box2d/debug_draw.rb +118 -0
- data/lib/box2d/events.rb +103 -0
- data/lib/box2d/fixed_stepper.rb +39 -0
- data/lib/box2d/handle.rb +46 -0
- data/lib/box2d/hit.rb +5 -0
- data/lib/box2d/joint.rb +197 -0
- data/lib/box2d/native.rb +1462 -0
- data/lib/box2d/native_loader.rb +45 -0
- data/lib/box2d/pixel_scale.rb +29 -0
- data/lib/box2d/shape.rb +109 -0
- data/lib/box2d/shape_definition.rb +44 -0
- data/lib/box2d/value_conversion.rb +80 -0
- data/lib/box2d/version.rb +7 -0
- data/lib/box2d/world.rb +135 -0
- data/lib/box2d/world_joints.rb +156 -0
- data/lib/box2d/world_queries.rb +122 -0
- data/lib/box2d/world_registry.rb +95 -0
- data/lib/box2d.rb +31 -0
- data/script/build_platform_gem.rb +24 -0
- data/script/deterministic_scene.rb +56 -0
- data/script/update_deterministic_snapshot.rb +11 -0
- data/script/verify_platform_gem.rb +24 -0
- metadata +164 -0
|
@@ -0,0 +1,977 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2023 Erin Catto
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
#include "island.h"
|
|
5
|
+
|
|
6
|
+
#include "body.h"
|
|
7
|
+
#include "contact.h"
|
|
8
|
+
#include "core.h"
|
|
9
|
+
#include "joint.h"
|
|
10
|
+
#include "solver_set.h"
|
|
11
|
+
#include "world.h"
|
|
12
|
+
|
|
13
|
+
#include <stddef.h>
|
|
14
|
+
|
|
15
|
+
B2_ARRAY_SOURCE( b2Island, b2Island )
|
|
16
|
+
B2_ARRAY_SOURCE( b2IslandSim, b2IslandSim )
|
|
17
|
+
|
|
18
|
+
b2Island* b2CreateIsland( b2World* world, int setIndex )
|
|
19
|
+
{
|
|
20
|
+
B2_ASSERT( setIndex == b2_awakeSet || setIndex >= b2_firstSleepingSet );
|
|
21
|
+
|
|
22
|
+
int islandId = b2AllocId( &world->islandIdPool );
|
|
23
|
+
|
|
24
|
+
if ( islandId == world->islands.count )
|
|
25
|
+
{
|
|
26
|
+
b2Island emptyIsland = { 0 };
|
|
27
|
+
b2IslandArray_Push( &world->islands, emptyIsland );
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
{
|
|
31
|
+
B2_ASSERT( world->islands.data[islandId].setIndex == B2_NULL_INDEX );
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );
|
|
35
|
+
|
|
36
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandId );
|
|
37
|
+
island->setIndex = setIndex;
|
|
38
|
+
island->localIndex = set->islandSims.count;
|
|
39
|
+
island->islandId = islandId;
|
|
40
|
+
island->headBody = B2_NULL_INDEX;
|
|
41
|
+
island->tailBody = B2_NULL_INDEX;
|
|
42
|
+
island->bodyCount = 0;
|
|
43
|
+
island->headContact = B2_NULL_INDEX;
|
|
44
|
+
island->tailContact = B2_NULL_INDEX;
|
|
45
|
+
island->contactCount = 0;
|
|
46
|
+
island->headJoint = B2_NULL_INDEX;
|
|
47
|
+
island->tailJoint = B2_NULL_INDEX;
|
|
48
|
+
island->jointCount = 0;
|
|
49
|
+
island->parentIsland = B2_NULL_INDEX;
|
|
50
|
+
island->constraintRemoveCount = 0;
|
|
51
|
+
|
|
52
|
+
b2IslandSim* islandSim = b2IslandSimArray_Add( &set->islandSims );
|
|
53
|
+
islandSim->islandId = islandId;
|
|
54
|
+
|
|
55
|
+
return island;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
void b2DestroyIsland( b2World* world, int islandId )
|
|
59
|
+
{
|
|
60
|
+
if (world->splitIslandId == islandId)
|
|
61
|
+
{
|
|
62
|
+
world->splitIslandId = B2_NULL_INDEX;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// assume island is empty
|
|
66
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandId );
|
|
67
|
+
b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, island->setIndex );
|
|
68
|
+
int movedIndex = b2IslandSimArray_RemoveSwap( &set->islandSims, island->localIndex );
|
|
69
|
+
if ( movedIndex != B2_NULL_INDEX )
|
|
70
|
+
{
|
|
71
|
+
// Fix index on moved element
|
|
72
|
+
b2IslandSim* movedElement = set->islandSims.data + island->localIndex;
|
|
73
|
+
int movedId = movedElement->islandId;
|
|
74
|
+
b2Island* movedIsland = b2IslandArray_Get( &world->islands, movedId );
|
|
75
|
+
B2_ASSERT( movedIsland->localIndex == movedIndex );
|
|
76
|
+
movedIsland->localIndex = island->localIndex;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Free island and id (preserve island revision)
|
|
80
|
+
island->islandId = B2_NULL_INDEX;
|
|
81
|
+
island->setIndex = B2_NULL_INDEX;
|
|
82
|
+
island->localIndex = B2_NULL_INDEX;
|
|
83
|
+
b2FreeId( &world->islandIdPool, islandId );
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static void b2AddContactToIsland( b2World* world, int islandId, b2Contact* contact )
|
|
87
|
+
{
|
|
88
|
+
B2_ASSERT( contact->islandId == B2_NULL_INDEX );
|
|
89
|
+
B2_ASSERT( contact->islandPrev == B2_NULL_INDEX );
|
|
90
|
+
B2_ASSERT( contact->islandNext == B2_NULL_INDEX );
|
|
91
|
+
|
|
92
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandId );
|
|
93
|
+
|
|
94
|
+
if ( island->headContact != B2_NULL_INDEX )
|
|
95
|
+
{
|
|
96
|
+
contact->islandNext = island->headContact;
|
|
97
|
+
b2Contact* headContact = b2ContactArray_Get( &world->contacts, island->headContact);
|
|
98
|
+
headContact->islandPrev = contact->contactId;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
island->headContact = contact->contactId;
|
|
102
|
+
if ( island->tailContact == B2_NULL_INDEX )
|
|
103
|
+
{
|
|
104
|
+
island->tailContact = island->headContact;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
island->contactCount += 1;
|
|
108
|
+
contact->islandId = islandId;
|
|
109
|
+
|
|
110
|
+
b2ValidateIsland( world, islandId );
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Link a contact into an island.
|
|
114
|
+
// This performs union-find and path compression to join islands.
|
|
115
|
+
// https://en.wikipedia.org/wiki/Disjoint-set_data_structure
|
|
116
|
+
void b2LinkContact( b2World* world, b2Contact* contact )
|
|
117
|
+
{
|
|
118
|
+
B2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) != 0 );
|
|
119
|
+
|
|
120
|
+
int bodyIdA = contact->edges[0].bodyId;
|
|
121
|
+
int bodyIdB = contact->edges[1].bodyId;
|
|
122
|
+
|
|
123
|
+
b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );
|
|
124
|
+
b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );
|
|
125
|
+
|
|
126
|
+
B2_ASSERT( bodyA->setIndex != b2_disabledSet && bodyB->setIndex != b2_disabledSet );
|
|
127
|
+
B2_ASSERT( bodyA->setIndex != b2_staticSet || bodyB->setIndex != b2_staticSet );
|
|
128
|
+
|
|
129
|
+
// Wake bodyB if bodyA is awake and bodyB is sleeping
|
|
130
|
+
if ( bodyA->setIndex == b2_awakeSet && bodyB->setIndex >= b2_firstSleepingSet )
|
|
131
|
+
{
|
|
132
|
+
b2WakeSolverSet( world, bodyB->setIndex );
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Wake bodyA if bodyB is awake and bodyA is sleeping
|
|
136
|
+
if ( bodyB->setIndex == b2_awakeSet && bodyA->setIndex >= b2_firstSleepingSet )
|
|
137
|
+
{
|
|
138
|
+
b2WakeSolverSet( world, bodyA->setIndex );
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
int islandIdA = bodyA->islandId;
|
|
142
|
+
int islandIdB = bodyB->islandId;
|
|
143
|
+
|
|
144
|
+
// Static bodies have null island indices.
|
|
145
|
+
B2_ASSERT( bodyA->setIndex != b2_staticSet || islandIdA == B2_NULL_INDEX );
|
|
146
|
+
B2_ASSERT( bodyB->setIndex != b2_staticSet || islandIdB == B2_NULL_INDEX );
|
|
147
|
+
B2_ASSERT( islandIdA != B2_NULL_INDEX || islandIdB != B2_NULL_INDEX );
|
|
148
|
+
|
|
149
|
+
if ( islandIdA == islandIdB )
|
|
150
|
+
{
|
|
151
|
+
// Contact in same island
|
|
152
|
+
b2AddContactToIsland( world, islandIdA, contact );
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Union-find root of islandA
|
|
157
|
+
b2Island* islandA = NULL;
|
|
158
|
+
if ( islandIdA != B2_NULL_INDEX )
|
|
159
|
+
{
|
|
160
|
+
islandA = b2IslandArray_Get( &world->islands, islandIdA );
|
|
161
|
+
int parentId = islandA->parentIsland;
|
|
162
|
+
while ( parentId != B2_NULL_INDEX )
|
|
163
|
+
{
|
|
164
|
+
b2Island* parent = b2IslandArray_Get( &world->islands, parentId );
|
|
165
|
+
if ( parent->parentIsland != B2_NULL_INDEX )
|
|
166
|
+
{
|
|
167
|
+
// path compression
|
|
168
|
+
islandA->parentIsland = parent->parentIsland;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
islandA = parent;
|
|
172
|
+
islandIdA = parentId;
|
|
173
|
+
parentId = islandA->parentIsland;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Union-find root of islandB
|
|
178
|
+
b2Island* islandB = NULL;
|
|
179
|
+
if ( islandIdB != B2_NULL_INDEX )
|
|
180
|
+
{
|
|
181
|
+
islandB = b2IslandArray_Get( &world->islands, islandIdB );
|
|
182
|
+
int parentId = islandB->parentIsland;
|
|
183
|
+
while ( islandB->parentIsland != B2_NULL_INDEX )
|
|
184
|
+
{
|
|
185
|
+
b2Island* parent = b2IslandArray_Get( &world->islands, parentId );
|
|
186
|
+
if ( parent->parentIsland != B2_NULL_INDEX )
|
|
187
|
+
{
|
|
188
|
+
// path compression
|
|
189
|
+
islandB->parentIsland = parent->parentIsland;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
islandB = parent;
|
|
193
|
+
islandIdB = parentId;
|
|
194
|
+
parentId = islandB->parentIsland;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
B2_ASSERT( islandA != NULL || islandB != NULL );
|
|
199
|
+
|
|
200
|
+
// Union-Find link island roots
|
|
201
|
+
if ( islandA != islandB && islandA != NULL && islandB != NULL )
|
|
202
|
+
{
|
|
203
|
+
B2_ASSERT( islandA != islandB );
|
|
204
|
+
B2_ASSERT( islandB->parentIsland == B2_NULL_INDEX );
|
|
205
|
+
islandB->parentIsland = islandIdA;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if ( islandA != NULL )
|
|
209
|
+
{
|
|
210
|
+
b2AddContactToIsland( world, islandIdA, contact );
|
|
211
|
+
}
|
|
212
|
+
else
|
|
213
|
+
{
|
|
214
|
+
b2AddContactToIsland( world, islandIdB, contact );
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// todo why not merge the islands right here?
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// This is called when a contact no longer has contact points or when a contact is destroyed.
|
|
221
|
+
void b2UnlinkContact( b2World* world, b2Contact* contact )
|
|
222
|
+
{
|
|
223
|
+
B2_ASSERT( contact->islandId != B2_NULL_INDEX );
|
|
224
|
+
|
|
225
|
+
// remove from island
|
|
226
|
+
int islandId = contact->islandId;
|
|
227
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandId );
|
|
228
|
+
|
|
229
|
+
if ( contact->islandPrev != B2_NULL_INDEX )
|
|
230
|
+
{
|
|
231
|
+
b2Contact* prevContact = b2ContactArray_Get( &world->contacts, contact->islandPrev);
|
|
232
|
+
B2_ASSERT( prevContact->islandNext == contact->contactId );
|
|
233
|
+
prevContact->islandNext = contact->islandNext;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if ( contact->islandNext != B2_NULL_INDEX )
|
|
237
|
+
{
|
|
238
|
+
b2Contact* nextContact = b2ContactArray_Get( &world->contacts, contact->islandNext );
|
|
239
|
+
B2_ASSERT( nextContact->islandPrev == contact->contactId );
|
|
240
|
+
nextContact->islandPrev = contact->islandPrev;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if ( island->headContact == contact->contactId )
|
|
244
|
+
{
|
|
245
|
+
island->headContact = contact->islandNext;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if ( island->tailContact == contact->contactId )
|
|
249
|
+
{
|
|
250
|
+
island->tailContact = contact->islandPrev;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
B2_ASSERT( island->contactCount > 0 );
|
|
254
|
+
island->contactCount -= 1;
|
|
255
|
+
island->constraintRemoveCount += 1;
|
|
256
|
+
|
|
257
|
+
contact->islandId = B2_NULL_INDEX;
|
|
258
|
+
contact->islandPrev = B2_NULL_INDEX;
|
|
259
|
+
contact->islandNext = B2_NULL_INDEX;
|
|
260
|
+
|
|
261
|
+
b2ValidateIsland( world, islandId );
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
static void b2AddJointToIsland( b2World* world, int islandId, b2Joint* joint )
|
|
265
|
+
{
|
|
266
|
+
B2_ASSERT( joint->islandId == B2_NULL_INDEX );
|
|
267
|
+
B2_ASSERT( joint->islandPrev == B2_NULL_INDEX );
|
|
268
|
+
B2_ASSERT( joint->islandNext == B2_NULL_INDEX );
|
|
269
|
+
|
|
270
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandId );
|
|
271
|
+
|
|
272
|
+
if ( island->headJoint != B2_NULL_INDEX )
|
|
273
|
+
{
|
|
274
|
+
joint->islandNext = island->headJoint;
|
|
275
|
+
b2Joint* headJoint = b2JointArray_Get( &world->joints, island->headJoint );
|
|
276
|
+
headJoint->islandPrev = joint->jointId;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
island->headJoint = joint->jointId;
|
|
280
|
+
if ( island->tailJoint == B2_NULL_INDEX )
|
|
281
|
+
{
|
|
282
|
+
island->tailJoint = island->headJoint;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
island->jointCount += 1;
|
|
286
|
+
joint->islandId = islandId;
|
|
287
|
+
|
|
288
|
+
b2ValidateIsland( world, islandId );
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
void b2LinkJoint( b2World* world, b2Joint* joint, bool mergeIslands )
|
|
292
|
+
{
|
|
293
|
+
b2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId );
|
|
294
|
+
b2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId );
|
|
295
|
+
|
|
296
|
+
if ( bodyA->setIndex == b2_awakeSet && bodyB->setIndex >= b2_firstSleepingSet )
|
|
297
|
+
{
|
|
298
|
+
b2WakeSolverSet( world, bodyB->setIndex );
|
|
299
|
+
}
|
|
300
|
+
else if ( bodyB->setIndex == b2_awakeSet && bodyA->setIndex >= b2_firstSleepingSet )
|
|
301
|
+
{
|
|
302
|
+
b2WakeSolverSet( world, bodyA->setIndex );
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
int islandIdA = bodyA->islandId;
|
|
306
|
+
int islandIdB = bodyB->islandId;
|
|
307
|
+
|
|
308
|
+
B2_ASSERT( islandIdA != B2_NULL_INDEX || islandIdB != B2_NULL_INDEX );
|
|
309
|
+
|
|
310
|
+
if ( islandIdA == islandIdB )
|
|
311
|
+
{
|
|
312
|
+
// Joint in same island
|
|
313
|
+
b2AddJointToIsland( world, islandIdA, joint );
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Union-find root of islandA
|
|
318
|
+
b2Island* islandA = NULL;
|
|
319
|
+
if ( islandIdA != B2_NULL_INDEX )
|
|
320
|
+
{
|
|
321
|
+
islandA = b2IslandArray_Get( &world->islands, islandIdA );
|
|
322
|
+
while ( islandA->parentIsland != B2_NULL_INDEX )
|
|
323
|
+
{
|
|
324
|
+
b2Island* parent = b2IslandArray_Get( &world->islands, islandA->parentIsland );
|
|
325
|
+
if ( parent->parentIsland != B2_NULL_INDEX )
|
|
326
|
+
{
|
|
327
|
+
// path compression
|
|
328
|
+
islandA->parentIsland = parent->parentIsland;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
islandIdA = islandA->parentIsland;
|
|
332
|
+
islandA = parent;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Union-find root of islandB
|
|
337
|
+
b2Island* islandB = NULL;
|
|
338
|
+
if ( islandIdB != B2_NULL_INDEX )
|
|
339
|
+
{
|
|
340
|
+
islandB = b2IslandArray_Get( &world->islands, islandIdB );
|
|
341
|
+
while ( islandB->parentIsland != B2_NULL_INDEX )
|
|
342
|
+
{
|
|
343
|
+
b2Island* parent = b2IslandArray_Get( &world->islands, islandB->parentIsland );
|
|
344
|
+
if ( parent->parentIsland != B2_NULL_INDEX )
|
|
345
|
+
{
|
|
346
|
+
// path compression
|
|
347
|
+
islandB->parentIsland = parent->parentIsland;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
islandIdB = islandB->parentIsland;
|
|
351
|
+
islandB = parent;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
B2_ASSERT( islandA != NULL || islandB != NULL );
|
|
356
|
+
|
|
357
|
+
// Union-Find link island roots
|
|
358
|
+
if ( islandA != islandB && islandA != NULL && islandB != NULL )
|
|
359
|
+
{
|
|
360
|
+
B2_ASSERT( islandA != islandB );
|
|
361
|
+
B2_ASSERT( islandB->parentIsland == B2_NULL_INDEX );
|
|
362
|
+
islandB->parentIsland = islandIdA;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if ( islandA != NULL )
|
|
366
|
+
{
|
|
367
|
+
b2AddJointToIsland( world, islandIdA, joint );
|
|
368
|
+
}
|
|
369
|
+
else
|
|
370
|
+
{
|
|
371
|
+
b2AddJointToIsland( world, islandIdB, joint );
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Joints need to have islands merged immediately when they are created
|
|
375
|
+
// to keep the island graph valid.
|
|
376
|
+
// However, when a body type is being changed the merge can be deferred until
|
|
377
|
+
// all joints are linked.
|
|
378
|
+
if (mergeIslands)
|
|
379
|
+
{
|
|
380
|
+
b2MergeAwakeIslands( world );
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
void b2UnlinkJoint( b2World* world, b2Joint* joint )
|
|
385
|
+
{
|
|
386
|
+
B2_ASSERT( joint->islandId != B2_NULL_INDEX );
|
|
387
|
+
|
|
388
|
+
// remove from island
|
|
389
|
+
int islandId = joint->islandId;
|
|
390
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandId );
|
|
391
|
+
|
|
392
|
+
if ( joint->islandPrev != B2_NULL_INDEX )
|
|
393
|
+
{
|
|
394
|
+
b2Joint* prevJoint = b2JointArray_Get( &world->joints, joint->islandPrev );
|
|
395
|
+
B2_ASSERT( prevJoint->islandNext == joint->jointId );
|
|
396
|
+
prevJoint->islandNext = joint->islandNext;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if ( joint->islandNext != B2_NULL_INDEX )
|
|
400
|
+
{
|
|
401
|
+
b2Joint* nextJoint = b2JointArray_Get( &world->joints, joint->islandNext );
|
|
402
|
+
B2_ASSERT( nextJoint->islandPrev == joint->jointId );
|
|
403
|
+
nextJoint->islandPrev = joint->islandPrev;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if ( island->headJoint == joint->jointId )
|
|
407
|
+
{
|
|
408
|
+
island->headJoint = joint->islandNext;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if ( island->tailJoint == joint->jointId )
|
|
412
|
+
{
|
|
413
|
+
island->tailJoint = joint->islandPrev;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
B2_ASSERT( island->jointCount > 0 );
|
|
417
|
+
island->jointCount -= 1;
|
|
418
|
+
island->constraintRemoveCount += 1;
|
|
419
|
+
|
|
420
|
+
joint->islandId = B2_NULL_INDEX;
|
|
421
|
+
joint->islandPrev = B2_NULL_INDEX;
|
|
422
|
+
joint->islandNext = B2_NULL_INDEX;
|
|
423
|
+
|
|
424
|
+
b2ValidateIsland( world, islandId );
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Merge an island into its root island.
|
|
428
|
+
// todo we can assume all islands are awake here
|
|
429
|
+
static void b2MergeIsland( b2World* world, b2Island* island )
|
|
430
|
+
{
|
|
431
|
+
B2_ASSERT( island->parentIsland != B2_NULL_INDEX );
|
|
432
|
+
|
|
433
|
+
int rootId = island->parentIsland;
|
|
434
|
+
b2Island* rootIsland = b2IslandArray_Get( &world->islands, rootId );
|
|
435
|
+
B2_ASSERT( rootIsland->parentIsland == B2_NULL_INDEX );
|
|
436
|
+
|
|
437
|
+
// remap island indices
|
|
438
|
+
int bodyId = island->headBody;
|
|
439
|
+
while ( bodyId != B2_NULL_INDEX )
|
|
440
|
+
{
|
|
441
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, bodyId );
|
|
442
|
+
body->islandId = rootId;
|
|
443
|
+
bodyId = body->islandNext;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
int contactId = island->headContact;
|
|
447
|
+
while ( contactId != B2_NULL_INDEX )
|
|
448
|
+
{
|
|
449
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
|
|
450
|
+
contact->islandId = rootId;
|
|
451
|
+
contactId = contact->islandNext;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
int jointId = island->headJoint;
|
|
455
|
+
while ( jointId != B2_NULL_INDEX )
|
|
456
|
+
{
|
|
457
|
+
b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
|
|
458
|
+
joint->islandId = rootId;
|
|
459
|
+
jointId = joint->islandNext;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// connect body lists
|
|
463
|
+
B2_ASSERT( rootIsland->tailBody != B2_NULL_INDEX );
|
|
464
|
+
b2Body* tailBody = b2BodyArray_Get( &world->bodies, rootIsland->tailBody );
|
|
465
|
+
B2_ASSERT( tailBody->islandNext == B2_NULL_INDEX );
|
|
466
|
+
tailBody->islandNext = island->headBody;
|
|
467
|
+
|
|
468
|
+
B2_ASSERT( island->headBody != B2_NULL_INDEX );
|
|
469
|
+
b2Body* headBody = b2BodyArray_Get( &world->bodies, island->headBody );
|
|
470
|
+
B2_ASSERT( headBody->islandPrev == B2_NULL_INDEX );
|
|
471
|
+
headBody->islandPrev = rootIsland->tailBody;
|
|
472
|
+
|
|
473
|
+
rootIsland->tailBody = island->tailBody;
|
|
474
|
+
rootIsland->bodyCount += island->bodyCount;
|
|
475
|
+
|
|
476
|
+
// connect contact lists
|
|
477
|
+
if ( rootIsland->headContact == B2_NULL_INDEX )
|
|
478
|
+
{
|
|
479
|
+
// Root island has no contacts
|
|
480
|
+
B2_ASSERT( rootIsland->tailContact == B2_NULL_INDEX && rootIsland->contactCount == 0 );
|
|
481
|
+
rootIsland->headContact = island->headContact;
|
|
482
|
+
rootIsland->tailContact = island->tailContact;
|
|
483
|
+
rootIsland->contactCount = island->contactCount;
|
|
484
|
+
}
|
|
485
|
+
else if ( island->headContact != B2_NULL_INDEX )
|
|
486
|
+
{
|
|
487
|
+
// Both islands have contacts
|
|
488
|
+
B2_ASSERT( island->tailContact != B2_NULL_INDEX && island->contactCount > 0 );
|
|
489
|
+
B2_ASSERT( rootIsland->tailContact != B2_NULL_INDEX && rootIsland->contactCount > 0 );
|
|
490
|
+
|
|
491
|
+
b2Contact* tailContact = b2ContactArray_Get( &world->contacts, rootIsland->tailContact );
|
|
492
|
+
B2_ASSERT( tailContact->islandNext == B2_NULL_INDEX );
|
|
493
|
+
tailContact->islandNext = island->headContact;
|
|
494
|
+
|
|
495
|
+
b2Contact* headContact = b2ContactArray_Get( &world->contacts, island->headContact );
|
|
496
|
+
B2_ASSERT( headContact->islandPrev == B2_NULL_INDEX );
|
|
497
|
+
headContact->islandPrev = rootIsland->tailContact;
|
|
498
|
+
|
|
499
|
+
rootIsland->tailContact = island->tailContact;
|
|
500
|
+
rootIsland->contactCount += island->contactCount;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if ( rootIsland->headJoint == B2_NULL_INDEX )
|
|
504
|
+
{
|
|
505
|
+
// Root island has no joints
|
|
506
|
+
B2_ASSERT( rootIsland->tailJoint == B2_NULL_INDEX && rootIsland->jointCount == 0 );
|
|
507
|
+
rootIsland->headJoint = island->headJoint;
|
|
508
|
+
rootIsland->tailJoint = island->tailJoint;
|
|
509
|
+
rootIsland->jointCount = island->jointCount;
|
|
510
|
+
}
|
|
511
|
+
else if ( island->headJoint != B2_NULL_INDEX )
|
|
512
|
+
{
|
|
513
|
+
// Both islands have joints
|
|
514
|
+
B2_ASSERT( island->tailJoint != B2_NULL_INDEX && island->jointCount > 0 );
|
|
515
|
+
B2_ASSERT( rootIsland->tailJoint != B2_NULL_INDEX && rootIsland->jointCount > 0 );
|
|
516
|
+
|
|
517
|
+
b2Joint* tailJoint = b2JointArray_Get( &world->joints, rootIsland->tailJoint );
|
|
518
|
+
B2_ASSERT( tailJoint->islandNext == B2_NULL_INDEX );
|
|
519
|
+
tailJoint->islandNext = island->headJoint;
|
|
520
|
+
|
|
521
|
+
b2Joint* headJoint = b2JointArray_Get( &world->joints, island->headJoint );
|
|
522
|
+
B2_ASSERT( headJoint->islandPrev == B2_NULL_INDEX );
|
|
523
|
+
headJoint->islandPrev = rootIsland->tailJoint;
|
|
524
|
+
|
|
525
|
+
rootIsland->tailJoint = island->tailJoint;
|
|
526
|
+
rootIsland->jointCount += island->jointCount;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Track removed constraints
|
|
530
|
+
rootIsland->constraintRemoveCount += island->constraintRemoveCount;
|
|
531
|
+
|
|
532
|
+
b2ValidateIsland( world, rootId );
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Iterate over all awake islands and merge any that need merging
|
|
536
|
+
// Islands that get merged into a root island will be removed from the awake island array
|
|
537
|
+
// and returned to the pool.
|
|
538
|
+
// todo this might be faster if b2IslandSim held the connectivity data
|
|
539
|
+
void b2MergeAwakeIslands( b2World* world )
|
|
540
|
+
{
|
|
541
|
+
b2TracyCZoneNC( merge_islands, "Merge Islands", b2_colorMediumTurquoise, true );
|
|
542
|
+
|
|
543
|
+
b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
|
|
544
|
+
b2IslandSim* islandSims = awakeSet->islandSims.data;
|
|
545
|
+
int awakeIslandCount = awakeSet->islandSims.count;
|
|
546
|
+
|
|
547
|
+
// Step 1: Ensure every child island points to its root island. This avoids merging a child island with
|
|
548
|
+
// a parent island that has already been merged with a grand-parent island.
|
|
549
|
+
for ( int i = 0; i < awakeIslandCount; ++i )
|
|
550
|
+
{
|
|
551
|
+
int islandId = islandSims[i].islandId;
|
|
552
|
+
|
|
553
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandId );
|
|
554
|
+
|
|
555
|
+
// find the root island
|
|
556
|
+
int rootId = islandId;
|
|
557
|
+
b2Island* rootIsland = island;
|
|
558
|
+
while ( rootIsland->parentIsland != B2_NULL_INDEX )
|
|
559
|
+
{
|
|
560
|
+
b2Island* parent = b2IslandArray_Get( &world->islands, rootIsland->parentIsland );
|
|
561
|
+
if ( parent->parentIsland != B2_NULL_INDEX )
|
|
562
|
+
{
|
|
563
|
+
// path compression
|
|
564
|
+
rootIsland->parentIsland = parent->parentIsland;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
rootId = rootIsland->parentIsland;
|
|
568
|
+
rootIsland = parent;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if ( rootIsland != island )
|
|
572
|
+
{
|
|
573
|
+
island->parentIsland = rootId;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Step 2: merge every awake island into its parent (which must be a root island)
|
|
578
|
+
// Reverse to support removal from awake array.
|
|
579
|
+
for ( int i = awakeIslandCount - 1; i >= 0; --i )
|
|
580
|
+
{
|
|
581
|
+
int islandId = islandSims[i].islandId;
|
|
582
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandId );
|
|
583
|
+
|
|
584
|
+
if ( island->parentIsland == B2_NULL_INDEX )
|
|
585
|
+
{
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
b2MergeIsland( world, island );
|
|
590
|
+
|
|
591
|
+
// this call does a remove swap from the end of the island sim array
|
|
592
|
+
b2DestroyIsland( world, islandId );
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
b2ValidateConnectivity( world );
|
|
596
|
+
|
|
597
|
+
b2TracyCZoneEnd( merge_islands );
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
#define B2_CONTACT_REMOVE_THRESHOLD 1
|
|
601
|
+
|
|
602
|
+
void b2SplitIsland( b2World* world, int baseId )
|
|
603
|
+
{
|
|
604
|
+
b2Island* baseIsland = b2IslandArray_Get( &world->islands, baseId );
|
|
605
|
+
int setIndex = baseIsland->setIndex;
|
|
606
|
+
|
|
607
|
+
if ( setIndex != b2_awakeSet )
|
|
608
|
+
{
|
|
609
|
+
// can only split awake island
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if ( baseIsland->constraintRemoveCount == 0 )
|
|
614
|
+
{
|
|
615
|
+
// this island doesn't need to be split
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
b2ValidateIsland( world, baseId );
|
|
620
|
+
|
|
621
|
+
int bodyCount = baseIsland->bodyCount;
|
|
622
|
+
|
|
623
|
+
b2Body* bodies = world->bodies.data;
|
|
624
|
+
b2ArenaAllocator* alloc = &world->arena;
|
|
625
|
+
|
|
626
|
+
// No lock is needed because I ensure the allocator is not used while this task is active.
|
|
627
|
+
int* stack = b2AllocateArenaItem( alloc, bodyCount * sizeof( int ), "island stack" );
|
|
628
|
+
int* bodyIds = b2AllocateArenaItem( alloc, bodyCount * sizeof( int ), "body ids" );
|
|
629
|
+
|
|
630
|
+
// Build array containing all body indices from base island. These
|
|
631
|
+
// serve as seed bodies for the depth first search (DFS).
|
|
632
|
+
int index = 0;
|
|
633
|
+
int nextBody = baseIsland->headBody;
|
|
634
|
+
while ( nextBody != B2_NULL_INDEX )
|
|
635
|
+
{
|
|
636
|
+
bodyIds[index++] = nextBody;
|
|
637
|
+
b2Body* body = bodies + nextBody;
|
|
638
|
+
|
|
639
|
+
// Clear visitation mark
|
|
640
|
+
body->isMarked = false;
|
|
641
|
+
|
|
642
|
+
nextBody = body->islandNext;
|
|
643
|
+
}
|
|
644
|
+
B2_ASSERT( index == bodyCount );
|
|
645
|
+
|
|
646
|
+
// Clear contact island flags. Only need to consider contacts
|
|
647
|
+
// already in the base island.
|
|
648
|
+
int nextContactId = baseIsland->headContact;
|
|
649
|
+
while ( nextContactId != B2_NULL_INDEX )
|
|
650
|
+
{
|
|
651
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, nextContactId );
|
|
652
|
+
contact->isMarked = false;
|
|
653
|
+
nextContactId = contact->islandNext;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Clear joint island flags.
|
|
657
|
+
int nextJoint = baseIsland->headJoint;
|
|
658
|
+
while ( nextJoint != B2_NULL_INDEX )
|
|
659
|
+
{
|
|
660
|
+
b2Joint* joint = b2JointArray_Get( &world->joints, nextJoint );
|
|
661
|
+
joint->isMarked = false;
|
|
662
|
+
nextJoint = joint->islandNext;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Done with the base split island.
|
|
666
|
+
b2DestroyIsland( world, baseId );
|
|
667
|
+
|
|
668
|
+
// Each island is found as a depth first search starting from a seed body
|
|
669
|
+
for ( int i = 0; i < bodyCount; ++i )
|
|
670
|
+
{
|
|
671
|
+
int seedIndex = bodyIds[i];
|
|
672
|
+
b2Body* seed = bodies + seedIndex;
|
|
673
|
+
B2_ASSERT( seed->setIndex == setIndex );
|
|
674
|
+
|
|
675
|
+
if ( seed->isMarked == true )
|
|
676
|
+
{
|
|
677
|
+
// The body has already been visited
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
int stackCount = 0;
|
|
682
|
+
stack[stackCount++] = seedIndex;
|
|
683
|
+
seed->isMarked = true;
|
|
684
|
+
|
|
685
|
+
// Create new island
|
|
686
|
+
// No lock needed because only a single island can split per time step. No islands are being used during the constraint
|
|
687
|
+
// solve. However, islands are touched during body finalization.
|
|
688
|
+
b2Island* island = b2CreateIsland( world, setIndex );
|
|
689
|
+
|
|
690
|
+
int islandId = island->islandId;
|
|
691
|
+
|
|
692
|
+
// Perform a depth first search (DFS) on the constraint graph.
|
|
693
|
+
while ( stackCount > 0 )
|
|
694
|
+
{
|
|
695
|
+
// Grab the next body off the stack and add it to the island.
|
|
696
|
+
int bodyId = stack[--stackCount];
|
|
697
|
+
b2Body* body = bodies + bodyId;
|
|
698
|
+
B2_ASSERT( body->setIndex == b2_awakeSet );
|
|
699
|
+
B2_ASSERT( body->isMarked == true );
|
|
700
|
+
|
|
701
|
+
// Add body to island
|
|
702
|
+
body->islandId = islandId;
|
|
703
|
+
if ( island->tailBody != B2_NULL_INDEX )
|
|
704
|
+
{
|
|
705
|
+
bodies[island->tailBody].islandNext = bodyId;
|
|
706
|
+
}
|
|
707
|
+
body->islandPrev = island->tailBody;
|
|
708
|
+
body->islandNext = B2_NULL_INDEX;
|
|
709
|
+
island->tailBody = bodyId;
|
|
710
|
+
|
|
711
|
+
if ( island->headBody == B2_NULL_INDEX )
|
|
712
|
+
{
|
|
713
|
+
island->headBody = bodyId;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
island->bodyCount += 1;
|
|
717
|
+
|
|
718
|
+
// Search all contacts connected to this body.
|
|
719
|
+
int contactKey = body->headContactKey;
|
|
720
|
+
while ( contactKey != B2_NULL_INDEX )
|
|
721
|
+
{
|
|
722
|
+
int contactId = contactKey >> 1;
|
|
723
|
+
int edgeIndex = contactKey & 1;
|
|
724
|
+
|
|
725
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
|
|
726
|
+
B2_ASSERT( contact->contactId == contactId );
|
|
727
|
+
|
|
728
|
+
// Next key
|
|
729
|
+
contactKey = contact->edges[edgeIndex].nextKey;
|
|
730
|
+
|
|
731
|
+
// Has this contact already been added to this island?
|
|
732
|
+
if ( contact->isMarked )
|
|
733
|
+
{
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Is this contact enabled and touching?
|
|
738
|
+
if ( ( contact->flags & b2_contactTouchingFlag ) == 0 )
|
|
739
|
+
{
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
contact->isMarked = true;
|
|
744
|
+
|
|
745
|
+
int otherEdgeIndex = edgeIndex ^ 1;
|
|
746
|
+
int otherBodyId = contact->edges[otherEdgeIndex].bodyId;
|
|
747
|
+
b2Body* otherBody = bodies + otherBodyId;
|
|
748
|
+
|
|
749
|
+
// Maybe add other body to stack
|
|
750
|
+
if ( otherBody->isMarked == false && otherBody->setIndex != b2_staticSet )
|
|
751
|
+
{
|
|
752
|
+
B2_ASSERT( stackCount < bodyCount );
|
|
753
|
+
stack[stackCount++] = otherBodyId;
|
|
754
|
+
otherBody->isMarked = true;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Add contact to island
|
|
758
|
+
contact->islandId = islandId;
|
|
759
|
+
if ( island->tailContact != B2_NULL_INDEX )
|
|
760
|
+
{
|
|
761
|
+
b2Contact* tailContact = b2ContactArray_Get( &world->contacts, island->tailContact );
|
|
762
|
+
tailContact->islandNext = contactId;
|
|
763
|
+
}
|
|
764
|
+
contact->islandPrev = island->tailContact;
|
|
765
|
+
contact->islandNext = B2_NULL_INDEX;
|
|
766
|
+
island->tailContact = contactId;
|
|
767
|
+
|
|
768
|
+
if ( island->headContact == B2_NULL_INDEX )
|
|
769
|
+
{
|
|
770
|
+
island->headContact = contactId;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
island->contactCount += 1;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Search all joints connect to this body.
|
|
777
|
+
int jointKey = body->headJointKey;
|
|
778
|
+
while ( jointKey != B2_NULL_INDEX )
|
|
779
|
+
{
|
|
780
|
+
int jointId = jointKey >> 1;
|
|
781
|
+
int edgeIndex = jointKey & 1;
|
|
782
|
+
|
|
783
|
+
b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
|
|
784
|
+
B2_ASSERT( joint->jointId == jointId );
|
|
785
|
+
|
|
786
|
+
// Next key
|
|
787
|
+
jointKey = joint->edges[edgeIndex].nextKey;
|
|
788
|
+
|
|
789
|
+
// Has this joint already been added to this island?
|
|
790
|
+
if ( joint->isMarked )
|
|
791
|
+
{
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
joint->isMarked = true;
|
|
796
|
+
|
|
797
|
+
int otherEdgeIndex = edgeIndex ^ 1;
|
|
798
|
+
int otherBodyId = joint->edges[otherEdgeIndex].bodyId;
|
|
799
|
+
b2Body* otherBody = bodies + otherBodyId;
|
|
800
|
+
|
|
801
|
+
// Don't simulate joints connected to disabled bodies.
|
|
802
|
+
if ( otherBody->setIndex == b2_disabledSet )
|
|
803
|
+
{
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Maybe add other body to stack
|
|
808
|
+
if ( otherBody->isMarked == false && otherBody->setIndex == b2_awakeSet )
|
|
809
|
+
{
|
|
810
|
+
B2_ASSERT( stackCount < bodyCount );
|
|
811
|
+
stack[stackCount++] = otherBodyId;
|
|
812
|
+
otherBody->isMarked = true;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Add joint to island
|
|
816
|
+
joint->islandId = islandId;
|
|
817
|
+
if ( island->tailJoint != B2_NULL_INDEX )
|
|
818
|
+
{
|
|
819
|
+
b2Joint* tailJoint = b2JointArray_Get( &world->joints, island->tailJoint );
|
|
820
|
+
tailJoint->islandNext = jointId;
|
|
821
|
+
}
|
|
822
|
+
joint->islandPrev = island->tailJoint;
|
|
823
|
+
joint->islandNext = B2_NULL_INDEX;
|
|
824
|
+
island->tailJoint = jointId;
|
|
825
|
+
|
|
826
|
+
if ( island->headJoint == B2_NULL_INDEX )
|
|
827
|
+
{
|
|
828
|
+
island->headJoint = jointId;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
island->jointCount += 1;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
b2ValidateIsland( world, islandId );
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
b2FreeArenaItem( alloc, bodyIds );
|
|
839
|
+
b2FreeArenaItem( alloc, stack );
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// Split an island because some contacts and/or joints have been removed.
|
|
843
|
+
// This is called during the constraint solve while islands are not being touched. This uses DFS and touches a lot of memory,
|
|
844
|
+
// so it can be quite slow.
|
|
845
|
+
// Note: contacts/joints connected to static bodies must belong to an island but don't affect island connectivity
|
|
846
|
+
// Note: static bodies are never in an island
|
|
847
|
+
// Note: this task interacts with some allocators without locks under the assumption that no other tasks
|
|
848
|
+
// are interacting with these data structures.
|
|
849
|
+
void b2SplitIslandTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )
|
|
850
|
+
{
|
|
851
|
+
b2TracyCZoneNC( split, "Split Island", b2_colorOlive, true );
|
|
852
|
+
|
|
853
|
+
B2_UNUSED( startIndex, endIndex, threadIndex );
|
|
854
|
+
|
|
855
|
+
uint64_t ticks = b2GetTicks();
|
|
856
|
+
b2World* world = context;
|
|
857
|
+
|
|
858
|
+
B2_ASSERT( world->splitIslandId != B2_NULL_INDEX );
|
|
859
|
+
|
|
860
|
+
b2SplitIsland( world, world->splitIslandId );
|
|
861
|
+
|
|
862
|
+
world->profile.splitIslands += b2GetMilliseconds( ticks );
|
|
863
|
+
b2TracyCZoneEnd( split );
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
#if B2_VALIDATE
|
|
867
|
+
void b2ValidateIsland( b2World* world, int islandId )
|
|
868
|
+
{
|
|
869
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandId );
|
|
870
|
+
B2_ASSERT( island->islandId == islandId );
|
|
871
|
+
B2_ASSERT( island->setIndex != B2_NULL_INDEX );
|
|
872
|
+
B2_ASSERT( island->headBody != B2_NULL_INDEX );
|
|
873
|
+
|
|
874
|
+
{
|
|
875
|
+
B2_ASSERT( island->tailBody != B2_NULL_INDEX );
|
|
876
|
+
B2_ASSERT( island->bodyCount > 0 );
|
|
877
|
+
if ( island->bodyCount > 1 )
|
|
878
|
+
{
|
|
879
|
+
B2_ASSERT( island->tailBody != island->headBody );
|
|
880
|
+
}
|
|
881
|
+
B2_ASSERT( island->bodyCount <= b2GetIdCount( &world->bodyIdPool ) );
|
|
882
|
+
|
|
883
|
+
int count = 0;
|
|
884
|
+
int bodyId = island->headBody;
|
|
885
|
+
while ( bodyId != B2_NULL_INDEX )
|
|
886
|
+
{
|
|
887
|
+
b2Body* body = b2BodyArray_Get(&world->bodies, bodyId);
|
|
888
|
+
B2_ASSERT( body->islandId == islandId );
|
|
889
|
+
B2_ASSERT( body->setIndex == island->setIndex );
|
|
890
|
+
count += 1;
|
|
891
|
+
|
|
892
|
+
if ( count == island->bodyCount )
|
|
893
|
+
{
|
|
894
|
+
B2_ASSERT( bodyId == island->tailBody );
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
bodyId = body->islandNext;
|
|
898
|
+
}
|
|
899
|
+
B2_ASSERT( count == island->bodyCount );
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
if ( island->headContact != B2_NULL_INDEX )
|
|
903
|
+
{
|
|
904
|
+
B2_ASSERT( island->tailContact != B2_NULL_INDEX );
|
|
905
|
+
B2_ASSERT( island->contactCount > 0 );
|
|
906
|
+
if ( island->contactCount > 1 )
|
|
907
|
+
{
|
|
908
|
+
B2_ASSERT( island->tailContact != island->headContact );
|
|
909
|
+
}
|
|
910
|
+
B2_ASSERT( island->contactCount <= b2GetIdCount( &world->contactIdPool ) );
|
|
911
|
+
|
|
912
|
+
int count = 0;
|
|
913
|
+
int contactId = island->headContact;
|
|
914
|
+
while ( contactId != B2_NULL_INDEX )
|
|
915
|
+
{
|
|
916
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
|
|
917
|
+
B2_ASSERT( contact->setIndex == island->setIndex );
|
|
918
|
+
B2_ASSERT( contact->islandId == islandId );
|
|
919
|
+
count += 1;
|
|
920
|
+
|
|
921
|
+
if ( count == island->contactCount )
|
|
922
|
+
{
|
|
923
|
+
B2_ASSERT( contactId == island->tailContact );
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
contactId = contact->islandNext;
|
|
927
|
+
}
|
|
928
|
+
B2_ASSERT( count == island->contactCount );
|
|
929
|
+
}
|
|
930
|
+
else
|
|
931
|
+
{
|
|
932
|
+
B2_ASSERT( island->tailContact == B2_NULL_INDEX );
|
|
933
|
+
B2_ASSERT( island->contactCount == 0 );
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
if ( island->headJoint != B2_NULL_INDEX )
|
|
937
|
+
{
|
|
938
|
+
B2_ASSERT( island->tailJoint != B2_NULL_INDEX );
|
|
939
|
+
B2_ASSERT( island->jointCount > 0 );
|
|
940
|
+
if ( island->jointCount > 1 )
|
|
941
|
+
{
|
|
942
|
+
B2_ASSERT( island->tailJoint != island->headJoint );
|
|
943
|
+
}
|
|
944
|
+
B2_ASSERT( island->jointCount <= b2GetIdCount( &world->jointIdPool ) );
|
|
945
|
+
|
|
946
|
+
int count = 0;
|
|
947
|
+
int jointId = island->headJoint;
|
|
948
|
+
while ( jointId != B2_NULL_INDEX )
|
|
949
|
+
{
|
|
950
|
+
b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
|
|
951
|
+
B2_ASSERT( joint->setIndex == island->setIndex );
|
|
952
|
+
count += 1;
|
|
953
|
+
|
|
954
|
+
if ( count == island->jointCount )
|
|
955
|
+
{
|
|
956
|
+
B2_ASSERT( jointId == island->tailJoint );
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
jointId = joint->islandNext;
|
|
960
|
+
}
|
|
961
|
+
B2_ASSERT( count == island->jointCount );
|
|
962
|
+
}
|
|
963
|
+
else
|
|
964
|
+
{
|
|
965
|
+
B2_ASSERT( island->tailJoint == B2_NULL_INDEX );
|
|
966
|
+
B2_ASSERT( island->jointCount == 0 );
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
#else
|
|
971
|
+
|
|
972
|
+
void b2ValidateIsland( b2World* world, int islandId )
|
|
973
|
+
{
|
|
974
|
+
B2_UNUSED( world );
|
|
975
|
+
B2_UNUSED( islandId );
|
|
976
|
+
}
|
|
977
|
+
#endif
|