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,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 )
|