#include "aabb.h"
#include "core.h"
#include "joint.h"
#include "simd.h"
#include "box3d/collision.h"
#include "box3d/constants.h"
#include "box3d/math_functions.h"
#include <float.h>
#include <stdio.h>
#include <string.h>
#define B3_TREE_STACK_SIZE 1024
static b3TreeNode b3_defaultTreeNode = {
.aabb = { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } },
.categoryBits = B3_DEFAULT_CATEGORY_BITS,
.children =
{
.child1 = B3_NULL_INDEX,
.child2 = B3_NULL_INDEX,
},
.parent = B3_NULL_INDEX,
.height = 0,
.flags = b3_allocatedNode,
};
static inline bool b3IsLeaf( const b3TreeNode* node )
{
return node->flags & b3_leafNode;
}
static inline bool b3IsAllocated( const b3TreeNode* node )
{
return node->flags & b3_allocatedNode;
}
static inline uint16_t b3MaxUInt16( uint16_t a, uint16_t b )
{
return a > b ? a : b;
}
b3DynamicTree b3DynamicTree_Create( int proxyCapacity )
{
int capacity = b3MaxInt( proxyCapacity, 16 );
b3DynamicTree tree;
memset( &tree, 0, sizeof( b3DynamicTree ) );
tree.version = B3_DYNAMIC_TREE_VERSION;
tree.root = B3_NULL_INDEX;
tree.nodeCapacity = 2 * capacity - 1;
tree.nodeCount = 0;
tree.nodes = (b3TreeNode*)b3Alloc( tree.nodeCapacity * sizeof( b3TreeNode ) );
B3_VALIDATE( ( (uintptr_t)tree.nodes & 0x3F ) == 0 );
memset( tree.nodes, 0, tree.nodeCapacity * sizeof( b3TreeNode ) );
for ( int i = 0; i < tree.nodeCapacity - 1; ++i )
{
tree.nodes[i].next = i + 1;
}
tree.nodes[tree.nodeCapacity - 1].next = B3_NULL_INDEX;
tree.freeList = 0;
tree.proxyCount = 0;
tree.leafIndices = NULL;
tree.leafBoxes = NULL;
tree.leafCenters = NULL;
tree.binIndices = NULL;
tree.rebuildCapacity = 0;
return tree;
}
void b3DynamicTree_Destroy( b3DynamicTree* tree )
{
b3Free( tree->nodes, tree->nodeCapacity * sizeof( b3TreeNode ) );
b3Free( tree->leafIndices, tree->rebuildCapacity * sizeof( int ) );
b3Free( tree->leafBoxes, tree->rebuildCapacity * sizeof( b3AABB ) );
b3Free( tree->leafCenters, tree->rebuildCapacity * sizeof( b3Vec3 ) );
b3Free( tree->binIndices, tree->rebuildCapacity * sizeof( int ) );
memset( tree, 0, sizeof( b3DynamicTree ) );
}
static int b3AllocateNode( b3DynamicTree* tree )
{
if ( tree->freeList == B3_NULL_INDEX )
{
B3_ASSERT( tree->nodeCount == tree->nodeCapacity );
b3TreeNode* oldNodes = tree->nodes;
int oldCapacity = tree->nodeCapacity;
tree->nodeCapacity += oldCapacity >> 1;
tree->nodes = (b3TreeNode*)b3Alloc( tree->nodeCapacity * sizeof( b3TreeNode ) );
B3_ASSERT( oldNodes != NULL );
memcpy( tree->nodes, oldNodes, tree->nodeCount * sizeof( b3TreeNode ) );
memset( tree->nodes + tree->nodeCount, 0, ( tree->nodeCapacity - tree->nodeCount ) * sizeof( b3TreeNode ) );
b3Free( oldNodes, oldCapacity * sizeof( b3TreeNode ) );
for ( int i = tree->nodeCount; i < tree->nodeCapacity - 1; ++i )
{
tree->nodes[i].next = i + 1;
}
tree->nodes[tree->nodeCapacity - 1].next = B3_NULL_INDEX;
tree->freeList = tree->nodeCount;
}
int nodeIndex = tree->freeList;
b3TreeNode* node = tree->nodes + nodeIndex;
tree->freeList = node->next;
*node = b3_defaultTreeNode;
++tree->nodeCount;
return nodeIndex;
}
static void b3FreeNode( b3DynamicTree* tree, int nodeId )
{
B3_ASSERT( 0 <= nodeId && nodeId < tree->nodeCapacity );
B3_ASSERT( 0 < tree->nodeCount );
tree->nodes[nodeId].next = tree->freeList;
tree->nodes[nodeId].flags = 0;
tree->freeList = nodeId;
--tree->nodeCount;
}
static int b3FindBestSibling( const b3DynamicTree* tree, b3AABB boxD )
{
b3Vec3 centerD = b3AABB_Center( boxD );
float areaD = b3Perimeter( boxD );
const b3TreeNode* nodes = tree->nodes;
int rootIndex = tree->root;
b3AABB rootBox = nodes[rootIndex].aabb;
float areaBase = b3Perimeter( rootBox );
float directCost = b3Perimeter( b3AABB_Union( rootBox, boxD ) );
float inheritedCost = 0.0f;
int bestSibling = rootIndex;
float bestCost = directCost;
int index = rootIndex;
while ( b3IsLeaf( nodes + index ) == false )
{
int child1 = nodes[index].children.child1;
int child2 = nodes[index].children.child2;
float cost = directCost + inheritedCost;
if ( cost < bestCost )
{
bestSibling = index;
bestCost = cost;
}
inheritedCost += directCost - areaBase;
bool leaf1 = b3IsLeaf( nodes + child1 );
bool leaf2 = b3IsLeaf( nodes + child2 );
float lowerCost1 = FLT_MAX;
b3AABB box1 = nodes[child1].aabb;
float directCost1 = b3Perimeter( b3AABB_Union( box1, boxD ) );
float area1 = 0.0f;
if ( leaf1 )
{
float cost1 = directCost1 + inheritedCost;
if ( cost1 < bestCost )
{
bestSibling = child1;
bestCost = cost1;
}
}
else
{
area1 = b3Perimeter( box1 );
lowerCost1 = inheritedCost + directCost1 + b3MinFloat( areaD - area1, 0.0f );
}
float lowerCost2 = FLT_MAX;
b3AABB box2 = nodes[child2].aabb;
float directCost2 = b3Perimeter( b3AABB_Union( box2, boxD ) );
float area2 = 0.0f;
if ( leaf2 )
{
float cost2 = directCost2 + inheritedCost;
if ( cost2 < bestCost )
{
bestSibling = child2;
bestCost = cost2;
}
}
else
{
area2 = b3Perimeter( box2 );
lowerCost2 = inheritedCost + directCost2 + b3MinFloat( areaD - area2, 0.0f );
}
if ( leaf1 && leaf2 )
{
break;
}
if ( bestCost <= lowerCost1 && bestCost <= lowerCost2 )
{
break;
}
if ( lowerCost1 == lowerCost2 && leaf1 == false )
{
B3_ASSERT( lowerCost1 < FLT_MAX );
B3_ASSERT( lowerCost2 < FLT_MAX );
b3Vec3 d1 = b3Sub( b3AABB_Center( box1 ), centerD );
b3Vec3 d2 = b3Sub( b3AABB_Center( box2 ), centerD );
lowerCost1 = b3LengthSquared( d1 );
lowerCost2 = b3LengthSquared( d2 );
}
if ( lowerCost1 < lowerCost2 && leaf1 == false )
{
index = child1;
areaBase = area1;
directCost = directCost1;
}
else
{
index = child2;
areaBase = area2;
directCost = directCost2;
}
B3_ASSERT( b3IsLeaf( nodes + index ) == false );
}
return bestSibling;
}
enum b3RotateType
{
b3_rotateNone,
b3_rotateBF,
b3_rotateBG,
b3_rotateCD,
b3_rotateCE
};
static void b3RotateNodes( b3DynamicTree* tree, int iA )
{
B3_ASSERT( iA != B3_NULL_INDEX );
b3TreeNode* nodes = tree->nodes;
b3TreeNode* A = nodes + iA;
if ( b3IsLeaf( A ) == true )
{
return;
}
int iB = A->children.child1;
int iC = A->children.child2;
B3_ASSERT( 0 <= iB && iB < tree->nodeCapacity );
B3_ASSERT( 0 <= iC && iC < tree->nodeCapacity );
b3TreeNode* B = nodes + iB;
b3TreeNode* C = nodes + iC;
bool isLeafB = b3IsLeaf( B );
bool isLeafC = b3IsLeaf( C );
if ( isLeafB == true && isLeafC == false )
{
int iF = C->children.child1;
int iG = C->children.child2;
b3TreeNode* F = nodes + iF;
b3TreeNode* G = nodes + iG;
B3_ASSERT( 0 <= iF && iF < tree->nodeCapacity );
B3_ASSERT( 0 <= iG && iG < tree->nodeCapacity );
float costBase = b3Perimeter( C->aabb );
b3AABB aabbBG = b3AABB_Union( B->aabb, G->aabb );
float costBF = b3Perimeter( aabbBG );
b3AABB aabbBF = b3AABB_Union( B->aabb, F->aabb );
float costBG = b3Perimeter( aabbBF );
if ( costBase < costBF && costBase < costBG )
{
return;
}
if ( costBF < costBG )
{
A->children.child1 = iF;
C->children.child1 = iB;
B->parent = iC;
F->parent = iA;
C->aabb = aabbBG;
C->height = 1 + b3MaxUInt16( B->height, G->height );
A->height = 1 + b3MaxUInt16( C->height, F->height );
C->categoryBits = B->categoryBits | G->categoryBits;
A->categoryBits = C->categoryBits | F->categoryBits;
C->flags |= ( B->flags | G->flags ) & b3_enlargedNode;
A->flags |= ( C->flags | F->flags ) & b3_enlargedNode;
}
else
{
A->children.child1 = iG;
C->children.child2 = iB;
B->parent = iC;
G->parent = iA;
C->aabb = aabbBF;
C->height = 1 + b3MaxUInt16( B->height, F->height );
A->height = 1 + b3MaxUInt16( C->height, G->height );
C->categoryBits = B->categoryBits | F->categoryBits;
A->categoryBits = C->categoryBits | G->categoryBits;
C->flags |= ( B->flags | F->flags ) & b3_enlargedNode;
A->flags |= ( C->flags | G->flags ) & b3_enlargedNode;
}
}
else if ( isLeafC == true && isLeafB == false )
{
int iD = B->children.child1;
int iE = B->children.child2;
b3TreeNode* D = nodes + iD;
b3TreeNode* E = nodes + iE;
B3_ASSERT( 0 <= iD && iD < tree->nodeCapacity );
B3_ASSERT( 0 <= iE && iE < tree->nodeCapacity );
float costBase = b3Perimeter( B->aabb );
b3AABB aabbCE = b3AABB_Union( C->aabb, E->aabb );
float costCD = b3Perimeter( aabbCE );
b3AABB aabbCD = b3AABB_Union( C->aabb, D->aabb );
float costCE = b3Perimeter( aabbCD );
if ( costBase < costCD && costBase < costCE )
{
return;
}
if ( costCD < costCE )
{
A->children.child2 = iD;
B->children.child1 = iC;
C->parent = iB;
D->parent = iA;
B->aabb = aabbCE;
B->height = 1 + b3MaxUInt16( C->height, E->height );
A->height = 1 + b3MaxUInt16( B->height, D->height );
B->categoryBits = C->categoryBits | E->categoryBits;
A->categoryBits = B->categoryBits | D->categoryBits;
B->flags |= ( C->flags | E->flags ) & b3_enlargedNode;
A->flags |= ( B->flags | D->flags ) & b3_enlargedNode;
}
else
{
A->children.child2 = iE;
B->children.child2 = iC;
C->parent = iB;
E->parent = iA;
B->aabb = aabbCD;
B->height = 1 + b3MaxUInt16( C->height, D->height );
A->height = 1 + b3MaxUInt16( B->height, E->height );
B->categoryBits = C->categoryBits | D->categoryBits;
A->categoryBits = B->categoryBits | E->categoryBits;
B->flags |= ( C->flags | D->flags ) & b3_enlargedNode;
A->flags |= ( B->flags | E->flags ) & b3_enlargedNode;
}
}
else if ( isLeafB == false && isLeafC == false )
{
int iD = B->children.child1;
int iE = B->children.child2;
int iF = C->children.child1;
int iG = C->children.child2;
B3_ASSERT( 0 <= iD && iD < tree->nodeCapacity );
B3_ASSERT( 0 <= iE && iE < tree->nodeCapacity );
B3_ASSERT( 0 <= iF && iF < tree->nodeCapacity );
B3_ASSERT( 0 <= iG && iG < tree->nodeCapacity );
b3TreeNode* D = nodes + iD;
b3TreeNode* E = nodes + iE;
b3TreeNode* F = nodes + iF;
b3TreeNode* G = nodes + iG;
float areaB = b3Perimeter( B->aabb );
float areaC = b3Perimeter( C->aabb );
float costBase = areaB + areaC;
enum b3RotateType bestRotation = b3_rotateNone;
float bestCost = costBase;
b3AABB aabbBG = b3AABB_Union( B->aabb, G->aabb );
float costBF = areaB + b3Perimeter( aabbBG );
if ( costBF < bestCost )
{
bestRotation = b3_rotateBF;
bestCost = costBF;
}
b3AABB aabbBF = b3AABB_Union( B->aabb, F->aabb );
float costBG = areaB + b3Perimeter( aabbBF );
if ( costBG < bestCost )
{
bestRotation = b3_rotateBG;
bestCost = costBG;
}
b3AABB aabbCE = b3AABB_Union( C->aabb, E->aabb );
float costCD = areaC + b3Perimeter( aabbCE );
if ( costCD < bestCost )
{
bestRotation = b3_rotateCD;
bestCost = costCD;
}
b3AABB aabbCD = b3AABB_Union( C->aabb, D->aabb );
float costCE = areaC + b3Perimeter( aabbCD );
if ( costCE < bestCost )
{
bestRotation = b3_rotateCE;
}
switch ( bestRotation )
{
case b3_rotateNone:
break;
case b3_rotateBF:
A->children.child1 = iF;
C->children.child1 = iB;
B->parent = iC;
F->parent = iA;
C->aabb = aabbBG;
C->height = 1 + b3MaxUInt16( B->height, G->height );
A->height = 1 + b3MaxUInt16( C->height, F->height );
C->categoryBits = B->categoryBits | G->categoryBits;
A->categoryBits = C->categoryBits | F->categoryBits;
C->flags |= ( B->flags | G->flags ) & b3_enlargedNode;
A->flags |= ( C->flags | F->flags ) & b3_enlargedNode;
break;
case b3_rotateBG:
A->children.child1 = iG;
C->children.child2 = iB;
B->parent = iC;
G->parent = iA;
C->aabb = aabbBF;
C->height = 1 + b3MaxUInt16( B->height, F->height );
A->height = 1 + b3MaxUInt16( C->height, G->height );
C->categoryBits = B->categoryBits | F->categoryBits;
A->categoryBits = C->categoryBits | G->categoryBits;
C->flags |= ( B->flags | F->flags ) & b3_enlargedNode;
A->flags |= ( C->flags | G->flags ) & b3_enlargedNode;
break;
case b3_rotateCD:
A->children.child2 = iD;
B->children.child1 = iC;
C->parent = iB;
D->parent = iA;
B->aabb = aabbCE;
B->height = 1 + b3MaxUInt16( C->height, E->height );
A->height = 1 + b3MaxUInt16( B->height, D->height );
B->categoryBits = C->categoryBits | E->categoryBits;
A->categoryBits = B->categoryBits | D->categoryBits;
B->flags |= ( C->flags | E->flags ) & b3_enlargedNode;
A->flags |= ( B->flags | D->flags ) & b3_enlargedNode;
break;
case b3_rotateCE:
A->children.child2 = iE;
B->children.child2 = iC;
C->parent = iB;
E->parent = iA;
B->aabb = aabbCD;
B->height = 1 + b3MaxUInt16( C->height, D->height );
A->height = 1 + b3MaxUInt16( B->height, E->height );
B->categoryBits = C->categoryBits | D->categoryBits;
A->categoryBits = B->categoryBits | E->categoryBits;
B->flags |= ( C->flags | D->flags ) & b3_enlargedNode;
A->flags |= ( B->flags | E->flags ) & b3_enlargedNode;
break;
default:
B3_ASSERT( false );
break;
}
}
}
static void b3InsertLeaf( b3DynamicTree* tree, int leaf, bool shouldRotate )
{
if ( tree->root == B3_NULL_INDEX )
{
tree->root = leaf;
tree->nodes[tree->root].parent = B3_NULL_INDEX;
return;
}
b3AABB leafAABB = tree->nodes[leaf].aabb;
int sibling = b3FindBestSibling( tree, leafAABB );
int oldParent = tree->nodes[sibling].parent;
int newParent = b3AllocateNode( tree );
b3TreeNode* nodes = tree->nodes;
nodes[newParent].parent = oldParent;
nodes[newParent].userData = UINT64_MAX;
nodes[newParent].aabb = b3AABB_Union( leafAABB, nodes[sibling].aabb );
nodes[newParent].categoryBits = nodes[leaf].categoryBits | nodes[sibling].categoryBits;
nodes[newParent].height = nodes[sibling].height + 1;
if ( oldParent != B3_NULL_INDEX )
{
if ( nodes[oldParent].children.child1 == sibling )
{
nodes[oldParent].children.child1 = newParent;
}
else
{
nodes[oldParent].children.child2 = newParent;
}
nodes[newParent].children.child1 = sibling;
nodes[newParent].children.child2 = leaf;
nodes[sibling].parent = newParent;
nodes[leaf].parent = newParent;
}
else
{
nodes[newParent].children.child1 = sibling;
nodes[newParent].children.child2 = leaf;
nodes[sibling].parent = newParent;
nodes[leaf].parent = newParent;
tree->root = newParent;
}
int index = nodes[leaf].parent;
while ( index != B3_NULL_INDEX )
{
int child1 = nodes[index].children.child1;
int child2 = nodes[index].children.child2;
B3_ASSERT( child1 != B3_NULL_INDEX );
B3_ASSERT( child2 != B3_NULL_INDEX );
nodes[index].aabb = b3AABB_Union( nodes[child1].aabb, nodes[child2].aabb );
nodes[index].categoryBits = nodes[child1].categoryBits | nodes[child2].categoryBits;
nodes[index].height = 1 + b3MaxUInt16( nodes[child1].height, nodes[child2].height );
nodes[index].flags |= ( nodes[child1].flags | nodes[child2].flags ) & b3_enlargedNode;
if ( shouldRotate )
{
b3RotateNodes( tree, index );
}
index = nodes[index].parent;
}
}
static void b3RemoveLeaf( b3DynamicTree* tree, int leaf )
{
if ( leaf == tree->root )
{
tree->root = B3_NULL_INDEX;
return;
}
b3TreeNode* nodes = tree->nodes;
int parent = nodes[leaf].parent;
int grandParent = nodes[parent].parent;
int sibling;
if ( nodes[parent].children.child1 == leaf )
{
sibling = nodes[parent].children.child2;
}
else
{
sibling = nodes[parent].children.child1;
}
if ( grandParent != B3_NULL_INDEX )
{
if ( nodes[grandParent].children.child1 == parent )
{
nodes[grandParent].children.child1 = sibling;
}
else
{
nodes[grandParent].children.child2 = sibling;
}
nodes[sibling].parent = grandParent;
b3FreeNode( tree, parent );
int index = grandParent;
while ( index != B3_NULL_INDEX )
{
b3TreeNode* node = nodes + index;
b3TreeNode* child1 = nodes + node->children.child1;
b3TreeNode* child2 = nodes + node->children.child2;
node->aabb = b3AABB_Union( child1->aabb, child2->aabb );
node->categoryBits = child1->categoryBits | child2->categoryBits;
node->height = 1 + b3MaxUInt16( child1->height, child2->height );
index = node->parent;
}
}
else
{
tree->root = sibling;
tree->nodes[sibling].parent = B3_NULL_INDEX;
b3FreeNode( tree, parent );
}
}
int b3DynamicTree_CreateProxy( b3DynamicTree* tree, b3AABB aabb, uint64_t categoryBits, uint64_t userData )
{
B3_ASSERT( b3IsValidAABB( aabb ) );
int proxyId = b3AllocateNode( tree );
b3TreeNode* node = tree->nodes + proxyId;
node->aabb = aabb;
node->userData = userData;
node->categoryBits = categoryBits;
node->height = 0;
node->flags = b3_allocatedNode | b3_leafNode;
bool shouldRotate = true;
b3InsertLeaf( tree, proxyId, shouldRotate );
tree->proxyCount += 1;
return proxyId;
}
void b3DynamicTree_DestroyProxy( b3DynamicTree* tree, int proxyId )
{
B3_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
B3_ASSERT( b3IsLeaf( tree->nodes + proxyId ) );
b3RemoveLeaf( tree, proxyId );
b3FreeNode( tree, proxyId );
B3_ASSERT( tree->proxyCount > 0 );
tree->proxyCount -= 1;
}
int b3DynamicTree_GetProxyCount( const b3DynamicTree* tree )
{
return tree->proxyCount;
}
void b3DynamicTree_MoveProxy( b3DynamicTree* tree, int proxyId, b3AABB aabb )
{
B3_ASSERT( b3IsValidAABB( aabb ) );
B3_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
B3_ASSERT( b3IsLeaf( tree->nodes + proxyId ) );
b3RemoveLeaf( tree, proxyId );
tree->nodes[proxyId].aabb = aabb;
bool shouldRotate = false;
b3InsertLeaf( tree, proxyId, shouldRotate );
}
void b3DynamicTree_EnlargeProxy( b3DynamicTree* tree, int proxyId, b3AABB aabb )
{
b3TreeNode* nodes = tree->nodes;
B3_VALIDATE( b3IsValidAABB( aabb ) );
B3_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
B3_VALIDATE( b3IsLeaf( tree->nodes + proxyId ) );
B3_VALIDATE( b3AABB_Contains( nodes[proxyId].aabb, aabb ) == false );
b3TreeNode* node = nodes + proxyId;
node->aabb = aabb;
int parentIndex = node->parent;
while ( parentIndex != B3_NULL_INDEX )
{
node = nodes + parentIndex;
bool changed = b3EnlargeAABB( &node->aabb, aabb );
node->flags |= b3_enlargedNode;
parentIndex = node->parent;
if ( changed == false )
{
break;
}
}
while ( parentIndex != B3_NULL_INDEX )
{
node = nodes + parentIndex;
if ( node->flags & b3_enlargedNode )
{
break;
}
node->flags |= b3_enlargedNode;
parentIndex = node->parent;
}
}
void b3DynamicTree_SetCategoryBits( b3DynamicTree* tree, int proxyId, uint64_t categoryBits )
{
b3TreeNode* nodes = tree->nodes;
B3_ASSERT( b3IsLeaf( nodes + proxyId ) );
nodes[proxyId].categoryBits = categoryBits;
int nodeIndex = nodes[proxyId].parent;
while ( nodeIndex != B3_NULL_INDEX )
{
b3TreeNode* node = nodes + nodeIndex;
int child1 = node->children.child1;
B3_ASSERT( child1 != B3_NULL_INDEX );
int child2 = node->children.child2;
B3_ASSERT( child2 != B3_NULL_INDEX );
node->categoryBits = nodes[child1].categoryBits | nodes[child2].categoryBits;
nodeIndex = node->parent;
}
}
uint64_t b3DynamicTree_GetCategoryBits( b3DynamicTree* tree, int proxyId )
{
B3_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
return tree->nodes[proxyId].categoryBits;
}
int b3DynamicTree_GetHeight( const b3DynamicTree* tree )
{
if ( tree->root == B3_NULL_INDEX )
{
return 0;
}
return tree->nodes[tree->root].height;
}
float b3DynamicTree_GetAreaRatio( const b3DynamicTree* tree )
{
if ( tree->root == B3_NULL_INDEX )
{
return 0.0f;
}
const b3TreeNode* root = tree->nodes + tree->root;
float rootArea = b3Perimeter( root->aabb );
float totalArea = 0.0f;
for ( int i = 0; i < tree->nodeCapacity; ++i )
{
const b3TreeNode* node = tree->nodes + i;
if ( b3IsAllocated( node ) == false || b3IsLeaf( node ) || i == tree->root )
{
continue;
}
totalArea += b3Perimeter( node->aabb );
}
return totalArea / rootArea;
}
b3AABB b3DynamicTree_GetRootBounds( const b3DynamicTree* tree )
{
if ( tree->root != B3_NULL_INDEX )
{
return tree->nodes[tree->root].aabb;
}
b3AABB empty = { b3Vec3_zero, b3Vec3_zero };
return empty;
}
#if B3_ENABLE_VALIDATION
static int b3ComputeHeightRecurse( const b3DynamicTree* tree, int nodeId )
{
B3_ASSERT( 0 <= nodeId && nodeId < tree->nodeCapacity );
b3TreeNode* node = tree->nodes + nodeId;
if ( b3IsLeaf( node ) )
{
return 0;
}
int height1 = b3ComputeHeightRecurse( tree, node->children.child1 );
int height2 = b3ComputeHeightRecurse( tree, node->children.child2 );
return 1 + b3MaxInt( height1, height2 );
}
static int b3ComputeHeight( const b3DynamicTree* tree )
{
int height = b3ComputeHeightRecurse( tree, tree->root );
return height;
}
static void b3ValidateStructure( const b3DynamicTree* tree, int index )
{
if ( index == B3_NULL_INDEX )
{
return;
}
if ( index == tree->root )
{
B3_ASSERT( tree->nodes[index].parent == B3_NULL_INDEX );
}
const b3TreeNode* node = tree->nodes + index;
B3_ASSERT( node->flags == 0 || ( node->flags & b3_allocatedNode ) != 0 );
if ( b3IsLeaf( node ) )
{
B3_ASSERT( node->height == 0 );
return;
}
int child1 = node->children.child1;
int child2 = node->children.child2;
B3_ASSERT( 0 <= child1 && child1 < tree->nodeCapacity );
B3_ASSERT( 0 <= child2 && child2 < tree->nodeCapacity );
B3_ASSERT( tree->nodes[child1].parent == index );
B3_ASSERT( tree->nodes[child2].parent == index );
if ( ( tree->nodes[child1].flags | tree->nodes[child2].flags ) & b3_enlargedNode )
{
B3_ASSERT( node->flags & b3_enlargedNode );
}
b3ValidateStructure( tree, child1 );
b3ValidateStructure( tree, child2 );
}
static void b3ValidateMetrics( const b3DynamicTree* tree, int index )
{
if ( index == B3_NULL_INDEX )
{
return;
}
const b3TreeNode* node = tree->nodes + index;
B3_VALIDATE( b3IsValidAABB( node->aabb ) );
if ( b3IsLeaf( node ) )
{
B3_ASSERT( node->height == 0 );
return;
}
int child1 = node->children.child1;
int child2 = node->children.child2;
B3_ASSERT( 0 <= child1 && child1 < tree->nodeCapacity );
B3_ASSERT( 0 <= child2 && child2 < tree->nodeCapacity );
int height1 = tree->nodes[child1].height;
int height2 = tree->nodes[child2].height;
int height = 1 + b3MaxInt( height1, height2 );
B3_ASSERT( node->height == height );
B3_ASSERT( b3AABB_Contains( node->aabb, tree->nodes[child1].aabb ) );
B3_ASSERT( b3AABB_Contains( node->aabb, tree->nodes[child2].aabb ) );
uint64_t categoryBits = tree->nodes[child1].categoryBits | tree->nodes[child2].categoryBits;
B3_ASSERT( node->categoryBits == categoryBits );
b3ValidateMetrics( tree, child1 );
b3ValidateMetrics( tree, child2 );
}
#endif
void b3DynamicTree_Validate( const b3DynamicTree* tree )
{
#if B3_ENABLE_VALIDATION
if ( tree->root == B3_NULL_INDEX )
{
return;
}
b3ValidateStructure( tree, tree->root );
b3ValidateMetrics( tree, tree->root );
int freeCount = 0;
int freeIndex = tree->freeList;
while ( freeIndex != B3_NULL_INDEX )
{
B3_ASSERT( 0 <= freeIndex && freeIndex < tree->nodeCapacity );
freeIndex = tree->nodes[freeIndex].next;
++freeCount;
}
int height = b3DynamicTree_GetHeight( tree );
int computedHeight = b3ComputeHeight( tree );
B3_ASSERT( height == computedHeight );
B3_ASSERT( tree->nodeCount + freeCount == tree->nodeCapacity );
#else
B3_UNUSED( tree );
#endif
}
void b3DynamicTree_ValidateNoEnlarged( const b3DynamicTree* tree )
{
#if B3_ENABLE_VALIDATION == 1
int capacity = tree->nodeCapacity;
const b3TreeNode* nodes = tree->nodes;
for ( int i = 0; i < capacity; ++i )
{
const b3TreeNode* node = nodes + i;
if ( node->flags & b3_allocatedNode )
{
B3_ASSERT( ( node->flags & b3_enlargedNode ) == 0 );
}
}
#else
B3_UNUSED( tree );
#endif
}
int b3DynamicTree_GetByteCount( const b3DynamicTree* tree )
{
size_t size = sizeof( b3DynamicTree ) + sizeof( b3TreeNode ) * tree->nodeCapacity +
tree->rebuildCapacity * ( sizeof( int ) + sizeof( b3AABB ) + sizeof( b3Vec3 ) + sizeof( int ) );
return (int)size;
}
b3TreeStats b3DynamicTree_Query( const b3DynamicTree* tree, b3AABB aabb, uint64_t maskBits, bool requireAllBits,
b3TreeQueryCallbackFcn* callback, void* context )
{
b3TreeStats result = { 0 };
if ( tree->nodeCount == 0 )
{
return result;
}
int stack[B3_TREE_STACK_SIZE];
int stackCount = 0;
stack[stackCount++] = tree->root;
while ( stackCount > 0 )
{
int nodeId = stack[--stackCount];
if ( nodeId == B3_NULL_INDEX )
{
B3_ASSERT( false );
continue;
}
const b3TreeNode* node = tree->nodes + nodeId;
result.nodeVisits += 1;
uint64_t bitMatch = requireAllBits ? ( node->categoryBits & maskBits ) == maskBits : ( node->categoryBits & maskBits );
if ( bitMatch && b3AABB_Overlaps( node->aabb, aabb ) )
{
if ( b3IsLeaf( node ) )
{
bool proceed = callback( nodeId, node->userData, context );
result.leafVisits += 1;
if ( proceed == false )
{
return result;
}
}
else
{
B3_ASSERT( stackCount < B3_TREE_STACK_SIZE - 1 );
if ( stackCount < B3_TREE_STACK_SIZE - 1 )
{
stack[stackCount++] = node->children.child1;
stack[stackCount++] = node->children.child2;
}
}
}
}
return result;
}
B3_FORCE_INLINE float b3DistanceToNodeSqr( b3Vec3 point, const b3TreeNode* node )
{
b3Vec3 r = b3Sub( point, b3Clamp( point, node->aabb.lowerBound, node->aabb.upperBound ) );
return b3Dot( r, r );
}
struct b3QueryClosestItem
{
int nodeIndex;
float distanceToNodeSqr;
};
b3TreeStats b3DynamicTree_QueryClosest( const b3DynamicTree* tree, b3Vec3 point, uint64_t maskBits, bool requireAllBits,
b3TreeQueryClosestCallbackFcn* callback, void* context, float* minDistanceSqr )
{
b3TreeStats result = { 0 };
if ( tree->nodeCount == 0 )
{
return result;
}
float minSqr = *minDistanceSqr;
struct b3QueryClosestItem stack[B3_TREE_STACK_SIZE];
int stackCount = 0;
float rootDistanceSqr = b3DistanceToNodeSqr( point, tree->nodes + tree->root );
stack[stackCount++] = (struct b3QueryClosestItem){
.nodeIndex = tree->root,
.distanceToNodeSqr = rootDistanceSqr,
};
while ( stackCount > 0 )
{
struct b3QueryClosestItem item = stack[--stackCount];
const b3TreeNode* node = tree->nodes + item.nodeIndex;
result.nodeVisits += 1;
uint64_t bitMatch = requireAllBits ? ( node->categoryBits & maskBits ) == maskBits : ( node->categoryBits & maskBits );
if ( bitMatch )
{
if ( item.distanceToNodeSqr < minSqr )
{
if ( b3IsLeaf( node ) )
{
float dd = callback( minSqr, item.nodeIndex, node->userData, context );
if ( dd < minSqr )
{
minSqr = dd;
}
result.leafVisits += 1;
}
else
{
B3_ASSERT( stackCount < B3_TREE_STACK_SIZE - 1 );
if ( stackCount < B3_TREE_STACK_SIZE - 1 )
{
int child1 = node->children.child1;
int child2 = node->children.child2;
struct b3QueryClosestItem item1 = {
.nodeIndex = child1,
.distanceToNodeSqr = b3DistanceToNodeSqr( point, tree->nodes + child1 ),
};
struct b3QueryClosestItem item2 = {
.nodeIndex = child2,
.distanceToNodeSqr = b3DistanceToNodeSqr( point, tree->nodes + child2 ),
};
if ( item2.distanceToNodeSqr < item1.distanceToNodeSqr )
{
stack[stackCount++] = item1;
stack[stackCount++] = item2;
}
else
{
stack[stackCount++] = item2;
stack[stackCount++] = item1;
}
}
}
}
}
}
*minDistanceSqr = minSqr;
return result;
}
b3TreeStats b3DynamicTree_RayCast( const b3DynamicTree* tree, const b3RayCastInput* input, uint64_t maskBits, bool requireAllBits,
b3TreeRayCastCallbackFcn* callback, void* context )
{
b3TreeStats result = { 0 };
if ( tree->nodeCount == 0 )
{
return result;
}
b3Vec3 p1 = input->origin;
b3Vec3 d = input->translation;
b3V32 pv1 = b3LoadV( &p1.x );
b3V32 dv = b3LoadV( &d.x );
float maxFraction = input->maxFraction;
b3Vec3 p2 = b3MulAdd( p1, maxFraction, d );
b3AABB segmentAABB = { b3Min( p1, p2 ), b3Max( p1, p2 ) };
int stack[B3_TREE_STACK_SIZE];
int stackCount = 0;
stack[stackCount++] = tree->root;
const b3TreeNode* nodes = tree->nodes;
b3RayCastInput subInput = *input;
while ( stackCount > 0 )
{
int nodeId = stack[--stackCount];
if ( nodeId == B3_NULL_INDEX )
{
B3_ASSERT( false );
continue;
}
const b3TreeNode* node = nodes + nodeId;
result.nodeVisits += 1;
b3AABB nodeAABB = node->aabb;
uint64_t bitMatch = requireAllBits ? ( node->categoryBits & maskBits ) == maskBits : ( node->categoryBits & maskBits );
if ( bitMatch == 0 || b3AABB_Overlaps( nodeAABB, segmentAABB ) == false )
{
continue;
}
b3V32 lower = b3LoadV( &nodeAABB.lowerBound.x );
b3V32 upper = b3LoadV( &nodeAABB.upperBound.x );
bool edgeOverlap = b3TestBoundsRayOverlap( lower, upper, pv1, dv );
if ( edgeOverlap == false )
{
continue;
}
if ( b3IsLeaf( node ) )
{
subInput.maxFraction = maxFraction;
float value = callback( &subInput, nodeId, node->userData, context );
result.leafVisits += 1;
if ( value == 0.0f )
{
return result;
}
if ( 0.0f < value && value <= maxFraction )
{
maxFraction = value;
p2 = b3MulAdd( p1, maxFraction, d );
segmentAABB.lowerBound = b3Min( p1, p2 );
segmentAABB.upperBound = b3Max( p1, p2 );
}
}
else
{
B3_ASSERT( stackCount < B3_TREE_STACK_SIZE - 1 );
if ( stackCount < B3_TREE_STACK_SIZE - 1 )
{
b3Vec3 c1 = b3AABB_Center( nodes[node->children.child1].aabb );
b3Vec3 c2 = b3AABB_Center( nodes[node->children.child2].aabb );
if ( b3DistanceSquared( c1, p1 ) < b3DistanceSquared( c2, p1 ) )
{
stack[stackCount++] = node->children.child2;
stack[stackCount++] = node->children.child1;
}
else
{
stack[stackCount++] = node->children.child1;
stack[stackCount++] = node->children.child2;
}
}
}
}
return result;
}
b3TreeStats b3DynamicTree_BoxCast( const b3DynamicTree* tree, const b3BoxCastInput* input, uint64_t maskBits, bool requireAllBits,
b3TreeBoxCastCallbackFcn* callback, void* context )
{
b3TreeStats stats = { 0 };
if ( tree->nodeCount == 0 )
{
return stats;
}
b3AABB originAABB = input->box;
b3Vec3 p1 = b3AABB_Center( originAABB );
b3Vec3 extension = b3AABB_Extents( originAABB );
b3Vec3 d = input->translation;
b3V32 pv1 = b3LoadV( &p1.x );
b3V32 dv = b3LoadV( &d.x );
b3V32 ev = b3LoadV( &extension.x );
float maxFraction = input->maxFraction;
b3Vec3 t = b3MulSV( maxFraction, input->translation );
b3AABB totalAABB = {
b3Min( originAABB.lowerBound, b3Add( originAABB.lowerBound, t ) ),
b3Max( originAABB.upperBound, b3Add( originAABB.upperBound, t ) ),
};
b3BoxCastInput subInput = *input;
const b3TreeNode* nodes = tree->nodes;
int stack[B3_TREE_STACK_SIZE];
int stackCount = 0;
stack[stackCount++] = tree->root;
while ( stackCount > 0 )
{
int nodeId = stack[--stackCount];
if ( nodeId == B3_NULL_INDEX )
{
B3_ASSERT( false );
continue;
}
const b3TreeNode* node = nodes + nodeId;
stats.nodeVisits += 1;
uint64_t bitMatch = requireAllBits ? ( node->categoryBits & maskBits ) == maskBits : ( node->categoryBits & maskBits );
if ( bitMatch == 0 || b3AABB_Overlaps( node->aabb, totalAABB ) == false )
{
continue;
}
b3V32 lower = b3SubV( b3LoadV( &node->aabb.lowerBound.x ), ev );
b3V32 upper = b3AddV( b3LoadV( &node->aabb.upperBound.x ), ev );
bool edgeOverlap = b3TestBoundsRayOverlap( lower, upper, pv1, dv );
if ( edgeOverlap == false )
{
continue;
}
if ( b3IsLeaf( node ) )
{
subInput.maxFraction = maxFraction;
float value = callback( &subInput, nodeId, node->userData, context );
stats.leafVisits += 1;
if ( value == 0.0f )
{
return stats;
}
if ( 0.0f < value && value < maxFraction )
{
maxFraction = value;
t = b3MulSV( maxFraction, input->translation );
totalAABB.lowerBound = b3Min( originAABB.lowerBound, b3Add( originAABB.lowerBound, t ) );
totalAABB.upperBound = b3Max( originAABB.upperBound, b3Add( originAABB.upperBound, t ) );
}
}
else
{
B3_ASSERT( stackCount < B3_TREE_STACK_SIZE - 1 );
if ( stackCount < B3_TREE_STACK_SIZE - 1 )
{
b3Vec3 c1 = b3AABB_Center( nodes[node->children.child1].aabb );
b3Vec3 c2 = b3AABB_Center( nodes[node->children.child2].aabb );
if ( b3DistanceSquared( c1, p1 ) < b3DistanceSquared( c2, p1 ) )
{
stack[stackCount++] = node->children.child2;
stack[stackCount++] = node->children.child1;
}
else
{
stack[stackCount++] = node->children.child1;
stack[stackCount++] = node->children.child2;
}
}
}
}
return stats;
}
#define B3_TREE_HEURISTIC 0
#if B3_TREE_HEURISTIC == 0
static int b3PartitionMid( int* indices, b3Vec3* centers, int count )
{
if ( count <= 2 )
{
return count / 2;
}
b3Vec3 lowerBound = centers[0];
b3Vec3 upperBound = centers[0];
for ( int i = 1; i < count; ++i )
{
lowerBound = b3Min( lowerBound, centers[i] );
upperBound = b3Max( upperBound, centers[i] );
}
b3Vec3 d = b3Sub( upperBound, lowerBound );
b3Vec3 c = b3MulSV( 0.5f, b3Add( lowerBound, upperBound ) );
int i1 = 0, i2 = count;
if ( d.x >= d.y && d.x >= d.z )
{
float pivot = c.x;
while ( i1 < i2 )
{
while ( i1 < i2 && centers[i1].x < pivot )
{
i1 += 1;
};
while ( i1 < i2 && centers[i2 - 1].x >= pivot )
{
i2 -= 1;
};
if ( i1 < i2 )
{
{
int temp = indices[i1];
indices[i1] = indices[i2 - 1];
indices[i2 - 1] = temp;
}
{
b3Vec3 temp = centers[i1];
centers[i1] = centers[i2 - 1];
centers[i2 - 1] = temp;
}
i1 += 1;
i2 -= 1;
}
}
}
else if ( d.y >= d.z )
{
float pivot = c.y;
while ( i1 < i2 )
{
while ( i1 < i2 && centers[i1].y < pivot )
{
i1 += 1;
};
while ( i1 < i2 && centers[i2 - 1].y >= pivot )
{
i2 -= 1;
};
if ( i1 < i2 )
{
{
int temp = indices[i1];
indices[i1] = indices[i2 - 1];
indices[i2 - 1] = temp;
}
{
b3Vec3 temp = centers[i1];
centers[i1] = centers[i2 - 1];
centers[i2 - 1] = temp;
}
i1 += 1;
i2 -= 1;
}
}
}
else
{
float pivot = c.z;
while ( i1 < i2 )
{
while ( i1 < i2 && centers[i1].z < pivot )
{
i1 += 1;
};
while ( i1 < i2 && centers[i2 - 1].z >= pivot )
{
i2 -= 1;
};
if ( i1 < i2 )
{
{
int temp = indices[i1];
indices[i1] = indices[i2 - 1];
indices[i2 - 1] = temp;
}
{
b3Vec3 temp = centers[i1];
centers[i1] = centers[i2 - 1];
centers[i2 - 1] = temp;
}
i1 += 1;
i2 -= 1;
}
}
}
B3_ASSERT( i1 == i2 );
if ( i1 > 0 && i1 < count )
{
return i1;
}
return count / 2;
}
#else
#define B3_BIN_COUNT 8
typedef struct b3TreeBin
{
b3AABB aabb;
int count;
} b3TreeBin;
typedef struct b3TreePlane
{
b3AABB leftAABB;
b3AABB rightAABB;
int leftCount;
int rightCount;
} b3TreePlane;
static int b3PartitionSAH( int* indices, int* binIndices, b3AABB* boxes, int count )
{
B3_ASSERT( count > 0 );
b3TreeBin bins[B3_BIN_COUNT];
b3TreePlane planes[B3_BIN_COUNT - 1];
b3Vec3 center = b3AABB_Center( boxes[0] );
b3AABB centroidAABB;
centroidAABB.lowerBound = center;
centroidAABB.upperBound = center;
for ( int i = 1; i < count; ++i )
{
center = b3AABB_Center( boxes[i] );
centroidAABB.lowerBound = b3Min( centroidAABB.lowerBound, center );
centroidAABB.upperBound = b3Max( centroidAABB.upperBound, center );
}
b3Vec3 d = b3Sub( centroidAABB.upperBound, centroidAABB.lowerBound );
int axisIndex;
float invD;
if ( d.x > d.y )
{
axisIndex = 0;
invD = d.x;
}
else
{
axisIndex = 1;
invD = d.y;
}
invD = invD > 0.0f ? 1.0f / invD : 0.0f;
for ( int i = 0; i < B3_BIN_COUNT; ++i )
{
bins[i].aabb.lowerBound = (b3Vec3){ FLT_MAX, FLT_MAX };
bins[i].aabb.upperBound = (b3Vec3){ -FLT_MAX, -FLT_MAX };
bins[i].count = 0;
}
float binCount = B3_BIN_COUNT;
float lowerBoundArray[2] = { centroidAABB.lowerBound.x, centroidAABB.lowerBound.y };
float minC = lowerBoundArray[axisIndex];
for ( int i = 0; i < count; ++i )
{
b3Vec3 c = b3AABB_Center( boxes[i] );
float cArray[2] = { c.x, c.y };
int binIndex = (int)( binCount * ( cArray[axisIndex] - minC ) * invD );
binIndex = b3ClampInt( binIndex, 0, B3_BIN_COUNT - 1 );
binIndices[i] = binIndex;
bins[binIndex].count += 1;
bins[binIndex].aabb = b3AABB_Union( bins[binIndex].aabb, boxes[i] );
}
int planeCount = B3_BIN_COUNT - 1;
planes[0].leftCount = bins[0].count;
planes[0].leftAABB = bins[0].aabb;
for ( int i = 1; i < planeCount; ++i )
{
planes[i].leftCount = planes[i - 1].leftCount + bins[i].count;
planes[i].leftAABB = b3AABB_Union( planes[i - 1].leftAABB, bins[i].aabb );
}
planes[planeCount - 1].rightCount = bins[planeCount].count;
planes[planeCount - 1].rightAABB = bins[planeCount].aabb;
for ( int i = planeCount - 2; i >= 0; --i )
{
planes[i].rightCount = planes[i + 1].rightCount + bins[i + 1].count;
planes[i].rightAABB = b3AABB_Union( planes[i + 1].rightAABB, bins[i + 1].aabb );
}
float minCost = FLT_MAX;
int bestPlane = 0;
for ( int i = 0; i < planeCount; ++i )
{
float leftArea = b3Perimeter( planes[i].leftAABB );
float rightArea = b3Perimeter( planes[i].rightAABB );
int leftCount = planes[i].leftCount;
int rightCount = planes[i].rightCount;
float cost = leftCount * leftArea + rightCount * rightArea;
if ( cost < minCost )
{
bestPlane = i;
minCost = cost;
}
}
int i1 = 0, i2 = count;
while ( i1 < i2 )
{
while ( i1 < i2 && binIndices[i1] < bestPlane )
{
i1 += 1;
};
while ( i1 < i2 && binIndices[i2 - 1] >= bestPlane )
{
i2 -= 1;
};
if ( i1 < i2 )
{
{
int temp = indices[i1];
indices[i1] = indices[i2 - 1];
indices[i2 - 1] = temp;
}
{
b3AABB temp = boxes[i1];
boxes[i1] = boxes[i2 - 1];
boxes[i2 - 1] = temp;
}
i1 += 1;
i2 -= 1;
}
}
B3_ASSERT( i1 == i2 );
if ( i1 > 0 && i1 < count )
{
return i1;
}
else
{
return count / 2;
}
}
#endif
struct b3RebuildItem
{
int nodeIndex;
int childCount;
int startIndex;
int splitIndex;
int endIndex;
};
static int b3BuildTree( b3DynamicTree* tree, int leafCount )
{
b3TreeNode* nodes = tree->nodes;
int* leafIndices = tree->leafIndices;
if ( leafCount == 1 )
{
nodes[leafIndices[0]].parent = B3_NULL_INDEX;
return leafIndices[0];
}
#if B3_TREE_HEURISTIC == 0
b3Vec3* leafCenters = tree->leafCenters;
#else
b3AABB* leafBoxes = tree->leafBoxes;
int* binIndices = tree->binIndices;
#endif
struct b3RebuildItem stack[B3_TREE_STACK_SIZE];
int top = 0;
stack[0].nodeIndex = b3AllocateNode( tree );
stack[0].childCount = -1;
stack[0].startIndex = 0;
stack[0].endIndex = leafCount;
#if B3_TREE_HEURISTIC == 0
stack[0].splitIndex = b3PartitionMid( leafIndices, leafCenters, leafCount );
#else
stack[0].splitIndex = b3PartitionSAH( leafIndices, binIndices, leafBoxes, leafCount );
#endif
while ( true )
{
struct b3RebuildItem* item = stack + top;
item->childCount += 1;
if ( item->childCount == 2 )
{
if ( top == 0 )
{
break;
}
struct b3RebuildItem* parentItem = stack + ( top - 1 );
b3TreeNode* parentNode = nodes + parentItem->nodeIndex;
if ( parentItem->childCount == 0 )
{
B3_ASSERT( parentNode->children.child1 == B3_NULL_INDEX );
parentNode->children.child1 = item->nodeIndex;
}
else
{
B3_ASSERT( parentItem->childCount == 1 );
B3_ASSERT( parentNode->children.child2 == B3_NULL_INDEX );
parentNode->children.child2 = item->nodeIndex;
}
b3TreeNode* node = nodes + item->nodeIndex;
B3_ASSERT( node->parent == B3_NULL_INDEX );
node->parent = parentItem->nodeIndex;
B3_ASSERT( node->children.child1 != B3_NULL_INDEX );
B3_ASSERT( node->children.child2 != B3_NULL_INDEX );
b3TreeNode* child1 = nodes + node->children.child1;
b3TreeNode* child2 = nodes + node->children.child2;
node->aabb = b3AABB_Union( child1->aabb, child2->aabb );
node->height = 1 + b3MaxUInt16( child1->height, child2->height );
node->categoryBits = child1->categoryBits | child2->categoryBits;
top -= 1;
}
else
{
int startIndex, endIndex;
if ( item->childCount == 0 )
{
startIndex = item->startIndex;
endIndex = item->splitIndex;
}
else
{
B3_ASSERT( item->childCount == 1 );
startIndex = item->splitIndex;
endIndex = item->endIndex;
}
int count = endIndex - startIndex;
if ( count == 1 )
{
int childIndex = leafIndices[startIndex];
b3TreeNode* node = nodes + item->nodeIndex;
if ( item->childCount == 0 )
{
B3_ASSERT( node->children.child1 == B3_NULL_INDEX );
node->children.child1 = childIndex;
}
else
{
B3_ASSERT( item->childCount == 1 );
B3_ASSERT( node->children.child2 == B3_NULL_INDEX );
node->children.child2 = childIndex;
}
b3TreeNode* childNode = nodes + childIndex;
B3_ASSERT( childNode->parent == B3_NULL_INDEX );
childNode->parent = item->nodeIndex;
}
else
{
B3_ASSERT( count > 0 );
B3_ASSERT( top < B3_TREE_STACK_SIZE );
top += 1;
struct b3RebuildItem* newItem = stack + top;
newItem->nodeIndex = b3AllocateNode( tree );
newItem->childCount = -1;
newItem->startIndex = startIndex;
newItem->endIndex = endIndex;
#if B3_TREE_HEURISTIC == 0
newItem->splitIndex = b3PartitionMid( leafIndices + startIndex, leafCenters + startIndex, count );
#else
newItem->splitIndex =
b3PartitionSAH( leafIndices + startIndex, binIndices + startIndex, leafBoxes + startIndex, count );
#endif
newItem->splitIndex += startIndex;
}
}
}
b3TreeNode* rootNode = nodes + stack[0].nodeIndex;
B3_ASSERT( rootNode->parent == B3_NULL_INDEX );
B3_ASSERT( rootNode->children.child1 != B3_NULL_INDEX );
B3_ASSERT( rootNode->children.child2 != B3_NULL_INDEX );
b3TreeNode* child1 = nodes + rootNode->children.child1;
b3TreeNode* child2 = nodes + rootNode->children.child2;
rootNode->aabb = b3AABB_Union( child1->aabb, child2->aabb );
rootNode->height = 1 + b3MaxUInt16( child1->height, child2->height );
rootNode->categoryBits = child1->categoryBits | child2->categoryBits;
return stack[0].nodeIndex;
}
int b3DynamicTree_Rebuild( b3DynamicTree* tree, bool fullBuild )
{
int proxyCount = tree->proxyCount;
if ( proxyCount == 0 )
{
return 0;
}
if ( proxyCount > tree->rebuildCapacity )
{
int newCapacity = proxyCount + proxyCount / 2;
b3Free( tree->leafIndices, tree->rebuildCapacity * sizeof( int ) );
tree->leafIndices = (int*)b3Alloc( newCapacity * sizeof( int ) );
#if B3_TREE_HEURISTIC == 0
b3Free( tree->leafCenters, tree->rebuildCapacity * sizeof( b3Vec3 ) );
tree->leafCenters = (b3Vec3*)b3Alloc( newCapacity * sizeof( b3Vec3 ) );
#else
b3Free( tree->leafBoxes, tree->rebuildCapacity * sizeof( b3AABB ) );
tree->leafBoxes = (b3AABB*)b3Alloc( newCapacity * sizeof( b3AABB ) );
b3Free( tree->binIndices, tree->rebuildCapacity * sizeof( int ) );
tree->binIndices = (int*)b3Alloc( newCapacity * sizeof( int ) );
#endif
tree->rebuildCapacity = newCapacity;
}
int leafCount = 0;
int stack[B3_TREE_STACK_SIZE];
int stackCount = 0;
int nodeIndex = tree->root;
b3TreeNode* nodes = tree->nodes;
b3TreeNode* node = nodes + nodeIndex;
int* leafIndices = tree->leafIndices;
#if B3_TREE_HEURISTIC == 0
b3Vec3* leafCenters = tree->leafCenters;
#else
b3AABB* leafBoxes = tree->leafBoxes;
#endif
while ( true )
{
if ( b3IsLeaf( node ) == true || ( ( node->flags & b3_enlargedNode ) == 0 && fullBuild == false ) )
{
leafIndices[leafCount] = nodeIndex;
#if B3_TREE_HEURISTIC == 0
leafCenters[leafCount] = b3AABB_Center( node->aabb );
#else
leafBoxes[leafCount] = node->aabb;
#endif
leafCount += 1;
node->parent = B3_NULL_INDEX;
}
else
{
int doomedNodeIndex = nodeIndex;
nodeIndex = node->children.child1;
B3_ASSERT( stackCount < B3_TREE_STACK_SIZE );
if ( stackCount < B3_TREE_STACK_SIZE )
{
stack[stackCount++] = node->children.child2;
}
node = nodes + nodeIndex;
b3FreeNode( tree, doomedNodeIndex );
continue;
}
if ( stackCount == 0 )
{
break;
}
nodeIndex = stack[--stackCount];
node = nodes + nodeIndex;
}
#if B3_ENABLE_VALIDATION == 1
int capacity = tree->nodeCapacity;
for ( int i = 0; i < capacity; ++i )
{
if ( nodes[i].flags & b3_allocatedNode )
{
B3_ASSERT( ( nodes[i].flags & b3_enlargedNode ) == 0 );
}
}
#endif
B3_ASSERT( leafCount <= proxyCount );
tree->root = b3BuildTree( tree, leafCount );
b3DynamicTree_Validate( tree );
return leafCount;
}
static FILE* b3OpenTreeFile( const char* fileName, const char* mode )
{
FILE* file = NULL;
#if defined( _MSC_VER )
errno_t e = fopen_s( &file, fileName, mode );
if ( e != 0 )
{
return NULL;
}
#else
file = fopen( fileName, mode );
if ( file == NULL )
{
return NULL;
}
#endif
return file;
}
void b3DynamicTree_Save( const b3DynamicTree* tree, const char* fileName )
{
FILE* file = b3OpenTreeFile( fileName, "wb" );
if ( file == NULL )
{
return;
}
b3DynamicTree temp = *tree;
temp.nodes = NULL;
temp.leafIndices = NULL;
temp.leafBoxes = NULL;
temp.leafCenters = NULL;
temp.binIndices = NULL;
temp.rebuildCapacity = 0;
fwrite( &temp, sizeof( b3DynamicTree ), 1, file );
if ( tree->nodeCapacity > 0 && tree->nodes != NULL )
{
fwrite( tree->nodes, sizeof( b3TreeNode ), tree->nodeCapacity, file );
}
fclose( file );
}
b3DynamicTree b3DynamicTree_Load( const char* fileName, float scale )
{
b3DynamicTree tree = { 0 };
FILE* file = b3OpenTreeFile( fileName, "rb" );
if ( file == NULL )
{
return tree;
}
int readCount = (int)fread( &tree, sizeof( b3DynamicTree ), 1, file );
if ( readCount != 1 )
{
fclose( file );
return tree;
}
if ( tree.version != B3_DYNAMIC_TREE_VERSION )
{
fclose( file );
memset( &tree, 0, sizeof( b3DynamicTree ) );
return tree;
}
if ( tree.nodeCapacity > 0 )
{
tree.nodes = (b3TreeNode*)b3Alloc( tree.nodeCapacity * sizeof( b3TreeNode ) );
readCount = (int)fread( tree.nodes, sizeof( b3TreeNode ), tree.nodeCapacity, file );
if ( readCount != tree.nodeCapacity )
{
b3Free( tree.nodes, tree.nodeCapacity * sizeof( b3TreeNode ) );
fclose( file );
memset( &tree, 0, sizeof( b3DynamicTree ) );
return tree;
}
for ( int i = 0; i < tree.nodeCapacity; ++i )
{
b3TreeNode* node = tree.nodes + i;
node->aabb.lowerBound = b3MulSV( scale, node->aabb.lowerBound );
node->aabb.upperBound = b3MulSV( scale, node->aabb.upperBound );
}
}
else
{
tree.nodes = NULL;
}
tree.leafIndices = NULL;
tree.leafBoxes = NULL;
tree.leafCenters = NULL;
tree.binIndices = NULL;
tree.rebuildCapacity = 0;
fclose( file );
return tree;
}