/// <summary>
 /// Creates a cache optimized copy of the tree that uses the minimum amount of memory possible.
 /// </summary>
 /// <returns>Cache and memory optimized copy of the tree.</returns>
 public unsafe Tree CreateOptimized()
 {
     var optimizedLeavesArray = new Leaf[leafCount];
     var optimizedNodesArray = new Node[nodeCount];
     CreateOptimized(optimizedLeavesArray, optimizedNodesArray);
     return new Tree(optimizedLeavesArray, optimizedNodesArray);
 }
        /// <summary>
        /// Creates a cache optimized version of the tree in the given leaves and nodes arrays.
        /// </summary>
        /// <param name="optimizedLeavesArray">Array to fill with optimized leaves.</param>
        /// <param name="optimizedNodesArray">Array to fill with optimized nodes.</param>
        public unsafe void CreateOptimized(Leaf[] optimizedLeavesArray, Node[] optimizedNodesArray)
        {
            if (optimizedLeavesArray.Length < LeafCount)
                throw new ArgumentException("Leaves array must be able to contain all leaves in this tree.");
            if (optimizedLeavesArray.Length < nodeCount)
                throw new ArgumentException("Nodes array must be able to contain all nodes in this tree.");

            fixed (Leaf* optimizedLeaves = optimizedLeavesArray)
            fixed (Node* optimizedNodes = optimizedNodesArray)
            {
                int optimizedNodeCount = 0;
                int optimizedLeafCount = 0;
                OptimizeDFS(-1, 0, optimizedNodes, optimizedLeaves, ref optimizedNodeCount, ref optimizedLeafCount);
                //OptimizeGroupDFS(optimizedNodes, optimizedLeaves, ref optimizedNodeCount, ref optimizedLeafCount);
                //OptimizeBFS(optimizedNodes, optimizedLeaves, ref optimizedNodeCount, ref optimizedLeafCount);
                Debug.Assert(optimizedNodeCount == nodeCount);
                Debug.Assert(optimizedLeafCount == leafCount);
            }
        }
        unsafe void OptimizeBFS(Node* optimizedNodes, Leaf* optimizedLeaves, ref int optimizedNodeCount, ref int optimizedLeafCount)
        {
            var nodesToVisit = new Queue<NodeToVisit>();
            nodesToVisit.Enqueue(new NodeToVisit { NodeIndex = 0, OptimizedIndex = 0, OptimizedParentIndex = -1 });
            optimizedNodeCount = 1;

            while (nodesToVisit.Count > 0)
            {
                var nodeToVisit = nodesToVisit.Dequeue();
                var node = nodes + nodeToVisit.NodeIndex;
                var optimizedNodeIndex = nodeToVisit.OptimizedIndex;
                var optimizedNode = optimizedNodes + optimizedNodeIndex;
                *optimizedNode = *node;
                optimizedNode->Parent = nodeToVisit.OptimizedParentIndex;
                var optimizedChildren = &optimizedNode->ChildA;
                var nodeChildren = &node->ChildA;

                for (int i = 0; i < node->ChildCount; ++i)
                {
                    if (nodeChildren[i] >= 0)
                    {
                        optimizedChildren[i] = optimizedNodeCount++;
                        nodesToVisit.Enqueue(new NodeToVisit { NodeIndex = nodeChildren[i], OptimizedIndex = optimizedChildren[i], OptimizedParentIndex = optimizedNodeIndex });
                    }
                    else
                    {
                        var leafIndex = Encode(nodeChildren[i]);
                        var optimizedLeafIndex = optimizedLeafCount++;
                        var optimizedLeaf = optimizedLeaves + optimizedLeafIndex;
                        optimizedLeaf->Id = leaves[leafIndex].Id;
                        optimizedLeaf->NodeIndex = optimizedNodeIndex;
                        optimizedLeaf->ChildIndex = i;
                        optimizedChildren[i] = Encode(optimizedLeafIndex);
                    }
                }
            }
        }
示例#4
0
        public unsafe LeafMove RemoveAt(int leafIndex)
        {
            if (leafIndex < 0 || leafIndex >= leafCount)
                throw new ArgumentOutOfRangeException("Leaf index must be a valid index in the tree's leaf array.");

            //Cache the leaf before overwriting.
            var leaf = leaves[leafIndex];

            //Delete the leaf from the leaves array.
            //This is done up front to make it easier for tests to catch bad behavior.
            var lastIndex = leafCount - 1;
            if (lastIndex != leafIndex)
            {
                //The removed leaf was in the middle of the leaves array. Take the last leaf and use it to fill the slot.
                //The node owner's index must be updated to point to the new location.
                var lastLeafOwner = nodes + leaves[lastIndex].NodeIndex;
                (&lastLeafOwner->ChildA)[leaves[lastIndex].ChildIndex] = Encode(leafIndex);
                leaves[leafIndex] = leaves[lastIndex];
            }
            leaves[lastIndex] = new Leaf();
            leafCount = lastIndex;

            var node = nodes + leaf.NodeIndex;
            var nodeChildren = &node->ChildA;
            var nodeBounds = &node->A;
            var nodeLeafCounts = &node->LeafCountA;

            //Remove the leaf from this node.
            //Note that the children must remain contiguous. Requires taking the last child of the node and moving into the slot
            //if the removed child was not the last child.
            //Further, if a child is moved and if it is a leaf, that leaf's ChildIndex must be updated.
            //If a child is moved and it is an internal node, all immediate children of that node must have their parent nodes updated.

            //Check to see if this node should collapse.
            if (node->ChildCount == 2 &&
                node->Parent >= 0) //The root cannot 'collapse'. The root is the only node that can end up with 1 child.
            {
                Debug.Assert(node->ChildCount != 1);
                //If there are only two children in the node, then the node containing the removed leaf will collapse.
                var otherIndex = leaf.ChildIndex == 0 ? 1 : 0;
                var otherChildIndex = nodeChildren[otherIndex];

                //Move the other node into the slot that used to point to the collapsing internal node.
                var parentNode = nodes + node->Parent;
                (&parentNode->A)[node->IndexInParent] = nodeBounds[otherIndex];
                (&parentNode->ChildA)[node->IndexInParent] = otherChildIndex;
                (&parentNode->LeafCountA)[node->IndexInParent] = nodeLeafCounts[otherIndex];

                if (otherChildIndex < 0)
                {
                    //It's a leaf. Update the leaf's reference in the leaves array.
                    var otherLeafIndex = Encode(otherChildIndex);
                    leaves[otherLeafIndex].NodeIndex = node->Parent;
                    leaves[otherLeafIndex].ChildIndex = node->IndexInParent;
                }
                else
                {
                    //It's an internal node. Update its parent node.
                    nodes[otherChildIndex].Parent = node->Parent;
                    nodes[otherChildIndex].IndexInParent = node->IndexInParent;

                }

                //Remove the now dead node.
                RemoveNodeAt(leaf.NodeIndex);

                //Work up the chain of parent pointers, refitting bounding boxes and decrementing leaf counts.
                //Note that this starts at the parent; we've already done the refit for the current level via collapse.
                RefitForRemoval(parentNode);

            }
            else
            {
                //The node has enough children that it should not collapse or it's the root; just need to remove the leaf.

                var lastChildIndex = node->ChildCount - 1;
                if (leaf.ChildIndex < lastChildIndex)
                {
                    //The removed leaf is not the last child of the node it belongs to.
                    //Move the last node into the position of the removed node.
                    nodeBounds[leaf.ChildIndex] = nodeBounds[lastChildIndex];
                    nodeChildren[leaf.ChildIndex] = nodeChildren[lastChildIndex];
                    nodeLeafCounts[leaf.ChildIndex] = nodeLeafCounts[lastChildIndex];
                    if (nodeChildren[lastChildIndex] >= 0)
                    {
                        //The moved child is an internal node.
                        //Update the child's IndexInParent pointer.
                        var movedInternalNode = nodes + nodeChildren[lastChildIndex];
                        movedInternalNode->IndexInParent = leaf.ChildIndex;
                    }
                    else
                    {
                        //The moved node is a leaf, so update the leaf array's reference.
                        leaves[Encode(nodeChildren[lastChildIndex])].ChildIndex = leaf.ChildIndex;
                    }
                }
                //Clear out the last slot.
                (&node->A)[lastChildIndex] = new BoundingBox { Min = new Vector3(float.MaxValue), Max = new Vector3(float.MinValue) };
                (&node->ChildA)[lastChildIndex] = -1;
                (&node->LeafCountA)[lastChildIndex] = 0;
                --node->ChildCount;

                //Work up the chain of parent pointers, refitting bounding boxes and decrementing leaf counts.
                RefitForRemoval(node);
            }

            return new LeafMove { OriginalIndex = lastIndex, NewIndex = leafIndex };
        }
        unsafe int OptimizeDFS(int optimizedParentIndex, int nodeIndex, Node* optimizedNodes, Leaf* optimizedLeaves, ref int optimizedNodeCount, ref int optimizedLeafCount)
        {
            var node = nodes + nodeIndex;
            var optimizedNodeIndex = optimizedNodeCount++;
            var optimizedNode = optimizedNodes + optimizedNodeIndex;
            *optimizedNode = *node;
            optimizedNode->Parent = optimizedParentIndex;
            var children = &optimizedNode->ChildA;
            for (int i = 0; i < node->ChildCount; ++i)
            {
                if (children[i] >= 0)
                {
                    children[i] = OptimizeDFS(optimizedNodeIndex, children[i], optimizedNodes, optimizedLeaves, ref optimizedNodeCount, ref optimizedLeafCount);
                }
                else
                {
                    var leafIndex = Encode(children[i]);
                    var optimizedLeafIndex = optimizedLeafCount++;
                    var optimizedLeaf = optimizedLeaves + optimizedLeafIndex;
                    optimizedLeaf->Id = leaves[leafIndex].Id;
                    optimizedLeaf->NodeIndex = optimizedNodeIndex;
                    optimizedLeaf->ChildIndex = i;
                    children[i] = Encode(optimizedLeafIndex);

                }
            }
            return optimizedNodeIndex;
        }
        unsafe void OptimizeGroupDFS(int optimizedNodeIndex, int optimizedParentNodeIndex, int nodeIndex, Node* optimizedNodes, Leaf* optimizedLeaves, ref int optimizedNodeCount, ref int optimizedLeafCount)
        {
            var node = nodes + nodeIndex;

            var optimizedNode = optimizedNodes + optimizedNodeIndex;
            *optimizedNode = *node;
            optimizedNode->Parent = optimizedParentNodeIndex;
            var optimizedChildren = &optimizedNode->ChildA;
            var nodeChildren = &node->ChildA;

            for (int i = 0; i < node->ChildCount; ++i)
            {

                if (nodeChildren[i] >= 0)
                {
                    optimizedChildren[i] = optimizedNodeCount++;

                }
                else
                {
                    var leafIndex = Encode(nodeChildren[i]);
                    var optimizedLeafIndex = optimizedLeafCount++;
                    var optimizedLeaf = optimizedLeaves + optimizedLeafIndex;
                    optimizedLeaf->Id = leaves[leafIndex].Id;
                    optimizedLeaf->NodeIndex = optimizedNodeIndex;
                    optimizedLeaf->ChildIndex = i;
                    optimizedChildren[i] = Encode(optimizedLeafIndex);

                }
            }
            for (int i = 0; i < node->ChildCount; ++i)
            {
                if (nodeChildren[i] >= 0)
                {
                    OptimizeGroupDFS(optimizedChildren[i], optimizedNodeIndex, nodeChildren[i], optimizedNodes, optimizedLeaves, ref optimizedNodeCount, ref optimizedLeafCount);
                }
            }
        }
 unsafe void OptimizeGroupDFS(Node* optimizedNodes, Leaf* optimizedLeaves, ref int optimizedNodeCount, ref int optimizedLeafCount)
 {
     optimizedNodeCount = 1;
     OptimizeGroupDFS(0, -1, 0, optimizedNodes, optimizedLeaves, ref optimizedNodeCount, ref optimizedLeafCount);
 }
示例#8
0
        public unsafe LeafMove RemoveAt(int leafIndex)
        {
            if (leafIndex < 0 || leafIndex >= leafCount)
            {
                throw new ArgumentOutOfRangeException("Leaf index must be a valid index in the tree's leaf array.");
            }

            //Cache the leaf before overwriting.
            var leaf = leaves[leafIndex];

            //Delete the leaf from the leaves array.
            //This is done up front to make it easier for tests to catch bad behavior.
            var lastIndex = leafCount - 1;

            if (lastIndex != leafIndex)
            {
                //The removed leaf was in the middle of the leaves array. Take the last leaf and use it to fill the slot.
                //The node owner's index must be updated to point to the new location.
                var lastLeafOwner = nodes + leaves[lastIndex].NodeIndex;
                (&lastLeafOwner->ChildA)[leaves[lastIndex].ChildIndex] = Encode(leafIndex);
                leaves[leafIndex] = leaves[lastIndex];
            }
            leaves[lastIndex] = new Leaf();
            leafCount         = lastIndex;

            var node           = nodes + leaf.NodeIndex;
            var nodeChildren   = &node->ChildA;
            var nodeBounds     = &node->A;
            var nodeLeafCounts = &node->LeafCountA;

            //Remove the leaf from this node.
            //Note that the children must remain contiguous. Requires taking the last child of the node and moving into the slot
            //if the removed child was not the last child.
            //Further, if a child is moved and if it is a leaf, that leaf's ChildIndex must be updated.
            //If a child is moved and it is an internal node, all immediate children of that node must have their parent nodes updated.

            //Check to see if this node should collapse.
            if (node->ChildCount == 2 &&
                node->Parent >= 0) //The root cannot 'collapse'. The root is the only node that can end up with 1 child.
            {
                Debug.Assert(node->ChildCount != 1);
                //If there are only two children in the node, then the node containing the removed leaf will collapse.
                var otherIndex      = leaf.ChildIndex == 0 ? 1 : 0;
                var otherChildIndex = nodeChildren[otherIndex];

                //Move the other node into the slot that used to point to the collapsing internal node.
                var parentNode = nodes + node->Parent;
                (&parentNode->A)[node->IndexInParent]          = nodeBounds[otherIndex];
                (&parentNode->ChildA)[node->IndexInParent]     = otherChildIndex;
                (&parentNode->LeafCountA)[node->IndexInParent] = nodeLeafCounts[otherIndex];

                if (otherChildIndex < 0)
                {
                    //It's a leaf. Update the leaf's reference in the leaves array.
                    var otherLeafIndex = Encode(otherChildIndex);
                    leaves[otherLeafIndex].NodeIndex  = node->Parent;
                    leaves[otherLeafIndex].ChildIndex = node->IndexInParent;
                }
                else
                {
                    //It's an internal node. Update its parent node.
                    nodes[otherChildIndex].Parent        = node->Parent;
                    nodes[otherChildIndex].IndexInParent = node->IndexInParent;
                }


                //Remove the now dead node.
                RemoveNodeAt(leaf.NodeIndex);

                //Work up the chain of parent pointers, refitting bounding boxes and decrementing leaf counts.
                //Note that this starts at the parent; we've already done the refit for the current level via collapse.
                RefitForRemoval(parentNode);
            }
            else
            {
                //The node has enough children that it should not collapse or it's the root; just need to remove the leaf.

                var lastChildIndex = node->ChildCount - 1;
                if (leaf.ChildIndex < lastChildIndex)
                {
                    //The removed leaf is not the last child of the node it belongs to.
                    //Move the last node into the position of the removed node.
                    nodeBounds[leaf.ChildIndex]     = nodeBounds[lastChildIndex];
                    nodeChildren[leaf.ChildIndex]   = nodeChildren[lastChildIndex];
                    nodeLeafCounts[leaf.ChildIndex] = nodeLeafCounts[lastChildIndex];
                    if (nodeChildren[lastChildIndex] >= 0)
                    {
                        //The moved child is an internal node.
                        //Update the child's IndexInParent pointer.
                        var movedInternalNode = nodes + nodeChildren[lastChildIndex];
                        movedInternalNode->IndexInParent = leaf.ChildIndex;
                    }
                    else
                    {
                        //The moved node is a leaf, so update the leaf array's reference.
                        leaves[Encode(nodeChildren[lastChildIndex])].ChildIndex = leaf.ChildIndex;
                    }
                }
                //Clear out the last slot.
                (&node->A)[lastChildIndex] = new BoundingBox {
                    Min = new Vector3(float.MaxValue), Max = new Vector3(float.MinValue)
                };
                (&node->ChildA)[lastChildIndex]     = -1;
                (&node->LeafCountA)[lastChildIndex] = 0;
                --node->ChildCount;

                //Work up the chain of parent pointers, refitting bounding boxes and decrementing leaf counts.
                RefitForRemoval(node);
            }



            return(new LeafMove {
                OriginalIndex = lastIndex, NewIndex = leafIndex
            });
        }