[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/codenamecpp/GLKeeper/main/src/AABBTree.cpp [Back]  [Original]

#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);
}

Web Proxy Viewer  |  New URL  |  Original Page