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,1989 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2023 Erin Catto
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
#include "aabb.h"
|
|
5
|
+
#include "constants.h"
|
|
6
|
+
#include "core.h"
|
|
7
|
+
|
|
8
|
+
#include "box2d/collision.h"
|
|
9
|
+
#include "box2d/math_functions.h"
|
|
10
|
+
|
|
11
|
+
#include <float.h>
|
|
12
|
+
#include <string.h>
|
|
13
|
+
|
|
14
|
+
#define B2_TREE_STACK_SIZE 1024
|
|
15
|
+
|
|
16
|
+
// todo externalize this to visualize internal nodes and speed up FindPairs
|
|
17
|
+
|
|
18
|
+
// A node in the dynamic tree.
|
|
19
|
+
typedef struct b2TreeNode
|
|
20
|
+
{
|
|
21
|
+
// The node bounding box
|
|
22
|
+
b2AABB aabb; // 16
|
|
23
|
+
|
|
24
|
+
// Category bits for collision filtering
|
|
25
|
+
uint64_t categoryBits; // 8
|
|
26
|
+
|
|
27
|
+
union
|
|
28
|
+
{
|
|
29
|
+
// Children (internal node)
|
|
30
|
+
struct
|
|
31
|
+
{
|
|
32
|
+
int32_t child1, child2;
|
|
33
|
+
} children;
|
|
34
|
+
|
|
35
|
+
/// User data (leaf node)
|
|
36
|
+
uint64_t userData;
|
|
37
|
+
}; // 8
|
|
38
|
+
|
|
39
|
+
union
|
|
40
|
+
{
|
|
41
|
+
/// The node parent index (allocated node)
|
|
42
|
+
int32_t parent;
|
|
43
|
+
|
|
44
|
+
/// The node freelist next index (free node)
|
|
45
|
+
int32_t next;
|
|
46
|
+
}; // 4
|
|
47
|
+
|
|
48
|
+
uint16_t height; // 2
|
|
49
|
+
uint16_t flags; // 2
|
|
50
|
+
} b2TreeNode;
|
|
51
|
+
|
|
52
|
+
static b2TreeNode b2_defaultTreeNode = {
|
|
53
|
+
.aabb = { { 0.0f, 0.0f }, { 0.0f, 0.0f } },
|
|
54
|
+
.categoryBits = B2_DEFAULT_CATEGORY_BITS,
|
|
55
|
+
.children =
|
|
56
|
+
{
|
|
57
|
+
.child1 = B2_NULL_INDEX,
|
|
58
|
+
.child2 = B2_NULL_INDEX,
|
|
59
|
+
},
|
|
60
|
+
.parent = B2_NULL_INDEX,
|
|
61
|
+
.height = 0,
|
|
62
|
+
.flags = b2_allocatedNode,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
static bool b2IsLeaf( const b2TreeNode* node )
|
|
66
|
+
{
|
|
67
|
+
return node->flags & b2_leafNode;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static bool b2IsAllocated( const b2TreeNode* node )
|
|
71
|
+
{
|
|
72
|
+
return node->flags & b2_allocatedNode;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static uint16_t b2MaxUInt16( uint16_t a, uint16_t b )
|
|
76
|
+
{
|
|
77
|
+
return a > b ? a : b;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
b2DynamicTree b2DynamicTree_Create( void )
|
|
81
|
+
{
|
|
82
|
+
b2DynamicTree tree;
|
|
83
|
+
tree.root = B2_NULL_INDEX;
|
|
84
|
+
|
|
85
|
+
tree.nodeCapacity = 16;
|
|
86
|
+
tree.nodeCount = 0;
|
|
87
|
+
tree.nodes = (b2TreeNode*)b2Alloc( tree.nodeCapacity * sizeof( b2TreeNode ) );
|
|
88
|
+
memset( tree.nodes, 0, tree.nodeCapacity * sizeof( b2TreeNode ) );
|
|
89
|
+
|
|
90
|
+
// Build a linked list for the free list.
|
|
91
|
+
for ( int i = 0; i < tree.nodeCapacity - 1; ++i )
|
|
92
|
+
{
|
|
93
|
+
tree.nodes[i].next = i + 1;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
tree.nodes[tree.nodeCapacity - 1].next = B2_NULL_INDEX;
|
|
97
|
+
tree.freeList = 0;
|
|
98
|
+
|
|
99
|
+
tree.proxyCount = 0;
|
|
100
|
+
|
|
101
|
+
tree.leafIndices = NULL;
|
|
102
|
+
tree.leafBoxes = NULL;
|
|
103
|
+
tree.leafCenters = NULL;
|
|
104
|
+
tree.binIndices = NULL;
|
|
105
|
+
tree.rebuildCapacity = 0;
|
|
106
|
+
|
|
107
|
+
return tree;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void b2DynamicTree_Destroy( b2DynamicTree* tree )
|
|
111
|
+
{
|
|
112
|
+
b2Free( tree->nodes, tree->nodeCapacity * sizeof( b2TreeNode ) );
|
|
113
|
+
b2Free( tree->leafIndices, tree->rebuildCapacity * sizeof( int32_t ) );
|
|
114
|
+
b2Free( tree->leafBoxes, tree->rebuildCapacity * sizeof( b2AABB ) );
|
|
115
|
+
b2Free( tree->leafCenters, tree->rebuildCapacity * sizeof( b2Vec2 ) );
|
|
116
|
+
b2Free( tree->binIndices, tree->rebuildCapacity * sizeof( int32_t ) );
|
|
117
|
+
|
|
118
|
+
memset( tree, 0, sizeof( b2DynamicTree ) );
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Allocate a node from the pool. Grow the pool if necessary.
|
|
122
|
+
static int b2AllocateNode( b2DynamicTree* tree )
|
|
123
|
+
{
|
|
124
|
+
// Expand the node pool as needed.
|
|
125
|
+
if ( tree->freeList == B2_NULL_INDEX )
|
|
126
|
+
{
|
|
127
|
+
B2_ASSERT( tree->nodeCount == tree->nodeCapacity );
|
|
128
|
+
|
|
129
|
+
// The free list is empty. Rebuild a bigger pool.
|
|
130
|
+
b2TreeNode* oldNodes = tree->nodes;
|
|
131
|
+
int oldCapacity = tree->nodeCapacity;
|
|
132
|
+
tree->nodeCapacity += oldCapacity >> 1;
|
|
133
|
+
tree->nodes = (b2TreeNode*)b2Alloc( tree->nodeCapacity * sizeof( b2TreeNode ) );
|
|
134
|
+
B2_ASSERT( oldNodes != NULL );
|
|
135
|
+
memcpy( tree->nodes, oldNodes, tree->nodeCount * sizeof( b2TreeNode ) );
|
|
136
|
+
memset( tree->nodes + tree->nodeCount, 0, ( tree->nodeCapacity - tree->nodeCount ) * sizeof( b2TreeNode ) );
|
|
137
|
+
b2Free( oldNodes, oldCapacity * sizeof( b2TreeNode ) );
|
|
138
|
+
|
|
139
|
+
// Build a linked list for the free list. The parent pointer becomes the "next" pointer.
|
|
140
|
+
// todo avoid building freelist?
|
|
141
|
+
for ( int i = tree->nodeCount; i < tree->nodeCapacity - 1; ++i )
|
|
142
|
+
{
|
|
143
|
+
tree->nodes[i].next = i + 1;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
tree->nodes[tree->nodeCapacity - 1].next = B2_NULL_INDEX;
|
|
147
|
+
tree->freeList = tree->nodeCount;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Peel a node off the free list.
|
|
151
|
+
int nodeIndex = tree->freeList;
|
|
152
|
+
b2TreeNode* node = tree->nodes + nodeIndex;
|
|
153
|
+
tree->freeList = node->next;
|
|
154
|
+
*node = b2_defaultTreeNode;
|
|
155
|
+
++tree->nodeCount;
|
|
156
|
+
return nodeIndex;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Return a node to the pool.
|
|
160
|
+
static void b2FreeNode( b2DynamicTree* tree, int nodeId )
|
|
161
|
+
{
|
|
162
|
+
B2_ASSERT( 0 <= nodeId && nodeId < tree->nodeCapacity );
|
|
163
|
+
B2_ASSERT( 0 < tree->nodeCount );
|
|
164
|
+
tree->nodes[nodeId].next = tree->freeList;
|
|
165
|
+
tree->nodes[nodeId].flags = 0;
|
|
166
|
+
tree->freeList = nodeId;
|
|
167
|
+
--tree->nodeCount;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Greedy algorithm for sibling selection using the SAH
|
|
171
|
+
// We have three nodes A-(B,C) and want to add a leaf D, there are three choices.
|
|
172
|
+
// 1: make a new parent for A and D : E-(A-(B,C), D)
|
|
173
|
+
// 2: associate D with B
|
|
174
|
+
// a: B is a leaf : A-(E-(B,D), C)
|
|
175
|
+
// b: B is an internal node: A-(B{D},C)
|
|
176
|
+
// 3: associate D with C
|
|
177
|
+
// a: C is a leaf : A-(B, E-(C,D))
|
|
178
|
+
// b: C is an internal node: A-(B, C{D})
|
|
179
|
+
// All of these have a clear cost except when B or C is an internal node. Hence we need to be greedy.
|
|
180
|
+
|
|
181
|
+
// The cost for cases 1, 2a, and 3a can be computed using the sibling cost formula.
|
|
182
|
+
// cost of sibling H = area(union(H, D)) + increased area of ancestors
|
|
183
|
+
|
|
184
|
+
// Suppose B (or C) is an internal node, then the lowest cost would be one of two cases:
|
|
185
|
+
// case1: D becomes a sibling of B
|
|
186
|
+
// case2: D becomes a descendant of B along with a new internal node of area(D).
|
|
187
|
+
static int b2FindBestSibling( const b2DynamicTree* tree, b2AABB boxD )
|
|
188
|
+
{
|
|
189
|
+
b2Vec2 centerD = b2AABB_Center( boxD );
|
|
190
|
+
float areaD = b2Perimeter( boxD );
|
|
191
|
+
|
|
192
|
+
const b2TreeNode* nodes = tree->nodes;
|
|
193
|
+
int rootIndex = tree->root;
|
|
194
|
+
|
|
195
|
+
b2AABB rootBox = nodes[rootIndex].aabb;
|
|
196
|
+
|
|
197
|
+
// Area of current node
|
|
198
|
+
float areaBase = b2Perimeter( rootBox );
|
|
199
|
+
|
|
200
|
+
// Area of inflated node
|
|
201
|
+
float directCost = b2Perimeter( b2AABB_Union( rootBox, boxD ) );
|
|
202
|
+
float inheritedCost = 0.0f;
|
|
203
|
+
|
|
204
|
+
int bestSibling = rootIndex;
|
|
205
|
+
float bestCost = directCost;
|
|
206
|
+
|
|
207
|
+
// Descend the tree from root, following a single greedy path.
|
|
208
|
+
int index = rootIndex;
|
|
209
|
+
while ( nodes[index].height > 0 )
|
|
210
|
+
{
|
|
211
|
+
int child1 = nodes[index].children.child1;
|
|
212
|
+
int child2 = nodes[index].children.child2;
|
|
213
|
+
|
|
214
|
+
// Cost of creating a new parent for this node and the new leaf
|
|
215
|
+
float cost = directCost + inheritedCost;
|
|
216
|
+
|
|
217
|
+
// Sometimes there are multiple identical costs within tolerance.
|
|
218
|
+
// This breaks the ties using the centroid distance.
|
|
219
|
+
if ( cost < bestCost )
|
|
220
|
+
{
|
|
221
|
+
bestSibling = index;
|
|
222
|
+
bestCost = cost;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Inheritance cost seen by children
|
|
226
|
+
inheritedCost += directCost - areaBase;
|
|
227
|
+
|
|
228
|
+
bool leaf1 = nodes[child1].height == 0;
|
|
229
|
+
bool leaf2 = nodes[child2].height == 0;
|
|
230
|
+
|
|
231
|
+
// Cost of descending into child 1
|
|
232
|
+
float lowerCost1 = FLT_MAX;
|
|
233
|
+
b2AABB box1 = nodes[child1].aabb;
|
|
234
|
+
float directCost1 = b2Perimeter( b2AABB_Union( box1, boxD ) );
|
|
235
|
+
float area1 = 0.0f;
|
|
236
|
+
if ( leaf1 )
|
|
237
|
+
{
|
|
238
|
+
// Child 1 is a leaf
|
|
239
|
+
// Cost of creating new node and increasing area of node P
|
|
240
|
+
float cost1 = directCost1 + inheritedCost;
|
|
241
|
+
|
|
242
|
+
// Need this here due to while condition above
|
|
243
|
+
if ( cost1 < bestCost )
|
|
244
|
+
{
|
|
245
|
+
bestSibling = child1;
|
|
246
|
+
bestCost = cost1;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else
|
|
250
|
+
{
|
|
251
|
+
// Child 1 is an internal node
|
|
252
|
+
area1 = b2Perimeter( box1 );
|
|
253
|
+
|
|
254
|
+
// Lower bound cost of inserting under child 1. The minimum accounts for two possibilities:
|
|
255
|
+
// 1. Child1 could be the sibling with cost1 = inheritedCost + directCost1
|
|
256
|
+
// 2. A descendent of child1 could be the sibling with the lower bound cost of
|
|
257
|
+
// cost1 = inheritedCost + (directCost1 - area1) + areaD
|
|
258
|
+
// This minimum here leads to the minimum of these two costs.
|
|
259
|
+
lowerCost1 = inheritedCost + directCost1 + b2MinFloat( areaD - area1, 0.0f );
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Cost of descending into child 2
|
|
263
|
+
float lowerCost2 = FLT_MAX;
|
|
264
|
+
b2AABB box2 = nodes[child2].aabb;
|
|
265
|
+
float directCost2 = b2Perimeter( b2AABB_Union( box2, boxD ) );
|
|
266
|
+
float area2 = 0.0f;
|
|
267
|
+
if ( leaf2 )
|
|
268
|
+
{
|
|
269
|
+
float cost2 = directCost2 + inheritedCost;
|
|
270
|
+
|
|
271
|
+
if ( cost2 < bestCost )
|
|
272
|
+
{
|
|
273
|
+
bestSibling = child2;
|
|
274
|
+
bestCost = cost2;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else
|
|
278
|
+
{
|
|
279
|
+
area2 = b2Perimeter( box2 );
|
|
280
|
+
lowerCost2 = inheritedCost + directCost2 + b2MinFloat( areaD - area2, 0.0f );
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if ( leaf1 && leaf2 )
|
|
284
|
+
{
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Can the cost possibly be decreased?
|
|
289
|
+
if ( bestCost <= lowerCost1 && bestCost <= lowerCost2 )
|
|
290
|
+
{
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if ( lowerCost1 == lowerCost2 && leaf1 == false )
|
|
295
|
+
{
|
|
296
|
+
B2_ASSERT( lowerCost1 < FLT_MAX );
|
|
297
|
+
B2_ASSERT( lowerCost2 < FLT_MAX );
|
|
298
|
+
|
|
299
|
+
// No clear choice based on lower bound surface area. This can happen when both
|
|
300
|
+
// children fully contain D. Fall back to node distance.
|
|
301
|
+
b2Vec2 d1 = b2Sub( b2AABB_Center( box1 ), centerD );
|
|
302
|
+
b2Vec2 d2 = b2Sub( b2AABB_Center( box2 ), centerD );
|
|
303
|
+
lowerCost1 = b2LengthSquared( d1 );
|
|
304
|
+
lowerCost2 = b2LengthSquared( d2 );
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Descend
|
|
308
|
+
if ( lowerCost1 < lowerCost2 && leaf1 == false )
|
|
309
|
+
{
|
|
310
|
+
index = child1;
|
|
311
|
+
areaBase = area1;
|
|
312
|
+
directCost = directCost1;
|
|
313
|
+
}
|
|
314
|
+
else
|
|
315
|
+
{
|
|
316
|
+
index = child2;
|
|
317
|
+
areaBase = area2;
|
|
318
|
+
directCost = directCost2;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
B2_ASSERT( nodes[index].height > 0 );
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return bestSibling;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
enum b2RotateType
|
|
328
|
+
{
|
|
329
|
+
b2_rotateNone,
|
|
330
|
+
b2_rotateBF,
|
|
331
|
+
b2_rotateBG,
|
|
332
|
+
b2_rotateCD,
|
|
333
|
+
b2_rotateCE
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// Perform a left or right rotation if node A is imbalanced.
|
|
337
|
+
// Returns the new root index.
|
|
338
|
+
static void b2RotateNodes( b2DynamicTree* tree, int iA )
|
|
339
|
+
{
|
|
340
|
+
B2_ASSERT( iA != B2_NULL_INDEX );
|
|
341
|
+
|
|
342
|
+
b2TreeNode* nodes = tree->nodes;
|
|
343
|
+
|
|
344
|
+
b2TreeNode* A = nodes + iA;
|
|
345
|
+
if ( A->height < 2 )
|
|
346
|
+
{
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
int iB = A->children.child1;
|
|
351
|
+
int iC = A->children.child2;
|
|
352
|
+
B2_ASSERT( 0 <= iB && iB < tree->nodeCapacity );
|
|
353
|
+
B2_ASSERT( 0 <= iC && iC < tree->nodeCapacity );
|
|
354
|
+
|
|
355
|
+
b2TreeNode* B = nodes + iB;
|
|
356
|
+
b2TreeNode* C = nodes + iC;
|
|
357
|
+
|
|
358
|
+
if ( B->height == 0 )
|
|
359
|
+
{
|
|
360
|
+
// B is a leaf and C is internal
|
|
361
|
+
B2_ASSERT( C->height > 0 );
|
|
362
|
+
|
|
363
|
+
int iF = C->children.child1;
|
|
364
|
+
int iG = C->children.child2;
|
|
365
|
+
b2TreeNode* F = nodes + iF;
|
|
366
|
+
b2TreeNode* G = nodes + iG;
|
|
367
|
+
B2_ASSERT( 0 <= iF && iF < tree->nodeCapacity );
|
|
368
|
+
B2_ASSERT( 0 <= iG && iG < tree->nodeCapacity );
|
|
369
|
+
|
|
370
|
+
// Base cost
|
|
371
|
+
float costBase = b2Perimeter( C->aabb );
|
|
372
|
+
|
|
373
|
+
// Cost of swapping B and F
|
|
374
|
+
b2AABB aabbBG = b2AABB_Union( B->aabb, G->aabb );
|
|
375
|
+
float costBF = b2Perimeter( aabbBG );
|
|
376
|
+
|
|
377
|
+
// Cost of swapping B and G
|
|
378
|
+
b2AABB aabbBF = b2AABB_Union( B->aabb, F->aabb );
|
|
379
|
+
float costBG = b2Perimeter( aabbBF );
|
|
380
|
+
|
|
381
|
+
if ( costBase < costBF && costBase < costBG )
|
|
382
|
+
{
|
|
383
|
+
// Rotation does not improve cost
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if ( costBF < costBG )
|
|
388
|
+
{
|
|
389
|
+
// Swap B and F
|
|
390
|
+
A->children.child1 = iF;
|
|
391
|
+
C->children.child1 = iB;
|
|
392
|
+
|
|
393
|
+
B->parent = iC;
|
|
394
|
+
F->parent = iA;
|
|
395
|
+
|
|
396
|
+
C->aabb = aabbBG;
|
|
397
|
+
|
|
398
|
+
C->height = 1 + b2MaxUInt16( B->height, G->height );
|
|
399
|
+
A->height = 1 + b2MaxUInt16( C->height, F->height );
|
|
400
|
+
C->categoryBits = B->categoryBits | G->categoryBits;
|
|
401
|
+
A->categoryBits = C->categoryBits | F->categoryBits;
|
|
402
|
+
C->flags |= ( B->flags | G->flags ) & b2_enlargedNode;
|
|
403
|
+
A->flags |= ( C->flags | F->flags ) & b2_enlargedNode;
|
|
404
|
+
}
|
|
405
|
+
else
|
|
406
|
+
{
|
|
407
|
+
// Swap B and G
|
|
408
|
+
A->children.child1 = iG;
|
|
409
|
+
C->children.child2 = iB;
|
|
410
|
+
|
|
411
|
+
B->parent = iC;
|
|
412
|
+
G->parent = iA;
|
|
413
|
+
|
|
414
|
+
C->aabb = aabbBF;
|
|
415
|
+
|
|
416
|
+
C->height = 1 + b2MaxUInt16( B->height, F->height );
|
|
417
|
+
A->height = 1 + b2MaxUInt16( C->height, G->height );
|
|
418
|
+
C->categoryBits = B->categoryBits | F->categoryBits;
|
|
419
|
+
A->categoryBits = C->categoryBits | G->categoryBits;
|
|
420
|
+
C->flags |= ( B->flags | F->flags ) & b2_enlargedNode;
|
|
421
|
+
A->flags |= ( C->flags | G->flags ) & b2_enlargedNode;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
else if ( C->height == 0 )
|
|
425
|
+
{
|
|
426
|
+
// C is a leaf and B is internal
|
|
427
|
+
B2_ASSERT( B->height > 0 );
|
|
428
|
+
|
|
429
|
+
int iD = B->children.child1;
|
|
430
|
+
int iE = B->children.child2;
|
|
431
|
+
b2TreeNode* D = nodes + iD;
|
|
432
|
+
b2TreeNode* E = nodes + iE;
|
|
433
|
+
B2_ASSERT( 0 <= iD && iD < tree->nodeCapacity );
|
|
434
|
+
B2_ASSERT( 0 <= iE && iE < tree->nodeCapacity );
|
|
435
|
+
|
|
436
|
+
// Base cost
|
|
437
|
+
float costBase = b2Perimeter( B->aabb );
|
|
438
|
+
|
|
439
|
+
// Cost of swapping C and D
|
|
440
|
+
b2AABB aabbCE = b2AABB_Union( C->aabb, E->aabb );
|
|
441
|
+
float costCD = b2Perimeter( aabbCE );
|
|
442
|
+
|
|
443
|
+
// Cost of swapping C and E
|
|
444
|
+
b2AABB aabbCD = b2AABB_Union( C->aabb, D->aabb );
|
|
445
|
+
float costCE = b2Perimeter( aabbCD );
|
|
446
|
+
|
|
447
|
+
if ( costBase < costCD && costBase < costCE )
|
|
448
|
+
{
|
|
449
|
+
// Rotation does not improve cost
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if ( costCD < costCE )
|
|
454
|
+
{
|
|
455
|
+
// Swap C and D
|
|
456
|
+
A->children.child2 = iD;
|
|
457
|
+
B->children.child1 = iC;
|
|
458
|
+
|
|
459
|
+
C->parent = iB;
|
|
460
|
+
D->parent = iA;
|
|
461
|
+
|
|
462
|
+
B->aabb = aabbCE;
|
|
463
|
+
|
|
464
|
+
B->height = 1 + b2MaxUInt16( C->height, E->height );
|
|
465
|
+
A->height = 1 + b2MaxUInt16( B->height, D->height );
|
|
466
|
+
B->categoryBits = C->categoryBits | E->categoryBits;
|
|
467
|
+
A->categoryBits = B->categoryBits | D->categoryBits;
|
|
468
|
+
B->flags |= ( C->flags | E->flags ) & b2_enlargedNode;
|
|
469
|
+
A->flags |= ( B->flags | D->flags ) & b2_enlargedNode;
|
|
470
|
+
}
|
|
471
|
+
else
|
|
472
|
+
{
|
|
473
|
+
// Swap C and E
|
|
474
|
+
A->children.child2 = iE;
|
|
475
|
+
B->children.child2 = iC;
|
|
476
|
+
|
|
477
|
+
C->parent = iB;
|
|
478
|
+
E->parent = iA;
|
|
479
|
+
|
|
480
|
+
B->aabb = aabbCD;
|
|
481
|
+
B->height = 1 + b2MaxUInt16( C->height, D->height );
|
|
482
|
+
A->height = 1 + b2MaxUInt16( B->height, E->height );
|
|
483
|
+
B->categoryBits = C->categoryBits | D->categoryBits;
|
|
484
|
+
A->categoryBits = B->categoryBits | E->categoryBits;
|
|
485
|
+
B->flags |= ( C->flags | D->flags ) & b2_enlargedNode;
|
|
486
|
+
A->flags |= ( B->flags | E->flags ) & b2_enlargedNode;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
else
|
|
490
|
+
{
|
|
491
|
+
int iD = B->children.child1;
|
|
492
|
+
int iE = B->children.child2;
|
|
493
|
+
int iF = C->children.child1;
|
|
494
|
+
int iG = C->children.child2;
|
|
495
|
+
|
|
496
|
+
b2TreeNode* D = nodes + iD;
|
|
497
|
+
b2TreeNode* E = nodes + iE;
|
|
498
|
+
b2TreeNode* F = nodes + iF;
|
|
499
|
+
b2TreeNode* G = nodes + iG;
|
|
500
|
+
|
|
501
|
+
B2_ASSERT( 0 <= iD && iD < tree->nodeCapacity );
|
|
502
|
+
B2_ASSERT( 0 <= iE && iE < tree->nodeCapacity );
|
|
503
|
+
B2_ASSERT( 0 <= iF && iF < tree->nodeCapacity );
|
|
504
|
+
B2_ASSERT( 0 <= iG && iG < tree->nodeCapacity );
|
|
505
|
+
|
|
506
|
+
// Base cost
|
|
507
|
+
float areaB = b2Perimeter( B->aabb );
|
|
508
|
+
float areaC = b2Perimeter( C->aabb );
|
|
509
|
+
float costBase = areaB + areaC;
|
|
510
|
+
enum b2RotateType bestRotation = b2_rotateNone;
|
|
511
|
+
float bestCost = costBase;
|
|
512
|
+
|
|
513
|
+
// Cost of swapping B and F
|
|
514
|
+
b2AABB aabbBG = b2AABB_Union( B->aabb, G->aabb );
|
|
515
|
+
float costBF = areaB + b2Perimeter( aabbBG );
|
|
516
|
+
if ( costBF < bestCost )
|
|
517
|
+
{
|
|
518
|
+
bestRotation = b2_rotateBF;
|
|
519
|
+
bestCost = costBF;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Cost of swapping B and G
|
|
523
|
+
b2AABB aabbBF = b2AABB_Union( B->aabb, F->aabb );
|
|
524
|
+
float costBG = areaB + b2Perimeter( aabbBF );
|
|
525
|
+
if ( costBG < bestCost )
|
|
526
|
+
{
|
|
527
|
+
bestRotation = b2_rotateBG;
|
|
528
|
+
bestCost = costBG;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Cost of swapping C and D
|
|
532
|
+
b2AABB aabbCE = b2AABB_Union( C->aabb, E->aabb );
|
|
533
|
+
float costCD = areaC + b2Perimeter( aabbCE );
|
|
534
|
+
if ( costCD < bestCost )
|
|
535
|
+
{
|
|
536
|
+
bestRotation = b2_rotateCD;
|
|
537
|
+
bestCost = costCD;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Cost of swapping C and E
|
|
541
|
+
b2AABB aabbCD = b2AABB_Union( C->aabb, D->aabb );
|
|
542
|
+
float costCE = areaC + b2Perimeter( aabbCD );
|
|
543
|
+
if ( costCE < bestCost )
|
|
544
|
+
{
|
|
545
|
+
bestRotation = b2_rotateCE;
|
|
546
|
+
// bestCost = costCE;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
switch ( bestRotation )
|
|
550
|
+
{
|
|
551
|
+
case b2_rotateNone:
|
|
552
|
+
break;
|
|
553
|
+
|
|
554
|
+
case b2_rotateBF:
|
|
555
|
+
A->children.child1 = iF;
|
|
556
|
+
C->children.child1 = iB;
|
|
557
|
+
|
|
558
|
+
B->parent = iC;
|
|
559
|
+
F->parent = iA;
|
|
560
|
+
|
|
561
|
+
C->aabb = aabbBG;
|
|
562
|
+
C->height = 1 + b2MaxUInt16( B->height, G->height );
|
|
563
|
+
A->height = 1 + b2MaxUInt16( C->height, F->height );
|
|
564
|
+
C->categoryBits = B->categoryBits | G->categoryBits;
|
|
565
|
+
A->categoryBits = C->categoryBits | F->categoryBits;
|
|
566
|
+
C->flags |= ( B->flags | G->flags ) & b2_enlargedNode;
|
|
567
|
+
A->flags |= ( C->flags | F->flags ) & b2_enlargedNode;
|
|
568
|
+
break;
|
|
569
|
+
|
|
570
|
+
case b2_rotateBG:
|
|
571
|
+
A->children.child1 = iG;
|
|
572
|
+
C->children.child2 = iB;
|
|
573
|
+
|
|
574
|
+
B->parent = iC;
|
|
575
|
+
G->parent = iA;
|
|
576
|
+
|
|
577
|
+
C->aabb = aabbBF;
|
|
578
|
+
C->height = 1 + b2MaxUInt16( B->height, F->height );
|
|
579
|
+
A->height = 1 + b2MaxUInt16( C->height, G->height );
|
|
580
|
+
C->categoryBits = B->categoryBits | F->categoryBits;
|
|
581
|
+
A->categoryBits = C->categoryBits | G->categoryBits;
|
|
582
|
+
C->flags |= ( B->flags | F->flags ) & b2_enlargedNode;
|
|
583
|
+
A->flags |= ( C->flags | G->flags ) & b2_enlargedNode;
|
|
584
|
+
break;
|
|
585
|
+
|
|
586
|
+
case b2_rotateCD:
|
|
587
|
+
A->children.child2 = iD;
|
|
588
|
+
B->children.child1 = iC;
|
|
589
|
+
|
|
590
|
+
C->parent = iB;
|
|
591
|
+
D->parent = iA;
|
|
592
|
+
|
|
593
|
+
B->aabb = aabbCE;
|
|
594
|
+
B->height = 1 + b2MaxUInt16( C->height, E->height );
|
|
595
|
+
A->height = 1 + b2MaxUInt16( B->height, D->height );
|
|
596
|
+
B->categoryBits = C->categoryBits | E->categoryBits;
|
|
597
|
+
A->categoryBits = B->categoryBits | D->categoryBits;
|
|
598
|
+
B->flags |= ( C->flags | E->flags ) & b2_enlargedNode;
|
|
599
|
+
A->flags |= ( B->flags | D->flags ) & b2_enlargedNode;
|
|
600
|
+
break;
|
|
601
|
+
|
|
602
|
+
case b2_rotateCE:
|
|
603
|
+
A->children.child2 = iE;
|
|
604
|
+
B->children.child2 = iC;
|
|
605
|
+
|
|
606
|
+
C->parent = iB;
|
|
607
|
+
E->parent = iA;
|
|
608
|
+
|
|
609
|
+
B->aabb = aabbCD;
|
|
610
|
+
B->height = 1 + b2MaxUInt16( C->height, D->height );
|
|
611
|
+
A->height = 1 + b2MaxUInt16( B->height, E->height );
|
|
612
|
+
B->categoryBits = C->categoryBits | D->categoryBits;
|
|
613
|
+
A->categoryBits = B->categoryBits | E->categoryBits;
|
|
614
|
+
B->flags |= ( C->flags | D->flags ) & b2_enlargedNode;
|
|
615
|
+
A->flags |= ( B->flags | E->flags ) & b2_enlargedNode;
|
|
616
|
+
break;
|
|
617
|
+
|
|
618
|
+
default:
|
|
619
|
+
B2_ASSERT( false );
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
static void b2InsertLeaf( b2DynamicTree* tree, int leaf, bool shouldRotate )
|
|
626
|
+
{
|
|
627
|
+
if ( tree->root == B2_NULL_INDEX )
|
|
628
|
+
{
|
|
629
|
+
tree->root = leaf;
|
|
630
|
+
tree->nodes[tree->root].parent = B2_NULL_INDEX;
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Stage 1: find the best sibling for this node
|
|
635
|
+
b2AABB leafAABB = tree->nodes[leaf].aabb;
|
|
636
|
+
int sibling = b2FindBestSibling( tree, leafAABB );
|
|
637
|
+
|
|
638
|
+
// Stage 2: create a new parent for the leaf and sibling
|
|
639
|
+
int oldParent = tree->nodes[sibling].parent;
|
|
640
|
+
int newParent = b2AllocateNode( tree );
|
|
641
|
+
|
|
642
|
+
// warning: node pointer can change after allocation
|
|
643
|
+
b2TreeNode* nodes = tree->nodes;
|
|
644
|
+
nodes[newParent].parent = oldParent;
|
|
645
|
+
nodes[newParent].userData = UINT64_MAX;
|
|
646
|
+
nodes[newParent].aabb = b2AABB_Union( leafAABB, nodes[sibling].aabb );
|
|
647
|
+
nodes[newParent].categoryBits = nodes[leaf].categoryBits | nodes[sibling].categoryBits;
|
|
648
|
+
nodes[newParent].height = nodes[sibling].height + 1;
|
|
649
|
+
|
|
650
|
+
if ( oldParent != B2_NULL_INDEX )
|
|
651
|
+
{
|
|
652
|
+
// The sibling was not the root.
|
|
653
|
+
if ( nodes[oldParent].children.child1 == sibling )
|
|
654
|
+
{
|
|
655
|
+
nodes[oldParent].children.child1 = newParent;
|
|
656
|
+
}
|
|
657
|
+
else
|
|
658
|
+
{
|
|
659
|
+
nodes[oldParent].children.child2 = newParent;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
nodes[newParent].children.child1 = sibling;
|
|
663
|
+
nodes[newParent].children.child2 = leaf;
|
|
664
|
+
nodes[sibling].parent = newParent;
|
|
665
|
+
nodes[leaf].parent = newParent;
|
|
666
|
+
}
|
|
667
|
+
else
|
|
668
|
+
{
|
|
669
|
+
// The sibling was the root.
|
|
670
|
+
nodes[newParent].children.child1 = sibling;
|
|
671
|
+
nodes[newParent].children.child2 = leaf;
|
|
672
|
+
nodes[sibling].parent = newParent;
|
|
673
|
+
nodes[leaf].parent = newParent;
|
|
674
|
+
tree->root = newParent;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Stage 3: walk back up the tree fixing heights and AABBs
|
|
678
|
+
int index = nodes[leaf].parent;
|
|
679
|
+
while ( index != B2_NULL_INDEX )
|
|
680
|
+
{
|
|
681
|
+
int child1 = nodes[index].children.child1;
|
|
682
|
+
int child2 = nodes[index].children.child2;
|
|
683
|
+
|
|
684
|
+
B2_ASSERT( child1 != B2_NULL_INDEX );
|
|
685
|
+
B2_ASSERT( child2 != B2_NULL_INDEX );
|
|
686
|
+
|
|
687
|
+
nodes[index].aabb = b2AABB_Union( nodes[child1].aabb, nodes[child2].aabb );
|
|
688
|
+
nodes[index].categoryBits = nodes[child1].categoryBits | nodes[child2].categoryBits;
|
|
689
|
+
nodes[index].height = 1 + b2MaxUInt16( nodes[child1].height, nodes[child2].height );
|
|
690
|
+
nodes[index].flags |= ( nodes[child1].flags | nodes[child2].flags ) & b2_enlargedNode;
|
|
691
|
+
|
|
692
|
+
if ( shouldRotate )
|
|
693
|
+
{
|
|
694
|
+
b2RotateNodes( tree, index );
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
index = nodes[index].parent;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
static void b2RemoveLeaf( b2DynamicTree* tree, int leaf )
|
|
702
|
+
{
|
|
703
|
+
if ( leaf == tree->root )
|
|
704
|
+
{
|
|
705
|
+
tree->root = B2_NULL_INDEX;
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
b2TreeNode* nodes = tree->nodes;
|
|
710
|
+
|
|
711
|
+
int parent = nodes[leaf].parent;
|
|
712
|
+
int grandParent = nodes[parent].parent;
|
|
713
|
+
int sibling;
|
|
714
|
+
if ( nodes[parent].children.child1 == leaf )
|
|
715
|
+
{
|
|
716
|
+
sibling = nodes[parent].children.child2;
|
|
717
|
+
}
|
|
718
|
+
else
|
|
719
|
+
{
|
|
720
|
+
sibling = nodes[parent].children.child1;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if ( grandParent != B2_NULL_INDEX )
|
|
724
|
+
{
|
|
725
|
+
// Destroy parent and connect sibling to grandParent.
|
|
726
|
+
if ( nodes[grandParent].children.child1 == parent )
|
|
727
|
+
{
|
|
728
|
+
nodes[grandParent].children.child1 = sibling;
|
|
729
|
+
}
|
|
730
|
+
else
|
|
731
|
+
{
|
|
732
|
+
nodes[grandParent].children.child2 = sibling;
|
|
733
|
+
}
|
|
734
|
+
nodes[sibling].parent = grandParent;
|
|
735
|
+
b2FreeNode( tree, parent );
|
|
736
|
+
|
|
737
|
+
// Adjust ancestor bounds.
|
|
738
|
+
int index = grandParent;
|
|
739
|
+
while ( index != B2_NULL_INDEX )
|
|
740
|
+
{
|
|
741
|
+
b2TreeNode* node = nodes + index;
|
|
742
|
+
b2TreeNode* child1 = nodes + node->children.child1;
|
|
743
|
+
b2TreeNode* child2 = nodes + node->children.child2;
|
|
744
|
+
|
|
745
|
+
// Fast union using SSE
|
|
746
|
+
//__m128 aabb1 = _mm_load_ps(&child1->aabb.lowerBound.x);
|
|
747
|
+
//__m128 aabb2 = _mm_load_ps(&child2->aabb.lowerBound.x);
|
|
748
|
+
//__m128 lower = _mm_min_ps(aabb1, aabb2);
|
|
749
|
+
//__m128 upper = _mm_max_ps(aabb1, aabb2);
|
|
750
|
+
//__m128 aabb = _mm_shuffle_ps(lower, upper, _MM_SHUFFLE(3, 2, 1, 0));
|
|
751
|
+
//_mm_store_ps(&node->aabb.lowerBound.x, aabb);
|
|
752
|
+
|
|
753
|
+
node->aabb = b2AABB_Union( child1->aabb, child2->aabb );
|
|
754
|
+
node->categoryBits = child1->categoryBits | child2->categoryBits;
|
|
755
|
+
node->height = 1 + b2MaxUInt16( child1->height, child2->height );
|
|
756
|
+
|
|
757
|
+
index = node->parent;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
else
|
|
761
|
+
{
|
|
762
|
+
tree->root = sibling;
|
|
763
|
+
tree->nodes[sibling].parent = B2_NULL_INDEX;
|
|
764
|
+
b2FreeNode( tree, parent );
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Create a proxy in the tree as a leaf node. We return the index of the node instead of a pointer so that we can grow
|
|
769
|
+
// the node pool.
|
|
770
|
+
int b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, uint64_t userData )
|
|
771
|
+
{
|
|
772
|
+
B2_ASSERT( -B2_HUGE < aabb.lowerBound.x && aabb.lowerBound.x < B2_HUGE );
|
|
773
|
+
B2_ASSERT( -B2_HUGE < aabb.lowerBound.y && aabb.lowerBound.y < B2_HUGE );
|
|
774
|
+
B2_ASSERT( -B2_HUGE < aabb.upperBound.x && aabb.upperBound.x < B2_HUGE );
|
|
775
|
+
B2_ASSERT( -B2_HUGE < aabb.upperBound.y && aabb.upperBound.y < B2_HUGE );
|
|
776
|
+
|
|
777
|
+
int proxyId = b2AllocateNode( tree );
|
|
778
|
+
b2TreeNode* node = tree->nodes + proxyId;
|
|
779
|
+
|
|
780
|
+
node->aabb = aabb;
|
|
781
|
+
node->userData = userData;
|
|
782
|
+
node->categoryBits = categoryBits;
|
|
783
|
+
node->height = 0;
|
|
784
|
+
node->flags = b2_allocatedNode | b2_leafNode;
|
|
785
|
+
|
|
786
|
+
bool shouldRotate = true;
|
|
787
|
+
b2InsertLeaf( tree, proxyId, shouldRotate );
|
|
788
|
+
|
|
789
|
+
tree->proxyCount += 1;
|
|
790
|
+
|
|
791
|
+
return proxyId;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
void b2DynamicTree_DestroyProxy( b2DynamicTree* tree, int proxyId )
|
|
795
|
+
{
|
|
796
|
+
B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
|
|
797
|
+
B2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) );
|
|
798
|
+
|
|
799
|
+
b2RemoveLeaf( tree, proxyId );
|
|
800
|
+
b2FreeNode( tree, proxyId );
|
|
801
|
+
|
|
802
|
+
B2_ASSERT( tree->proxyCount > 0 );
|
|
803
|
+
tree->proxyCount -= 1;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
int b2DynamicTree_GetProxyCount( const b2DynamicTree* tree )
|
|
807
|
+
{
|
|
808
|
+
return tree->proxyCount;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
void b2DynamicTree_MoveProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb )
|
|
812
|
+
{
|
|
813
|
+
B2_ASSERT( b2IsValidAABB( aabb ) );
|
|
814
|
+
B2_ASSERT( aabb.upperBound.x - aabb.lowerBound.x < B2_HUGE );
|
|
815
|
+
B2_ASSERT( aabb.upperBound.y - aabb.lowerBound.y < B2_HUGE );
|
|
816
|
+
B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
|
|
817
|
+
B2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) );
|
|
818
|
+
|
|
819
|
+
b2RemoveLeaf( tree, proxyId );
|
|
820
|
+
|
|
821
|
+
tree->nodes[proxyId].aabb = aabb;
|
|
822
|
+
|
|
823
|
+
bool shouldRotate = false;
|
|
824
|
+
b2InsertLeaf( tree, proxyId, shouldRotate );
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
void b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb )
|
|
828
|
+
{
|
|
829
|
+
b2TreeNode* nodes = tree->nodes;
|
|
830
|
+
|
|
831
|
+
B2_ASSERT( b2IsValidAABB( aabb ) );
|
|
832
|
+
B2_ASSERT( aabb.upperBound.x - aabb.lowerBound.x < B2_HUGE );
|
|
833
|
+
B2_ASSERT( aabb.upperBound.y - aabb.lowerBound.y < B2_HUGE );
|
|
834
|
+
B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
|
|
835
|
+
B2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) );
|
|
836
|
+
|
|
837
|
+
// Caller must ensure this
|
|
838
|
+
B2_ASSERT( b2AABB_Contains( nodes[proxyId].aabb, aabb ) == false );
|
|
839
|
+
|
|
840
|
+
nodes[proxyId].aabb = aabb;
|
|
841
|
+
|
|
842
|
+
int parentIndex = nodes[proxyId].parent;
|
|
843
|
+
while (parentIndex != B2_NULL_INDEX)
|
|
844
|
+
{
|
|
845
|
+
bool changed = b2EnlargeAABB( &nodes[parentIndex].aabb, aabb );
|
|
846
|
+
nodes[parentIndex].flags |= b2_enlargedNode;
|
|
847
|
+
parentIndex = nodes[parentIndex].parent;
|
|
848
|
+
|
|
849
|
+
if (changed == false)
|
|
850
|
+
{
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
while (parentIndex != B2_NULL_INDEX)
|
|
856
|
+
{
|
|
857
|
+
if (nodes[parentIndex].flags & b2_enlargedNode)
|
|
858
|
+
{
|
|
859
|
+
// early out because this ancestor was previously ascended and marked as enlarged
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
nodes[parentIndex].flags |= b2_enlargedNode;
|
|
864
|
+
parentIndex = nodes[parentIndex].parent;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
void b2DynamicTree_SetCategoryBits( b2DynamicTree* tree, int proxyId, uint64_t categoryBits )
|
|
869
|
+
{
|
|
870
|
+
b2TreeNode* nodes = tree->nodes;
|
|
871
|
+
|
|
872
|
+
B2_ASSERT( nodes[proxyId].children.child1 == B2_NULL_INDEX );
|
|
873
|
+
B2_ASSERT( nodes[proxyId].children.child2 == B2_NULL_INDEX );
|
|
874
|
+
B2_ASSERT( (nodes[proxyId].flags & b2_leafNode) == b2_leafNode );
|
|
875
|
+
|
|
876
|
+
nodes[proxyId].categoryBits = categoryBits;
|
|
877
|
+
|
|
878
|
+
// Fix up category bits in ancestor internal nodes
|
|
879
|
+
int nodeIndex = nodes[proxyId].parent;
|
|
880
|
+
while ( nodeIndex != B2_NULL_INDEX )
|
|
881
|
+
{
|
|
882
|
+
b2TreeNode* node = nodes + nodeIndex;
|
|
883
|
+
int child1 = node->children.child1;
|
|
884
|
+
B2_ASSERT( child1 != B2_NULL_INDEX );
|
|
885
|
+
int child2 = node->children.child2;
|
|
886
|
+
B2_ASSERT( child2 != B2_NULL_INDEX );
|
|
887
|
+
node->categoryBits = nodes[child1].categoryBits | nodes[child2].categoryBits;
|
|
888
|
+
|
|
889
|
+
nodeIndex = node->parent;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
uint64_t b2DynamicTree_GetCategoryBits( b2DynamicTree* tree, int proxyId )
|
|
894
|
+
{
|
|
895
|
+
B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
|
|
896
|
+
return tree->nodes[proxyId].categoryBits;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
int b2DynamicTree_GetHeight( const b2DynamicTree* tree )
|
|
900
|
+
{
|
|
901
|
+
if ( tree->root == B2_NULL_INDEX )
|
|
902
|
+
{
|
|
903
|
+
return 0;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
return tree->nodes[tree->root].height;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
float b2DynamicTree_GetAreaRatio( const b2DynamicTree* tree )
|
|
910
|
+
{
|
|
911
|
+
if ( tree->root == B2_NULL_INDEX )
|
|
912
|
+
{
|
|
913
|
+
return 0.0f;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
const b2TreeNode* root = tree->nodes + tree->root;
|
|
917
|
+
float rootArea = b2Perimeter( root->aabb );
|
|
918
|
+
|
|
919
|
+
float totalArea = 0.0f;
|
|
920
|
+
for ( int i = 0; i < tree->nodeCapacity; ++i )
|
|
921
|
+
{
|
|
922
|
+
const b2TreeNode* node = tree->nodes + i;
|
|
923
|
+
if ( b2IsAllocated(node) == false || b2IsLeaf( node ) || i == tree->root )
|
|
924
|
+
{
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
totalArea += b2Perimeter( node->aabb );
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
return totalArea / rootArea;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
b2AABB b2DynamicTree_GetRootBounds( const b2DynamicTree* tree )
|
|
935
|
+
{
|
|
936
|
+
if (tree->root != B2_NULL_INDEX)
|
|
937
|
+
{
|
|
938
|
+
return tree->nodes[tree->root].aabb;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
b2AABB empty = { b2Vec2_zero, b2Vec2_zero };
|
|
942
|
+
return empty;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
#if B2_VALIDATE
|
|
946
|
+
// Compute the height of a sub-tree.
|
|
947
|
+
static int b2ComputeHeight( const b2DynamicTree* tree, int nodeId )
|
|
948
|
+
{
|
|
949
|
+
B2_ASSERT( 0 <= nodeId && nodeId < tree->nodeCapacity );
|
|
950
|
+
b2TreeNode* node = tree->nodes + nodeId;
|
|
951
|
+
|
|
952
|
+
if ( b2IsLeaf( node ) )
|
|
953
|
+
{
|
|
954
|
+
return 0;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
int height1 = b2ComputeHeight( tree, node->children.child1 );
|
|
958
|
+
int height2 = b2ComputeHeight( tree, node->children.child2 );
|
|
959
|
+
return 1 + b2MaxInt( height1, height2 );
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
static void b2ValidateStructure( const b2DynamicTree* tree, int index )
|
|
963
|
+
{
|
|
964
|
+
if ( index == B2_NULL_INDEX )
|
|
965
|
+
{
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
if ( index == tree->root )
|
|
970
|
+
{
|
|
971
|
+
B2_ASSERT( tree->nodes[index].parent == B2_NULL_INDEX );
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
const b2TreeNode* node = tree->nodes + index;
|
|
975
|
+
|
|
976
|
+
B2_ASSERT( node->flags == 0 || ( node->flags & b2_allocatedNode ) != 0 );
|
|
977
|
+
|
|
978
|
+
if ( b2IsLeaf( node ) )
|
|
979
|
+
{
|
|
980
|
+
B2_ASSERT( node->height == 0 );
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
int child1 = node->children.child1;
|
|
985
|
+
int child2 = node->children.child2;
|
|
986
|
+
|
|
987
|
+
B2_ASSERT( 0 <= child1 && child1 < tree->nodeCapacity );
|
|
988
|
+
B2_ASSERT( 0 <= child2 && child2 < tree->nodeCapacity );
|
|
989
|
+
|
|
990
|
+
B2_ASSERT( tree->nodes[child1].parent == index );
|
|
991
|
+
B2_ASSERT( tree->nodes[child2].parent == index );
|
|
992
|
+
|
|
993
|
+
if ( ( tree->nodes[child1].flags | tree->nodes[child2].flags ) & b2_enlargedNode )
|
|
994
|
+
{
|
|
995
|
+
B2_ASSERT( node->flags & b2_enlargedNode );
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
b2ValidateStructure( tree, child1 );
|
|
999
|
+
b2ValidateStructure( tree, child2 );
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
static void b2ValidateMetrics( const b2DynamicTree* tree, int index )
|
|
1003
|
+
{
|
|
1004
|
+
if ( index == B2_NULL_INDEX )
|
|
1005
|
+
{
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
const b2TreeNode* node = tree->nodes + index;
|
|
1010
|
+
|
|
1011
|
+
if ( b2IsLeaf( node ) )
|
|
1012
|
+
{
|
|
1013
|
+
B2_ASSERT( node->height == 0 );
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
int child1 = node->children.child1;
|
|
1018
|
+
int child2 = node->children.child2;
|
|
1019
|
+
|
|
1020
|
+
B2_ASSERT( 0 <= child1 && child1 < tree->nodeCapacity );
|
|
1021
|
+
B2_ASSERT( 0 <= child2 && child2 < tree->nodeCapacity );
|
|
1022
|
+
|
|
1023
|
+
int height1 = tree->nodes[child1].height;
|
|
1024
|
+
int height2 = tree->nodes[child2].height;
|
|
1025
|
+
int height = 1 + b2MaxInt( height1, height2 );
|
|
1026
|
+
B2_ASSERT( node->height == height );
|
|
1027
|
+
|
|
1028
|
+
// b2AABB aabb = b2AABB_Union(tree->nodes[child1].aabb, tree->nodes[child2].aabb);
|
|
1029
|
+
|
|
1030
|
+
B2_ASSERT( b2AABB_Contains( node->aabb, tree->nodes[child1].aabb ) );
|
|
1031
|
+
B2_ASSERT( b2AABB_Contains( node->aabb, tree->nodes[child2].aabb ) );
|
|
1032
|
+
|
|
1033
|
+
// B2_ASSERT(aabb.lowerBound.x == node->aabb.lowerBound.x);
|
|
1034
|
+
// B2_ASSERT(aabb.lowerBound.y == node->aabb.lowerBound.y);
|
|
1035
|
+
// B2_ASSERT(aabb.upperBound.x == node->aabb.upperBound.x);
|
|
1036
|
+
// B2_ASSERT(aabb.upperBound.y == node->aabb.upperBound.y);
|
|
1037
|
+
|
|
1038
|
+
uint64_t categoryBits = tree->nodes[child1].categoryBits | tree->nodes[child2].categoryBits;
|
|
1039
|
+
B2_ASSERT( node->categoryBits == categoryBits );
|
|
1040
|
+
|
|
1041
|
+
b2ValidateMetrics( tree, child1 );
|
|
1042
|
+
b2ValidateMetrics( tree, child2 );
|
|
1043
|
+
}
|
|
1044
|
+
#endif
|
|
1045
|
+
|
|
1046
|
+
void b2DynamicTree_Validate( const b2DynamicTree* tree )
|
|
1047
|
+
{
|
|
1048
|
+
#if B2_VALIDATE
|
|
1049
|
+
if ( tree->root == B2_NULL_INDEX )
|
|
1050
|
+
{
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
b2ValidateStructure( tree, tree->root );
|
|
1055
|
+
b2ValidateMetrics( tree, tree->root );
|
|
1056
|
+
|
|
1057
|
+
int freeCount = 0;
|
|
1058
|
+
int freeIndex = tree->freeList;
|
|
1059
|
+
while ( freeIndex != B2_NULL_INDEX )
|
|
1060
|
+
{
|
|
1061
|
+
B2_ASSERT( 0 <= freeIndex && freeIndex < tree->nodeCapacity );
|
|
1062
|
+
freeIndex = tree->nodes[freeIndex].next;
|
|
1063
|
+
++freeCount;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
int height = b2DynamicTree_GetHeight( tree );
|
|
1067
|
+
int computedHeight = b2ComputeHeight( tree, tree->root );
|
|
1068
|
+
B2_ASSERT( height == computedHeight );
|
|
1069
|
+
|
|
1070
|
+
B2_ASSERT( tree->nodeCount + freeCount == tree->nodeCapacity );
|
|
1071
|
+
#else
|
|
1072
|
+
B2_UNUSED( tree );
|
|
1073
|
+
#endif
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
void b2DynamicTree_ValidateNoEnlarged(const b2DynamicTree* tree)
|
|
1077
|
+
{
|
|
1078
|
+
#if B2_VALIDATE == 1
|
|
1079
|
+
int capacity = tree->nodeCapacity;
|
|
1080
|
+
const b2TreeNode* nodes = tree->nodes;
|
|
1081
|
+
for ( int i = 0; i < capacity; ++i )
|
|
1082
|
+
{
|
|
1083
|
+
const b2TreeNode* node = nodes + i;
|
|
1084
|
+
if ( node->flags & b2_allocatedNode )
|
|
1085
|
+
{
|
|
1086
|
+
B2_ASSERT( ( node->flags & b2_enlargedNode ) == 0 );
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
#else
|
|
1090
|
+
B2_UNUSED( tree );
|
|
1091
|
+
#endif
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
int b2DynamicTree_GetByteCount( const b2DynamicTree* tree )
|
|
1095
|
+
{
|
|
1096
|
+
size_t size = sizeof( b2DynamicTree ) + sizeof( b2TreeNode ) * tree->nodeCapacity +
|
|
1097
|
+
tree->rebuildCapacity * ( sizeof( int ) + sizeof( b2AABB ) + sizeof( b2Vec2 ) + sizeof( int ) );
|
|
1098
|
+
|
|
1099
|
+
return (int)size;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
uint64_t b2DynamicTree_GetUserData( const b2DynamicTree* tree, int proxyId )
|
|
1103
|
+
{
|
|
1104
|
+
B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
|
|
1105
|
+
return tree->nodes[proxyId].userData;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
b2AABB b2DynamicTree_GetAABB( const b2DynamicTree* tree, int proxyId )
|
|
1109
|
+
{
|
|
1110
|
+
B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
|
|
1111
|
+
return tree->nodes[proxyId].aabb;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
b2TreeStats b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits, b2TreeQueryCallbackFcn* callback,
|
|
1115
|
+
void* context )
|
|
1116
|
+
{
|
|
1117
|
+
b2TreeStats result = { 0 };
|
|
1118
|
+
|
|
1119
|
+
if ( tree->nodeCount == 0 )
|
|
1120
|
+
{
|
|
1121
|
+
return result;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
int stack[B2_TREE_STACK_SIZE];
|
|
1125
|
+
int stackCount = 0;
|
|
1126
|
+
stack[stackCount++] = tree->root;
|
|
1127
|
+
|
|
1128
|
+
while ( stackCount > 0 )
|
|
1129
|
+
{
|
|
1130
|
+
int nodeId = stack[--stackCount];
|
|
1131
|
+
if ( nodeId == B2_NULL_INDEX )
|
|
1132
|
+
{
|
|
1133
|
+
// todo huh?
|
|
1134
|
+
B2_ASSERT( false );
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const b2TreeNode* node = tree->nodes + nodeId;
|
|
1139
|
+
result.nodeVisits += 1;
|
|
1140
|
+
|
|
1141
|
+
if ( b2AABB_Overlaps( node->aabb, aabb ) && ( node->categoryBits & maskBits ) != 0 )
|
|
1142
|
+
{
|
|
1143
|
+
if ( b2IsLeaf( node ) )
|
|
1144
|
+
{
|
|
1145
|
+
// callback to user code with proxy id
|
|
1146
|
+
bool proceed = callback( nodeId, node->userData, context );
|
|
1147
|
+
result.leafVisits += 1;
|
|
1148
|
+
|
|
1149
|
+
if ( proceed == false )
|
|
1150
|
+
{
|
|
1151
|
+
return result;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
else
|
|
1155
|
+
{
|
|
1156
|
+
if ( stackCount < B2_TREE_STACK_SIZE - 1 )
|
|
1157
|
+
{
|
|
1158
|
+
stack[stackCount++] = node->children.child1;
|
|
1159
|
+
stack[stackCount++] = node->children.child2;
|
|
1160
|
+
}
|
|
1161
|
+
else
|
|
1162
|
+
{
|
|
1163
|
+
B2_ASSERT( stackCount < B2_TREE_STACK_SIZE - 1 );
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
return result;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
b2TreeStats b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits,
|
|
1173
|
+
b2TreeRayCastCallbackFcn* callback, void* context )
|
|
1174
|
+
{
|
|
1175
|
+
b2TreeStats result = { 0 };
|
|
1176
|
+
|
|
1177
|
+
if ( tree->nodeCount == 0 )
|
|
1178
|
+
{
|
|
1179
|
+
return result;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
b2Vec2 p1 = input->origin;
|
|
1183
|
+
b2Vec2 d = input->translation;
|
|
1184
|
+
|
|
1185
|
+
b2Vec2 r = b2Normalize( d );
|
|
1186
|
+
|
|
1187
|
+
// v is perpendicular to the segment.
|
|
1188
|
+
b2Vec2 v = b2CrossSV( 1.0f, r );
|
|
1189
|
+
b2Vec2 abs_v = b2Abs( v );
|
|
1190
|
+
|
|
1191
|
+
// Separating axis for segment (Gino, p80).
|
|
1192
|
+
// |dot(v, p1 - c)| > dot(|v|, h)
|
|
1193
|
+
|
|
1194
|
+
float maxFraction = input->maxFraction;
|
|
1195
|
+
|
|
1196
|
+
b2Vec2 p2 = b2MulAdd( p1, maxFraction, d );
|
|
1197
|
+
|
|
1198
|
+
// Build a bounding box for the segment.
|
|
1199
|
+
b2AABB segmentAABB = { b2Min( p1, p2 ), b2Max( p1, p2 ) };
|
|
1200
|
+
|
|
1201
|
+
int stack[B2_TREE_STACK_SIZE];
|
|
1202
|
+
int stackCount = 0;
|
|
1203
|
+
stack[stackCount++] = tree->root;
|
|
1204
|
+
|
|
1205
|
+
const b2TreeNode* nodes = tree->nodes;
|
|
1206
|
+
|
|
1207
|
+
b2RayCastInput subInput = *input;
|
|
1208
|
+
|
|
1209
|
+
while ( stackCount > 0 )
|
|
1210
|
+
{
|
|
1211
|
+
int nodeId = stack[--stackCount];
|
|
1212
|
+
if ( nodeId == B2_NULL_INDEX )
|
|
1213
|
+
{
|
|
1214
|
+
// todo is this possible?
|
|
1215
|
+
B2_ASSERT( false );
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
const b2TreeNode* node = nodes + nodeId;
|
|
1220
|
+
result.nodeVisits += 1;
|
|
1221
|
+
|
|
1222
|
+
b2AABB nodeAABB = node->aabb;
|
|
1223
|
+
|
|
1224
|
+
if ( ( node->categoryBits & maskBits ) == 0 || b2AABB_Overlaps( nodeAABB, segmentAABB ) == false )
|
|
1225
|
+
{
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// Separating axis for segment (Gino, p80).
|
|
1230
|
+
// |dot(v, p1 - c)| > dot(|v|, h)
|
|
1231
|
+
// radius extension is added to the node in this case
|
|
1232
|
+
b2Vec2 c = b2AABB_Center( nodeAABB );
|
|
1233
|
+
b2Vec2 h = b2AABB_Extents( nodeAABB );
|
|
1234
|
+
float term1 = b2AbsFloat( b2Dot( v, b2Sub( p1, c ) ) );
|
|
1235
|
+
float term2 = b2Dot( abs_v, h );
|
|
1236
|
+
if ( term2 < term1 )
|
|
1237
|
+
{
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
if ( b2IsLeaf( node ) )
|
|
1242
|
+
{
|
|
1243
|
+
subInput.maxFraction = maxFraction;
|
|
1244
|
+
|
|
1245
|
+
float value = callback( &subInput, nodeId, node->userData, context );
|
|
1246
|
+
result.leafVisits += 1;
|
|
1247
|
+
|
|
1248
|
+
// The user may return -1 to indicate this shape should be skipped
|
|
1249
|
+
|
|
1250
|
+
if ( value == 0.0f )
|
|
1251
|
+
{
|
|
1252
|
+
// The client has terminated the ray cast.
|
|
1253
|
+
return result;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
if ( 0.0f < value && value <= maxFraction )
|
|
1257
|
+
{
|
|
1258
|
+
// Update segment bounding box.
|
|
1259
|
+
maxFraction = value;
|
|
1260
|
+
p2 = b2MulAdd( p1, maxFraction, d );
|
|
1261
|
+
segmentAABB.lowerBound = b2Min( p1, p2 );
|
|
1262
|
+
segmentAABB.upperBound = b2Max( p1, p2 );
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
else
|
|
1266
|
+
{
|
|
1267
|
+
if ( stackCount < B2_TREE_STACK_SIZE - 1 )
|
|
1268
|
+
{
|
|
1269
|
+
b2Vec2 c1 = b2AABB_Center( nodes[node->children.child1].aabb );
|
|
1270
|
+
b2Vec2 c2 = b2AABB_Center( nodes[node->children.child2].aabb );
|
|
1271
|
+
if ( b2DistanceSquared( c1, p1 ) < b2DistanceSquared( c2, p1 ) )
|
|
1272
|
+
{
|
|
1273
|
+
stack[stackCount++] = node->children.child2;
|
|
1274
|
+
stack[stackCount++] = node->children.child1;
|
|
1275
|
+
}
|
|
1276
|
+
else
|
|
1277
|
+
{
|
|
1278
|
+
stack[stackCount++] = node->children.child1;
|
|
1279
|
+
stack[stackCount++] = node->children.child2;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
else
|
|
1283
|
+
{
|
|
1284
|
+
B2_ASSERT( stackCount < B2_TREE_STACK_SIZE - 1 );
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
return result;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
b2TreeStats b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits,
|
|
1293
|
+
b2TreeShapeCastCallbackFcn* callback, void* context )
|
|
1294
|
+
{
|
|
1295
|
+
b2TreeStats stats = { 0 };
|
|
1296
|
+
|
|
1297
|
+
if ( tree->nodeCount == 0 || input->proxy.count == 0 )
|
|
1298
|
+
{
|
|
1299
|
+
return stats;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
b2AABB originAABB = { input->proxy.points[0], input->proxy.points[0] };
|
|
1303
|
+
for ( int i = 1; i < input->proxy.count; ++i )
|
|
1304
|
+
{
|
|
1305
|
+
originAABB.lowerBound = b2Min( originAABB.lowerBound, input->proxy.points[i] );
|
|
1306
|
+
originAABB.upperBound = b2Max( originAABB.upperBound, input->proxy.points[i] );
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
b2Vec2 radius = { input->proxy.radius, input->proxy.radius };
|
|
1310
|
+
|
|
1311
|
+
originAABB.lowerBound = b2Sub( originAABB.lowerBound, radius );
|
|
1312
|
+
originAABB.upperBound = b2Add( originAABB.upperBound, radius );
|
|
1313
|
+
|
|
1314
|
+
b2Vec2 p1 = b2AABB_Center( originAABB );
|
|
1315
|
+
b2Vec2 extension = b2AABB_Extents( originAABB );
|
|
1316
|
+
|
|
1317
|
+
// v is perpendicular to the segment.
|
|
1318
|
+
b2Vec2 r = input->translation;
|
|
1319
|
+
b2Vec2 v = b2CrossSV( 1.0f, r );
|
|
1320
|
+
b2Vec2 abs_v = b2Abs( v );
|
|
1321
|
+
|
|
1322
|
+
// Separating axis for segment (Gino, p80).
|
|
1323
|
+
// |dot(v, p1 - c)| > dot(|v|, h)
|
|
1324
|
+
|
|
1325
|
+
float maxFraction = input->maxFraction;
|
|
1326
|
+
|
|
1327
|
+
// Build total box for the shape cast
|
|
1328
|
+
b2Vec2 t = b2MulSV( maxFraction, input->translation );
|
|
1329
|
+
b2AABB totalAABB = {
|
|
1330
|
+
b2Min( originAABB.lowerBound, b2Add( originAABB.lowerBound, t ) ),
|
|
1331
|
+
b2Max( originAABB.upperBound, b2Add( originAABB.upperBound, t ) ),
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1334
|
+
b2ShapeCastInput subInput = *input;
|
|
1335
|
+
const b2TreeNode* nodes = tree->nodes;
|
|
1336
|
+
|
|
1337
|
+
int stack[B2_TREE_STACK_SIZE];
|
|
1338
|
+
int stackCount = 0;
|
|
1339
|
+
stack[stackCount++] = tree->root;
|
|
1340
|
+
|
|
1341
|
+
while ( stackCount > 0 )
|
|
1342
|
+
{
|
|
1343
|
+
int nodeId = stack[--stackCount];
|
|
1344
|
+
if ( nodeId == B2_NULL_INDEX )
|
|
1345
|
+
{
|
|
1346
|
+
// todo is this possible?
|
|
1347
|
+
B2_ASSERT( false );
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
const b2TreeNode* node = nodes + nodeId;
|
|
1352
|
+
stats.nodeVisits += 1;
|
|
1353
|
+
|
|
1354
|
+
if ( ( node->categoryBits & maskBits ) == 0 || b2AABB_Overlaps( node->aabb, totalAABB ) == false )
|
|
1355
|
+
{
|
|
1356
|
+
continue;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// Separating axis for segment (Gino, p80).
|
|
1360
|
+
// |dot(v, p1 - c)| > dot(|v|, h)
|
|
1361
|
+
// radius extension is added to the node in this case
|
|
1362
|
+
b2Vec2 c = b2AABB_Center( node->aabb );
|
|
1363
|
+
b2Vec2 h = b2Add( b2AABB_Extents( node->aabb ), extension );
|
|
1364
|
+
float term1 = b2AbsFloat( b2Dot( v, b2Sub( p1, c ) ) );
|
|
1365
|
+
float term2 = b2Dot( abs_v, h );
|
|
1366
|
+
if ( term2 < term1 )
|
|
1367
|
+
{
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
if ( b2IsLeaf( node ) )
|
|
1372
|
+
{
|
|
1373
|
+
subInput.maxFraction = maxFraction;
|
|
1374
|
+
|
|
1375
|
+
float value = callback( &subInput, nodeId, node->userData, context );
|
|
1376
|
+
stats.leafVisits += 1;
|
|
1377
|
+
|
|
1378
|
+
if ( value == 0.0f )
|
|
1379
|
+
{
|
|
1380
|
+
// The client has terminated the ray cast.
|
|
1381
|
+
return stats;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
if ( 0.0f < value && value < maxFraction )
|
|
1385
|
+
{
|
|
1386
|
+
// Update segment bounding box.
|
|
1387
|
+
maxFraction = value;
|
|
1388
|
+
t = b2MulSV( maxFraction, input->translation );
|
|
1389
|
+
totalAABB.lowerBound = b2Min( originAABB.lowerBound, b2Add( originAABB.lowerBound, t ) );
|
|
1390
|
+
totalAABB.upperBound = b2Max( originAABB.upperBound, b2Add( originAABB.upperBound, t ) );
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
else
|
|
1394
|
+
{
|
|
1395
|
+
if ( stackCount < B2_TREE_STACK_SIZE - 1 )
|
|
1396
|
+
{
|
|
1397
|
+
b2Vec2 c1 = b2AABB_Center( nodes[node->children.child1].aabb );
|
|
1398
|
+
b2Vec2 c2 = b2AABB_Center( nodes[node->children.child2].aabb );
|
|
1399
|
+
if ( b2DistanceSquared( c1, p1 ) < b2DistanceSquared( c2, p1 ) )
|
|
1400
|
+
{
|
|
1401
|
+
stack[stackCount++] = node->children.child2;
|
|
1402
|
+
stack[stackCount++] = node->children.child1;
|
|
1403
|
+
}
|
|
1404
|
+
else
|
|
1405
|
+
{
|
|
1406
|
+
stack[stackCount++] = node->children.child1;
|
|
1407
|
+
stack[stackCount++] = node->children.child2;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
else
|
|
1411
|
+
{
|
|
1412
|
+
B2_ASSERT( stackCount < B2_TREE_STACK_SIZE - 1 );
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
return stats;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
// Median split == 0, Surface area heuristic == 1
|
|
1421
|
+
#define B2_TREE_HEURISTIC 0
|
|
1422
|
+
|
|
1423
|
+
#if B2_TREE_HEURISTIC == 0
|
|
1424
|
+
|
|
1425
|
+
// Median split heuristic
|
|
1426
|
+
static int b2PartitionMid( int* indices, b2Vec2* centers, int count )
|
|
1427
|
+
{
|
|
1428
|
+
// Handle trivial case
|
|
1429
|
+
if ( count <= 2 )
|
|
1430
|
+
{
|
|
1431
|
+
return count / 2;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
b2Vec2 lowerBound = centers[0];
|
|
1435
|
+
b2Vec2 upperBound = centers[0];
|
|
1436
|
+
|
|
1437
|
+
for ( int i = 1; i < count; ++i )
|
|
1438
|
+
{
|
|
1439
|
+
lowerBound = b2Min( lowerBound, centers[i] );
|
|
1440
|
+
upperBound = b2Max( upperBound, centers[i] );
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
b2Vec2 d = b2Sub( upperBound, lowerBound );
|
|
1444
|
+
b2Vec2 c = { 0.5f * ( lowerBound.x + upperBound.x ), 0.5f * ( lowerBound.y + upperBound.y ) };
|
|
1445
|
+
|
|
1446
|
+
// Partition longest axis using the Hoare partition scheme
|
|
1447
|
+
// https://en.wikipedia.org/wiki/Quicksort
|
|
1448
|
+
// https://nicholasvadivelu.com/2021/01/11/array-partition/
|
|
1449
|
+
int i1 = 0, i2 = count;
|
|
1450
|
+
if ( d.x > d.y )
|
|
1451
|
+
{
|
|
1452
|
+
float pivot = c.x;
|
|
1453
|
+
|
|
1454
|
+
while ( i1 < i2 )
|
|
1455
|
+
{
|
|
1456
|
+
while ( i1 < i2 && centers[i1].x < pivot )
|
|
1457
|
+
{
|
|
1458
|
+
i1 += 1;
|
|
1459
|
+
};
|
|
1460
|
+
|
|
1461
|
+
while ( i1 < i2 && centers[i2 - 1].x >= pivot )
|
|
1462
|
+
{
|
|
1463
|
+
i2 -= 1;
|
|
1464
|
+
};
|
|
1465
|
+
|
|
1466
|
+
if ( i1 < i2 )
|
|
1467
|
+
{
|
|
1468
|
+
// Swap indices
|
|
1469
|
+
{
|
|
1470
|
+
int temp = indices[i1];
|
|
1471
|
+
indices[i1] = indices[i2 - 1];
|
|
1472
|
+
indices[i2 - 1] = temp;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// Swap centers
|
|
1476
|
+
{
|
|
1477
|
+
b2Vec2 temp = centers[i1];
|
|
1478
|
+
centers[i1] = centers[i2 - 1];
|
|
1479
|
+
centers[i2 - 1] = temp;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
i1 += 1;
|
|
1483
|
+
i2 -= 1;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
else
|
|
1488
|
+
{
|
|
1489
|
+
float pivot = c.y;
|
|
1490
|
+
|
|
1491
|
+
while ( i1 < i2 )
|
|
1492
|
+
{
|
|
1493
|
+
while ( i1 < i2 && centers[i1].y < pivot )
|
|
1494
|
+
{
|
|
1495
|
+
i1 += 1;
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1498
|
+
while ( i1 < i2 && centers[i2 - 1].y >= pivot )
|
|
1499
|
+
{
|
|
1500
|
+
i2 -= 1;
|
|
1501
|
+
};
|
|
1502
|
+
|
|
1503
|
+
if ( i1 < i2 )
|
|
1504
|
+
{
|
|
1505
|
+
// Swap indices
|
|
1506
|
+
{
|
|
1507
|
+
int temp = indices[i1];
|
|
1508
|
+
indices[i1] = indices[i2 - 1];
|
|
1509
|
+
indices[i2 - 1] = temp;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
// Swap centers
|
|
1513
|
+
{
|
|
1514
|
+
b2Vec2 temp = centers[i1];
|
|
1515
|
+
centers[i1] = centers[i2 - 1];
|
|
1516
|
+
centers[i2 - 1] = temp;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
i1 += 1;
|
|
1520
|
+
i2 -= 1;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
B2_ASSERT( i1 == i2 );
|
|
1525
|
+
|
|
1526
|
+
if ( i1 > 0 && i1 < count )
|
|
1527
|
+
{
|
|
1528
|
+
return i1;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
return count / 2;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
#else
|
|
1535
|
+
|
|
1536
|
+
#define B2_BIN_COUNT 64
|
|
1537
|
+
|
|
1538
|
+
typedef struct b2TreeBin
|
|
1539
|
+
{
|
|
1540
|
+
b2AABB aabb;
|
|
1541
|
+
int count;
|
|
1542
|
+
} b2TreeBin;
|
|
1543
|
+
|
|
1544
|
+
typedef struct b2TreePlane
|
|
1545
|
+
{
|
|
1546
|
+
b2AABB leftAABB;
|
|
1547
|
+
b2AABB rightAABB;
|
|
1548
|
+
int leftCount;
|
|
1549
|
+
int rightCount;
|
|
1550
|
+
} b2TreePlane;
|
|
1551
|
+
|
|
1552
|
+
// "On Fast Construction of SAH-based Bounding Volume Hierarchies" by Ingo Wald
|
|
1553
|
+
// Returns the left child count
|
|
1554
|
+
static int b2PartitionSAH( int* indices, int* binIndices, b2AABB* boxes, int count )
|
|
1555
|
+
{
|
|
1556
|
+
B2_ASSERT( count > 0 );
|
|
1557
|
+
|
|
1558
|
+
b2TreeBin bins[B2_BIN_COUNT];
|
|
1559
|
+
b2TreePlane planes[B2_BIN_COUNT - 1];
|
|
1560
|
+
|
|
1561
|
+
b2Vec2 center = b2AABB_Center( boxes[0] );
|
|
1562
|
+
b2AABB centroidAABB;
|
|
1563
|
+
centroidAABB.lowerBound = center;
|
|
1564
|
+
centroidAABB.upperBound = center;
|
|
1565
|
+
|
|
1566
|
+
for ( int i = 1; i < count; ++i )
|
|
1567
|
+
{
|
|
1568
|
+
center = b2AABB_Center( boxes[i] );
|
|
1569
|
+
centroidAABB.lowerBound = b2Min( centroidAABB.lowerBound, center );
|
|
1570
|
+
centroidAABB.upperBound = b2Max( centroidAABB.upperBound, center );
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
b2Vec2 d = b2Sub( centroidAABB.upperBound, centroidAABB.lowerBound );
|
|
1574
|
+
|
|
1575
|
+
// Find longest axis
|
|
1576
|
+
int axisIndex;
|
|
1577
|
+
float invD;
|
|
1578
|
+
if ( d.x > d.y )
|
|
1579
|
+
{
|
|
1580
|
+
axisIndex = 0;
|
|
1581
|
+
invD = d.x;
|
|
1582
|
+
}
|
|
1583
|
+
else
|
|
1584
|
+
{
|
|
1585
|
+
axisIndex = 1;
|
|
1586
|
+
invD = d.y;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
invD = invD > 0.0f ? 1.0f / invD : 0.0f;
|
|
1590
|
+
|
|
1591
|
+
// Initialize bin bounds and count
|
|
1592
|
+
for ( int i = 0; i < B2_BIN_COUNT; ++i )
|
|
1593
|
+
{
|
|
1594
|
+
bins[i].aabb.lowerBound = ( b2Vec2 ){ FLT_MAX, FLT_MAX };
|
|
1595
|
+
bins[i].aabb.upperBound = ( b2Vec2 ){ -FLT_MAX, -FLT_MAX };
|
|
1596
|
+
bins[i].count = 0;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// Assign boxes to bins and compute bin boxes
|
|
1600
|
+
// TODO_ERIN optimize
|
|
1601
|
+
float binCount = B2_BIN_COUNT;
|
|
1602
|
+
float lowerBoundArray[2] = { centroidAABB.lowerBound.x, centroidAABB.lowerBound.y };
|
|
1603
|
+
float minC = lowerBoundArray[axisIndex];
|
|
1604
|
+
for ( int i = 0; i < count; ++i )
|
|
1605
|
+
{
|
|
1606
|
+
b2Vec2 c = b2AABB_Center( boxes[i] );
|
|
1607
|
+
float cArray[2] = { c.x, c.y };
|
|
1608
|
+
int binIndex = (int)( binCount * ( cArray[axisIndex] - minC ) * invD );
|
|
1609
|
+
binIndex = b2ClampInt( binIndex, 0, B2_BIN_COUNT - 1 );
|
|
1610
|
+
binIndices[i] = binIndex;
|
|
1611
|
+
bins[binIndex].count += 1;
|
|
1612
|
+
bins[binIndex].aabb = b2AABB_Union( bins[binIndex].aabb, boxes[i] );
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
int planeCount = B2_BIN_COUNT - 1;
|
|
1616
|
+
|
|
1617
|
+
// Prepare all the left planes, candidates for left child
|
|
1618
|
+
planes[0].leftCount = bins[0].count;
|
|
1619
|
+
planes[0].leftAABB = bins[0].aabb;
|
|
1620
|
+
for ( int i = 1; i < planeCount; ++i )
|
|
1621
|
+
{
|
|
1622
|
+
planes[i].leftCount = planes[i - 1].leftCount + bins[i].count;
|
|
1623
|
+
planes[i].leftAABB = b2AABB_Union( planes[i - 1].leftAABB, bins[i].aabb );
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
// Prepare all the right planes, candidates for right child
|
|
1627
|
+
planes[planeCount - 1].rightCount = bins[planeCount].count;
|
|
1628
|
+
planes[planeCount - 1].rightAABB = bins[planeCount].aabb;
|
|
1629
|
+
for ( int i = planeCount - 2; i >= 0; --i )
|
|
1630
|
+
{
|
|
1631
|
+
planes[i].rightCount = planes[i + 1].rightCount + bins[i + 1].count;
|
|
1632
|
+
planes[i].rightAABB = b2AABB_Union( planes[i + 1].rightAABB, bins[i + 1].aabb );
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
// Find best split to minimize SAH
|
|
1636
|
+
float minCost = FLT_MAX;
|
|
1637
|
+
int bestPlane = 0;
|
|
1638
|
+
for ( int i = 0; i < planeCount; ++i )
|
|
1639
|
+
{
|
|
1640
|
+
float leftArea = b2Perimeter( planes[i].leftAABB );
|
|
1641
|
+
float rightArea = b2Perimeter( planes[i].rightAABB );
|
|
1642
|
+
int leftCount = planes[i].leftCount;
|
|
1643
|
+
int rightCount = planes[i].rightCount;
|
|
1644
|
+
|
|
1645
|
+
float cost = leftCount * leftArea + rightCount * rightArea;
|
|
1646
|
+
if ( cost < minCost )
|
|
1647
|
+
{
|
|
1648
|
+
bestPlane = i;
|
|
1649
|
+
minCost = cost;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// Partition node indices and boxes using the Hoare partition scheme
|
|
1654
|
+
// https://en.wikipedia.org/wiki/Quicksort
|
|
1655
|
+
// https://nicholasvadivelu.com/2021/01/11/array-partition/
|
|
1656
|
+
int i1 = 0, i2 = count;
|
|
1657
|
+
while ( i1 < i2 )
|
|
1658
|
+
{
|
|
1659
|
+
while ( i1 < i2 && binIndices[i1] < bestPlane )
|
|
1660
|
+
{
|
|
1661
|
+
i1 += 1;
|
|
1662
|
+
};
|
|
1663
|
+
|
|
1664
|
+
while ( i1 < i2 && binIndices[i2 - 1] >= bestPlane )
|
|
1665
|
+
{
|
|
1666
|
+
i2 -= 1;
|
|
1667
|
+
};
|
|
1668
|
+
|
|
1669
|
+
if ( i1 < i2 )
|
|
1670
|
+
{
|
|
1671
|
+
// Swap indices
|
|
1672
|
+
{
|
|
1673
|
+
int temp = indices[i1];
|
|
1674
|
+
indices[i1] = indices[i2 - 1];
|
|
1675
|
+
indices[i2 - 1] = temp;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
// Swap boxes
|
|
1679
|
+
{
|
|
1680
|
+
b2AABB temp = boxes[i1];
|
|
1681
|
+
boxes[i1] = boxes[i2 - 1];
|
|
1682
|
+
boxes[i2 - 1] = temp;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
i1 += 1;
|
|
1686
|
+
i2 -= 1;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
B2_ASSERT( i1 == i2 );
|
|
1690
|
+
|
|
1691
|
+
if ( i1 > 0 && i1 < count )
|
|
1692
|
+
{
|
|
1693
|
+
return i1;
|
|
1694
|
+
}
|
|
1695
|
+
else
|
|
1696
|
+
{
|
|
1697
|
+
return count / 2;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
#endif
|
|
1702
|
+
|
|
1703
|
+
// Temporary data used to track the rebuild of a tree node
|
|
1704
|
+
struct b2RebuildItem
|
|
1705
|
+
{
|
|
1706
|
+
int nodeIndex;
|
|
1707
|
+
int childCount;
|
|
1708
|
+
|
|
1709
|
+
// Leaf indices
|
|
1710
|
+
int startIndex;
|
|
1711
|
+
int splitIndex;
|
|
1712
|
+
int endIndex;
|
|
1713
|
+
};
|
|
1714
|
+
|
|
1715
|
+
// Returns root node index
|
|
1716
|
+
static int b2BuildTree( b2DynamicTree* tree, int leafCount )
|
|
1717
|
+
{
|
|
1718
|
+
b2TreeNode* nodes = tree->nodes;
|
|
1719
|
+
int* leafIndices = tree->leafIndices;
|
|
1720
|
+
|
|
1721
|
+
if ( leafCount == 1 )
|
|
1722
|
+
{
|
|
1723
|
+
nodes[leafIndices[0]].parent = B2_NULL_INDEX;
|
|
1724
|
+
return leafIndices[0];
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
#if B2_TREE_HEURISTIC == 0
|
|
1728
|
+
b2Vec2* leafCenters = tree->leafCenters;
|
|
1729
|
+
#else
|
|
1730
|
+
b2AABB* leafBoxes = tree->leafBoxes;
|
|
1731
|
+
int* binIndices = tree->binIndices;
|
|
1732
|
+
#endif
|
|
1733
|
+
|
|
1734
|
+
// todo large stack item
|
|
1735
|
+
struct b2RebuildItem stack[B2_TREE_STACK_SIZE];
|
|
1736
|
+
int top = 0;
|
|
1737
|
+
|
|
1738
|
+
stack[0].nodeIndex = b2AllocateNode( tree );
|
|
1739
|
+
stack[0].childCount = -1;
|
|
1740
|
+
stack[0].startIndex = 0;
|
|
1741
|
+
stack[0].endIndex = leafCount;
|
|
1742
|
+
#if B2_TREE_HEURISTIC == 0
|
|
1743
|
+
stack[0].splitIndex = b2PartitionMid( leafIndices, leafCenters, leafCount );
|
|
1744
|
+
#else
|
|
1745
|
+
stack[0].splitIndex = b2PartitionSAH( leafIndices, binIndices, leafBoxes, leafCount );
|
|
1746
|
+
#endif
|
|
1747
|
+
|
|
1748
|
+
while ( true )
|
|
1749
|
+
{
|
|
1750
|
+
struct b2RebuildItem* item = stack + top;
|
|
1751
|
+
|
|
1752
|
+
item->childCount += 1;
|
|
1753
|
+
|
|
1754
|
+
if ( item->childCount == 2 )
|
|
1755
|
+
{
|
|
1756
|
+
// This internal node has both children established
|
|
1757
|
+
|
|
1758
|
+
if ( top == 0 )
|
|
1759
|
+
{
|
|
1760
|
+
// all done
|
|
1761
|
+
break;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
struct b2RebuildItem* parentItem = stack + ( top - 1 );
|
|
1765
|
+
b2TreeNode* parentNode = nodes + parentItem->nodeIndex;
|
|
1766
|
+
|
|
1767
|
+
if ( parentItem->childCount == 0 )
|
|
1768
|
+
{
|
|
1769
|
+
B2_ASSERT( parentNode->children.child1 == B2_NULL_INDEX );
|
|
1770
|
+
parentNode->children.child1 = item->nodeIndex;
|
|
1771
|
+
}
|
|
1772
|
+
else
|
|
1773
|
+
{
|
|
1774
|
+
B2_ASSERT( parentItem->childCount == 1 );
|
|
1775
|
+
B2_ASSERT( parentNode->children.child2 == B2_NULL_INDEX );
|
|
1776
|
+
parentNode->children.child2 = item->nodeIndex;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
b2TreeNode* node = nodes + item->nodeIndex;
|
|
1780
|
+
|
|
1781
|
+
B2_ASSERT( node->parent == B2_NULL_INDEX );
|
|
1782
|
+
node->parent = parentItem->nodeIndex;
|
|
1783
|
+
|
|
1784
|
+
B2_ASSERT( node->children.child1 != B2_NULL_INDEX );
|
|
1785
|
+
B2_ASSERT( node->children.child2 != B2_NULL_INDEX );
|
|
1786
|
+
b2TreeNode* child1 = nodes + node->children.child1;
|
|
1787
|
+
b2TreeNode* child2 = nodes + node->children.child2;
|
|
1788
|
+
|
|
1789
|
+
node->aabb = b2AABB_Union( child1->aabb, child2->aabb );
|
|
1790
|
+
node->height = 1 + b2MaxUInt16( child1->height, child2->height );
|
|
1791
|
+
node->categoryBits = child1->categoryBits | child2->categoryBits;
|
|
1792
|
+
|
|
1793
|
+
// Pop stack
|
|
1794
|
+
top -= 1;
|
|
1795
|
+
}
|
|
1796
|
+
else
|
|
1797
|
+
{
|
|
1798
|
+
int startIndex, endIndex;
|
|
1799
|
+
if ( item->childCount == 0 )
|
|
1800
|
+
{
|
|
1801
|
+
startIndex = item->startIndex;
|
|
1802
|
+
endIndex = item->splitIndex;
|
|
1803
|
+
}
|
|
1804
|
+
else
|
|
1805
|
+
{
|
|
1806
|
+
B2_ASSERT( item->childCount == 1 );
|
|
1807
|
+
startIndex = item->splitIndex;
|
|
1808
|
+
endIndex = item->endIndex;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
int count = endIndex - startIndex;
|
|
1812
|
+
|
|
1813
|
+
if ( count == 1 )
|
|
1814
|
+
{
|
|
1815
|
+
int childIndex = leafIndices[startIndex];
|
|
1816
|
+
b2TreeNode* node = nodes + item->nodeIndex;
|
|
1817
|
+
|
|
1818
|
+
if ( item->childCount == 0 )
|
|
1819
|
+
{
|
|
1820
|
+
B2_ASSERT( node->children.child1 == B2_NULL_INDEX );
|
|
1821
|
+
node->children.child1 = childIndex;
|
|
1822
|
+
}
|
|
1823
|
+
else
|
|
1824
|
+
{
|
|
1825
|
+
B2_ASSERT( item->childCount == 1 );
|
|
1826
|
+
B2_ASSERT( node->children.child2 == B2_NULL_INDEX );
|
|
1827
|
+
node->children.child2 = childIndex;
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
b2TreeNode* childNode = nodes + childIndex;
|
|
1831
|
+
B2_ASSERT( childNode->parent == B2_NULL_INDEX );
|
|
1832
|
+
childNode->parent = item->nodeIndex;
|
|
1833
|
+
}
|
|
1834
|
+
else
|
|
1835
|
+
{
|
|
1836
|
+
B2_ASSERT( count > 0 );
|
|
1837
|
+
B2_ASSERT( top < B2_TREE_STACK_SIZE );
|
|
1838
|
+
|
|
1839
|
+
top += 1;
|
|
1840
|
+
struct b2RebuildItem* newItem = stack + top;
|
|
1841
|
+
newItem->nodeIndex = b2AllocateNode( tree );
|
|
1842
|
+
newItem->childCount = -1;
|
|
1843
|
+
newItem->startIndex = startIndex;
|
|
1844
|
+
newItem->endIndex = endIndex;
|
|
1845
|
+
#if B2_TREE_HEURISTIC == 0
|
|
1846
|
+
newItem->splitIndex = b2PartitionMid( leafIndices + startIndex, leafCenters + startIndex, count );
|
|
1847
|
+
#else
|
|
1848
|
+
newItem->splitIndex =
|
|
1849
|
+
b2PartitionSAH( leafIndices + startIndex, binIndices + startIndex, leafBoxes + startIndex, count );
|
|
1850
|
+
#endif
|
|
1851
|
+
newItem->splitIndex += startIndex;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
b2TreeNode* rootNode = nodes + stack[0].nodeIndex;
|
|
1857
|
+
B2_ASSERT( rootNode->parent == B2_NULL_INDEX );
|
|
1858
|
+
B2_ASSERT( rootNode->children.child1 != B2_NULL_INDEX );
|
|
1859
|
+
B2_ASSERT( rootNode->children.child2 != B2_NULL_INDEX );
|
|
1860
|
+
|
|
1861
|
+
b2TreeNode* child1 = nodes + rootNode->children.child1;
|
|
1862
|
+
b2TreeNode* child2 = nodes + rootNode->children.child2;
|
|
1863
|
+
|
|
1864
|
+
rootNode->aabb = b2AABB_Union( child1->aabb, child2->aabb );
|
|
1865
|
+
rootNode->height = 1 + b2MaxUInt16( child1->height, child2->height );
|
|
1866
|
+
rootNode->categoryBits = child1->categoryBits | child2->categoryBits;
|
|
1867
|
+
|
|
1868
|
+
return stack[0].nodeIndex;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
// Not safe to access tree during this operation because it may grow
|
|
1872
|
+
int b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild )
|
|
1873
|
+
{
|
|
1874
|
+
int proxyCount = tree->proxyCount;
|
|
1875
|
+
if ( proxyCount == 0 )
|
|
1876
|
+
{
|
|
1877
|
+
return 0;
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
// Ensure capacity for rebuild space
|
|
1881
|
+
if ( proxyCount > tree->rebuildCapacity )
|
|
1882
|
+
{
|
|
1883
|
+
int newCapacity = proxyCount + proxyCount / 2;
|
|
1884
|
+
|
|
1885
|
+
b2Free( tree->leafIndices, tree->rebuildCapacity * sizeof( int ) );
|
|
1886
|
+
tree->leafIndices = b2Alloc( newCapacity * sizeof( int ) );
|
|
1887
|
+
|
|
1888
|
+
#if B2_TREE_HEURISTIC == 0
|
|
1889
|
+
b2Free( tree->leafCenters, tree->rebuildCapacity * sizeof( b2Vec2 ) );
|
|
1890
|
+
tree->leafCenters = b2Alloc( newCapacity * sizeof( b2Vec2 ) );
|
|
1891
|
+
#else
|
|
1892
|
+
b2Free( tree->leafBoxes, tree->rebuildCapacity * sizeof( b2AABB ) );
|
|
1893
|
+
tree->leafBoxes = b2Alloc( newCapacity * sizeof( b2AABB ) );
|
|
1894
|
+
b2Free( tree->binIndices, tree->rebuildCapacity * sizeof( int ) );
|
|
1895
|
+
tree->binIndices = b2Alloc( newCapacity * sizeof( int ) );
|
|
1896
|
+
#endif
|
|
1897
|
+
tree->rebuildCapacity = newCapacity;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
int leafCount = 0;
|
|
1901
|
+
int stack[B2_TREE_STACK_SIZE];
|
|
1902
|
+
int stackCount = 0;
|
|
1903
|
+
|
|
1904
|
+
int nodeIndex = tree->root;
|
|
1905
|
+
b2TreeNode* nodes = tree->nodes;
|
|
1906
|
+
b2TreeNode* node = nodes + nodeIndex;
|
|
1907
|
+
|
|
1908
|
+
// These are the nodes that get sorted to rebuild the tree.
|
|
1909
|
+
// I'm using indices because the node pool may grow during the build.
|
|
1910
|
+
int* leafIndices = tree->leafIndices;
|
|
1911
|
+
|
|
1912
|
+
#if B2_TREE_HEURISTIC == 0
|
|
1913
|
+
b2Vec2* leafCenters = tree->leafCenters;
|
|
1914
|
+
#else
|
|
1915
|
+
b2AABB* leafBoxes = tree->leafBoxes;
|
|
1916
|
+
#endif
|
|
1917
|
+
|
|
1918
|
+
// Gather all proxy nodes that have grown and all internal nodes that haven't grown. Both are
|
|
1919
|
+
// considered leaves in the tree rebuild.
|
|
1920
|
+
// Free all internal nodes that have grown.
|
|
1921
|
+
// todo use a node growth metric instead of simply enlarged to reduce rebuild size and frequency
|
|
1922
|
+
// this should be weighed against B2_AABB_MARGIN
|
|
1923
|
+
while ( true )
|
|
1924
|
+
{
|
|
1925
|
+
if ( node->height == 0 || ( ( node->flags & b2_enlargedNode ) == 0 && fullBuild == false ) )
|
|
1926
|
+
{
|
|
1927
|
+
leafIndices[leafCount] = nodeIndex;
|
|
1928
|
+
#if B2_TREE_HEURISTIC == 0
|
|
1929
|
+
leafCenters[leafCount] = b2AABB_Center( node->aabb );
|
|
1930
|
+
#else
|
|
1931
|
+
leafBoxes[leafCount] = node->aabb;
|
|
1932
|
+
#endif
|
|
1933
|
+
leafCount += 1;
|
|
1934
|
+
|
|
1935
|
+
// Detach
|
|
1936
|
+
node->parent = B2_NULL_INDEX;
|
|
1937
|
+
}
|
|
1938
|
+
else
|
|
1939
|
+
{
|
|
1940
|
+
int doomedNodeIndex = nodeIndex;
|
|
1941
|
+
|
|
1942
|
+
// Handle children
|
|
1943
|
+
nodeIndex = node->children.child1;
|
|
1944
|
+
|
|
1945
|
+
if ( stackCount < B2_TREE_STACK_SIZE )
|
|
1946
|
+
{
|
|
1947
|
+
stack[stackCount++] = node->children.child2;
|
|
1948
|
+
}
|
|
1949
|
+
else
|
|
1950
|
+
{
|
|
1951
|
+
B2_ASSERT( stackCount < B2_TREE_STACK_SIZE );
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
node = nodes + nodeIndex;
|
|
1955
|
+
|
|
1956
|
+
// Remove doomed node
|
|
1957
|
+
b2FreeNode( tree, doomedNodeIndex );
|
|
1958
|
+
|
|
1959
|
+
continue;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
if ( stackCount == 0 )
|
|
1963
|
+
{
|
|
1964
|
+
break;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
nodeIndex = stack[--stackCount];
|
|
1968
|
+
node = nodes + nodeIndex;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
#if B2_VALIDATE == 1
|
|
1972
|
+
int capacity = tree->nodeCapacity;
|
|
1973
|
+
for ( int i = 0; i < capacity; ++i )
|
|
1974
|
+
{
|
|
1975
|
+
if ( nodes[i].flags & b2_allocatedNode )
|
|
1976
|
+
{
|
|
1977
|
+
B2_ASSERT( ( nodes[i].flags & b2_enlargedNode ) == 0 );
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
#endif
|
|
1981
|
+
|
|
1982
|
+
B2_ASSERT( leafCount <= proxyCount );
|
|
1983
|
+
|
|
1984
|
+
tree->root = b2BuildTree( tree, leafCount );
|
|
1985
|
+
|
|
1986
|
+
b2DynamicTree_Validate( tree );
|
|
1987
|
+
|
|
1988
|
+
return leafCount;
|
|
1989
|
+
}
|