#include "stdafx.h"
#include "AABBTree.h"
//////////////////////////////////////////////////////////////////////////
// AABBTree implementation borrowed from here
// https://github.com/JamesRandall/SimpleVoxelEngine/blob/master/voxelEngine/src/AABBTree.h
//////////////////////////////////////////////////////////////////////////
AABBTree::AABBTree(unsigned int initialSize)
: mRootNodeIndex(NULL_TREE_NODE)
, mAllocatedCount()
, mNextFreeNodeIndex()
, mCapacity(initialSize)
, mGrowthSize(initialSize)
{
cxx_assert(initialSize > 1);
mTreeNodes.resize(initialSize);
// setup initial nodes
for (unsigned int inode = 0; inode < initialSize; ++inode)
{
TreeNode& treeNode = mTreeNodes[inode];
treeNode.mNextNodeIndex = inode + 1;
}
mTreeNodes[initialSize - 1].mNextNodeIndex = NULL_TREE_NODE;
}
void AABBTree::AllocateTreeNode(TreeNodeIndex* treeNodeIndex)
{
cxx_assert(treeNodeIndex);
// no free nodes, allocate additional memory
if (mNextFreeNodeIndex == NULL_TREE_NODE)
{
cxx_assert(mAllocatedCount == mCapacity);
mCapacity += mGrowthSize;
mTreeNodes.resize(mCapacity);
// setup nodes
for (unsigned int inode = mAllocatedCount; inode < mCapacity; ++inode)
{
TreeNode& treeNode = mTreeNodes[inode];
treeNode.mNextNodeIndex = inode + 1;
}
mTreeNodes[mCapacity - 1].mNextNodeIndex = NULL_TREE_NODE;
mNextFreeNodeIndex = mAllocatedCount;
}
*treeNodeIndex = mNextFreeNodeIndex;
TreeNode& treeNode = mTreeNodes[mNextFreeNodeIndex];
treeNode.mParentNodeIndex = NULL_TREE_NODE;
treeNode.mLeftNodeIndex = NULL_TREE_NODE;
treeNode.mRightNodeIndex = NULL_TREE_NODE;
mNextFreeNodeIndex = treeNode.mNextNodeIndex;
mAllocatedCount++;
}
void AABBTree::DeallocateTreeNode(TreeNodeIndex treeNodeIndex)
{
cxx_assert(treeNodeIndex != NULL_TREE_NODE);
TreeNode& treeNode = mTreeNodes[treeNodeIndex];
treeNode.mNextNodeIndex = mNextFreeNodeIndex;
mNextFreeNodeIndex = treeNodeIndex;
mAllocatedCount--;
}
void AABBTree::InsertLeaf(TreeNodeIndex leafNodeIndex)
{
cxx_assert(leafNodeIndex != NULL_TREE_NODE);
// make sure we're inserting a new leaf
cxx_assert(mTreeNodes[leafNodeIndex].mParentNodeIndex == NULL_TREE_NODE);
cxx_assert(mTreeNodes[leafNodeIndex].mLeftNodeIndex == NULL_TREE_NODE);
cxx_assert(mTreeNodes[leafNodeIndex].mRightNodeIndex == NULL_TREE_NODE);
// if the tree is empty then we make the root the leaf
if (mRootNodeIndex == NULL_TREE_NODE)
{
mRootNodeIndex = leafNodeIndex;
return;
}
// search for the best place to put the new leaf in the tree
// we use surface area and depth as search heuristics
TreeNodeIndex treeNodeIndex = mRootNodeIndex;
TreeNode& leafNode = mTreeNodes[leafNodeIndex];
while (!mTreeNodes[treeNodeIndex].IsLeafNode())
{
// because of the test in the while loop above we know we are never a leaf inside it
const TreeNode& treeNode = mTreeNodes[treeNodeIndex];
TreeNodeIndex leftNodeIndex = treeNode.mLeftNodeIndex;
TreeNodeIndex rightNodeIndex = treeNode.mRightNodeIndex;
const TreeNode& leftNode = mTreeNodes[leftNodeIndex];
const TreeNode& rightNode = mTreeNodes[rightNodeIndex];
float combinedAabbSurfaceArea = treeNode.mBoundingBox.union_with(leafNode.mBoundingBox).get_surface_area();
float newParentNodeCost = 2.0f * combinedAabbSurfaceArea;
float minimumPushDownCost = 2.0f * (combinedAabbSurfaceArea - treeNode.mBoundingBox.get_surface_area());
// use the costs to figure out whether to create a new parent here or descend
float costLeft;
float costRight;
if (leftNode.IsLeafNode())
{
costLeft = leafNode.mBoundingBox.union_with(leftNode.mBoundingBox).get_surface_area() + minimumPushDownCost;
}
else
{
cxx::aabbox newLeftAabb = leafNode.mBoundingBox.union_with(leftNode.mBoundingBox);
costLeft = (newLeftAabb.get_surface_area() - leftNode.mBoundingBox.get_surface_area()) + minimumPushDownCost;
}
if (rightNode.IsLeafNode())
{
costRight = leafNode.mBoundingBox.union_with(rightNode.mBoundingBox).get_surface_area() + minimumPushDownCost;
}
else
{
cxx::aabbox newRightAabb = leafNode.mBoundingBox.union_with(rightNode.mBoundingBox);
costRight = (newRightAabb.get_surface_area() - rightNode.mBoundingBox.get_surface_area()) + minimumPushDownCost;
}
// if the cost of creating a new parent node here is less than descending in either direction then
// we know we need to create a new parent node, errrr, here and attach the leaf to that
if (newParentNodeCost < costLeft && newParentNodeCost < costRight)
break;
// otherwise descend in the cheapest direction
if (costLeft < costRight)
{
treeNodeIndex = leftNodeIndex;
}
else
{
treeNodeIndex = rightNodeIndex;
}
}
// the leafs sibling is going to be the node we found above and we are going to create a new
// parent node and attach the leaf and this item
TreeNodeIndex leafSiblingIndex = treeNodeIndex;
TreeNode& leafSibling = mTreeNodes[leafSiblingIndex];
TreeNodeIndex oldParentIndex = leafSibling.mParentNodeIndex;
TreeNodeIndex newParentIndex = NULL_TREE_NODE;
AllocateTreeNode(&newParentIndex);
TreeNode& newParent = mTreeNodes[newParentIndex];
newParent.mParentNodeIndex = oldParentIndex;
newParent.mBoundingBox = leafNode.mBoundingBox.union_with(leafSibling.mBoundingBox); // the new parents aabb is the leaf aabb combined with it's siblings aabb
newParent.mLeftNodeIndex = leafSiblingIndex;
newParent.mRightNodeIndex = leafNodeIndex;
leafNode.mParentNodeIndex = newParentIndex;
leafSibling.mParentNodeIndex = newParentIndex;
if (oldParentIndex == NULL_TREE_NODE)
{
// the old parent was the root and so this is now the root
mRootNodeIndex = newParentIndex;
}
else
{
// the old parent was not the root and so we need to patch the left or right index to
// point to the new node
TreeNode& oldParent = mTreeNodes[oldParentIndex];
if (oldParent.mLeftNodeIndex == leafSiblingIndex)
{
oldParent.mLeftNodeIndex = newParentIndex;
}
else
{
oldParent.mRightNodeIndex = newParentIndex;
}
}
// finally we need to walk back up the tree fixing heights and areas
treeNodeIndex = leafNode.mParentNodeIndex;
FixUpwardsTree(treeNodeIndex);
}
void AABBTree::RemoveLeaf(TreeNodeIndex leafNodeIndex)
{
cxx_assert(leafNodeIndex != NULL_TREE_NODE);
// if the leaf is the root then we can just clear the root pointer and return
if (leafNodeIndex == mRootNodeIndex)
{
mRootNodeIndex = NULL_TREE_NODE;
return;
}
TreeNode& leafNode = mTreeNodes[leafNodeIndex];
TreeNodeIndex parentNodeIndex = leafNode.mParentNodeIndex;
const TreeNode& parentNode = mTreeNodes[parentNodeIndex];
TreeNodeIndex grandParentNodeIndex = parentNode.mParentNodeIndex;
TreeNodeIndex siblingNodeIndex = parentNode.mLeftNodeIndex == leafNodeIndex ? parentNode.mRightNodeIndex : parentNode.mLeftNodeIndex;
cxx_assert(siblingNodeIndex != NULL_TREE_NODE); // we must have a sibling
TreeNode& siblingNode = mTreeNodes[siblingNodeIndex];
if (grandParentNodeIndex != NULL_TREE_NODE)
{
// if we have a grand parent (i.e. the parent is not the root) then destroy the parent and connect the sibling to the grandparent in its
// place
TreeNode& grandParentNode = mTreeNodes[grandParentNodeIndex];
if (grandParentNode.mLeftNodeIndex == parentNodeIndex)
{
grandParentNode.mLeftNodeIndex = siblingNodeIndex;
}
else
{
grandParentNode.mRightNodeIndex = siblingNodeIndex;
}
siblingNode.mParentNodeIndex = grandParentNodeIndex;
DeallocateTreeNode(parentNodeIndex);
FixUpwardsTree(grandParentNodeIndex);
}
else
{
// if we have no grandparent then the parent is the root and so our sibling becomes the root and has it's parent removed
mRootNodeIndex = siblingNodeIndex;
siblingNode.mParentNodeIndex = NULL_TREE_NODE;
DeallocateTreeNode(parentNodeIndex);
}
leafNode.mParentNodeIndex = NULL_TREE_NODE;
}
void AABBTree::UpdateLeaf(TreeNodeIndex leafNodeIndex, const cxx::aabbox& boundingBox)
{
cxx_assert(leafNodeIndex != NULL_TREE_NODE);
TreeNode& node = mTreeNodes[leafNodeIndex];
// if the node contains the new aabb then we just leave things
// TODO: when we add velocity this check should kick in as often an update will lie within the velocity fattened initial aabb
// to support this we might need to differentiate between velocity fattened aabb and actual aabb
if (node.mBoundingBox.contains(boundingBox))
return;
RemoveLeaf(leafNodeIndex);
node.mBoundingBox = boundingBox;
InsertLeaf(leafNodeIndex);
}
void AABBTree::FixUpwardsTree(TreeNodeIndex treeNodeIndex)
{
cxx_assert(treeNodeIndex != NULL_TREE_NODE);
while (treeNodeIndex != NULL_TREE_NODE)
{
TreeNode& treeNode = mTreeNodes[treeNodeIndex];
// every node should be a parent
cxx_assert(treeNode.mLeftNodeIndex != NULL_TREE_NODE && treeNode.mRightNodeIndex != NULL_TREE_NODE);
// fix height and area
const TreeNode& leftNode = mTreeNodes[treeNode.mLeftNodeIndex];
const TreeNode& rightNode = mTreeNodes[treeNode.mRightNodeIndex];
treeNode.mBoundingBox = leftNode.mBoundingBox.union_with(rightNode.mBoundingBox);
treeNodeIndex = treeNode.mParentNodeIndex;
}
}
void AABBTree::DebugRender(DebugRenderer& renderer)
{
if (mRootNodeIndex != NULL_TREE_NODE)
{
DebugRenderNode(renderer, mTreeNodes[mRootNodeIndex]);
}
}
void AABBTree::DebugRenderNode(DebugRenderer& renderer, TreeNode& treeNode)
{
if (treeNode.IsLeafNode())
{
const Color32& drawColor = treeNode.mObject->mDebugColor;
if (drawColor.mA > 0)
{
renderer.SetRenderColor(drawColor);
renderer.RenderAABoundingBox(treeNode.mBoundingBox);
}
return;
}
if (treeNode.mLeftNodeIndex != NULL_TREE_NODE)
{
DebugRenderNode(renderer, mTreeNodes[treeNode.mLeftNodeIndex]);
}
if (treeNode.mRightNodeIndex != NULL_TREE_NODE)
{
DebugRenderNode(renderer, mTreeNodes[treeNode.mRightNodeIndex]);
}
}
void AABBTree::UpdateTree()
{
// todo
}
void AABBTree::Cleanup()
{
// drop all entities links
mEntitiesMap.clear();
mRootNodeIndex = NULL_TREE_NODE;
mNextFreeNodeIndex = 0;
// setup initial nodes
for (unsigned int inode = 0; inode < mCapacity; ++inode)
{
TreeNode& treeNode = mTreeNodes[inode];
treeNode.mNextNodeIndex = inode + 1;
}
mTreeNodes[mCapacity - 1].mNextNodeIndex = NULL_TREE_NODE;
}
void AABBTree::InsertObject(SceneObject* entity)
{
cxx_assert(entity);
entity->ComputeTransformation();
TreeNodeIndex nodeIndex = NULL_TREE_NODE;
AllocateTreeNode(&nodeIndex);
TreeNode& treeNode = mTreeNodes[nodeIndex];
treeNode.mBoundingBox = entity->GetWorldBounds();
treeNode.mObject = entity;
InsertLeaf(nodeIndex);
mEntitiesMap[entity] = nodeIndex;
}
void AABBTree::RemoveObject(SceneObject* entity)
{
cxx_assert(entity);
TreeNodeIndex nodeIndex = mEntitiesMap[entity];
mEntitiesMap.erase(entity);
RemoveLeaf(nodeIndex);
DeallocateTreeNode(nodeIndex);
}
void AABBTree::UpdateObject(SceneObject* entity)
{
cxx_assert(entity);
entity->ComputeTransformation();
const cxx::aabbox& boundingBox = entity->GetWorldBounds();
TreeNodeIndex nodeIndex = mEntitiesMap[entity];
UpdateLeaf(nodeIndex, boundingBox);
}