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,3301 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2023 Erin Catto
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS )
|
|
5
|
+
#define _CRT_SECURE_NO_WARNINGS
|
|
6
|
+
#endif
|
|
7
|
+
|
|
8
|
+
#include "world.h"
|
|
9
|
+
|
|
10
|
+
#include "aabb.h"
|
|
11
|
+
#include "arena_allocator.h"
|
|
12
|
+
#include "array.h"
|
|
13
|
+
#include "bitset.h"
|
|
14
|
+
#include "body.h"
|
|
15
|
+
#include "broad_phase.h"
|
|
16
|
+
#include "constants.h"
|
|
17
|
+
#include "constraint_graph.h"
|
|
18
|
+
#include "contact.h"
|
|
19
|
+
#include "core.h"
|
|
20
|
+
#include "ctz.h"
|
|
21
|
+
#include "island.h"
|
|
22
|
+
#include "joint.h"
|
|
23
|
+
#include "sensor.h"
|
|
24
|
+
#include "shape.h"
|
|
25
|
+
#include "solver.h"
|
|
26
|
+
#include "solver_set.h"
|
|
27
|
+
|
|
28
|
+
#include "box2d/box2d.h"
|
|
29
|
+
|
|
30
|
+
#include <float.h>
|
|
31
|
+
#include <stdio.h>
|
|
32
|
+
#include <string.h>
|
|
33
|
+
|
|
34
|
+
_Static_assert( B2_MAX_WORLDS > 0, "must be 1 or more" );
|
|
35
|
+
_Static_assert( B2_MAX_WORLDS < UINT16_MAX, "B2_MAX_WORLDS limit exceeded" );
|
|
36
|
+
b2World b2_worlds[B2_MAX_WORLDS];
|
|
37
|
+
|
|
38
|
+
B2_ARRAY_SOURCE( b2BodyMoveEvent, b2BodyMoveEvent )
|
|
39
|
+
B2_ARRAY_SOURCE( b2ContactBeginTouchEvent, b2ContactBeginTouchEvent )
|
|
40
|
+
B2_ARRAY_SOURCE( b2ContactEndTouchEvent, b2ContactEndTouchEvent )
|
|
41
|
+
B2_ARRAY_SOURCE( b2ContactHitEvent, b2ContactHitEvent )
|
|
42
|
+
B2_ARRAY_SOURCE( b2SensorBeginTouchEvent, b2SensorBeginTouchEvent )
|
|
43
|
+
B2_ARRAY_SOURCE( b2SensorEndTouchEvent, b2SensorEndTouchEvent )
|
|
44
|
+
B2_ARRAY_SOURCE( b2TaskContext, b2TaskContext )
|
|
45
|
+
|
|
46
|
+
b2World* b2GetWorldFromId( b2WorldId id )
|
|
47
|
+
{
|
|
48
|
+
B2_ASSERT( 1 <= id.index1 && id.index1 <= B2_MAX_WORLDS );
|
|
49
|
+
b2World* world = b2_worlds + ( id.index1 - 1 );
|
|
50
|
+
B2_ASSERT( id.index1 == world->worldId + 1 );
|
|
51
|
+
B2_ASSERT( id.generation == world->generation );
|
|
52
|
+
return world;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
b2World* b2GetWorld( int index )
|
|
56
|
+
{
|
|
57
|
+
B2_ASSERT( 0 <= index && index < B2_MAX_WORLDS );
|
|
58
|
+
b2World* world = b2_worlds + index;
|
|
59
|
+
B2_ASSERT( world->worldId == index );
|
|
60
|
+
return world;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
b2World* b2GetWorldLocked( int index )
|
|
64
|
+
{
|
|
65
|
+
B2_ASSERT( 0 <= index && index < B2_MAX_WORLDS );
|
|
66
|
+
b2World* world = b2_worlds + index;
|
|
67
|
+
B2_ASSERT( world->worldId == index );
|
|
68
|
+
if ( world->locked )
|
|
69
|
+
{
|
|
70
|
+
B2_ASSERT( false );
|
|
71
|
+
return NULL;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return world;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static void* b2DefaultAddTaskFcn( b2TaskCallback* task, int count, int minRange, void* taskContext, void* userContext )
|
|
78
|
+
{
|
|
79
|
+
B2_UNUSED( minRange, userContext );
|
|
80
|
+
task( 0, count, 0, taskContext );
|
|
81
|
+
return NULL;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static void b2DefaultFinishTaskFcn( void* userTask, void* userContext )
|
|
85
|
+
{
|
|
86
|
+
B2_UNUSED( userTask, userContext );
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static float b2DefaultFrictionCallback( float frictionA, int materialA, float frictionB, int materialB )
|
|
90
|
+
{
|
|
91
|
+
B2_UNUSED( materialA, materialB );
|
|
92
|
+
return sqrtf( frictionA * frictionB );
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static float b2DefaultRestitutionCallback( float restitutionA, int materialA, float restitutionB, int materialB )
|
|
96
|
+
{
|
|
97
|
+
B2_UNUSED( materialA, materialB );
|
|
98
|
+
return b2MaxFloat( restitutionA, restitutionB );
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
b2WorldId b2CreateWorld( const b2WorldDef* def )
|
|
102
|
+
{
|
|
103
|
+
_Static_assert( B2_MAX_WORLDS < UINT16_MAX, "B2_MAX_WORLDS limit exceeded" );
|
|
104
|
+
B2_CHECK_DEF( def );
|
|
105
|
+
|
|
106
|
+
int worldId = B2_NULL_INDEX;
|
|
107
|
+
for ( int i = 0; i < B2_MAX_WORLDS; ++i )
|
|
108
|
+
{
|
|
109
|
+
if ( b2_worlds[i].inUse == false )
|
|
110
|
+
{
|
|
111
|
+
worldId = i;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if ( worldId == B2_NULL_INDEX )
|
|
117
|
+
{
|
|
118
|
+
return (b2WorldId){ 0 };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
b2InitializeContactRegisters();
|
|
122
|
+
|
|
123
|
+
b2World* world = b2_worlds + worldId;
|
|
124
|
+
uint16_t generation = world->generation;
|
|
125
|
+
|
|
126
|
+
*world = (b2World){ 0 };
|
|
127
|
+
|
|
128
|
+
world->worldId = (uint16_t)worldId;
|
|
129
|
+
world->generation = generation;
|
|
130
|
+
world->inUse = true;
|
|
131
|
+
|
|
132
|
+
world->arena = b2CreateArenaAllocator( 2048 );
|
|
133
|
+
b2CreateBroadPhase( &world->broadPhase );
|
|
134
|
+
b2CreateGraph( &world->constraintGraph, 16 );
|
|
135
|
+
|
|
136
|
+
// pools
|
|
137
|
+
world->bodyIdPool = b2CreateIdPool();
|
|
138
|
+
world->bodies = b2BodyArray_Create( 16 );
|
|
139
|
+
world->solverSets = b2SolverSetArray_Create( 8 );
|
|
140
|
+
|
|
141
|
+
// add empty static, active, and disabled body sets
|
|
142
|
+
world->solverSetIdPool = b2CreateIdPool();
|
|
143
|
+
b2SolverSet set = { 0 };
|
|
144
|
+
|
|
145
|
+
// static set
|
|
146
|
+
set.setIndex = b2AllocId( &world->solverSetIdPool );
|
|
147
|
+
b2SolverSetArray_Push( &world->solverSets, set );
|
|
148
|
+
B2_ASSERT( world->solverSets.data[b2_staticSet].setIndex == b2_staticSet );
|
|
149
|
+
|
|
150
|
+
// disabled set
|
|
151
|
+
set.setIndex = b2AllocId( &world->solverSetIdPool );
|
|
152
|
+
b2SolverSetArray_Push( &world->solverSets, set );
|
|
153
|
+
B2_ASSERT( world->solverSets.data[b2_disabledSet].setIndex == b2_disabledSet );
|
|
154
|
+
|
|
155
|
+
// awake set
|
|
156
|
+
set.setIndex = b2AllocId( &world->solverSetIdPool );
|
|
157
|
+
b2SolverSetArray_Push( &world->solverSets, set );
|
|
158
|
+
B2_ASSERT( world->solverSets.data[b2_awakeSet].setIndex == b2_awakeSet );
|
|
159
|
+
|
|
160
|
+
world->shapeIdPool = b2CreateIdPool();
|
|
161
|
+
world->shapes = b2ShapeArray_Create( 16 );
|
|
162
|
+
|
|
163
|
+
world->chainIdPool = b2CreateIdPool();
|
|
164
|
+
world->chainShapes = b2ChainShapeArray_Create( 4 );
|
|
165
|
+
|
|
166
|
+
world->contactIdPool = b2CreateIdPool();
|
|
167
|
+
world->contacts = b2ContactArray_Create( 16 );
|
|
168
|
+
|
|
169
|
+
world->jointIdPool = b2CreateIdPool();
|
|
170
|
+
world->joints = b2JointArray_Create( 16 );
|
|
171
|
+
|
|
172
|
+
world->islandIdPool = b2CreateIdPool();
|
|
173
|
+
world->islands = b2IslandArray_Create( 8 );
|
|
174
|
+
|
|
175
|
+
world->sensors = b2SensorArray_Create( 4 );
|
|
176
|
+
|
|
177
|
+
world->bodyMoveEvents = b2BodyMoveEventArray_Create( 4 );
|
|
178
|
+
world->sensorBeginEvents = b2SensorBeginTouchEventArray_Create( 4 );
|
|
179
|
+
world->sensorEndEvents[0] = b2SensorEndTouchEventArray_Create( 4 );
|
|
180
|
+
world->sensorEndEvents[1] = b2SensorEndTouchEventArray_Create( 4 );
|
|
181
|
+
world->contactBeginEvents = b2ContactBeginTouchEventArray_Create( 4 );
|
|
182
|
+
world->contactEndEvents[0] = b2ContactEndTouchEventArray_Create( 4 );
|
|
183
|
+
world->contactEndEvents[1] = b2ContactEndTouchEventArray_Create( 4 );
|
|
184
|
+
world->contactHitEvents = b2ContactHitEventArray_Create( 4 );
|
|
185
|
+
world->endEventArrayIndex = 0;
|
|
186
|
+
|
|
187
|
+
world->stepIndex = 0;
|
|
188
|
+
world->splitIslandId = B2_NULL_INDEX;
|
|
189
|
+
world->activeTaskCount = 0;
|
|
190
|
+
world->taskCount = 0;
|
|
191
|
+
world->gravity = def->gravity;
|
|
192
|
+
world->hitEventThreshold = def->hitEventThreshold;
|
|
193
|
+
world->restitutionThreshold = def->restitutionThreshold;
|
|
194
|
+
world->maxLinearSpeed = def->maximumLinearSpeed;
|
|
195
|
+
world->maxContactPushSpeed = def->maxContactPushSpeed;
|
|
196
|
+
world->contactHertz = def->contactHertz;
|
|
197
|
+
world->contactDampingRatio = def->contactDampingRatio;
|
|
198
|
+
world->jointHertz = def->jointHertz;
|
|
199
|
+
world->jointDampingRatio = def->jointDampingRatio;
|
|
200
|
+
|
|
201
|
+
if ( def->frictionCallback == NULL )
|
|
202
|
+
{
|
|
203
|
+
world->frictionCallback = b2DefaultFrictionCallback;
|
|
204
|
+
}
|
|
205
|
+
else
|
|
206
|
+
{
|
|
207
|
+
world->frictionCallback = def->frictionCallback;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if ( def->restitutionCallback == NULL )
|
|
211
|
+
{
|
|
212
|
+
world->restitutionCallback = b2DefaultRestitutionCallback;
|
|
213
|
+
}
|
|
214
|
+
else
|
|
215
|
+
{
|
|
216
|
+
world->restitutionCallback = def->restitutionCallback;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
world->enableSleep = def->enableSleep;
|
|
220
|
+
world->locked = false;
|
|
221
|
+
world->enableWarmStarting = true;
|
|
222
|
+
world->enableContinuous = def->enableContinuous;
|
|
223
|
+
world->enableSpeculative = true;
|
|
224
|
+
world->userTreeTask = NULL;
|
|
225
|
+
world->userData = def->userData;
|
|
226
|
+
|
|
227
|
+
if ( def->workerCount > 0 && def->enqueueTask != NULL && def->finishTask != NULL )
|
|
228
|
+
{
|
|
229
|
+
world->workerCount = b2MinInt( def->workerCount, B2_MAX_WORKERS );
|
|
230
|
+
world->enqueueTaskFcn = def->enqueueTask;
|
|
231
|
+
world->finishTaskFcn = def->finishTask;
|
|
232
|
+
world->userTaskContext = def->userTaskContext;
|
|
233
|
+
}
|
|
234
|
+
else
|
|
235
|
+
{
|
|
236
|
+
world->workerCount = 1;
|
|
237
|
+
world->enqueueTaskFcn = b2DefaultAddTaskFcn;
|
|
238
|
+
world->finishTaskFcn = b2DefaultFinishTaskFcn;
|
|
239
|
+
world->userTaskContext = NULL;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
world->taskContexts = b2TaskContextArray_Create( world->workerCount );
|
|
243
|
+
b2TaskContextArray_Resize( &world->taskContexts, world->workerCount );
|
|
244
|
+
|
|
245
|
+
world->sensorTaskContexts = b2SensorTaskContextArray_Create( world->workerCount );
|
|
246
|
+
b2SensorTaskContextArray_Resize( &world->sensorTaskContexts, world->workerCount );
|
|
247
|
+
|
|
248
|
+
for ( int i = 0; i < world->workerCount; ++i )
|
|
249
|
+
{
|
|
250
|
+
world->taskContexts.data[i].contactStateBitSet = b2CreateBitSet( 1024 );
|
|
251
|
+
world->taskContexts.data[i].enlargedSimBitSet = b2CreateBitSet( 256 );
|
|
252
|
+
world->taskContexts.data[i].awakeIslandBitSet = b2CreateBitSet( 256 );
|
|
253
|
+
|
|
254
|
+
world->sensorTaskContexts.data[i].eventBits = b2CreateBitSet( 128 );
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
world->debugBodySet = b2CreateBitSet( 256 );
|
|
258
|
+
world->debugJointSet = b2CreateBitSet( 256 );
|
|
259
|
+
world->debugContactSet = b2CreateBitSet( 256 );
|
|
260
|
+
world->debugIslandSet = b2CreateBitSet( 256 );
|
|
261
|
+
|
|
262
|
+
// add one to worldId so that 0 represents a null b2WorldId
|
|
263
|
+
return (b2WorldId){ (uint16_t)( worldId + 1 ), world->generation };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
void b2DestroyWorld( b2WorldId worldId )
|
|
267
|
+
{
|
|
268
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
269
|
+
|
|
270
|
+
b2DestroyBitSet( &world->debugBodySet );
|
|
271
|
+
b2DestroyBitSet( &world->debugJointSet );
|
|
272
|
+
b2DestroyBitSet( &world->debugContactSet );
|
|
273
|
+
b2DestroyBitSet( &world->debugIslandSet );
|
|
274
|
+
|
|
275
|
+
for ( int i = 0; i < world->workerCount; ++i )
|
|
276
|
+
{
|
|
277
|
+
b2DestroyBitSet( &world->taskContexts.data[i].contactStateBitSet );
|
|
278
|
+
b2DestroyBitSet( &world->taskContexts.data[i].enlargedSimBitSet );
|
|
279
|
+
b2DestroyBitSet( &world->taskContexts.data[i].awakeIslandBitSet );
|
|
280
|
+
|
|
281
|
+
b2DestroyBitSet( &world->sensorTaskContexts.data[i].eventBits );
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
b2TaskContextArray_Destroy( &world->taskContexts );
|
|
285
|
+
b2SensorTaskContextArray_Destroy( &world->sensorTaskContexts );
|
|
286
|
+
|
|
287
|
+
b2BodyMoveEventArray_Destroy( &world->bodyMoveEvents );
|
|
288
|
+
b2SensorBeginTouchEventArray_Destroy( &world->sensorBeginEvents );
|
|
289
|
+
b2SensorEndTouchEventArray_Destroy( world->sensorEndEvents + 0 );
|
|
290
|
+
b2SensorEndTouchEventArray_Destroy( world->sensorEndEvents + 1 );
|
|
291
|
+
b2ContactBeginTouchEventArray_Destroy( &world->contactBeginEvents );
|
|
292
|
+
b2ContactEndTouchEventArray_Destroy( world->contactEndEvents + 0 );
|
|
293
|
+
b2ContactEndTouchEventArray_Destroy( world->contactEndEvents + 1 );
|
|
294
|
+
b2ContactHitEventArray_Destroy( &world->contactHitEvents );
|
|
295
|
+
|
|
296
|
+
int chainCapacity = world->chainShapes.count;
|
|
297
|
+
for ( int i = 0; i < chainCapacity; ++i )
|
|
298
|
+
{
|
|
299
|
+
b2ChainShape* chain = world->chainShapes.data + i;
|
|
300
|
+
if ( chain->id != B2_NULL_INDEX )
|
|
301
|
+
{
|
|
302
|
+
b2FreeChainData( chain );
|
|
303
|
+
}
|
|
304
|
+
else
|
|
305
|
+
{
|
|
306
|
+
B2_ASSERT( chain->shapeIndices == NULL );
|
|
307
|
+
B2_ASSERT( chain->materials == NULL );
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
int sensorCount = world->sensors.count;
|
|
312
|
+
for ( int i = 0; i < sensorCount; ++i )
|
|
313
|
+
{
|
|
314
|
+
b2ShapeRefArray_Destroy( &world->sensors.data[i].overlaps1 );
|
|
315
|
+
b2ShapeRefArray_Destroy( &world->sensors.data[i].overlaps2 );
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
b2SensorArray_Destroy( &world->sensors );
|
|
319
|
+
|
|
320
|
+
b2BodyArray_Destroy( &world->bodies );
|
|
321
|
+
b2ShapeArray_Destroy( &world->shapes );
|
|
322
|
+
b2ChainShapeArray_Destroy( &world->chainShapes );
|
|
323
|
+
b2ContactArray_Destroy( &world->contacts );
|
|
324
|
+
b2JointArray_Destroy( &world->joints );
|
|
325
|
+
b2IslandArray_Destroy( &world->islands );
|
|
326
|
+
|
|
327
|
+
// Destroy solver sets
|
|
328
|
+
int setCapacity = world->solverSets.count;
|
|
329
|
+
for ( int i = 0; i < setCapacity; ++i )
|
|
330
|
+
{
|
|
331
|
+
b2SolverSet* set = world->solverSets.data + i;
|
|
332
|
+
if ( set->setIndex != B2_NULL_INDEX )
|
|
333
|
+
{
|
|
334
|
+
b2DestroySolverSet( world, i );
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
b2SolverSetArray_Destroy( &world->solverSets );
|
|
339
|
+
|
|
340
|
+
b2DestroyGraph( &world->constraintGraph );
|
|
341
|
+
b2DestroyBroadPhase( &world->broadPhase );
|
|
342
|
+
|
|
343
|
+
b2DestroyIdPool( &world->bodyIdPool );
|
|
344
|
+
b2DestroyIdPool( &world->shapeIdPool );
|
|
345
|
+
b2DestroyIdPool( &world->chainIdPool );
|
|
346
|
+
b2DestroyIdPool( &world->contactIdPool );
|
|
347
|
+
b2DestroyIdPool( &world->jointIdPool );
|
|
348
|
+
b2DestroyIdPool( &world->islandIdPool );
|
|
349
|
+
b2DestroyIdPool( &world->solverSetIdPool );
|
|
350
|
+
|
|
351
|
+
b2DestroyArenaAllocator( &world->arena );
|
|
352
|
+
|
|
353
|
+
// Wipe world but preserve generation
|
|
354
|
+
uint16_t generation = world->generation;
|
|
355
|
+
*world = (b2World){ 0 };
|
|
356
|
+
world->worldId = 0;
|
|
357
|
+
world->generation = generation + 1;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
static void b2CollideTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )
|
|
361
|
+
{
|
|
362
|
+
b2TracyCZoneNC( collide_task, "Collide", b2_colorDodgerBlue, true );
|
|
363
|
+
|
|
364
|
+
b2StepContext* stepContext = context;
|
|
365
|
+
b2World* world = stepContext->world;
|
|
366
|
+
B2_ASSERT( (int)threadIndex < world->workerCount );
|
|
367
|
+
b2TaskContext* taskContext = world->taskContexts.data + threadIndex;
|
|
368
|
+
b2ContactSim** contactSims = stepContext->contacts;
|
|
369
|
+
b2Shape* shapes = world->shapes.data;
|
|
370
|
+
b2Body* bodies = world->bodies.data;
|
|
371
|
+
|
|
372
|
+
B2_ASSERT( startIndex < endIndex );
|
|
373
|
+
|
|
374
|
+
for ( int contactIndex = startIndex; contactIndex < endIndex; ++contactIndex )
|
|
375
|
+
{
|
|
376
|
+
b2ContactSim* contactSim = contactSims[contactIndex];
|
|
377
|
+
|
|
378
|
+
int contactId = contactSim->contactId;
|
|
379
|
+
|
|
380
|
+
b2Shape* shapeA = shapes + contactSim->shapeIdA;
|
|
381
|
+
b2Shape* shapeB = shapes + contactSim->shapeIdB;
|
|
382
|
+
|
|
383
|
+
// Do proxies still overlap?
|
|
384
|
+
bool overlap = b2AABB_Overlaps( shapeA->fatAABB, shapeB->fatAABB );
|
|
385
|
+
if ( overlap == false )
|
|
386
|
+
{
|
|
387
|
+
contactSim->simFlags |= b2_simDisjoint;
|
|
388
|
+
contactSim->simFlags &= ~b2_simTouchingFlag;
|
|
389
|
+
b2SetBit( &taskContext->contactStateBitSet, contactId );
|
|
390
|
+
}
|
|
391
|
+
else
|
|
392
|
+
{
|
|
393
|
+
bool wasTouching = ( contactSim->simFlags & b2_simTouchingFlag );
|
|
394
|
+
|
|
395
|
+
// Update contact respecting shape/body order (A,B)
|
|
396
|
+
b2Body* bodyA = bodies + shapeA->bodyId;
|
|
397
|
+
b2Body* bodyB = bodies + shapeB->bodyId;
|
|
398
|
+
b2BodySim* bodySimA = b2GetBodySim( world, bodyA );
|
|
399
|
+
b2BodySim* bodySimB = b2GetBodySim( world, bodyB );
|
|
400
|
+
|
|
401
|
+
// avoid cache misses in b2PrepareContactsTask
|
|
402
|
+
contactSim->bodySimIndexA = bodyA->setIndex == b2_awakeSet ? bodyA->localIndex : B2_NULL_INDEX;
|
|
403
|
+
contactSim->invMassA = bodySimA->invMass;
|
|
404
|
+
contactSim->invIA = bodySimA->invInertia;
|
|
405
|
+
|
|
406
|
+
contactSim->bodySimIndexB = bodyB->setIndex == b2_awakeSet ? bodyB->localIndex : B2_NULL_INDEX;
|
|
407
|
+
contactSim->invMassB = bodySimB->invMass;
|
|
408
|
+
contactSim->invIB = bodySimB->invInertia;
|
|
409
|
+
|
|
410
|
+
b2Transform transformA = bodySimA->transform;
|
|
411
|
+
b2Transform transformB = bodySimB->transform;
|
|
412
|
+
|
|
413
|
+
b2Vec2 centerOffsetA = b2RotateVector( transformA.q, bodySimA->localCenter );
|
|
414
|
+
b2Vec2 centerOffsetB = b2RotateVector( transformB.q, bodySimB->localCenter );
|
|
415
|
+
|
|
416
|
+
// This updates solid contacts and sensors
|
|
417
|
+
bool touching =
|
|
418
|
+
b2UpdateContact( world, contactSim, shapeA, transformA, centerOffsetA, shapeB, transformB, centerOffsetB );
|
|
419
|
+
|
|
420
|
+
// State changes that affect island connectivity. Also affects contact and sensor events.
|
|
421
|
+
if ( touching == true && wasTouching == false )
|
|
422
|
+
{
|
|
423
|
+
contactSim->simFlags |= b2_simStartedTouching;
|
|
424
|
+
b2SetBit( &taskContext->contactStateBitSet, contactId );
|
|
425
|
+
}
|
|
426
|
+
else if ( touching == false && wasTouching == true )
|
|
427
|
+
{
|
|
428
|
+
contactSim->simFlags |= b2_simStoppedTouching;
|
|
429
|
+
b2SetBit( &taskContext->contactStateBitSet, contactId );
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// To make this work, the time of impact code needs to adjust the target
|
|
433
|
+
// distance based on the number of TOI events for a body.
|
|
434
|
+
// if (touching && bodySimB->isFast)
|
|
435
|
+
//{
|
|
436
|
+
// b2Manifold* manifold = &contactSim->manifold;
|
|
437
|
+
// int pointCount = manifold->pointCount;
|
|
438
|
+
// for (int i = 0; i < pointCount; ++i)
|
|
439
|
+
// {
|
|
440
|
+
// // trick the solver into pushing the fast shapes apart
|
|
441
|
+
// manifold->points[i].separation -= 0.25f * B2_SPECULATIVE_DISTANCE;
|
|
442
|
+
// }
|
|
443
|
+
//}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
b2TracyCZoneEnd( collide_task );
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
static void b2UpdateTreesTask( int startIndex, int endIndex, uint32_t threadIndex, void* context )
|
|
451
|
+
{
|
|
452
|
+
B2_UNUSED( startIndex );
|
|
453
|
+
B2_UNUSED( endIndex );
|
|
454
|
+
B2_UNUSED( threadIndex );
|
|
455
|
+
|
|
456
|
+
b2TracyCZoneNC( tree_task, "Rebuild BVH", b2_colorFireBrick, true );
|
|
457
|
+
|
|
458
|
+
b2World* world = context;
|
|
459
|
+
b2BroadPhase_RebuildTrees( &world->broadPhase );
|
|
460
|
+
|
|
461
|
+
b2TracyCZoneEnd( tree_task );
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
static void b2AddNonTouchingContact( b2World* world, b2Contact* contact, b2ContactSim* contactSim )
|
|
465
|
+
{
|
|
466
|
+
B2_ASSERT( contact->setIndex == b2_awakeSet );
|
|
467
|
+
b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
|
|
468
|
+
contact->colorIndex = B2_NULL_INDEX;
|
|
469
|
+
contact->localIndex = set->contactSims.count;
|
|
470
|
+
|
|
471
|
+
b2ContactSim* newContactSim = b2ContactSimArray_Add( &set->contactSims );
|
|
472
|
+
memcpy( newContactSim, contactSim, sizeof( b2ContactSim ) );
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
static void b2RemoveNonTouchingContact( b2World* world, int setIndex, int localIndex )
|
|
476
|
+
{
|
|
477
|
+
b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );
|
|
478
|
+
int movedIndex = b2ContactSimArray_RemoveSwap( &set->contactSims, localIndex );
|
|
479
|
+
if ( movedIndex != B2_NULL_INDEX )
|
|
480
|
+
{
|
|
481
|
+
b2ContactSim* movedContactSim = set->contactSims.data + localIndex;
|
|
482
|
+
b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId );
|
|
483
|
+
B2_ASSERT( movedContact->setIndex == setIndex );
|
|
484
|
+
B2_ASSERT( movedContact->localIndex == movedIndex );
|
|
485
|
+
B2_ASSERT( movedContact->colorIndex == B2_NULL_INDEX );
|
|
486
|
+
movedContact->localIndex = localIndex;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Narrow-phase collision
|
|
491
|
+
static void b2Collide( b2StepContext* context )
|
|
492
|
+
{
|
|
493
|
+
b2World* world = context->world;
|
|
494
|
+
|
|
495
|
+
B2_ASSERT( world->workerCount > 0 );
|
|
496
|
+
|
|
497
|
+
b2TracyCZoneNC( collide, "Narrow Phase", b2_colorDodgerBlue, true );
|
|
498
|
+
|
|
499
|
+
// Task that can be done in parallel with the narrow-phase
|
|
500
|
+
// - rebuild the collision tree for dynamic and kinematic bodies to keep their query performance good
|
|
501
|
+
// todo_erin move this to start when contacts are being created
|
|
502
|
+
world->userTreeTask = world->enqueueTaskFcn( &b2UpdateTreesTask, 1, 1, world, world->userTaskContext );
|
|
503
|
+
world->taskCount += 1;
|
|
504
|
+
world->activeTaskCount += world->userTreeTask == NULL ? 0 : 1;
|
|
505
|
+
|
|
506
|
+
// gather contacts into a single array for easier parallel-for
|
|
507
|
+
int contactCount = 0;
|
|
508
|
+
b2GraphColor* graphColors = world->constraintGraph.colors;
|
|
509
|
+
for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )
|
|
510
|
+
{
|
|
511
|
+
contactCount += graphColors[i].contactSims.count;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
int nonTouchingCount = world->solverSets.data[b2_awakeSet].contactSims.count;
|
|
515
|
+
contactCount += nonTouchingCount;
|
|
516
|
+
|
|
517
|
+
if ( contactCount == 0 )
|
|
518
|
+
{
|
|
519
|
+
b2TracyCZoneEnd( collide );
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
b2ContactSim** contactSims = b2AllocateArenaItem( &world->arena, contactCount * sizeof( b2ContactSim* ), "contacts" );
|
|
524
|
+
|
|
525
|
+
int contactIndex = 0;
|
|
526
|
+
for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )
|
|
527
|
+
{
|
|
528
|
+
b2GraphColor* color = graphColors + i;
|
|
529
|
+
int count = color->contactSims.count;
|
|
530
|
+
b2ContactSim* base = color->contactSims.data;
|
|
531
|
+
for ( int j = 0; j < count; ++j )
|
|
532
|
+
{
|
|
533
|
+
contactSims[contactIndex] = base + j;
|
|
534
|
+
contactIndex += 1;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
{
|
|
539
|
+
b2ContactSim* base = world->solverSets.data[b2_awakeSet].contactSims.data;
|
|
540
|
+
for ( int i = 0; i < nonTouchingCount; ++i )
|
|
541
|
+
{
|
|
542
|
+
contactSims[contactIndex] = base + i;
|
|
543
|
+
contactIndex += 1;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
B2_ASSERT( contactIndex == contactCount );
|
|
548
|
+
|
|
549
|
+
context->contacts = contactSims;
|
|
550
|
+
|
|
551
|
+
// Contact bit set on ids because contact pointers are unstable as they move between touching and not touching.
|
|
552
|
+
int contactIdCapacity = b2GetIdCapacity( &world->contactIdPool );
|
|
553
|
+
for ( int i = 0; i < world->workerCount; ++i )
|
|
554
|
+
{
|
|
555
|
+
b2SetBitCountAndClear( &world->taskContexts.data[i].contactStateBitSet, contactIdCapacity );
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Task should take at least 40us on a 4GHz CPU (10K cycles)
|
|
559
|
+
int minRange = 64;
|
|
560
|
+
void* userCollideTask = world->enqueueTaskFcn( &b2CollideTask, contactCount, minRange, context, world->userTaskContext );
|
|
561
|
+
world->taskCount += 1;
|
|
562
|
+
if ( userCollideTask != NULL )
|
|
563
|
+
{
|
|
564
|
+
world->finishTaskFcn( userCollideTask, world->userTaskContext );
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
b2FreeArenaItem( &world->arena, contactSims );
|
|
568
|
+
context->contacts = NULL;
|
|
569
|
+
contactSims = NULL;
|
|
570
|
+
|
|
571
|
+
// Serially update contact state
|
|
572
|
+
// todo_erin bring this zone together with island merge
|
|
573
|
+
b2TracyCZoneNC( contact_state, "Contact State", b2_colorLightSlateGray, true );
|
|
574
|
+
|
|
575
|
+
// Bitwise OR all contact bits
|
|
576
|
+
b2BitSet* bitSet = &world->taskContexts.data[0].contactStateBitSet;
|
|
577
|
+
for ( int i = 1; i < world->workerCount; ++i )
|
|
578
|
+
{
|
|
579
|
+
b2InPlaceUnion( bitSet, &world->taskContexts.data[i].contactStateBitSet );
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
|
|
583
|
+
|
|
584
|
+
int endEventArrayIndex = world->endEventArrayIndex;
|
|
585
|
+
|
|
586
|
+
const b2Shape* shapes = world->shapes.data;
|
|
587
|
+
uint16_t worldId = world->worldId;
|
|
588
|
+
|
|
589
|
+
// Process contact state changes. Iterate over set bits
|
|
590
|
+
for ( uint32_t k = 0; k < bitSet->blockCount; ++k )
|
|
591
|
+
{
|
|
592
|
+
uint64_t bits = bitSet->bits[k];
|
|
593
|
+
while ( bits != 0 )
|
|
594
|
+
{
|
|
595
|
+
uint32_t ctz = b2CTZ64( bits );
|
|
596
|
+
int contactId = (int)( 64 * k + ctz );
|
|
597
|
+
|
|
598
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
|
|
599
|
+
B2_ASSERT( contact->setIndex == b2_awakeSet );
|
|
600
|
+
|
|
601
|
+
int colorIndex = contact->colorIndex;
|
|
602
|
+
int localIndex = contact->localIndex;
|
|
603
|
+
|
|
604
|
+
b2ContactSim* contactSim = NULL;
|
|
605
|
+
if ( colorIndex != B2_NULL_INDEX )
|
|
606
|
+
{
|
|
607
|
+
// contact lives in constraint graph
|
|
608
|
+
B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );
|
|
609
|
+
b2GraphColor* color = graphColors + colorIndex;
|
|
610
|
+
contactSim = b2ContactSimArray_Get( &color->contactSims, localIndex );
|
|
611
|
+
}
|
|
612
|
+
else
|
|
613
|
+
{
|
|
614
|
+
contactSim = b2ContactSimArray_Get( &awakeSet->contactSims, localIndex );
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const b2Shape* shapeA = shapes + contact->shapeIdA;
|
|
618
|
+
const b2Shape* shapeB = shapes + contact->shapeIdB;
|
|
619
|
+
b2ShapeId shapeIdA = { shapeA->id + 1, worldId, shapeA->generation };
|
|
620
|
+
b2ShapeId shapeIdB = { shapeB->id + 1, worldId, shapeB->generation };
|
|
621
|
+
uint32_t flags = contact->flags;
|
|
622
|
+
uint32_t simFlags = contactSim->simFlags;
|
|
623
|
+
|
|
624
|
+
if ( simFlags & b2_simDisjoint )
|
|
625
|
+
{
|
|
626
|
+
// Bounding boxes no longer overlap
|
|
627
|
+
b2DestroyContact( world, contact, false );
|
|
628
|
+
contact = NULL;
|
|
629
|
+
contactSim = NULL;
|
|
630
|
+
}
|
|
631
|
+
else if ( simFlags & b2_simStartedTouching )
|
|
632
|
+
{
|
|
633
|
+
B2_ASSERT( contact->islandId == B2_NULL_INDEX );
|
|
634
|
+
// Contact is solid
|
|
635
|
+
if ( flags & b2_contactEnableContactEvents )
|
|
636
|
+
{
|
|
637
|
+
b2ContactBeginTouchEvent event = { shapeIdA, shapeIdB, contactSim->manifold };
|
|
638
|
+
b2ContactBeginTouchEventArray_Push( &world->contactBeginEvents, event );
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
B2_ASSERT( contactSim->manifold.pointCount > 0 );
|
|
642
|
+
B2_ASSERT( contact->setIndex == b2_awakeSet );
|
|
643
|
+
|
|
644
|
+
// Link first because this wakes colliding bodies and ensures the body sims
|
|
645
|
+
// are in the correct place.
|
|
646
|
+
contact->flags |= b2_contactTouchingFlag;
|
|
647
|
+
b2LinkContact( world, contact );
|
|
648
|
+
|
|
649
|
+
// Make sure these didn't change
|
|
650
|
+
B2_ASSERT( contact->colorIndex == B2_NULL_INDEX );
|
|
651
|
+
B2_ASSERT( contact->localIndex == localIndex );
|
|
652
|
+
|
|
653
|
+
// Contact sim pointer may have become orphaned due to awake set growth,
|
|
654
|
+
// so I just need to refresh it.
|
|
655
|
+
contactSim = b2ContactSimArray_Get( &awakeSet->contactSims, localIndex );
|
|
656
|
+
|
|
657
|
+
contactSim->simFlags &= ~b2_simStartedTouching;
|
|
658
|
+
|
|
659
|
+
b2AddContactToGraph( world, contactSim, contact );
|
|
660
|
+
b2RemoveNonTouchingContact( world, b2_awakeSet, localIndex );
|
|
661
|
+
contactSim = NULL;
|
|
662
|
+
}
|
|
663
|
+
else if ( simFlags & b2_simStoppedTouching )
|
|
664
|
+
{
|
|
665
|
+
contactSim->simFlags &= ~b2_simStoppedTouching;
|
|
666
|
+
|
|
667
|
+
// Contact is solid
|
|
668
|
+
contact->flags &= ~b2_contactTouchingFlag;
|
|
669
|
+
|
|
670
|
+
if ( contact->flags & b2_contactEnableContactEvents )
|
|
671
|
+
{
|
|
672
|
+
b2ContactEndTouchEvent event = { shapeIdA, shapeIdB };
|
|
673
|
+
b2ContactEndTouchEventArray_Push( world->contactEndEvents + endEventArrayIndex, event );
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
B2_ASSERT( contactSim->manifold.pointCount == 0 );
|
|
677
|
+
|
|
678
|
+
b2UnlinkContact( world, contact );
|
|
679
|
+
int bodyIdA = contact->edges[0].bodyId;
|
|
680
|
+
int bodyIdB = contact->edges[1].bodyId;
|
|
681
|
+
|
|
682
|
+
b2AddNonTouchingContact( world, contact, contactSim );
|
|
683
|
+
b2RemoveContactFromGraph( world, bodyIdA, bodyIdB, colorIndex, localIndex );
|
|
684
|
+
contact = NULL;
|
|
685
|
+
contactSim = NULL;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Clear the smallest set bit
|
|
689
|
+
bits = bits & ( bits - 1 );
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
b2ValidateSolverSets( world );
|
|
694
|
+
b2ValidateContacts( world );
|
|
695
|
+
|
|
696
|
+
b2TracyCZoneEnd( contact_state );
|
|
697
|
+
b2TracyCZoneEnd( collide );
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
void b2World_Step( b2WorldId worldId, float timeStep, int subStepCount )
|
|
701
|
+
{
|
|
702
|
+
B2_ASSERT( b2IsValidFloat( timeStep ) );
|
|
703
|
+
B2_ASSERT( 0 < subStepCount );
|
|
704
|
+
|
|
705
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
706
|
+
B2_ASSERT( world->locked == false );
|
|
707
|
+
if ( world->locked )
|
|
708
|
+
{
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// Prepare to capture events
|
|
713
|
+
// Ensure user does not access stale data if there is an early return
|
|
714
|
+
b2BodyMoveEventArray_Clear( &world->bodyMoveEvents );
|
|
715
|
+
b2SensorBeginTouchEventArray_Clear( &world->sensorBeginEvents );
|
|
716
|
+
b2ContactBeginTouchEventArray_Clear( &world->contactBeginEvents );
|
|
717
|
+
b2ContactHitEventArray_Clear( &world->contactHitEvents );
|
|
718
|
+
|
|
719
|
+
world->profile = (b2Profile){ 0 };
|
|
720
|
+
|
|
721
|
+
if ( timeStep == 0.0f )
|
|
722
|
+
{
|
|
723
|
+
// Swap end event array buffers
|
|
724
|
+
world->endEventArrayIndex = 1 - world->endEventArrayIndex;
|
|
725
|
+
b2SensorEndTouchEventArray_Clear( world->sensorEndEvents + world->endEventArrayIndex );
|
|
726
|
+
b2ContactEndTouchEventArray_Clear( world->contactEndEvents + world->endEventArrayIndex );
|
|
727
|
+
|
|
728
|
+
// todo_erin would be useful to still process collision while paused
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
b2TracyCZoneNC( world_step, "Step", b2_colorBox2DGreen, true );
|
|
733
|
+
|
|
734
|
+
world->locked = true;
|
|
735
|
+
world->activeTaskCount = 0;
|
|
736
|
+
world->taskCount = 0;
|
|
737
|
+
|
|
738
|
+
uint64_t stepTicks = b2GetTicks();
|
|
739
|
+
|
|
740
|
+
// Update collision pairs and create contacts
|
|
741
|
+
{
|
|
742
|
+
uint64_t pairTicks = b2GetTicks();
|
|
743
|
+
b2UpdateBroadPhasePairs( world );
|
|
744
|
+
world->profile.pairs = b2GetMilliseconds( pairTicks );
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
b2StepContext context = { 0 };
|
|
748
|
+
context.world = world;
|
|
749
|
+
context.dt = timeStep;
|
|
750
|
+
context.subStepCount = b2MaxInt( 1, subStepCount );
|
|
751
|
+
|
|
752
|
+
if ( timeStep > 0.0f )
|
|
753
|
+
{
|
|
754
|
+
context.inv_dt = 1.0f / timeStep;
|
|
755
|
+
context.h = timeStep / context.subStepCount;
|
|
756
|
+
context.inv_h = context.subStepCount * context.inv_dt;
|
|
757
|
+
}
|
|
758
|
+
else
|
|
759
|
+
{
|
|
760
|
+
context.inv_dt = 0.0f;
|
|
761
|
+
context.h = 0.0f;
|
|
762
|
+
context.inv_h = 0.0f;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
world->inv_h = context.inv_h;
|
|
766
|
+
|
|
767
|
+
// Hertz values get reduced for large time steps
|
|
768
|
+
float contactHertz = b2MinFloat( world->contactHertz, 0.25f * context.inv_h );
|
|
769
|
+
float jointHertz = b2MinFloat( world->jointHertz, 0.125f * context.inv_h );
|
|
770
|
+
|
|
771
|
+
context.contactSoftness = b2MakeSoft( contactHertz, world->contactDampingRatio, context.h );
|
|
772
|
+
context.staticSoftness = b2MakeSoft( 2.0f * contactHertz, world->contactDampingRatio, context.h );
|
|
773
|
+
context.jointSoftness = b2MakeSoft( jointHertz, world->jointDampingRatio, context.h );
|
|
774
|
+
|
|
775
|
+
context.restitutionThreshold = world->restitutionThreshold;
|
|
776
|
+
context.maxLinearVelocity = world->maxLinearSpeed;
|
|
777
|
+
context.enableWarmStarting = world->enableWarmStarting;
|
|
778
|
+
|
|
779
|
+
// Update contacts
|
|
780
|
+
{
|
|
781
|
+
uint64_t collideTicks = b2GetTicks();
|
|
782
|
+
b2Collide( &context );
|
|
783
|
+
world->profile.collide = b2GetMilliseconds( collideTicks );
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Integrate velocities, solve velocity constraints, and integrate positions.
|
|
787
|
+
if ( context.dt > 0.0f )
|
|
788
|
+
{
|
|
789
|
+
uint64_t solveTicks = b2GetTicks();
|
|
790
|
+
b2Solve( world, &context );
|
|
791
|
+
world->profile.solve = b2GetMilliseconds( solveTicks );
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Update sensors
|
|
795
|
+
{
|
|
796
|
+
uint64_t sensorTicks = b2GetTicks();
|
|
797
|
+
b2OverlapSensors( world );
|
|
798
|
+
world->profile.sensors = b2GetMilliseconds( sensorTicks );
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
world->profile.step = b2GetMilliseconds( stepTicks );
|
|
802
|
+
|
|
803
|
+
B2_ASSERT( b2GetArenaAllocation( &world->arena ) == 0 );
|
|
804
|
+
|
|
805
|
+
// Ensure stack is large enough
|
|
806
|
+
b2GrowArena( &world->arena );
|
|
807
|
+
|
|
808
|
+
// Make sure all tasks that were started were also finished
|
|
809
|
+
B2_ASSERT( world->activeTaskCount == 0 );
|
|
810
|
+
|
|
811
|
+
b2TracyCZoneEnd( world_step );
|
|
812
|
+
|
|
813
|
+
// Swap end event array buffers
|
|
814
|
+
world->endEventArrayIndex = 1 - world->endEventArrayIndex;
|
|
815
|
+
b2SensorEndTouchEventArray_Clear( world->sensorEndEvents + world->endEventArrayIndex );
|
|
816
|
+
b2ContactEndTouchEventArray_Clear( world->contactEndEvents + world->endEventArrayIndex );
|
|
817
|
+
world->locked = false;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
static void b2DrawShape( b2DebugDraw* draw, b2Shape* shape, b2Transform xf, b2HexColor color )
|
|
821
|
+
{
|
|
822
|
+
switch ( shape->type )
|
|
823
|
+
{
|
|
824
|
+
case b2_capsuleShape:
|
|
825
|
+
{
|
|
826
|
+
b2Capsule* capsule = &shape->capsule;
|
|
827
|
+
b2Vec2 p1 = b2TransformPoint( xf, capsule->center1 );
|
|
828
|
+
b2Vec2 p2 = b2TransformPoint( xf, capsule->center2 );
|
|
829
|
+
draw->DrawSolidCapsuleFcn( p1, p2, capsule->radius, color, draw->context );
|
|
830
|
+
}
|
|
831
|
+
break;
|
|
832
|
+
|
|
833
|
+
case b2_circleShape:
|
|
834
|
+
{
|
|
835
|
+
b2Circle* circle = &shape->circle;
|
|
836
|
+
xf.p = b2TransformPoint( xf, circle->center );
|
|
837
|
+
draw->DrawSolidCircleFcn( xf, circle->radius, color, draw->context );
|
|
838
|
+
}
|
|
839
|
+
break;
|
|
840
|
+
|
|
841
|
+
case b2_polygonShape:
|
|
842
|
+
{
|
|
843
|
+
b2Polygon* poly = &shape->polygon;
|
|
844
|
+
draw->DrawSolidPolygonFcn( xf, poly->vertices, poly->count, poly->radius, color, draw->context );
|
|
845
|
+
}
|
|
846
|
+
break;
|
|
847
|
+
|
|
848
|
+
case b2_segmentShape:
|
|
849
|
+
{
|
|
850
|
+
b2Segment* segment = &shape->segment;
|
|
851
|
+
b2Vec2 p1 = b2TransformPoint( xf, segment->point1 );
|
|
852
|
+
b2Vec2 p2 = b2TransformPoint( xf, segment->point2 );
|
|
853
|
+
draw->DrawSegmentFcn( p1, p2, color, draw->context );
|
|
854
|
+
}
|
|
855
|
+
break;
|
|
856
|
+
|
|
857
|
+
case b2_chainSegmentShape:
|
|
858
|
+
{
|
|
859
|
+
b2Segment* segment = &shape->chainSegment.segment;
|
|
860
|
+
b2Vec2 p1 = b2TransformPoint( xf, segment->point1 );
|
|
861
|
+
b2Vec2 p2 = b2TransformPoint( xf, segment->point2 );
|
|
862
|
+
draw->DrawSegmentFcn( p1, p2, color, draw->context );
|
|
863
|
+
draw->DrawPointFcn( p2, 4.0f, color, draw->context );
|
|
864
|
+
draw->DrawSegmentFcn( p1, b2Lerp( p1, p2, 0.1f ), b2_colorPaleGreen, draw->context );
|
|
865
|
+
}
|
|
866
|
+
break;
|
|
867
|
+
|
|
868
|
+
default:
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
struct DrawContext
|
|
874
|
+
{
|
|
875
|
+
b2World* world;
|
|
876
|
+
b2DebugDraw* draw;
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
static bool DrawQueryCallback( int proxyId, uint64_t userData, void* context )
|
|
880
|
+
{
|
|
881
|
+
B2_UNUSED( proxyId );
|
|
882
|
+
|
|
883
|
+
int shapeId = (int)userData;
|
|
884
|
+
|
|
885
|
+
struct DrawContext* drawContext = context;
|
|
886
|
+
b2World* world = drawContext->world;
|
|
887
|
+
b2DebugDraw* draw = drawContext->draw;
|
|
888
|
+
|
|
889
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
890
|
+
B2_ASSERT( shape->id == shapeId );
|
|
891
|
+
|
|
892
|
+
b2SetBit( &world->debugBodySet, shape->bodyId );
|
|
893
|
+
|
|
894
|
+
if ( draw->drawShapes )
|
|
895
|
+
{
|
|
896
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
|
|
897
|
+
b2BodySim* bodySim = b2GetBodySim( world, body );
|
|
898
|
+
|
|
899
|
+
b2HexColor color;
|
|
900
|
+
|
|
901
|
+
if ( shape->customColor != 0 )
|
|
902
|
+
{
|
|
903
|
+
color = shape->customColor;
|
|
904
|
+
}
|
|
905
|
+
else if ( body->type == b2_dynamicBody && body->mass == 0.0f )
|
|
906
|
+
{
|
|
907
|
+
// Bad body
|
|
908
|
+
color = b2_colorRed;
|
|
909
|
+
}
|
|
910
|
+
else if ( body->setIndex == b2_disabledSet )
|
|
911
|
+
{
|
|
912
|
+
color = b2_colorSlateGray;
|
|
913
|
+
}
|
|
914
|
+
else if ( shape->sensorIndex != B2_NULL_INDEX )
|
|
915
|
+
{
|
|
916
|
+
color = b2_colorWheat;
|
|
917
|
+
}
|
|
918
|
+
else if ( bodySim->isBullet && body->setIndex == b2_awakeSet )
|
|
919
|
+
{
|
|
920
|
+
color = b2_colorTurquoise;
|
|
921
|
+
}
|
|
922
|
+
else if ( body->isSpeedCapped )
|
|
923
|
+
{
|
|
924
|
+
color = b2_colorYellow;
|
|
925
|
+
}
|
|
926
|
+
else if ( bodySim->isFast )
|
|
927
|
+
{
|
|
928
|
+
color = b2_colorSalmon;
|
|
929
|
+
}
|
|
930
|
+
else if ( body->type == b2_staticBody )
|
|
931
|
+
{
|
|
932
|
+
color = b2_colorPaleGreen;
|
|
933
|
+
}
|
|
934
|
+
else if ( body->type == b2_kinematicBody )
|
|
935
|
+
{
|
|
936
|
+
color = b2_colorRoyalBlue;
|
|
937
|
+
}
|
|
938
|
+
else if ( body->setIndex == b2_awakeSet )
|
|
939
|
+
{
|
|
940
|
+
color = b2_colorPink;
|
|
941
|
+
}
|
|
942
|
+
else
|
|
943
|
+
{
|
|
944
|
+
color = b2_colorGray;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
b2DrawShape( draw, shape, bodySim->transform, color );
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
if ( draw->drawBounds )
|
|
951
|
+
{
|
|
952
|
+
b2AABB aabb = shape->fatAABB;
|
|
953
|
+
|
|
954
|
+
b2Vec2 vs[4] = { { aabb.lowerBound.x, aabb.lowerBound.y },
|
|
955
|
+
{ aabb.upperBound.x, aabb.lowerBound.y },
|
|
956
|
+
{ aabb.upperBound.x, aabb.upperBound.y },
|
|
957
|
+
{ aabb.lowerBound.x, aabb.upperBound.y } };
|
|
958
|
+
|
|
959
|
+
draw->DrawPolygonFcn( vs, 4, b2_colorGold, draw->context );
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
return true;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// todo this has varying order for moving shapes, causing flicker when overlapping shapes are moving
|
|
966
|
+
// solution: display order by shape id modulus 3, keep 3 buckets in GLSolid* and flush in 3 passes.
|
|
967
|
+
static void b2DrawWithBounds( b2World* world, b2DebugDraw* draw )
|
|
968
|
+
{
|
|
969
|
+
B2_ASSERT( b2IsValidAABB( draw->drawingBounds ) );
|
|
970
|
+
|
|
971
|
+
const float k_impulseScale = 1.0f;
|
|
972
|
+
const float k_axisScale = 0.3f;
|
|
973
|
+
b2HexColor speculativeColor = b2_colorGainsboro;
|
|
974
|
+
b2HexColor addColor = b2_colorGreen;
|
|
975
|
+
b2HexColor persistColor = b2_colorBlue;
|
|
976
|
+
b2HexColor normalColor = b2_colorDimGray;
|
|
977
|
+
b2HexColor impulseColor = b2_colorMagenta;
|
|
978
|
+
b2HexColor frictionColor = b2_colorYellow;
|
|
979
|
+
|
|
980
|
+
b2HexColor graphColors[B2_GRAPH_COLOR_COUNT] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen,
|
|
981
|
+
b2_colorCyan, b2_colorBlue, b2_colorViolet, b2_colorPink,
|
|
982
|
+
b2_colorChocolate, b2_colorGoldenRod, b2_colorCoral, b2_colorBlack };
|
|
983
|
+
|
|
984
|
+
int bodyCapacity = b2GetIdCapacity( &world->bodyIdPool );
|
|
985
|
+
b2SetBitCountAndClear( &world->debugBodySet, bodyCapacity );
|
|
986
|
+
|
|
987
|
+
int jointCapacity = b2GetIdCapacity( &world->jointIdPool );
|
|
988
|
+
b2SetBitCountAndClear( &world->debugJointSet, jointCapacity );
|
|
989
|
+
|
|
990
|
+
int contactCapacity = b2GetIdCapacity( &world->contactIdPool );
|
|
991
|
+
b2SetBitCountAndClear( &world->debugContactSet, contactCapacity );
|
|
992
|
+
|
|
993
|
+
struct DrawContext drawContext = { world, draw };
|
|
994
|
+
|
|
995
|
+
for ( int i = 0; i < b2_bodyTypeCount; ++i )
|
|
996
|
+
{
|
|
997
|
+
b2DynamicTree_Query( world->broadPhase.trees + i, draw->drawingBounds, B2_DEFAULT_MASK_BITS, DrawQueryCallback,
|
|
998
|
+
&drawContext );
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
uint32_t wordCount = world->debugBodySet.blockCount;
|
|
1002
|
+
uint64_t* bits = world->debugBodySet.bits;
|
|
1003
|
+
for ( uint32_t k = 0; k < wordCount; ++k )
|
|
1004
|
+
{
|
|
1005
|
+
uint64_t word = bits[k];
|
|
1006
|
+
while ( word != 0 )
|
|
1007
|
+
{
|
|
1008
|
+
uint32_t ctz = b2CTZ64( word );
|
|
1009
|
+
uint32_t bodyId = 64 * k + ctz;
|
|
1010
|
+
|
|
1011
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, bodyId );
|
|
1012
|
+
|
|
1013
|
+
if ( draw->drawBodyNames && body->name[0] != 0 )
|
|
1014
|
+
{
|
|
1015
|
+
b2Vec2 offset = { 0.1f, 0.1f };
|
|
1016
|
+
b2BodySim* bodySim = b2GetBodySim( world, body );
|
|
1017
|
+
|
|
1018
|
+
b2Transform transform = { bodySim->center, bodySim->transform.q };
|
|
1019
|
+
b2Vec2 p = b2TransformPoint( transform, offset );
|
|
1020
|
+
draw->DrawStringFcn( p, body->name, b2_colorBlueViolet, draw->context );
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
if ( draw->drawMass && body->type == b2_dynamicBody )
|
|
1024
|
+
{
|
|
1025
|
+
b2Vec2 offset = { 0.1f, 0.1f };
|
|
1026
|
+
b2BodySim* bodySim = b2GetBodySim( world, body );
|
|
1027
|
+
|
|
1028
|
+
b2Transform transform = { bodySim->center, bodySim->transform.q };
|
|
1029
|
+
draw->DrawTransformFcn( transform, draw->context );
|
|
1030
|
+
|
|
1031
|
+
b2Vec2 p = b2TransformPoint( transform, offset );
|
|
1032
|
+
|
|
1033
|
+
char buffer[32];
|
|
1034
|
+
snprintf( buffer, 32, " %.2f", body->mass );
|
|
1035
|
+
draw->DrawStringFcn( p, buffer, b2_colorWhite, draw->context );
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
if ( draw->drawJoints )
|
|
1039
|
+
{
|
|
1040
|
+
int jointKey = body->headJointKey;
|
|
1041
|
+
while ( jointKey != B2_NULL_INDEX )
|
|
1042
|
+
{
|
|
1043
|
+
int jointId = jointKey >> 1;
|
|
1044
|
+
int edgeIndex = jointKey & 1;
|
|
1045
|
+
b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
|
|
1046
|
+
|
|
1047
|
+
// avoid double draw
|
|
1048
|
+
if ( b2GetBit( &world->debugJointSet, jointId ) == false )
|
|
1049
|
+
{
|
|
1050
|
+
b2DrawJoint( draw, world, joint );
|
|
1051
|
+
b2SetBit( &world->debugJointSet, jointId );
|
|
1052
|
+
}
|
|
1053
|
+
else
|
|
1054
|
+
{
|
|
1055
|
+
// todo testing
|
|
1056
|
+
edgeIndex += 0;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
jointKey = joint->edges[edgeIndex].nextKey;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const float linearSlop = B2_LINEAR_SLOP;
|
|
1064
|
+
if ( draw->drawContacts && body->type == b2_dynamicBody && body->setIndex == b2_awakeSet )
|
|
1065
|
+
{
|
|
1066
|
+
int contactKey = body->headContactKey;
|
|
1067
|
+
while ( contactKey != B2_NULL_INDEX )
|
|
1068
|
+
{
|
|
1069
|
+
int contactId = contactKey >> 1;
|
|
1070
|
+
int edgeIndex = contactKey & 1;
|
|
1071
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
|
|
1072
|
+
contactKey = contact->edges[edgeIndex].nextKey;
|
|
1073
|
+
|
|
1074
|
+
if ( contact->setIndex != b2_awakeSet || contact->colorIndex == B2_NULL_INDEX )
|
|
1075
|
+
{
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// avoid double draw
|
|
1080
|
+
if ( b2GetBit( &world->debugContactSet, contactId ) == false )
|
|
1081
|
+
{
|
|
1082
|
+
B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < B2_GRAPH_COLOR_COUNT );
|
|
1083
|
+
|
|
1084
|
+
b2GraphColor* gc = world->constraintGraph.colors + contact->colorIndex;
|
|
1085
|
+
b2ContactSim* contactSim = b2ContactSimArray_Get( &gc->contactSims, contact->localIndex );
|
|
1086
|
+
int pointCount = contactSim->manifold.pointCount;
|
|
1087
|
+
b2Vec2 normal = contactSim->manifold.normal;
|
|
1088
|
+
char buffer[32];
|
|
1089
|
+
|
|
1090
|
+
for ( int j = 0; j < pointCount; ++j )
|
|
1091
|
+
{
|
|
1092
|
+
b2ManifoldPoint* point = contactSim->manifold.points + j;
|
|
1093
|
+
|
|
1094
|
+
if ( draw->drawGraphColors )
|
|
1095
|
+
{
|
|
1096
|
+
// graph color
|
|
1097
|
+
float pointSize = contact->colorIndex == B2_OVERFLOW_INDEX ? 7.5f : 5.0f;
|
|
1098
|
+
draw->DrawPointFcn( point->point, pointSize, graphColors[contact->colorIndex], draw->context );
|
|
1099
|
+
// g_draw.DrawString(point->position, "%d", point->color);
|
|
1100
|
+
}
|
|
1101
|
+
else if ( point->separation > linearSlop )
|
|
1102
|
+
{
|
|
1103
|
+
// Speculative
|
|
1104
|
+
draw->DrawPointFcn( point->point, 5.0f, speculativeColor, draw->context );
|
|
1105
|
+
}
|
|
1106
|
+
else if ( point->persisted == false )
|
|
1107
|
+
{
|
|
1108
|
+
// Add
|
|
1109
|
+
draw->DrawPointFcn( point->point, 10.0f, addColor, draw->context );
|
|
1110
|
+
}
|
|
1111
|
+
else if ( point->persisted == true )
|
|
1112
|
+
{
|
|
1113
|
+
// Persist
|
|
1114
|
+
draw->DrawPointFcn( point->point, 5.0f, persistColor, draw->context );
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
if ( draw->drawContactNormals )
|
|
1118
|
+
{
|
|
1119
|
+
b2Vec2 p1 = point->point;
|
|
1120
|
+
b2Vec2 p2 = b2MulAdd( p1, k_axisScale, normal );
|
|
1121
|
+
draw->DrawSegmentFcn( p1, p2, normalColor, draw->context );
|
|
1122
|
+
}
|
|
1123
|
+
else if ( draw->drawContactImpulses )
|
|
1124
|
+
{
|
|
1125
|
+
b2Vec2 p1 = point->point;
|
|
1126
|
+
b2Vec2 p2 = b2MulAdd( p1, k_impulseScale * point->normalImpulse, normal );
|
|
1127
|
+
draw->DrawSegmentFcn( p1, p2, impulseColor, draw->context );
|
|
1128
|
+
snprintf( buffer, B2_ARRAY_COUNT( buffer ), "%.1f", 1000.0f * point->normalImpulse );
|
|
1129
|
+
draw->DrawStringFcn( p1, buffer, b2_colorWhite, draw->context );
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
if ( draw->drawContactFeatures )
|
|
1133
|
+
{
|
|
1134
|
+
snprintf( buffer, B2_ARRAY_COUNT( buffer ), "%d", point->id );
|
|
1135
|
+
draw->DrawStringFcn( point->point, buffer, b2_colorOrange, draw->context );
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
if ( draw->drawFrictionImpulses )
|
|
1139
|
+
{
|
|
1140
|
+
b2Vec2 tangent = b2RightPerp( normal );
|
|
1141
|
+
b2Vec2 p1 = point->point;
|
|
1142
|
+
b2Vec2 p2 = b2MulAdd( p1, k_impulseScale * point->tangentImpulse, tangent );
|
|
1143
|
+
draw->DrawSegmentFcn( p1, p2, frictionColor, draw->context );
|
|
1144
|
+
snprintf( buffer, B2_ARRAY_COUNT( buffer ), "%.1f", 1000.0f * point->tangentImpulse );
|
|
1145
|
+
draw->DrawStringFcn( p1, buffer, b2_colorWhite, draw->context );
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
b2SetBit( &world->debugContactSet, contactId );
|
|
1150
|
+
}
|
|
1151
|
+
else
|
|
1152
|
+
{
|
|
1153
|
+
// todo testing
|
|
1154
|
+
edgeIndex += 0;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
contactKey = contact->edges[edgeIndex].nextKey;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// Clear the smallest set bit
|
|
1162
|
+
word = word & ( word - 1 );
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw )
|
|
1168
|
+
{
|
|
1169
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1170
|
+
B2_ASSERT( world->locked == false );
|
|
1171
|
+
if ( world->locked )
|
|
1172
|
+
{
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// todo it seems bounds drawing is fast enough for regular usage
|
|
1177
|
+
if ( draw->useDrawingBounds )
|
|
1178
|
+
{
|
|
1179
|
+
b2DrawWithBounds( world, draw );
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
if ( draw->drawShapes )
|
|
1184
|
+
{
|
|
1185
|
+
int setCount = world->solverSets.count;
|
|
1186
|
+
for ( int setIndex = 0; setIndex < setCount; ++setIndex )
|
|
1187
|
+
{
|
|
1188
|
+
b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );
|
|
1189
|
+
int bodyCount = set->bodySims.count;
|
|
1190
|
+
for ( int bodyIndex = 0; bodyIndex < bodyCount; ++bodyIndex )
|
|
1191
|
+
{
|
|
1192
|
+
b2BodySim* bodySim = set->bodySims.data + bodyIndex;
|
|
1193
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, bodySim->bodyId );
|
|
1194
|
+
B2_ASSERT( body->setIndex == setIndex );
|
|
1195
|
+
|
|
1196
|
+
b2Transform xf = bodySim->transform;
|
|
1197
|
+
int shapeId = body->headShapeId;
|
|
1198
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
1199
|
+
{
|
|
1200
|
+
b2Shape* shape = world->shapes.data + shapeId;
|
|
1201
|
+
b2HexColor color;
|
|
1202
|
+
|
|
1203
|
+
if ( shape->customColor != 0 )
|
|
1204
|
+
{
|
|
1205
|
+
color = shape->customColor;
|
|
1206
|
+
}
|
|
1207
|
+
else if ( body->type == b2_dynamicBody && body->mass == 0.0f )
|
|
1208
|
+
{
|
|
1209
|
+
// Bad body
|
|
1210
|
+
color = b2_colorRed;
|
|
1211
|
+
}
|
|
1212
|
+
else if ( body->setIndex == b2_disabledSet )
|
|
1213
|
+
{
|
|
1214
|
+
color = b2_colorSlateGray;
|
|
1215
|
+
}
|
|
1216
|
+
else if ( shape->sensorIndex != B2_NULL_INDEX )
|
|
1217
|
+
{
|
|
1218
|
+
color = b2_colorWheat;
|
|
1219
|
+
}
|
|
1220
|
+
else if ( bodySim->isBullet && body->setIndex == b2_awakeSet )
|
|
1221
|
+
{
|
|
1222
|
+
color = b2_colorTurquoise;
|
|
1223
|
+
}
|
|
1224
|
+
else if ( body->isSpeedCapped )
|
|
1225
|
+
{
|
|
1226
|
+
color = b2_colorYellow;
|
|
1227
|
+
}
|
|
1228
|
+
else if ( bodySim->isFast )
|
|
1229
|
+
{
|
|
1230
|
+
color = b2_colorSalmon;
|
|
1231
|
+
}
|
|
1232
|
+
else if ( body->type == b2_staticBody )
|
|
1233
|
+
{
|
|
1234
|
+
color = b2_colorPaleGreen;
|
|
1235
|
+
}
|
|
1236
|
+
else if ( body->type == b2_kinematicBody )
|
|
1237
|
+
{
|
|
1238
|
+
color = b2_colorRoyalBlue;
|
|
1239
|
+
}
|
|
1240
|
+
else if ( body->setIndex == b2_awakeSet )
|
|
1241
|
+
{
|
|
1242
|
+
color = b2_colorPink;
|
|
1243
|
+
}
|
|
1244
|
+
else
|
|
1245
|
+
{
|
|
1246
|
+
color = b2_colorGray;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
b2DrawShape( draw, shape, xf, color );
|
|
1250
|
+
shapeId = shape->nextShapeId;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
if ( draw->drawJoints )
|
|
1257
|
+
{
|
|
1258
|
+
int count = world->joints.count;
|
|
1259
|
+
for ( int i = 0; i < count; ++i )
|
|
1260
|
+
{
|
|
1261
|
+
b2Joint* joint = world->joints.data + i;
|
|
1262
|
+
if ( joint->setIndex == B2_NULL_INDEX )
|
|
1263
|
+
{
|
|
1264
|
+
continue;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
b2DrawJoint( draw, world, joint );
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
if ( draw->drawBounds )
|
|
1272
|
+
{
|
|
1273
|
+
b2HexColor color = b2_colorGold;
|
|
1274
|
+
|
|
1275
|
+
int setCount = world->solverSets.count;
|
|
1276
|
+
for ( int setIndex = 0; setIndex < setCount; ++setIndex )
|
|
1277
|
+
{
|
|
1278
|
+
b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );
|
|
1279
|
+
int bodyCount = set->bodySims.count;
|
|
1280
|
+
for ( int bodyIndex = 0; bodyIndex < bodyCount; ++bodyIndex )
|
|
1281
|
+
{
|
|
1282
|
+
b2BodySim* bodySim = set->bodySims.data + bodyIndex;
|
|
1283
|
+
|
|
1284
|
+
char buffer[32];
|
|
1285
|
+
snprintf( buffer, 32, "%d", bodySim->bodyId );
|
|
1286
|
+
draw->DrawStringFcn( bodySim->center, buffer, b2_colorWhite, draw->context );
|
|
1287
|
+
|
|
1288
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, bodySim->bodyId );
|
|
1289
|
+
B2_ASSERT( body->setIndex == setIndex );
|
|
1290
|
+
|
|
1291
|
+
int shapeId = body->headShapeId;
|
|
1292
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
1293
|
+
{
|
|
1294
|
+
b2Shape* shape = world->shapes.data + shapeId;
|
|
1295
|
+
b2AABB aabb = shape->fatAABB;
|
|
1296
|
+
|
|
1297
|
+
b2Vec2 vs[4] = { { aabb.lowerBound.x, aabb.lowerBound.y },
|
|
1298
|
+
{ aabb.upperBound.x, aabb.lowerBound.y },
|
|
1299
|
+
{ aabb.upperBound.x, aabb.upperBound.y },
|
|
1300
|
+
{ aabb.lowerBound.x, aabb.upperBound.y } };
|
|
1301
|
+
|
|
1302
|
+
draw->DrawPolygonFcn( vs, 4, color, draw->context );
|
|
1303
|
+
|
|
1304
|
+
shapeId = shape->nextShapeId;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
if ( draw->drawBodyNames )
|
|
1311
|
+
{
|
|
1312
|
+
b2Vec2 offset = { 0.05f, 0.05f };
|
|
1313
|
+
int count = world->bodies.count;
|
|
1314
|
+
for ( int i = 0; i < count; ++i )
|
|
1315
|
+
{
|
|
1316
|
+
b2Body* body = world->bodies.data + i;
|
|
1317
|
+
if ( body->setIndex == B2_NULL_INDEX )
|
|
1318
|
+
{
|
|
1319
|
+
continue;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
if ( body->name[0] == 0 )
|
|
1323
|
+
{
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
b2BodySim* bodySim = b2GetBodySim( world, body );
|
|
1328
|
+
|
|
1329
|
+
b2Transform transform = { bodySim->center, bodySim->transform.q };
|
|
1330
|
+
b2Vec2 p = b2TransformPoint( transform, offset );
|
|
1331
|
+
draw->DrawStringFcn( p, body->name, b2_colorBlueViolet, draw->context );
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
if ( draw->drawMass )
|
|
1336
|
+
{
|
|
1337
|
+
b2Vec2 offset = { 0.1f, 0.1f };
|
|
1338
|
+
int setCount = world->solverSets.count;
|
|
1339
|
+
for ( int setIndex = 0; setIndex < setCount; ++setIndex )
|
|
1340
|
+
{
|
|
1341
|
+
b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex );
|
|
1342
|
+
int bodyCount = set->bodySims.count;
|
|
1343
|
+
for ( int bodyIndex = 0; bodyIndex < bodyCount; ++bodyIndex )
|
|
1344
|
+
{
|
|
1345
|
+
b2BodySim* bodySim = set->bodySims.data + bodyIndex;
|
|
1346
|
+
|
|
1347
|
+
b2Transform transform = { bodySim->center, bodySim->transform.q };
|
|
1348
|
+
draw->DrawTransformFcn( transform, draw->context );
|
|
1349
|
+
|
|
1350
|
+
b2Vec2 p = b2TransformPoint( transform, offset );
|
|
1351
|
+
|
|
1352
|
+
char buffer[32];
|
|
1353
|
+
float mass = bodySim->invMass > 0.0f ? 1.0f / bodySim->invMass : 0.0f;
|
|
1354
|
+
snprintf( buffer, 32, " %.2f", mass );
|
|
1355
|
+
draw->DrawStringFcn( p, buffer, b2_colorWhite, draw->context );
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
if ( draw->drawContacts )
|
|
1361
|
+
{
|
|
1362
|
+
const float k_impulseScale = 1.0f;
|
|
1363
|
+
const float k_axisScale = 0.3f;
|
|
1364
|
+
const float linearSlop = B2_LINEAR_SLOP;
|
|
1365
|
+
|
|
1366
|
+
b2HexColor speculativeColor = b2_colorLightGray;
|
|
1367
|
+
b2HexColor addColor = b2_colorGreen;
|
|
1368
|
+
b2HexColor persistColor = b2_colorBlue;
|
|
1369
|
+
b2HexColor normalColor = b2_colorDimGray;
|
|
1370
|
+
b2HexColor impulseColor = b2_colorMagenta;
|
|
1371
|
+
b2HexColor frictionColor = b2_colorYellow;
|
|
1372
|
+
|
|
1373
|
+
b2HexColor colors[B2_GRAPH_COLOR_COUNT] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen,
|
|
1374
|
+
b2_colorCyan, b2_colorBlue, b2_colorViolet, b2_colorPink,
|
|
1375
|
+
b2_colorChocolate, b2_colorGoldenRod, b2_colorCoral, b2_colorBlack };
|
|
1376
|
+
|
|
1377
|
+
for ( int colorIndex = 0; colorIndex < B2_GRAPH_COLOR_COUNT; ++colorIndex )
|
|
1378
|
+
{
|
|
1379
|
+
b2GraphColor* graphColor = world->constraintGraph.colors + colorIndex;
|
|
1380
|
+
|
|
1381
|
+
int contactCount = graphColor->contactSims.count;
|
|
1382
|
+
for ( int contactIndex = 0; contactIndex < contactCount; ++contactIndex )
|
|
1383
|
+
{
|
|
1384
|
+
b2ContactSim* contact = graphColor->contactSims.data + contactIndex;
|
|
1385
|
+
int pointCount = contact->manifold.pointCount;
|
|
1386
|
+
b2Vec2 normal = contact->manifold.normal;
|
|
1387
|
+
char buffer[32];
|
|
1388
|
+
|
|
1389
|
+
for ( int j = 0; j < pointCount; ++j )
|
|
1390
|
+
{
|
|
1391
|
+
b2ManifoldPoint* point = contact->manifold.points + j;
|
|
1392
|
+
|
|
1393
|
+
if ( draw->drawGraphColors && 0 <= colorIndex && colorIndex <= B2_GRAPH_COLOR_COUNT )
|
|
1394
|
+
{
|
|
1395
|
+
// graph color
|
|
1396
|
+
float pointSize = colorIndex == B2_OVERFLOW_INDEX ? 7.5f : 5.0f;
|
|
1397
|
+
draw->DrawPointFcn( point->point, pointSize, colors[colorIndex], draw->context );
|
|
1398
|
+
// g_draw.DrawString(point->position, "%d", point->color);
|
|
1399
|
+
}
|
|
1400
|
+
else if ( point->separation > linearSlop )
|
|
1401
|
+
{
|
|
1402
|
+
// Speculative
|
|
1403
|
+
draw->DrawPointFcn( point->point, 5.0f, speculativeColor, draw->context );
|
|
1404
|
+
}
|
|
1405
|
+
else if ( point->persisted == false )
|
|
1406
|
+
{
|
|
1407
|
+
// Add
|
|
1408
|
+
draw->DrawPointFcn( point->point, 10.0f, addColor, draw->context );
|
|
1409
|
+
}
|
|
1410
|
+
else if ( point->persisted == true )
|
|
1411
|
+
{
|
|
1412
|
+
// Persist
|
|
1413
|
+
draw->DrawPointFcn( point->point, 5.0f, persistColor, draw->context );
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
if ( draw->drawContactNormals )
|
|
1417
|
+
{
|
|
1418
|
+
b2Vec2 p1 = point->point;
|
|
1419
|
+
b2Vec2 p2 = b2MulAdd( p1, k_axisScale, normal );
|
|
1420
|
+
draw->DrawSegmentFcn( p1, p2, normalColor, draw->context );
|
|
1421
|
+
}
|
|
1422
|
+
else if ( draw->drawContactImpulses )
|
|
1423
|
+
{
|
|
1424
|
+
b2Vec2 p1 = point->point;
|
|
1425
|
+
b2Vec2 p2 = b2MulAdd( p1, k_impulseScale * point->normalImpulse, normal );
|
|
1426
|
+
draw->DrawSegmentFcn( p1, p2, impulseColor, draw->context );
|
|
1427
|
+
snprintf( buffer, B2_ARRAY_COUNT( buffer ), "%.2f", 1000.0f * point->normalImpulse );
|
|
1428
|
+
draw->DrawStringFcn( p1, buffer, b2_colorWhite, draw->context );
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
if ( draw->drawContactFeatures )
|
|
1432
|
+
{
|
|
1433
|
+
snprintf( buffer, B2_ARRAY_COUNT( buffer ), "%d", point->id );
|
|
1434
|
+
draw->DrawStringFcn( point->point, buffer, b2_colorOrange, draw->context );
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
if ( draw->drawFrictionImpulses )
|
|
1438
|
+
{
|
|
1439
|
+
b2Vec2 tangent = b2RightPerp( normal );
|
|
1440
|
+
b2Vec2 p1 = point->point;
|
|
1441
|
+
b2Vec2 p2 = b2MulAdd( p1, k_impulseScale * point->tangentImpulse, tangent );
|
|
1442
|
+
draw->DrawSegmentFcn( p1, p2, frictionColor, draw->context );
|
|
1443
|
+
snprintf( buffer, B2_ARRAY_COUNT( buffer ), "%.2f", point->tangentImpulse );
|
|
1444
|
+
draw->DrawStringFcn( p1, buffer, b2_colorWhite, draw->context );
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
if ( draw->drawIslands )
|
|
1452
|
+
{
|
|
1453
|
+
int count = world->islands.count;
|
|
1454
|
+
for ( int i = 0; i < count; ++i )
|
|
1455
|
+
{
|
|
1456
|
+
b2Island* island = world->islands.data + i;
|
|
1457
|
+
if ( island->setIndex == B2_NULL_INDEX )
|
|
1458
|
+
{
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
int shapeCount = 0;
|
|
1463
|
+
b2AABB aabb = {
|
|
1464
|
+
.lowerBound = { FLT_MAX, FLT_MAX },
|
|
1465
|
+
.upperBound = { -FLT_MAX, -FLT_MAX },
|
|
1466
|
+
};
|
|
1467
|
+
|
|
1468
|
+
int bodyId = island->headBody;
|
|
1469
|
+
while ( bodyId != B2_NULL_INDEX )
|
|
1470
|
+
{
|
|
1471
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, bodyId );
|
|
1472
|
+
int shapeId = body->headShapeId;
|
|
1473
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
1474
|
+
{
|
|
1475
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
1476
|
+
aabb = b2AABB_Union( aabb, shape->fatAABB );
|
|
1477
|
+
shapeCount += 1;
|
|
1478
|
+
shapeId = shape->nextShapeId;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
bodyId = body->islandNext;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
if ( shapeCount > 0 )
|
|
1485
|
+
{
|
|
1486
|
+
b2Vec2 vs[4] = { { aabb.lowerBound.x, aabb.lowerBound.y },
|
|
1487
|
+
{ aabb.upperBound.x, aabb.lowerBound.y },
|
|
1488
|
+
{ aabb.upperBound.x, aabb.upperBound.y },
|
|
1489
|
+
{ aabb.lowerBound.x, aabb.upperBound.y } };
|
|
1490
|
+
|
|
1491
|
+
draw->DrawPolygonFcn( vs, 4, b2_colorOrangeRed, draw->context );
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
b2BodyEvents b2World_GetBodyEvents( b2WorldId worldId )
|
|
1498
|
+
{
|
|
1499
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1500
|
+
B2_ASSERT( world->locked == false );
|
|
1501
|
+
if ( world->locked )
|
|
1502
|
+
{
|
|
1503
|
+
return (b2BodyEvents){ 0 };
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
int count = world->bodyMoveEvents.count;
|
|
1507
|
+
b2BodyEvents events = { world->bodyMoveEvents.data, count };
|
|
1508
|
+
return events;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
b2SensorEvents b2World_GetSensorEvents( b2WorldId worldId )
|
|
1512
|
+
{
|
|
1513
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1514
|
+
B2_ASSERT( world->locked == false );
|
|
1515
|
+
if ( world->locked )
|
|
1516
|
+
{
|
|
1517
|
+
return (b2SensorEvents){ 0 };
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// Careful to use previous buffer
|
|
1521
|
+
int endEventArrayIndex = 1 - world->endEventArrayIndex;
|
|
1522
|
+
|
|
1523
|
+
int beginCount = world->sensorBeginEvents.count;
|
|
1524
|
+
int endCount = world->sensorEndEvents[endEventArrayIndex].count;
|
|
1525
|
+
|
|
1526
|
+
b2SensorEvents events = {
|
|
1527
|
+
.beginEvents = world->sensorBeginEvents.data,
|
|
1528
|
+
.endEvents = world->sensorEndEvents[endEventArrayIndex].data,
|
|
1529
|
+
.beginCount = beginCount,
|
|
1530
|
+
.endCount = endCount,
|
|
1531
|
+
};
|
|
1532
|
+
return events;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
b2ContactEvents b2World_GetContactEvents( b2WorldId worldId )
|
|
1536
|
+
{
|
|
1537
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1538
|
+
B2_ASSERT( world->locked == false );
|
|
1539
|
+
if ( world->locked )
|
|
1540
|
+
{
|
|
1541
|
+
return (b2ContactEvents){ 0 };
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
// Careful to use previous buffer
|
|
1545
|
+
int endEventArrayIndex = 1 - world->endEventArrayIndex;
|
|
1546
|
+
|
|
1547
|
+
int beginCount = world->contactBeginEvents.count;
|
|
1548
|
+
int endCount = world->contactEndEvents[endEventArrayIndex].count;
|
|
1549
|
+
int hitCount = world->contactHitEvents.count;
|
|
1550
|
+
|
|
1551
|
+
b2ContactEvents events = {
|
|
1552
|
+
.beginEvents = world->contactBeginEvents.data,
|
|
1553
|
+
.endEvents = world->contactEndEvents[endEventArrayIndex].data,
|
|
1554
|
+
.hitEvents = world->contactHitEvents.data,
|
|
1555
|
+
.beginCount = beginCount,
|
|
1556
|
+
.endCount = endCount,
|
|
1557
|
+
.hitCount = hitCount,
|
|
1558
|
+
};
|
|
1559
|
+
|
|
1560
|
+
return events;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
bool b2World_IsValid( b2WorldId id )
|
|
1564
|
+
{
|
|
1565
|
+
if ( id.index1 < 1 || B2_MAX_WORLDS < id.index1 )
|
|
1566
|
+
{
|
|
1567
|
+
return false;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
b2World* world = b2_worlds + ( id.index1 - 1 );
|
|
1571
|
+
|
|
1572
|
+
if ( world->worldId != id.index1 - 1 )
|
|
1573
|
+
{
|
|
1574
|
+
// world is not allocated
|
|
1575
|
+
return false;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
return id.generation == world->generation;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
bool b2Body_IsValid( b2BodyId id )
|
|
1582
|
+
{
|
|
1583
|
+
if ( B2_MAX_WORLDS <= id.world0 )
|
|
1584
|
+
{
|
|
1585
|
+
// invalid world
|
|
1586
|
+
return false;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
b2World* world = b2_worlds + id.world0;
|
|
1590
|
+
if ( world->worldId != id.world0 )
|
|
1591
|
+
{
|
|
1592
|
+
// world is free
|
|
1593
|
+
return false;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
if ( id.index1 < 1 || world->bodies.count < id.index1 )
|
|
1597
|
+
{
|
|
1598
|
+
// invalid index
|
|
1599
|
+
return false;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
b2Body* body = world->bodies.data + ( id.index1 - 1 );
|
|
1603
|
+
if ( body->setIndex == B2_NULL_INDEX )
|
|
1604
|
+
{
|
|
1605
|
+
// this was freed
|
|
1606
|
+
return false;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
B2_ASSERT( body->localIndex != B2_NULL_INDEX );
|
|
1610
|
+
|
|
1611
|
+
if ( body->generation != id.generation )
|
|
1612
|
+
{
|
|
1613
|
+
// this id is orphaned
|
|
1614
|
+
return false;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
return true;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
bool b2Shape_IsValid( b2ShapeId id )
|
|
1621
|
+
{
|
|
1622
|
+
if ( B2_MAX_WORLDS <= id.world0 )
|
|
1623
|
+
{
|
|
1624
|
+
return false;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
b2World* world = b2_worlds + id.world0;
|
|
1628
|
+
if ( world->worldId != id.world0 )
|
|
1629
|
+
{
|
|
1630
|
+
// world is free
|
|
1631
|
+
return false;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
int shapeId = id.index1 - 1;
|
|
1635
|
+
if ( shapeId < 0 || world->shapes.count <= shapeId )
|
|
1636
|
+
{
|
|
1637
|
+
return false;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
b2Shape* shape = world->shapes.data + shapeId;
|
|
1641
|
+
if ( shape->id == B2_NULL_INDEX )
|
|
1642
|
+
{
|
|
1643
|
+
// shape is free
|
|
1644
|
+
return false;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
B2_ASSERT( shape->id == shapeId );
|
|
1648
|
+
|
|
1649
|
+
return id.generation == shape->generation;
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
bool b2Chain_IsValid( b2ChainId id )
|
|
1653
|
+
{
|
|
1654
|
+
if ( B2_MAX_WORLDS <= id.world0 )
|
|
1655
|
+
{
|
|
1656
|
+
return false;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
b2World* world = b2_worlds + id.world0;
|
|
1660
|
+
if ( world->worldId != id.world0 )
|
|
1661
|
+
{
|
|
1662
|
+
// world is free
|
|
1663
|
+
return false;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
int chainId = id.index1 - 1;
|
|
1667
|
+
if ( chainId < 0 || world->chainShapes.count <= chainId )
|
|
1668
|
+
{
|
|
1669
|
+
return false;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
b2ChainShape* chain = world->chainShapes.data + chainId;
|
|
1673
|
+
if ( chain->id == B2_NULL_INDEX )
|
|
1674
|
+
{
|
|
1675
|
+
// chain is free
|
|
1676
|
+
return false;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
B2_ASSERT( chain->id == chainId );
|
|
1680
|
+
|
|
1681
|
+
return id.generation == chain->generation;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
bool b2Joint_IsValid( b2JointId id )
|
|
1685
|
+
{
|
|
1686
|
+
if ( B2_MAX_WORLDS <= id.world0 )
|
|
1687
|
+
{
|
|
1688
|
+
return false;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
b2World* world = b2_worlds + id.world0;
|
|
1692
|
+
if ( world->worldId != id.world0 )
|
|
1693
|
+
{
|
|
1694
|
+
// world is free
|
|
1695
|
+
return false;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
int jointId = id.index1 - 1;
|
|
1699
|
+
if ( jointId < 0 || world->joints.count <= jointId )
|
|
1700
|
+
{
|
|
1701
|
+
return false;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
b2Joint* joint = world->joints.data + jointId;
|
|
1705
|
+
if ( joint->jointId == B2_NULL_INDEX )
|
|
1706
|
+
{
|
|
1707
|
+
// joint is free
|
|
1708
|
+
return false;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
B2_ASSERT( joint->jointId == jointId );
|
|
1712
|
+
|
|
1713
|
+
return id.generation == joint->generation;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
void b2World_EnableSleeping( b2WorldId worldId, bool flag )
|
|
1717
|
+
{
|
|
1718
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1719
|
+
B2_ASSERT( world->locked == false );
|
|
1720
|
+
if ( world->locked )
|
|
1721
|
+
{
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
if ( flag == world->enableSleep )
|
|
1726
|
+
{
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
world->enableSleep = flag;
|
|
1731
|
+
|
|
1732
|
+
if ( flag == false )
|
|
1733
|
+
{
|
|
1734
|
+
int setCount = world->solverSets.count;
|
|
1735
|
+
for ( int i = b2_firstSleepingSet; i < setCount; ++i )
|
|
1736
|
+
{
|
|
1737
|
+
b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, i );
|
|
1738
|
+
if ( set->bodySims.count > 0 )
|
|
1739
|
+
{
|
|
1740
|
+
b2WakeSolverSet( world, i );
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
bool b2World_IsSleepingEnabled( b2WorldId worldId )
|
|
1747
|
+
{
|
|
1748
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1749
|
+
return world->enableSleep;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
void b2World_EnableWarmStarting( b2WorldId worldId, bool flag )
|
|
1753
|
+
{
|
|
1754
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1755
|
+
B2_ASSERT( world->locked == false );
|
|
1756
|
+
if ( world->locked )
|
|
1757
|
+
{
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
world->enableWarmStarting = flag;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
bool b2World_IsWarmStartingEnabled( b2WorldId worldId )
|
|
1765
|
+
{
|
|
1766
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1767
|
+
return world->enableWarmStarting;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
int b2World_GetAwakeBodyCount( b2WorldId worldId )
|
|
1771
|
+
{
|
|
1772
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1773
|
+
b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
|
|
1774
|
+
return awakeSet->bodySims.count;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
void b2World_EnableContinuous( b2WorldId worldId, bool flag )
|
|
1778
|
+
{
|
|
1779
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1780
|
+
B2_ASSERT( world->locked == false );
|
|
1781
|
+
if ( world->locked )
|
|
1782
|
+
{
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
world->enableContinuous = flag;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
bool b2World_IsContinuousEnabled( b2WorldId worldId )
|
|
1790
|
+
{
|
|
1791
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1792
|
+
return world->enableContinuous;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
void b2World_SetRestitutionThreshold( b2WorldId worldId, float value )
|
|
1796
|
+
{
|
|
1797
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1798
|
+
B2_ASSERT( world->locked == false );
|
|
1799
|
+
if ( world->locked )
|
|
1800
|
+
{
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
world->restitutionThreshold = b2ClampFloat( value, 0.0f, FLT_MAX );
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
float b2World_GetRestitutionThreshold( b2WorldId worldId )
|
|
1808
|
+
{
|
|
1809
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1810
|
+
return world->restitutionThreshold;
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
void b2World_SetHitEventThreshold( b2WorldId worldId, float value )
|
|
1814
|
+
{
|
|
1815
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1816
|
+
B2_ASSERT( world->locked == false );
|
|
1817
|
+
if ( world->locked )
|
|
1818
|
+
{
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
world->hitEventThreshold = b2ClampFloat( value, 0.0f, FLT_MAX );
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
float b2World_GetHitEventThreshold( b2WorldId worldId )
|
|
1826
|
+
{
|
|
1827
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1828
|
+
return world->hitEventThreshold;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushSpeed )
|
|
1832
|
+
{
|
|
1833
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1834
|
+
B2_ASSERT( world->locked == false );
|
|
1835
|
+
if ( world->locked )
|
|
1836
|
+
{
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
world->contactHertz = b2ClampFloat( hertz, 0.0f, FLT_MAX );
|
|
1841
|
+
world->contactDampingRatio = b2ClampFloat( dampingRatio, 0.0f, FLT_MAX );
|
|
1842
|
+
world->maxContactPushSpeed = b2ClampFloat( pushSpeed, 0.0f, FLT_MAX );
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
void b2World_SetJointTuning( b2WorldId worldId, float hertz, float dampingRatio )
|
|
1846
|
+
{
|
|
1847
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1848
|
+
B2_ASSERT( world->locked == false );
|
|
1849
|
+
if ( world->locked )
|
|
1850
|
+
{
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
world->jointHertz = b2ClampFloat( hertz, 0.0f, FLT_MAX );
|
|
1855
|
+
world->jointDampingRatio = b2ClampFloat( dampingRatio, 0.0f, FLT_MAX );
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
void b2World_SetMaximumLinearSpeed( b2WorldId worldId, float maximumLinearSpeed )
|
|
1859
|
+
{
|
|
1860
|
+
B2_ASSERT( b2IsValidFloat( maximumLinearSpeed ) && maximumLinearSpeed > 0.0f );
|
|
1861
|
+
|
|
1862
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1863
|
+
B2_ASSERT( world->locked == false );
|
|
1864
|
+
if ( world->locked )
|
|
1865
|
+
{
|
|
1866
|
+
return;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
world->maxLinearSpeed = maximumLinearSpeed;
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
float b2World_GetMaximumLinearSpeed( b2WorldId worldId )
|
|
1873
|
+
{
|
|
1874
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1875
|
+
return world->maxLinearSpeed;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
b2Profile b2World_GetProfile( b2WorldId worldId )
|
|
1879
|
+
{
|
|
1880
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1881
|
+
return world->profile;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
b2Counters b2World_GetCounters( b2WorldId worldId )
|
|
1885
|
+
{
|
|
1886
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1887
|
+
b2Counters s = { 0 };
|
|
1888
|
+
s.bodyCount = b2GetIdCount( &world->bodyIdPool );
|
|
1889
|
+
s.shapeCount = b2GetIdCount( &world->shapeIdPool );
|
|
1890
|
+
s.contactCount = b2GetIdCount( &world->contactIdPool );
|
|
1891
|
+
s.jointCount = b2GetIdCount( &world->jointIdPool );
|
|
1892
|
+
s.islandCount = b2GetIdCount( &world->islandIdPool );
|
|
1893
|
+
|
|
1894
|
+
b2DynamicTree* staticTree = world->broadPhase.trees + b2_staticBody;
|
|
1895
|
+
s.staticTreeHeight = b2DynamicTree_GetHeight( staticTree );
|
|
1896
|
+
|
|
1897
|
+
b2DynamicTree* dynamicTree = world->broadPhase.trees + b2_dynamicBody;
|
|
1898
|
+
b2DynamicTree* kinematicTree = world->broadPhase.trees + b2_kinematicBody;
|
|
1899
|
+
s.treeHeight = b2MaxInt( b2DynamicTree_GetHeight( dynamicTree ), b2DynamicTree_GetHeight( kinematicTree ) );
|
|
1900
|
+
|
|
1901
|
+
s.stackUsed = b2GetMaxArenaAllocation( &world->arena );
|
|
1902
|
+
s.byteCount = b2GetByteCount();
|
|
1903
|
+
s.taskCount = world->taskCount;
|
|
1904
|
+
|
|
1905
|
+
for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )
|
|
1906
|
+
{
|
|
1907
|
+
s.colorCounts[i] = world->constraintGraph.colors[i].contactSims.count + world->constraintGraph.colors[i].jointSims.count;
|
|
1908
|
+
}
|
|
1909
|
+
return s;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
void b2World_SetUserData( b2WorldId worldId, void* userData )
|
|
1913
|
+
{
|
|
1914
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1915
|
+
world->userData = userData;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
void* b2World_GetUserData( b2WorldId worldId )
|
|
1919
|
+
{
|
|
1920
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1921
|
+
return world->userData;
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
void b2World_SetFrictionCallback( b2WorldId worldId, b2FrictionCallback* callback )
|
|
1925
|
+
{
|
|
1926
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1927
|
+
if ( world->locked )
|
|
1928
|
+
{
|
|
1929
|
+
return;
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
if ( callback != NULL )
|
|
1933
|
+
{
|
|
1934
|
+
world->frictionCallback = callback;
|
|
1935
|
+
}
|
|
1936
|
+
else
|
|
1937
|
+
{
|
|
1938
|
+
world->frictionCallback = b2DefaultFrictionCallback;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
void b2World_SetRestitutionCallback( b2WorldId worldId, b2RestitutionCallback* callback )
|
|
1943
|
+
{
|
|
1944
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1945
|
+
if ( world->locked )
|
|
1946
|
+
{
|
|
1947
|
+
return;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
if ( callback != NULL )
|
|
1951
|
+
{
|
|
1952
|
+
world->restitutionCallback = callback;
|
|
1953
|
+
}
|
|
1954
|
+
else
|
|
1955
|
+
{
|
|
1956
|
+
world->restitutionCallback = b2DefaultRestitutionCallback;
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
void b2World_DumpMemoryStats( b2WorldId worldId )
|
|
1961
|
+
{
|
|
1962
|
+
FILE* file = fopen( "box2d_memory.txt", "w" );
|
|
1963
|
+
if ( file == NULL )
|
|
1964
|
+
{
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
1969
|
+
|
|
1970
|
+
// id pools
|
|
1971
|
+
fprintf( file, "id pools\n" );
|
|
1972
|
+
fprintf( file, "body ids: %d\n", b2GetIdBytes( &world->bodyIdPool ) );
|
|
1973
|
+
fprintf( file, "solver set ids: %d\n", b2GetIdBytes( &world->solverSetIdPool ) );
|
|
1974
|
+
fprintf( file, "joint ids: %d\n", b2GetIdBytes( &world->jointIdPool ) );
|
|
1975
|
+
fprintf( file, "contact ids: %d\n", b2GetIdBytes( &world->contactIdPool ) );
|
|
1976
|
+
fprintf( file, "island ids: %d\n", b2GetIdBytes( &world->islandIdPool ) );
|
|
1977
|
+
fprintf( file, "shape ids: %d\n", b2GetIdBytes( &world->shapeIdPool ) );
|
|
1978
|
+
fprintf( file, "chain ids: %d\n", b2GetIdBytes( &world->chainIdPool ) );
|
|
1979
|
+
fprintf( file, "\n" );
|
|
1980
|
+
|
|
1981
|
+
// world arrays
|
|
1982
|
+
fprintf( file, "world arrays\n" );
|
|
1983
|
+
fprintf( file, "bodies: %d\n", b2BodyArray_ByteCount( &world->bodies ) );
|
|
1984
|
+
fprintf( file, "solver sets: %d\n", b2SolverSetArray_ByteCount( &world->solverSets ) );
|
|
1985
|
+
fprintf( file, "joints: %d\n", b2JointArray_ByteCount( &world->joints ) );
|
|
1986
|
+
fprintf( file, "contacts: %d\n", b2ContactArray_ByteCount( &world->contacts ) );
|
|
1987
|
+
fprintf( file, "islands: %d\n", b2IslandArray_ByteCount( &world->islands ) );
|
|
1988
|
+
fprintf( file, "shapes: %d\n", b2ShapeArray_ByteCount( &world->shapes ) );
|
|
1989
|
+
fprintf( file, "chains: %d\n", b2ChainShapeArray_ByteCount( &world->chainShapes ) );
|
|
1990
|
+
fprintf( file, "\n" );
|
|
1991
|
+
|
|
1992
|
+
// broad-phase
|
|
1993
|
+
fprintf( file, "broad-phase\n" );
|
|
1994
|
+
fprintf( file, "static tree: %d\n", b2DynamicTree_GetByteCount( world->broadPhase.trees + b2_staticBody ) );
|
|
1995
|
+
fprintf( file, "kinematic tree: %d\n", b2DynamicTree_GetByteCount( world->broadPhase.trees + b2_kinematicBody ) );
|
|
1996
|
+
fprintf( file, "dynamic tree: %d\n", b2DynamicTree_GetByteCount( world->broadPhase.trees + b2_dynamicBody ) );
|
|
1997
|
+
b2HashSet* moveSet = &world->broadPhase.moveSet;
|
|
1998
|
+
fprintf( file, "moveSet: %d (%d, %d)\n", b2GetHashSetBytes( moveSet ), moveSet->count, moveSet->capacity );
|
|
1999
|
+
fprintf( file, "moveArray: %d\n", b2IntArray_ByteCount( &world->broadPhase.moveArray ) );
|
|
2000
|
+
b2HashSet* pairSet = &world->broadPhase.pairSet;
|
|
2001
|
+
fprintf( file, "pairSet: %d (%d, %d)\n", b2GetHashSetBytes( pairSet ), pairSet->count, pairSet->capacity );
|
|
2002
|
+
fprintf( file, "\n" );
|
|
2003
|
+
|
|
2004
|
+
// solver sets
|
|
2005
|
+
int bodySimCapacity = 0;
|
|
2006
|
+
int bodyStateCapacity = 0;
|
|
2007
|
+
int jointSimCapacity = 0;
|
|
2008
|
+
int contactSimCapacity = 0;
|
|
2009
|
+
int islandSimCapacity = 0;
|
|
2010
|
+
int solverSetCapacity = world->solverSets.count;
|
|
2011
|
+
for ( int i = 0; i < solverSetCapacity; ++i )
|
|
2012
|
+
{
|
|
2013
|
+
b2SolverSet* set = world->solverSets.data + i;
|
|
2014
|
+
if ( set->setIndex == B2_NULL_INDEX )
|
|
2015
|
+
{
|
|
2016
|
+
continue;
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
bodySimCapacity += set->bodySims.capacity;
|
|
2020
|
+
bodyStateCapacity += set->bodyStates.capacity;
|
|
2021
|
+
jointSimCapacity += set->jointSims.capacity;
|
|
2022
|
+
contactSimCapacity += set->contactSims.capacity;
|
|
2023
|
+
islandSimCapacity += set->islandSims.capacity;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
fprintf( file, "solver sets\n" );
|
|
2027
|
+
fprintf( file, "body sim: %d\n", bodySimCapacity * (int)sizeof( b2BodySim ) );
|
|
2028
|
+
fprintf( file, "body state: %d\n", bodyStateCapacity * (int)sizeof( b2BodyState ) );
|
|
2029
|
+
fprintf( file, "joint sim: %d\n", jointSimCapacity * (int)sizeof( b2JointSim ) );
|
|
2030
|
+
fprintf( file, "contact sim: %d\n", contactSimCapacity * (int)sizeof( b2ContactSim ) );
|
|
2031
|
+
fprintf( file, "island sim: %d\n", islandSimCapacity * (int)sizeof( islandSimCapacity ) );
|
|
2032
|
+
fprintf( file, "\n" );
|
|
2033
|
+
|
|
2034
|
+
// constraint graph
|
|
2035
|
+
int bodyBitSetBytes = 0;
|
|
2036
|
+
contactSimCapacity = 0;
|
|
2037
|
+
jointSimCapacity = 0;
|
|
2038
|
+
for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )
|
|
2039
|
+
{
|
|
2040
|
+
b2GraphColor* c = world->constraintGraph.colors + i;
|
|
2041
|
+
bodyBitSetBytes += b2GetBitSetBytes( &c->bodySet );
|
|
2042
|
+
contactSimCapacity += c->contactSims.capacity;
|
|
2043
|
+
jointSimCapacity += c->jointSims.capacity;
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
fprintf( file, "constraint graph\n" );
|
|
2047
|
+
fprintf( file, "body bit sets: %d\n", bodyBitSetBytes );
|
|
2048
|
+
fprintf( file, "joint sim: %d\n", jointSimCapacity * (int)sizeof( b2JointSim ) );
|
|
2049
|
+
fprintf( file, "contact sim: %d\n", contactSimCapacity * (int)sizeof( b2ContactSim ) );
|
|
2050
|
+
fprintf( file, "\n" );
|
|
2051
|
+
|
|
2052
|
+
// stack allocator
|
|
2053
|
+
fprintf( file, "stack allocator: %d\n\n", world->arena.capacity );
|
|
2054
|
+
|
|
2055
|
+
// chain shapes
|
|
2056
|
+
// todo
|
|
2057
|
+
|
|
2058
|
+
fclose( file );
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
typedef struct WorldQueryContext
|
|
2062
|
+
{
|
|
2063
|
+
b2World* world;
|
|
2064
|
+
b2OverlapResultFcn* fcn;
|
|
2065
|
+
b2QueryFilter filter;
|
|
2066
|
+
void* userContext;
|
|
2067
|
+
} WorldQueryContext;
|
|
2068
|
+
|
|
2069
|
+
static bool TreeQueryCallback( int proxyId, uint64_t userData, void* context )
|
|
2070
|
+
{
|
|
2071
|
+
B2_UNUSED( proxyId );
|
|
2072
|
+
|
|
2073
|
+
int shapeId = (int)userData;
|
|
2074
|
+
|
|
2075
|
+
WorldQueryContext* worldContext = context;
|
|
2076
|
+
b2World* world = worldContext->world;
|
|
2077
|
+
|
|
2078
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
2079
|
+
|
|
2080
|
+
b2Filter shapeFilter = shape->filter;
|
|
2081
|
+
b2QueryFilter queryFilter = worldContext->filter;
|
|
2082
|
+
|
|
2083
|
+
if ( ( shapeFilter.categoryBits & queryFilter.maskBits ) == 0 || ( shapeFilter.maskBits & queryFilter.categoryBits ) == 0 )
|
|
2084
|
+
{
|
|
2085
|
+
return true;
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
b2ShapeId id = { shapeId + 1, world->worldId, shape->generation };
|
|
2089
|
+
bool result = worldContext->fcn( id, worldContext->userContext );
|
|
2090
|
+
return result;
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
b2TreeStats b2World_OverlapAABB( b2WorldId worldId, b2AABB aabb, b2QueryFilter filter, b2OverlapResultFcn* fcn, void* context )
|
|
2094
|
+
{
|
|
2095
|
+
b2TreeStats treeStats = { 0 };
|
|
2096
|
+
|
|
2097
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2098
|
+
B2_ASSERT( world->locked == false );
|
|
2099
|
+
if ( world->locked )
|
|
2100
|
+
{
|
|
2101
|
+
return treeStats;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
B2_ASSERT( b2IsValidAABB( aabb ) );
|
|
2105
|
+
|
|
2106
|
+
WorldQueryContext worldContext = { world, fcn, filter, context };
|
|
2107
|
+
|
|
2108
|
+
for ( int i = 0; i < b2_bodyTypeCount; ++i )
|
|
2109
|
+
{
|
|
2110
|
+
b2TreeStats treeResult =
|
|
2111
|
+
b2DynamicTree_Query( world->broadPhase.trees + i, aabb, filter.maskBits, TreeQueryCallback, &worldContext );
|
|
2112
|
+
|
|
2113
|
+
treeStats.nodeVisits += treeResult.nodeVisits;
|
|
2114
|
+
treeStats.leafVisits += treeResult.leafVisits;
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
return treeStats;
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
typedef struct WorldOverlapContext
|
|
2121
|
+
{
|
|
2122
|
+
b2World* world;
|
|
2123
|
+
b2OverlapResultFcn* fcn;
|
|
2124
|
+
b2QueryFilter filter;
|
|
2125
|
+
const b2ShapeProxy* proxy;
|
|
2126
|
+
void* userContext;
|
|
2127
|
+
} WorldOverlapContext;
|
|
2128
|
+
|
|
2129
|
+
static bool TreeOverlapCallback( int proxyId, uint64_t userData, void* context )
|
|
2130
|
+
{
|
|
2131
|
+
B2_UNUSED( proxyId );
|
|
2132
|
+
|
|
2133
|
+
int shapeId = (int)userData;
|
|
2134
|
+
|
|
2135
|
+
WorldOverlapContext* worldContext = context;
|
|
2136
|
+
b2World* world = worldContext->world;
|
|
2137
|
+
|
|
2138
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
2139
|
+
|
|
2140
|
+
b2Filter shapeFilter = shape->filter;
|
|
2141
|
+
b2QueryFilter queryFilter = worldContext->filter;
|
|
2142
|
+
|
|
2143
|
+
if ( ( shapeFilter.categoryBits & queryFilter.maskBits ) == 0 || ( shapeFilter.maskBits & queryFilter.categoryBits ) == 0 )
|
|
2144
|
+
{
|
|
2145
|
+
return true;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
|
|
2149
|
+
b2Transform transform = b2GetBodyTransformQuick( world, body );
|
|
2150
|
+
|
|
2151
|
+
b2DistanceInput input;
|
|
2152
|
+
input.proxyA = *worldContext->proxy;
|
|
2153
|
+
input.proxyB = b2MakeShapeDistanceProxy( shape );
|
|
2154
|
+
input.transformA = b2Transform_identity;
|
|
2155
|
+
input.transformB = transform;
|
|
2156
|
+
input.useRadii = true;
|
|
2157
|
+
|
|
2158
|
+
b2SimplexCache cache = { 0 };
|
|
2159
|
+
b2DistanceOutput output = b2ShapeDistance( &input, &cache, NULL, 0 );
|
|
2160
|
+
|
|
2161
|
+
float tolerance = 0.1f * B2_LINEAR_SLOP;
|
|
2162
|
+
if ( output.distance > tolerance )
|
|
2163
|
+
{
|
|
2164
|
+
return true;
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
b2ShapeId id = { shape->id + 1, world->worldId, shape->generation };
|
|
2168
|
+
bool result = worldContext->fcn( id, worldContext->userContext );
|
|
2169
|
+
return result;
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
b2TreeStats b2World_OverlapShape( b2WorldId worldId, const b2ShapeProxy* proxy, b2QueryFilter filter, b2OverlapResultFcn* fcn,
|
|
2173
|
+
void* context )
|
|
2174
|
+
{
|
|
2175
|
+
b2TreeStats treeStats = { 0 };
|
|
2176
|
+
|
|
2177
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2178
|
+
B2_ASSERT( world->locked == false );
|
|
2179
|
+
if ( world->locked )
|
|
2180
|
+
{
|
|
2181
|
+
return treeStats;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
b2AABB aabb = b2MakeAABB( proxy->points, proxy->count, proxy->radius );
|
|
2185
|
+
WorldOverlapContext worldContext = {
|
|
2186
|
+
world, fcn, filter, proxy, context,
|
|
2187
|
+
};
|
|
2188
|
+
|
|
2189
|
+
for ( int i = 0; i < b2_bodyTypeCount; ++i )
|
|
2190
|
+
{
|
|
2191
|
+
b2TreeStats treeResult =
|
|
2192
|
+
b2DynamicTree_Query( world->broadPhase.trees + i, aabb, filter.maskBits, TreeOverlapCallback, &worldContext );
|
|
2193
|
+
|
|
2194
|
+
treeStats.nodeVisits += treeResult.nodeVisits;
|
|
2195
|
+
treeStats.leafVisits += treeResult.leafVisits;
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
return treeStats;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
typedef struct WorldRayCastContext
|
|
2202
|
+
{
|
|
2203
|
+
b2World* world;
|
|
2204
|
+
b2CastResultFcn* fcn;
|
|
2205
|
+
b2QueryFilter filter;
|
|
2206
|
+
float fraction;
|
|
2207
|
+
void* userContext;
|
|
2208
|
+
} WorldRayCastContext;
|
|
2209
|
+
|
|
2210
|
+
static float RayCastCallback( const b2RayCastInput* input, int proxyId, uint64_t userData, void* context )
|
|
2211
|
+
{
|
|
2212
|
+
B2_UNUSED( proxyId );
|
|
2213
|
+
|
|
2214
|
+
int shapeId = (int)userData;
|
|
2215
|
+
|
|
2216
|
+
WorldRayCastContext* worldContext = context;
|
|
2217
|
+
b2World* world = worldContext->world;
|
|
2218
|
+
|
|
2219
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
2220
|
+
b2Filter shapeFilter = shape->filter;
|
|
2221
|
+
b2QueryFilter queryFilter = worldContext->filter;
|
|
2222
|
+
|
|
2223
|
+
if ( ( shapeFilter.categoryBits & queryFilter.maskBits ) == 0 || ( shapeFilter.maskBits & queryFilter.categoryBits ) == 0 )
|
|
2224
|
+
{
|
|
2225
|
+
return input->maxFraction;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
|
|
2229
|
+
b2Transform transform = b2GetBodyTransformQuick( world, body );
|
|
2230
|
+
b2CastOutput output = b2RayCastShape( input, shape, transform );
|
|
2231
|
+
|
|
2232
|
+
if ( output.hit )
|
|
2233
|
+
{
|
|
2234
|
+
b2ShapeId id = { shapeId + 1, world->worldId, shape->generation };
|
|
2235
|
+
float fraction = worldContext->fcn( id, output.point, output.normal, output.fraction, worldContext->userContext );
|
|
2236
|
+
|
|
2237
|
+
// The user may return -1 to skip this shape
|
|
2238
|
+
if ( 0.0f <= fraction && fraction <= 1.0f )
|
|
2239
|
+
{
|
|
2240
|
+
worldContext->fraction = fraction;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
return fraction;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
return input->maxFraction;
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
b2TreeStats b2World_CastRay( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter, b2CastResultFcn* fcn,
|
|
2250
|
+
void* context )
|
|
2251
|
+
{
|
|
2252
|
+
b2TreeStats treeStats = { 0 };
|
|
2253
|
+
|
|
2254
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2255
|
+
B2_ASSERT( world->locked == false );
|
|
2256
|
+
if ( world->locked )
|
|
2257
|
+
{
|
|
2258
|
+
return treeStats;
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
B2_ASSERT( b2IsValidVec2( origin ) );
|
|
2262
|
+
B2_ASSERT( b2IsValidVec2( translation ) );
|
|
2263
|
+
|
|
2264
|
+
b2RayCastInput input = { origin, translation, 1.0f };
|
|
2265
|
+
|
|
2266
|
+
WorldRayCastContext worldContext = { world, fcn, filter, 1.0f, context };
|
|
2267
|
+
|
|
2268
|
+
for ( int i = 0; i < b2_bodyTypeCount; ++i )
|
|
2269
|
+
{
|
|
2270
|
+
b2TreeStats treeResult =
|
|
2271
|
+
b2DynamicTree_RayCast( world->broadPhase.trees + i, &input, filter.maskBits, RayCastCallback, &worldContext );
|
|
2272
|
+
treeStats.nodeVisits += treeResult.nodeVisits;
|
|
2273
|
+
treeStats.leafVisits += treeResult.leafVisits;
|
|
2274
|
+
|
|
2275
|
+
if ( worldContext.fraction == 0.0f )
|
|
2276
|
+
{
|
|
2277
|
+
return treeStats;
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
input.maxFraction = worldContext.fraction;
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
return treeStats;
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
// This callback finds the closest hit. This is the most common callback used in games.
|
|
2287
|
+
static float b2RayCastClosestFcn( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context )
|
|
2288
|
+
{
|
|
2289
|
+
b2RayResult* rayResult = (b2RayResult*)context;
|
|
2290
|
+
rayResult->shapeId = shapeId;
|
|
2291
|
+
rayResult->point = point;
|
|
2292
|
+
rayResult->normal = normal;
|
|
2293
|
+
rayResult->fraction = fraction;
|
|
2294
|
+
rayResult->hit = true;
|
|
2295
|
+
return fraction;
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
b2RayResult b2World_CastRayClosest( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter )
|
|
2299
|
+
{
|
|
2300
|
+
b2RayResult result = { 0 };
|
|
2301
|
+
|
|
2302
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2303
|
+
B2_ASSERT( world->locked == false );
|
|
2304
|
+
if ( world->locked )
|
|
2305
|
+
{
|
|
2306
|
+
return result;
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
B2_ASSERT( b2IsValidVec2( origin ) );
|
|
2310
|
+
B2_ASSERT( b2IsValidVec2( translation ) );
|
|
2311
|
+
|
|
2312
|
+
b2RayCastInput input = { origin, translation, 1.0f };
|
|
2313
|
+
WorldRayCastContext worldContext = { world, b2RayCastClosestFcn, filter, 1.0f, &result };
|
|
2314
|
+
|
|
2315
|
+
for ( int i = 0; i < b2_bodyTypeCount; ++i )
|
|
2316
|
+
{
|
|
2317
|
+
b2TreeStats treeResult =
|
|
2318
|
+
b2DynamicTree_RayCast( world->broadPhase.trees + i, &input, filter.maskBits, RayCastCallback, &worldContext );
|
|
2319
|
+
result.nodeVisits += treeResult.nodeVisits;
|
|
2320
|
+
result.leafVisits += treeResult.leafVisits;
|
|
2321
|
+
|
|
2322
|
+
if ( worldContext.fraction == 0.0f )
|
|
2323
|
+
{
|
|
2324
|
+
return result;
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
input.maxFraction = worldContext.fraction;
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
return result;
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
static float ShapeCastCallback( const b2ShapeCastInput* input, int proxyId, uint64_t userData, void* context )
|
|
2334
|
+
{
|
|
2335
|
+
B2_UNUSED( proxyId );
|
|
2336
|
+
|
|
2337
|
+
int shapeId = (int)userData;
|
|
2338
|
+
|
|
2339
|
+
WorldRayCastContext* worldContext = context;
|
|
2340
|
+
b2World* world = worldContext->world;
|
|
2341
|
+
|
|
2342
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
2343
|
+
b2Filter shapeFilter = shape->filter;
|
|
2344
|
+
b2QueryFilter queryFilter = worldContext->filter;
|
|
2345
|
+
|
|
2346
|
+
if ( ( shapeFilter.categoryBits & queryFilter.maskBits ) == 0 || ( shapeFilter.maskBits & queryFilter.categoryBits ) == 0 )
|
|
2347
|
+
{
|
|
2348
|
+
return input->maxFraction;
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
|
|
2352
|
+
b2Transform transform = b2GetBodyTransformQuick( world, body );
|
|
2353
|
+
|
|
2354
|
+
b2CastOutput output = b2ShapeCastShape( input, shape, transform );
|
|
2355
|
+
|
|
2356
|
+
if ( output.hit )
|
|
2357
|
+
{
|
|
2358
|
+
b2ShapeId id = { shapeId + 1, world->worldId, shape->generation };
|
|
2359
|
+
float fraction = worldContext->fcn( id, output.point, output.normal, output.fraction, worldContext->userContext );
|
|
2360
|
+
|
|
2361
|
+
// The user may return -1 to skip this shape
|
|
2362
|
+
if ( 0.0f <= fraction && fraction <= 1.0f )
|
|
2363
|
+
{
|
|
2364
|
+
worldContext->fraction = fraction;
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
return fraction;
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
return input->maxFraction;
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
b2TreeStats b2World_CastShape( b2WorldId worldId, const b2ShapeProxy* proxy, b2Vec2 translation, b2QueryFilter filter,
|
|
2374
|
+
b2CastResultFcn* fcn, void* context )
|
|
2375
|
+
{
|
|
2376
|
+
b2TreeStats treeStats = { 0 };
|
|
2377
|
+
|
|
2378
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2379
|
+
B2_ASSERT( world->locked == false );
|
|
2380
|
+
if ( world->locked )
|
|
2381
|
+
{
|
|
2382
|
+
return treeStats;
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
B2_ASSERT( b2IsValidVec2( translation ) );
|
|
2386
|
+
|
|
2387
|
+
b2ShapeCastInput input = { 0 };
|
|
2388
|
+
input.proxy = *proxy;
|
|
2389
|
+
input.translation = translation;
|
|
2390
|
+
input.maxFraction = 1.0f;
|
|
2391
|
+
|
|
2392
|
+
WorldRayCastContext worldContext = { world, fcn, filter, 1.0f, context };
|
|
2393
|
+
|
|
2394
|
+
for ( int i = 0; i < b2_bodyTypeCount; ++i )
|
|
2395
|
+
{
|
|
2396
|
+
b2TreeStats treeResult =
|
|
2397
|
+
b2DynamicTree_ShapeCast( world->broadPhase.trees + i, &input, filter.maskBits, ShapeCastCallback, &worldContext );
|
|
2398
|
+
treeStats.nodeVisits += treeResult.nodeVisits;
|
|
2399
|
+
treeStats.leafVisits += treeResult.leafVisits;
|
|
2400
|
+
|
|
2401
|
+
if ( worldContext.fraction == 0.0f )
|
|
2402
|
+
{
|
|
2403
|
+
return treeStats;
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
input.maxFraction = worldContext.fraction;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
return treeStats;
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
typedef struct b2MoverContext
|
|
2413
|
+
{
|
|
2414
|
+
b2World* world;
|
|
2415
|
+
b2QueryFilter filter;
|
|
2416
|
+
b2ShapeProxy proxy;
|
|
2417
|
+
b2Transform transform;
|
|
2418
|
+
void* userContext;
|
|
2419
|
+
} b2CharacterCallbackContext;
|
|
2420
|
+
|
|
2421
|
+
typedef struct WorldMoverCastContext
|
|
2422
|
+
{
|
|
2423
|
+
b2World* world;
|
|
2424
|
+
b2QueryFilter filter;
|
|
2425
|
+
float fraction;
|
|
2426
|
+
} WorldMoverCastContext;
|
|
2427
|
+
|
|
2428
|
+
static float MoverCastCallback( const b2ShapeCastInput* input, int proxyId, uint64_t userData, void* context )
|
|
2429
|
+
{
|
|
2430
|
+
B2_UNUSED( proxyId );
|
|
2431
|
+
|
|
2432
|
+
int shapeId = (int)userData;
|
|
2433
|
+
WorldMoverCastContext* worldContext = context;
|
|
2434
|
+
b2World* world = worldContext->world;
|
|
2435
|
+
|
|
2436
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
2437
|
+
b2Filter shapeFilter = shape->filter;
|
|
2438
|
+
b2QueryFilter queryFilter = worldContext->filter;
|
|
2439
|
+
|
|
2440
|
+
if ( ( shapeFilter.categoryBits & queryFilter.maskBits ) == 0 || ( shapeFilter.maskBits & queryFilter.categoryBits ) == 0 )
|
|
2441
|
+
{
|
|
2442
|
+
return worldContext->fraction;
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
|
|
2446
|
+
b2Transform transform = b2GetBodyTransformQuick( world, body );
|
|
2447
|
+
|
|
2448
|
+
b2CastOutput output = b2ShapeCastShape( input, shape, transform );
|
|
2449
|
+
if ( output.fraction == 0.0f )
|
|
2450
|
+
{
|
|
2451
|
+
// Ignore overlapping shapes
|
|
2452
|
+
return worldContext->fraction;
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
worldContext->fraction = output.fraction;
|
|
2456
|
+
return output.fraction;
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
float b2World_CastMover( b2WorldId worldId, const b2Capsule* mover, b2Vec2 translation, b2QueryFilter filter )
|
|
2460
|
+
{
|
|
2461
|
+
B2_ASSERT( b2IsValidVec2( translation ) );
|
|
2462
|
+
B2_ASSERT( mover->radius > 2.0f * B2_LINEAR_SLOP );
|
|
2463
|
+
|
|
2464
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2465
|
+
B2_ASSERT( world->locked == false );
|
|
2466
|
+
if ( world->locked )
|
|
2467
|
+
{
|
|
2468
|
+
return 1.0f;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
b2ShapeCastInput input = { 0 };
|
|
2472
|
+
input.proxy.points[0] = mover->center1;
|
|
2473
|
+
input.proxy.points[1] = mover->center2;
|
|
2474
|
+
input.proxy.count = 2;
|
|
2475
|
+
input.proxy.radius = mover->radius;
|
|
2476
|
+
input.translation = translation;
|
|
2477
|
+
input.maxFraction = 1.0f;
|
|
2478
|
+
input.canEncroach = true;
|
|
2479
|
+
|
|
2480
|
+
WorldMoverCastContext worldContext = { world, filter, 1.0f };
|
|
2481
|
+
|
|
2482
|
+
for ( int i = 0; i < b2_bodyTypeCount; ++i )
|
|
2483
|
+
{
|
|
2484
|
+
b2DynamicTree_ShapeCast( world->broadPhase.trees + i, &input, filter.maskBits, MoverCastCallback, &worldContext );
|
|
2485
|
+
|
|
2486
|
+
if ( worldContext.fraction == 0.0f )
|
|
2487
|
+
{
|
|
2488
|
+
return 0.0f;
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
input.maxFraction = worldContext.fraction;
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
return worldContext.fraction;
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
typedef struct WorldMoverContext
|
|
2498
|
+
{
|
|
2499
|
+
b2World* world;
|
|
2500
|
+
b2PlaneResultFcn* fcn;
|
|
2501
|
+
b2QueryFilter filter;
|
|
2502
|
+
b2Capsule mover;
|
|
2503
|
+
void* userContext;
|
|
2504
|
+
} WorldMoverContext;
|
|
2505
|
+
|
|
2506
|
+
static bool TreeCollideCallback( int proxyId, uint64_t userData, void* context )
|
|
2507
|
+
{
|
|
2508
|
+
B2_UNUSED( proxyId );
|
|
2509
|
+
|
|
2510
|
+
int shapeId = (int)userData;
|
|
2511
|
+
WorldMoverContext* worldContext = (WorldMoverContext*)context;
|
|
2512
|
+
b2World* world = worldContext->world;
|
|
2513
|
+
|
|
2514
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
2515
|
+
|
|
2516
|
+
b2Filter shapeFilter = shape->filter;
|
|
2517
|
+
b2QueryFilter queryFilter = worldContext->filter;
|
|
2518
|
+
|
|
2519
|
+
if ( ( shapeFilter.categoryBits & queryFilter.maskBits ) == 0 || ( shapeFilter.maskBits & queryFilter.categoryBits ) == 0 )
|
|
2520
|
+
{
|
|
2521
|
+
return true;
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
|
|
2525
|
+
b2Transform transform = b2GetBodyTransformQuick( world, body );
|
|
2526
|
+
|
|
2527
|
+
b2PlaneResult result = b2CollideMover( shape, transform, &worldContext->mover );
|
|
2528
|
+
|
|
2529
|
+
if ( result.hit )
|
|
2530
|
+
{
|
|
2531
|
+
b2ShapeId id = { shape->id + 1, world->worldId, shape->generation };
|
|
2532
|
+
return worldContext->fcn( id, &result, worldContext->userContext );
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
return true;
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
// It is tempting to use a shape proxy for the mover, but this makes handling deep overlap difficult and the generality may
|
|
2539
|
+
// not be worth it.
|
|
2540
|
+
void b2World_CollideMover( b2WorldId worldId, const b2Capsule* mover, b2QueryFilter filter, b2PlaneResultFcn* fcn, void* context )
|
|
2541
|
+
{
|
|
2542
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2543
|
+
B2_ASSERT( world->locked == false );
|
|
2544
|
+
if ( world->locked )
|
|
2545
|
+
{
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
b2Vec2 r = { mover->radius, mover->radius };
|
|
2550
|
+
|
|
2551
|
+
b2AABB aabb;
|
|
2552
|
+
aabb.lowerBound = b2Sub( b2Min( mover->center1, mover->center2 ), r );
|
|
2553
|
+
aabb.upperBound = b2Add( b2Max( mover->center1, mover->center2 ), r );
|
|
2554
|
+
|
|
2555
|
+
WorldMoverContext worldContext = {
|
|
2556
|
+
world, fcn, filter, *mover, context,
|
|
2557
|
+
};
|
|
2558
|
+
|
|
2559
|
+
for ( int i = 0; i < b2_bodyTypeCount; ++i )
|
|
2560
|
+
{
|
|
2561
|
+
b2DynamicTree_Query( world->broadPhase.trees + i, aabb, filter.maskBits, TreeCollideCallback, &worldContext );
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
#if 0
|
|
2566
|
+
|
|
2567
|
+
void b2World_Dump()
|
|
2568
|
+
{
|
|
2569
|
+
if (m_locked)
|
|
2570
|
+
{
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
b2OpenDump("box2d_dump.inl");
|
|
2575
|
+
|
|
2576
|
+
b2Dump("b2Vec2 g(%.9g, %.9g);\n", m_gravity.x, m_gravity.y);
|
|
2577
|
+
b2Dump("m_world->SetGravity(g);\n");
|
|
2578
|
+
|
|
2579
|
+
b2Dump("b2Body** sims = (b2Body**)b2Alloc(%d * sizeof(b2Body*));\n", m_bodyCount);
|
|
2580
|
+
b2Dump("b2Joint** joints = (b2Joint**)b2Alloc(%d * sizeof(b2Joint*));\n", m_jointCount);
|
|
2581
|
+
|
|
2582
|
+
int32 i = 0;
|
|
2583
|
+
for (b2Body* b = m_bodyList; b; b = b->m_next)
|
|
2584
|
+
{
|
|
2585
|
+
b->m_islandIndex = i;
|
|
2586
|
+
b->Dump();
|
|
2587
|
+
++i;
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
i = 0;
|
|
2591
|
+
for (b2Joint* j = m_jointList; j; j = j->m_next)
|
|
2592
|
+
{
|
|
2593
|
+
j->m_index = i;
|
|
2594
|
+
++i;
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
// First pass on joints, skip gear joints.
|
|
2598
|
+
for (b2Joint* j = m_jointList; j; j = j->m_next)
|
|
2599
|
+
{
|
|
2600
|
+
if (j->m_type == e_gearJoint)
|
|
2601
|
+
{
|
|
2602
|
+
continue;
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
b2Dump("{\n");
|
|
2606
|
+
j->Dump();
|
|
2607
|
+
b2Dump("}\n");
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
// Second pass on joints, only gear joints.
|
|
2611
|
+
for (b2Joint* j = m_jointList; j; j = j->m_next)
|
|
2612
|
+
{
|
|
2613
|
+
if (j->m_type != e_gearJoint)
|
|
2614
|
+
{
|
|
2615
|
+
continue;
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
b2Dump("{\n");
|
|
2619
|
+
j->Dump();
|
|
2620
|
+
b2Dump("}\n");
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
b2Dump("b2Free(joints);\n");
|
|
2624
|
+
b2Dump("b2Free(sims);\n");
|
|
2625
|
+
b2Dump("joints = nullptr;\n");
|
|
2626
|
+
b2Dump("sims = nullptr;\n");
|
|
2627
|
+
|
|
2628
|
+
b2CloseDump();
|
|
2629
|
+
}
|
|
2630
|
+
#endif
|
|
2631
|
+
|
|
2632
|
+
void b2World_SetCustomFilterCallback( b2WorldId worldId, b2CustomFilterFcn* fcn, void* context )
|
|
2633
|
+
{
|
|
2634
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2635
|
+
world->customFilterFcn = fcn;
|
|
2636
|
+
world->customFilterContext = context;
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
void b2World_SetPreSolveCallback( b2WorldId worldId, b2PreSolveFcn* fcn, void* context )
|
|
2640
|
+
{
|
|
2641
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2642
|
+
world->preSolveFcn = fcn;
|
|
2643
|
+
world->preSolveContext = context;
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
void b2World_SetGravity( b2WorldId worldId, b2Vec2 gravity )
|
|
2647
|
+
{
|
|
2648
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2649
|
+
world->gravity = gravity;
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
b2Vec2 b2World_GetGravity( b2WorldId worldId )
|
|
2653
|
+
{
|
|
2654
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2655
|
+
return world->gravity;
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
struct ExplosionContext
|
|
2659
|
+
{
|
|
2660
|
+
b2World* world;
|
|
2661
|
+
b2Vec2 position;
|
|
2662
|
+
float radius;
|
|
2663
|
+
float falloff;
|
|
2664
|
+
float impulsePerLength;
|
|
2665
|
+
};
|
|
2666
|
+
|
|
2667
|
+
static bool ExplosionCallback( int proxyId, uint64_t userData, void* context )
|
|
2668
|
+
{
|
|
2669
|
+
B2_UNUSED( proxyId );
|
|
2670
|
+
|
|
2671
|
+
int shapeId = (int)userData;
|
|
2672
|
+
|
|
2673
|
+
struct ExplosionContext* explosionContext = context;
|
|
2674
|
+
b2World* world = explosionContext->world;
|
|
2675
|
+
|
|
2676
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
2677
|
+
|
|
2678
|
+
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
|
|
2679
|
+
B2_ASSERT( body->type == b2_dynamicBody );
|
|
2680
|
+
|
|
2681
|
+
b2Transform transform = b2GetBodyTransformQuick( world, body );
|
|
2682
|
+
|
|
2683
|
+
b2DistanceInput input;
|
|
2684
|
+
input.proxyA = b2MakeShapeDistanceProxy( shape );
|
|
2685
|
+
input.proxyB = b2MakeProxy( &explosionContext->position, 1, 0.0f );
|
|
2686
|
+
input.transformA = transform;
|
|
2687
|
+
input.transformB = b2Transform_identity;
|
|
2688
|
+
input.useRadii = true;
|
|
2689
|
+
|
|
2690
|
+
b2SimplexCache cache = { 0 };
|
|
2691
|
+
b2DistanceOutput output = b2ShapeDistance( &input, &cache, NULL, 0 );
|
|
2692
|
+
|
|
2693
|
+
float radius = explosionContext->radius;
|
|
2694
|
+
float falloff = explosionContext->falloff;
|
|
2695
|
+
if ( output.distance > radius + falloff )
|
|
2696
|
+
{
|
|
2697
|
+
return true;
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
b2WakeBody( world, body );
|
|
2701
|
+
|
|
2702
|
+
if ( body->setIndex != b2_awakeSet )
|
|
2703
|
+
{
|
|
2704
|
+
return true;
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
b2Vec2 closestPoint = output.pointA;
|
|
2708
|
+
if ( output.distance == 0.0f )
|
|
2709
|
+
{
|
|
2710
|
+
b2Vec2 localCentroid = b2GetShapeCentroid( shape );
|
|
2711
|
+
closestPoint = b2TransformPoint( transform, localCentroid );
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
b2Vec2 direction = b2Sub( closestPoint, explosionContext->position );
|
|
2715
|
+
if ( b2LengthSquared( direction ) > 100.0f * FLT_EPSILON * FLT_EPSILON )
|
|
2716
|
+
{
|
|
2717
|
+
direction = b2Normalize( direction );
|
|
2718
|
+
}
|
|
2719
|
+
else
|
|
2720
|
+
{
|
|
2721
|
+
direction = (b2Vec2){ 1.0f, 0.0f };
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
b2Vec2 localLine = b2InvRotateVector( transform.q, b2LeftPerp( direction ) );
|
|
2725
|
+
float perimeter = b2GetShapeProjectedPerimeter( shape, localLine );
|
|
2726
|
+
float scale = 1.0f;
|
|
2727
|
+
if ( output.distance > radius && falloff > 0.0f )
|
|
2728
|
+
{
|
|
2729
|
+
scale = b2ClampFloat( ( radius + falloff - output.distance ) / falloff, 0.0f, 1.0f );
|
|
2730
|
+
}
|
|
2731
|
+
|
|
2732
|
+
float magnitude = explosionContext->impulsePerLength * perimeter * scale;
|
|
2733
|
+
b2Vec2 impulse = b2MulSV( magnitude, direction );
|
|
2734
|
+
|
|
2735
|
+
int localIndex = body->localIndex;
|
|
2736
|
+
b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
|
|
2737
|
+
b2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex );
|
|
2738
|
+
b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex );
|
|
2739
|
+
state->linearVelocity = b2MulAdd( state->linearVelocity, bodySim->invMass, impulse );
|
|
2740
|
+
state->angularVelocity += bodySim->invInertia * b2Cross( b2Sub( closestPoint, bodySim->center ), impulse );
|
|
2741
|
+
|
|
2742
|
+
return true;
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
void b2World_Explode( b2WorldId worldId, const b2ExplosionDef* explosionDef )
|
|
2746
|
+
{
|
|
2747
|
+
uint64_t maskBits = explosionDef->maskBits;
|
|
2748
|
+
b2Vec2 position = explosionDef->position;
|
|
2749
|
+
float radius = explosionDef->radius;
|
|
2750
|
+
float falloff = explosionDef->falloff;
|
|
2751
|
+
float impulsePerLength = explosionDef->impulsePerLength;
|
|
2752
|
+
|
|
2753
|
+
B2_ASSERT( b2IsValidVec2( position ) );
|
|
2754
|
+
B2_ASSERT( b2IsValidFloat( radius ) && radius >= 0.0f );
|
|
2755
|
+
B2_ASSERT( b2IsValidFloat( falloff ) && falloff >= 0.0f );
|
|
2756
|
+
B2_ASSERT( b2IsValidFloat( impulsePerLength ) );
|
|
2757
|
+
|
|
2758
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2759
|
+
B2_ASSERT( world->locked == false );
|
|
2760
|
+
if ( world->locked )
|
|
2761
|
+
{
|
|
2762
|
+
return;
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
struct ExplosionContext explosionContext = { world, position, radius, falloff, impulsePerLength };
|
|
2766
|
+
|
|
2767
|
+
b2AABB aabb;
|
|
2768
|
+
aabb.lowerBound.x = position.x - ( radius + falloff );
|
|
2769
|
+
aabb.lowerBound.y = position.y - ( radius + falloff );
|
|
2770
|
+
aabb.upperBound.x = position.x + ( radius + falloff );
|
|
2771
|
+
aabb.upperBound.y = position.y + ( radius + falloff );
|
|
2772
|
+
|
|
2773
|
+
b2DynamicTree_Query( world->broadPhase.trees + b2_dynamicBody, aabb, maskBits, ExplosionCallback, &explosionContext );
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
void b2World_RebuildStaticTree( b2WorldId worldId )
|
|
2777
|
+
{
|
|
2778
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2779
|
+
B2_ASSERT( world->locked == false );
|
|
2780
|
+
if ( world->locked )
|
|
2781
|
+
{
|
|
2782
|
+
return;
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
b2DynamicTree* staticTree = world->broadPhase.trees + b2_staticBody;
|
|
2786
|
+
b2DynamicTree_Rebuild( staticTree, true );
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
void b2World_EnableSpeculative( b2WorldId worldId, bool flag )
|
|
2790
|
+
{
|
|
2791
|
+
b2World* world = b2GetWorldFromId( worldId );
|
|
2792
|
+
world->enableSpeculative = flag;
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
#if B2_VALIDATE
|
|
2796
|
+
// When validating islands ids I have to compare the root island
|
|
2797
|
+
// ids because islands are not merged until the next time step.
|
|
2798
|
+
static int b2GetRootIslandId( b2World* world, int islandId )
|
|
2799
|
+
{
|
|
2800
|
+
if ( islandId == B2_NULL_INDEX )
|
|
2801
|
+
{
|
|
2802
|
+
return B2_NULL_INDEX;
|
|
2803
|
+
}
|
|
2804
|
+
|
|
2805
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandId );
|
|
2806
|
+
|
|
2807
|
+
int rootId = islandId;
|
|
2808
|
+
b2Island* rootIsland = island;
|
|
2809
|
+
while ( rootIsland->parentIsland != B2_NULL_INDEX )
|
|
2810
|
+
{
|
|
2811
|
+
b2Island* parent = b2IslandArray_Get( &world->islands, rootIsland->parentIsland );
|
|
2812
|
+
rootId = rootIsland->parentIsland;
|
|
2813
|
+
rootIsland = parent;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
return rootId;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
// This validates island graph connectivity for each body
|
|
2820
|
+
void b2ValidateConnectivity( b2World* world )
|
|
2821
|
+
{
|
|
2822
|
+
b2Body* bodies = world->bodies.data;
|
|
2823
|
+
int bodyCapacity = world->bodies.count;
|
|
2824
|
+
|
|
2825
|
+
for ( int bodyIndex = 0; bodyIndex < bodyCapacity; ++bodyIndex )
|
|
2826
|
+
{
|
|
2827
|
+
b2Body* body = bodies + bodyIndex;
|
|
2828
|
+
if ( body->id == B2_NULL_INDEX )
|
|
2829
|
+
{
|
|
2830
|
+
b2ValidateFreeId( &world->bodyIdPool, bodyIndex );
|
|
2831
|
+
continue;
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
b2ValidateUsedId( &world->bodyIdPool, bodyIndex );
|
|
2835
|
+
|
|
2836
|
+
B2_ASSERT( bodyIndex == body->id );
|
|
2837
|
+
|
|
2838
|
+
// Need to get the root island because islands are not merged until the next time step
|
|
2839
|
+
int bodyIslandId = b2GetRootIslandId( world, body->islandId );
|
|
2840
|
+
int bodySetIndex = body->setIndex;
|
|
2841
|
+
|
|
2842
|
+
int contactKey = body->headContactKey;
|
|
2843
|
+
while ( contactKey != B2_NULL_INDEX )
|
|
2844
|
+
{
|
|
2845
|
+
int contactId = contactKey >> 1;
|
|
2846
|
+
int edgeIndex = contactKey & 1;
|
|
2847
|
+
|
|
2848
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
|
|
2849
|
+
|
|
2850
|
+
bool touching = ( contact->flags & b2_contactTouchingFlag ) != 0;
|
|
2851
|
+
if ( touching )
|
|
2852
|
+
{
|
|
2853
|
+
if ( bodySetIndex != b2_staticSet )
|
|
2854
|
+
{
|
|
2855
|
+
int contactIslandId = b2GetRootIslandId( world, contact->islandId );
|
|
2856
|
+
B2_ASSERT( contactIslandId == bodyIslandId );
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
else
|
|
2860
|
+
{
|
|
2861
|
+
B2_ASSERT( contact->islandId == B2_NULL_INDEX );
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
contactKey = contact->edges[edgeIndex].nextKey;
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
int jointKey = body->headJointKey;
|
|
2868
|
+
while ( jointKey != B2_NULL_INDEX )
|
|
2869
|
+
{
|
|
2870
|
+
int jointId = jointKey >> 1;
|
|
2871
|
+
int edgeIndex = jointKey & 1;
|
|
2872
|
+
|
|
2873
|
+
b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
|
|
2874
|
+
|
|
2875
|
+
int otherEdgeIndex = edgeIndex ^ 1;
|
|
2876
|
+
|
|
2877
|
+
b2Body* otherBody = b2BodyArray_Get( &world->bodies, joint->edges[otherEdgeIndex].bodyId );
|
|
2878
|
+
|
|
2879
|
+
if ( bodySetIndex == b2_disabledSet || otherBody->setIndex == b2_disabledSet )
|
|
2880
|
+
{
|
|
2881
|
+
B2_ASSERT( joint->islandId == B2_NULL_INDEX );
|
|
2882
|
+
}
|
|
2883
|
+
else if ( bodySetIndex == b2_staticSet )
|
|
2884
|
+
{
|
|
2885
|
+
if ( otherBody->setIndex == b2_staticSet )
|
|
2886
|
+
{
|
|
2887
|
+
B2_ASSERT( joint->islandId == B2_NULL_INDEX );
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
else
|
|
2891
|
+
{
|
|
2892
|
+
int jointIslandId = b2GetRootIslandId( world, joint->islandId );
|
|
2893
|
+
B2_ASSERT( jointIslandId == bodyIslandId );
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
jointKey = joint->edges[edgeIndex].nextKey;
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
// Validates solver sets, but not island connectivity
|
|
2902
|
+
void b2ValidateSolverSets( b2World* world )
|
|
2903
|
+
{
|
|
2904
|
+
B2_ASSERT( b2GetIdCapacity( &world->bodyIdPool ) == world->bodies.count );
|
|
2905
|
+
B2_ASSERT( b2GetIdCapacity( &world->contactIdPool ) == world->contacts.count );
|
|
2906
|
+
B2_ASSERT( b2GetIdCapacity( &world->jointIdPool ) == world->joints.count );
|
|
2907
|
+
B2_ASSERT( b2GetIdCapacity( &world->islandIdPool ) == world->islands.count );
|
|
2908
|
+
B2_ASSERT( b2GetIdCapacity( &world->solverSetIdPool ) == world->solverSets.count );
|
|
2909
|
+
|
|
2910
|
+
int activeSetCount = 0;
|
|
2911
|
+
int totalBodyCount = 0;
|
|
2912
|
+
int totalJointCount = 0;
|
|
2913
|
+
int totalContactCount = 0;
|
|
2914
|
+
int totalIslandCount = 0;
|
|
2915
|
+
|
|
2916
|
+
// Validate all solver sets
|
|
2917
|
+
int setCount = world->solverSets.count;
|
|
2918
|
+
for ( int setIndex = 0; setIndex < setCount; ++setIndex )
|
|
2919
|
+
{
|
|
2920
|
+
b2SolverSet* set = world->solverSets.data + setIndex;
|
|
2921
|
+
if ( set->setIndex != B2_NULL_INDEX )
|
|
2922
|
+
{
|
|
2923
|
+
activeSetCount += 1;
|
|
2924
|
+
|
|
2925
|
+
if ( setIndex == b2_staticSet )
|
|
2926
|
+
{
|
|
2927
|
+
B2_ASSERT( set->contactSims.count == 0 );
|
|
2928
|
+
B2_ASSERT( set->islandSims.count == 0 );
|
|
2929
|
+
B2_ASSERT( set->bodyStates.count == 0 );
|
|
2930
|
+
}
|
|
2931
|
+
else if ( setIndex == b2_awakeSet )
|
|
2932
|
+
{
|
|
2933
|
+
B2_ASSERT( set->bodySims.count == set->bodyStates.count );
|
|
2934
|
+
B2_ASSERT( set->jointSims.count == 0 );
|
|
2935
|
+
}
|
|
2936
|
+
else if ( setIndex == b2_disabledSet )
|
|
2937
|
+
{
|
|
2938
|
+
B2_ASSERT( set->islandSims.count == 0 );
|
|
2939
|
+
B2_ASSERT( set->bodyStates.count == 0 );
|
|
2940
|
+
}
|
|
2941
|
+
else
|
|
2942
|
+
{
|
|
2943
|
+
B2_ASSERT( set->bodyStates.count == 0 );
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
// Validate bodies
|
|
2947
|
+
{
|
|
2948
|
+
b2Body* bodies = world->bodies.data;
|
|
2949
|
+
B2_ASSERT( set->bodySims.count >= 0 );
|
|
2950
|
+
totalBodyCount += set->bodySims.count;
|
|
2951
|
+
for ( int i = 0; i < set->bodySims.count; ++i )
|
|
2952
|
+
{
|
|
2953
|
+
b2BodySim* bodySim = set->bodySims.data + i;
|
|
2954
|
+
|
|
2955
|
+
int bodyId = bodySim->bodyId;
|
|
2956
|
+
B2_ASSERT( 0 <= bodyId && bodyId < world->bodies.count );
|
|
2957
|
+
b2Body* body = bodies + bodyId;
|
|
2958
|
+
B2_ASSERT( body->setIndex == setIndex );
|
|
2959
|
+
B2_ASSERT( body->localIndex == i );
|
|
2960
|
+
B2_ASSERT( body->generation == body->generation );
|
|
2961
|
+
|
|
2962
|
+
if ( setIndex == b2_disabledSet )
|
|
2963
|
+
{
|
|
2964
|
+
B2_ASSERT( body->headContactKey == B2_NULL_INDEX );
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
// Validate body shapes
|
|
2968
|
+
int prevShapeId = B2_NULL_INDEX;
|
|
2969
|
+
int shapeId = body->headShapeId;
|
|
2970
|
+
while ( shapeId != B2_NULL_INDEX )
|
|
2971
|
+
{
|
|
2972
|
+
b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId );
|
|
2973
|
+
B2_ASSERT( shape->id == shapeId );
|
|
2974
|
+
B2_ASSERT( shape->prevShapeId == prevShapeId );
|
|
2975
|
+
|
|
2976
|
+
if ( setIndex == b2_disabledSet )
|
|
2977
|
+
{
|
|
2978
|
+
B2_ASSERT( shape->proxyKey == B2_NULL_INDEX );
|
|
2979
|
+
}
|
|
2980
|
+
else if ( setIndex == b2_staticSet )
|
|
2981
|
+
{
|
|
2982
|
+
B2_ASSERT( B2_PROXY_TYPE( shape->proxyKey ) == b2_staticBody );
|
|
2983
|
+
}
|
|
2984
|
+
else
|
|
2985
|
+
{
|
|
2986
|
+
b2BodyType proxyType = B2_PROXY_TYPE( shape->proxyKey );
|
|
2987
|
+
B2_ASSERT( proxyType == b2_kinematicBody || proxyType == b2_dynamicBody );
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
prevShapeId = shapeId;
|
|
2991
|
+
shapeId = shape->nextShapeId;
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
// Validate body contacts
|
|
2995
|
+
int contactKey = body->headContactKey;
|
|
2996
|
+
while ( contactKey != B2_NULL_INDEX )
|
|
2997
|
+
{
|
|
2998
|
+
int contactId = contactKey >> 1;
|
|
2999
|
+
int edgeIndex = contactKey & 1;
|
|
3000
|
+
|
|
3001
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId );
|
|
3002
|
+
B2_ASSERT( contact->setIndex != b2_staticSet );
|
|
3003
|
+
B2_ASSERT( contact->edges[0].bodyId == bodyId || contact->edges[1].bodyId == bodyId );
|
|
3004
|
+
contactKey = contact->edges[edgeIndex].nextKey;
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
// Validate body joints
|
|
3008
|
+
int jointKey = body->headJointKey;
|
|
3009
|
+
while ( jointKey != B2_NULL_INDEX )
|
|
3010
|
+
{
|
|
3011
|
+
int jointId = jointKey >> 1;
|
|
3012
|
+
int edgeIndex = jointKey & 1;
|
|
3013
|
+
|
|
3014
|
+
b2Joint* joint = b2JointArray_Get( &world->joints, jointId );
|
|
3015
|
+
|
|
3016
|
+
int otherEdgeIndex = edgeIndex ^ 1;
|
|
3017
|
+
|
|
3018
|
+
b2Body* otherBody = b2BodyArray_Get( &world->bodies, joint->edges[otherEdgeIndex].bodyId );
|
|
3019
|
+
|
|
3020
|
+
if ( setIndex == b2_disabledSet || otherBody->setIndex == b2_disabledSet )
|
|
3021
|
+
{
|
|
3022
|
+
B2_ASSERT( joint->setIndex == b2_disabledSet );
|
|
3023
|
+
}
|
|
3024
|
+
else if ( setIndex == b2_staticSet && otherBody->setIndex == b2_staticSet )
|
|
3025
|
+
{
|
|
3026
|
+
B2_ASSERT( joint->setIndex == b2_staticSet );
|
|
3027
|
+
}
|
|
3028
|
+
else if ( setIndex == b2_awakeSet )
|
|
3029
|
+
{
|
|
3030
|
+
B2_ASSERT( joint->setIndex == b2_awakeSet );
|
|
3031
|
+
}
|
|
3032
|
+
else if ( setIndex >= b2_firstSleepingSet )
|
|
3033
|
+
{
|
|
3034
|
+
B2_ASSERT( joint->setIndex == setIndex );
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3037
|
+
b2JointSim* jointSim = b2GetJointSim( world, joint );
|
|
3038
|
+
B2_ASSERT( jointSim->jointId == jointId );
|
|
3039
|
+
B2_ASSERT( jointSim->bodyIdA == joint->edges[0].bodyId );
|
|
3040
|
+
B2_ASSERT( jointSim->bodyIdB == joint->edges[1].bodyId );
|
|
3041
|
+
|
|
3042
|
+
jointKey = joint->edges[edgeIndex].nextKey;
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
// Validate contacts
|
|
3048
|
+
{
|
|
3049
|
+
B2_ASSERT( set->contactSims.count >= 0 );
|
|
3050
|
+
totalContactCount += set->contactSims.count;
|
|
3051
|
+
for ( int i = 0; i < set->contactSims.count; ++i )
|
|
3052
|
+
{
|
|
3053
|
+
b2ContactSim* contactSim = set->contactSims.data + i;
|
|
3054
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, contactSim->contactId );
|
|
3055
|
+
if ( setIndex == b2_awakeSet )
|
|
3056
|
+
{
|
|
3057
|
+
// contact should be non-touching if awake
|
|
3058
|
+
// or it could be this contact hasn't been transferred yet
|
|
3059
|
+
B2_ASSERT( contactSim->manifold.pointCount == 0 ||
|
|
3060
|
+
( contactSim->simFlags & b2_simStartedTouching ) != 0 );
|
|
3061
|
+
}
|
|
3062
|
+
B2_ASSERT( contact->setIndex == setIndex );
|
|
3063
|
+
B2_ASSERT( contact->colorIndex == B2_NULL_INDEX );
|
|
3064
|
+
B2_ASSERT( contact->localIndex == i );
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
// Validate joints
|
|
3069
|
+
{
|
|
3070
|
+
B2_ASSERT( set->jointSims.count >= 0 );
|
|
3071
|
+
totalJointCount += set->jointSims.count;
|
|
3072
|
+
for ( int i = 0; i < set->jointSims.count; ++i )
|
|
3073
|
+
{
|
|
3074
|
+
b2JointSim* jointSim = set->jointSims.data + i;
|
|
3075
|
+
b2Joint* joint = b2JointArray_Get( &world->joints, jointSim->jointId );
|
|
3076
|
+
B2_ASSERT( joint->setIndex == setIndex );
|
|
3077
|
+
B2_ASSERT( joint->colorIndex == B2_NULL_INDEX );
|
|
3078
|
+
B2_ASSERT( joint->localIndex == i );
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
|
|
3082
|
+
// Validate islands
|
|
3083
|
+
{
|
|
3084
|
+
B2_ASSERT( set->islandSims.count >= 0 );
|
|
3085
|
+
totalIslandCount += set->islandSims.count;
|
|
3086
|
+
for ( int i = 0; i < set->islandSims.count; ++i )
|
|
3087
|
+
{
|
|
3088
|
+
b2IslandSim* islandSim = set->islandSims.data + i;
|
|
3089
|
+
b2Island* island = b2IslandArray_Get( &world->islands, islandSim->islandId );
|
|
3090
|
+
B2_ASSERT( island->setIndex == setIndex );
|
|
3091
|
+
B2_ASSERT( island->localIndex == i );
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
else
|
|
3096
|
+
{
|
|
3097
|
+
B2_ASSERT( set->bodySims.count == 0 );
|
|
3098
|
+
B2_ASSERT( set->contactSims.count == 0 );
|
|
3099
|
+
B2_ASSERT( set->jointSims.count == 0 );
|
|
3100
|
+
B2_ASSERT( set->islandSims.count == 0 );
|
|
3101
|
+
B2_ASSERT( set->bodyStates.count == 0 );
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
int setIdCount = b2GetIdCount( &world->solverSetIdPool );
|
|
3106
|
+
B2_ASSERT( activeSetCount == setIdCount );
|
|
3107
|
+
|
|
3108
|
+
int bodyIdCount = b2GetIdCount( &world->bodyIdPool );
|
|
3109
|
+
B2_ASSERT( totalBodyCount == bodyIdCount );
|
|
3110
|
+
|
|
3111
|
+
int islandIdCount = b2GetIdCount( &world->islandIdPool );
|
|
3112
|
+
B2_ASSERT( totalIslandCount == islandIdCount );
|
|
3113
|
+
|
|
3114
|
+
// Validate constraint graph
|
|
3115
|
+
for ( int colorIndex = 0; colorIndex < B2_GRAPH_COLOR_COUNT; ++colorIndex )
|
|
3116
|
+
{
|
|
3117
|
+
b2GraphColor* color = world->constraintGraph.colors + colorIndex;
|
|
3118
|
+
{
|
|
3119
|
+
B2_ASSERT( color->contactSims.count >= 0 );
|
|
3120
|
+
totalContactCount += color->contactSims.count;
|
|
3121
|
+
for ( int i = 0; i < color->contactSims.count; ++i )
|
|
3122
|
+
{
|
|
3123
|
+
b2ContactSim* contactSim = color->contactSims.data + i;
|
|
3124
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, contactSim->contactId );
|
|
3125
|
+
// contact should be touching in the constraint graph or awaiting transfer to non-touching
|
|
3126
|
+
B2_ASSERT( contactSim->manifold.pointCount > 0 ||
|
|
3127
|
+
( contactSim->simFlags & ( b2_simStoppedTouching | b2_simDisjoint ) ) != 0 );
|
|
3128
|
+
B2_ASSERT( contact->setIndex == b2_awakeSet );
|
|
3129
|
+
B2_ASSERT( contact->colorIndex == colorIndex );
|
|
3130
|
+
B2_ASSERT( contact->localIndex == i );
|
|
3131
|
+
|
|
3132
|
+
int bodyIdA = contact->edges[0].bodyId;
|
|
3133
|
+
int bodyIdB = contact->edges[1].bodyId;
|
|
3134
|
+
|
|
3135
|
+
if ( colorIndex < B2_OVERFLOW_INDEX )
|
|
3136
|
+
{
|
|
3137
|
+
b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );
|
|
3138
|
+
b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );
|
|
3139
|
+
B2_ASSERT( b2GetBit( &color->bodySet, bodyIdA ) == ( bodyA->type != b2_staticBody ) );
|
|
3140
|
+
B2_ASSERT( b2GetBit( &color->bodySet, bodyIdB ) == ( bodyB->type != b2_staticBody ) );
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
{
|
|
3146
|
+
B2_ASSERT( color->jointSims.count >= 0 );
|
|
3147
|
+
totalJointCount += color->jointSims.count;
|
|
3148
|
+
for ( int i = 0; i < color->jointSims.count; ++i )
|
|
3149
|
+
{
|
|
3150
|
+
b2JointSim* jointSim = color->jointSims.data + i;
|
|
3151
|
+
b2Joint* joint = b2JointArray_Get( &world->joints, jointSim->jointId );
|
|
3152
|
+
B2_ASSERT( joint->setIndex == b2_awakeSet );
|
|
3153
|
+
B2_ASSERT( joint->colorIndex == colorIndex );
|
|
3154
|
+
B2_ASSERT( joint->localIndex == i );
|
|
3155
|
+
|
|
3156
|
+
int bodyIdA = joint->edges[0].bodyId;
|
|
3157
|
+
int bodyIdB = joint->edges[1].bodyId;
|
|
3158
|
+
|
|
3159
|
+
if ( colorIndex < B2_OVERFLOW_INDEX )
|
|
3160
|
+
{
|
|
3161
|
+
b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );
|
|
3162
|
+
b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );
|
|
3163
|
+
B2_ASSERT( b2GetBit( &color->bodySet, bodyIdA ) == ( bodyA->type != b2_staticBody ) );
|
|
3164
|
+
B2_ASSERT( b2GetBit( &color->bodySet, bodyIdB ) == ( bodyB->type != b2_staticBody ) );
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3170
|
+
int contactIdCount = b2GetIdCount( &world->contactIdPool );
|
|
3171
|
+
B2_ASSERT( totalContactCount == contactIdCount );
|
|
3172
|
+
B2_ASSERT( totalContactCount == (int)world->broadPhase.pairSet.count );
|
|
3173
|
+
|
|
3174
|
+
int jointIdCount = b2GetIdCount( &world->jointIdPool );
|
|
3175
|
+
B2_ASSERT( totalJointCount == jointIdCount );
|
|
3176
|
+
|
|
3177
|
+
// Validate shapes
|
|
3178
|
+
// This is very slow on compounds
|
|
3179
|
+
#if 0
|
|
3180
|
+
int shapeCapacity = b2Array(world->shapeArray).count;
|
|
3181
|
+
for (int shapeIndex = 0; shapeIndex < shapeCapacity; shapeIndex += 1)
|
|
3182
|
+
{
|
|
3183
|
+
b2Shape* shape = world->shapeArray + shapeIndex;
|
|
3184
|
+
if (shape->id != shapeIndex)
|
|
3185
|
+
{
|
|
3186
|
+
continue;
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
B2_ASSERT(0 <= shape->bodyId && shape->bodyId < b2Array(world->bodyArray).count);
|
|
3190
|
+
|
|
3191
|
+
b2Body* body = world->bodyArray + shape->bodyId;
|
|
3192
|
+
B2_ASSERT(0 <= body->setIndex && body->setIndex < b2Array(world->solverSetArray).count);
|
|
3193
|
+
|
|
3194
|
+
b2SolverSet* set = world->solverSetArray + body->setIndex;
|
|
3195
|
+
B2_ASSERT(0 <= body->localIndex && body->localIndex < set->sims.count);
|
|
3196
|
+
|
|
3197
|
+
b2BodySim* bodySim = set->sims.data + body->localIndex;
|
|
3198
|
+
B2_ASSERT(bodySim->bodyId == shape->bodyId);
|
|
3199
|
+
|
|
3200
|
+
bool found = false;
|
|
3201
|
+
int shapeCount = 0;
|
|
3202
|
+
int index = body->headShapeId;
|
|
3203
|
+
while (index != B2_NULL_INDEX)
|
|
3204
|
+
{
|
|
3205
|
+
b2CheckId(world->shapeArray, index);
|
|
3206
|
+
b2Shape* s = world->shapeArray + index;
|
|
3207
|
+
if (index == shapeIndex)
|
|
3208
|
+
{
|
|
3209
|
+
found = true;
|
|
3210
|
+
}
|
|
3211
|
+
|
|
3212
|
+
index = s->nextShapeId;
|
|
3213
|
+
shapeCount += 1;
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3216
|
+
B2_ASSERT(found);
|
|
3217
|
+
B2_ASSERT(shapeCount == body->shapeCount);
|
|
3218
|
+
}
|
|
3219
|
+
#endif
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
// Validate contact touching status.
|
|
3223
|
+
void b2ValidateContacts( b2World* world )
|
|
3224
|
+
{
|
|
3225
|
+
int contactCount = world->contacts.count;
|
|
3226
|
+
B2_ASSERT( contactCount == b2GetIdCapacity( &world->contactIdPool ) );
|
|
3227
|
+
int allocatedContactCount = 0;
|
|
3228
|
+
|
|
3229
|
+
for ( int contactIndex = 0; contactIndex < contactCount; ++contactIndex )
|
|
3230
|
+
{
|
|
3231
|
+
b2Contact* contact = b2ContactArray_Get( &world->contacts, contactIndex );
|
|
3232
|
+
if ( contact->contactId == B2_NULL_INDEX )
|
|
3233
|
+
{
|
|
3234
|
+
continue;
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
B2_ASSERT( contact->contactId == contactIndex );
|
|
3238
|
+
|
|
3239
|
+
allocatedContactCount += 1;
|
|
3240
|
+
|
|
3241
|
+
bool touching = ( contact->flags & b2_contactTouchingFlag ) != 0;
|
|
3242
|
+
|
|
3243
|
+
int setId = contact->setIndex;
|
|
3244
|
+
|
|
3245
|
+
if ( setId == b2_awakeSet )
|
|
3246
|
+
{
|
|
3247
|
+
// If touching and not a sensor
|
|
3248
|
+
if ( touching )
|
|
3249
|
+
{
|
|
3250
|
+
B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < B2_GRAPH_COLOR_COUNT );
|
|
3251
|
+
}
|
|
3252
|
+
else
|
|
3253
|
+
{
|
|
3254
|
+
B2_ASSERT( contact->colorIndex == B2_NULL_INDEX );
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
else if ( setId >= b2_firstSleepingSet )
|
|
3258
|
+
{
|
|
3259
|
+
// Only touching contacts allowed in a sleeping set
|
|
3260
|
+
B2_ASSERT( touching == true );
|
|
3261
|
+
}
|
|
3262
|
+
else
|
|
3263
|
+
{
|
|
3264
|
+
// Sleeping and non-touching contacts or sensor contacts belong in the disabled set
|
|
3265
|
+
B2_ASSERT( touching == false && setId == b2_disabledSet );
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
b2ContactSim* contactSim = b2GetContactSim( world, contact );
|
|
3269
|
+
B2_ASSERT( contactSim->contactId == contactIndex );
|
|
3270
|
+
B2_ASSERT( contactSim->bodyIdA == contact->edges[0].bodyId );
|
|
3271
|
+
B2_ASSERT( contactSim->bodyIdB == contact->edges[1].bodyId );
|
|
3272
|
+
|
|
3273
|
+
// Sim touching is true for solid and sensor contacts
|
|
3274
|
+
bool simTouching = ( contactSim->simFlags & b2_simTouchingFlag ) != 0;
|
|
3275
|
+
B2_ASSERT( touching == simTouching );
|
|
3276
|
+
|
|
3277
|
+
B2_ASSERT( 0 <= contactSim->manifold.pointCount && contactSim->manifold.pointCount <= 2 );
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
int contactIdCount = b2GetIdCount( &world->contactIdPool );
|
|
3281
|
+
B2_ASSERT( allocatedContactCount == contactIdCount );
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
#else
|
|
3285
|
+
|
|
3286
|
+
void b2ValidateConnectivity( b2World* world )
|
|
3287
|
+
{
|
|
3288
|
+
B2_UNUSED( world );
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
void b2ValidateSolverSets( b2World* world )
|
|
3292
|
+
{
|
|
3293
|
+
B2_UNUSED( world );
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3296
|
+
void b2ValidateContacts( b2World* world )
|
|
3297
|
+
{
|
|
3298
|
+
B2_UNUSED( world );
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
#endif
|