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