internal override bool TryToInsert(LeafNode node, out Node treeNode) { InternalNode newTreeNode = InternalNode.nodePool.Take(); BoundingBox.CreateMerged(ref BoundingBox, ref node.BoundingBox, out newTreeNode.BoundingBox); Vector3 offset; Vector3.Subtract(ref newTreeNode.BoundingBox.Max, ref newTreeNode.BoundingBox.Min, out offset); newTreeNode.currentVolume = offset.X * offset.Y * offset.Z; newTreeNode.childA = this; newTreeNode.childB = node; treeNode = newTreeNode; return(true); }
/// <summary> /// Adds an entry to the hierarchy. /// </summary> /// <param name="entry">Entry to add.</param> public override void Add(BroadPhaseEntry entry) { base.Add(entry); //Entities do not set up their own bounding box before getting stuck in here. If they're all zeroed out, the tree will be horrible. Vector3 offset; Vector3.Subtract(ref entry.boundingBox.Max, ref entry.boundingBox.Min, out offset); if (offset.X * offset.Y * offset.Z == 0) { entry.UpdateBoundingBox(); } //Could buffer additions to get a better construction in the tree. LeafNode node = leafNodes.Take(); node.Initialize(entry); if (root == null) { //Empty tree. This is the first and only node. root = node; } else { if (root.IsLeaf) //Root is alone. { root.TryToInsert(node, out root); } else { BoundingBox.CreateMerged(ref node.BoundingBox, ref root.BoundingBox, out root.BoundingBox); InternalNode internalNode = (InternalNode)root; Vector3.Subtract(ref root.BoundingBox.Max, ref root.BoundingBox.Min, out offset); internalNode.currentVolume = offset.X * offset.Y * offset.Z; //internalNode.maximumVolume = internalNode.currentVolume * InternalNode.MaximumVolumeScale; //The caller is responsible for the merge. Node treeNode = root; while (!treeNode.TryToInsert(node, out treeNode)) { ; //TryToInsert returns the next node, if any, and updates node bounding box. } } } }
private void Reconstruct(RawList <LeafNode> leafNodes, int begin, int end) { //It is known that we have 2 children; this is safe. //This is because this is only an internal node if the parent figured out it involved more than 2 leaf nodes, OR //this node was the initiator of the revalidation (in which case, it was an internal node with 2+ children). BoundingBox.CreateMerged(ref leafNodes.Elements[begin].BoundingBox, ref leafNodes.Elements[begin + 1].BoundingBox, out BoundingBox); for (int i = begin + 2; i < end; i++) { BoundingBox.CreateMerged(ref BoundingBox, ref leafNodes.Elements[i].BoundingBox, out BoundingBox); } Vector3 offset; Vector3.Subtract(ref BoundingBox.Max, ref BoundingBox.Min, out offset); currentVolume = offset.X * offset.Y * offset.Z; maximumVolume = currentVolume * MaximumVolumeScale; //Pick an axis and sort along it. if (offset.X > offset.Y && offset.X > offset.Z) //Maximum variance axis is X. { Array.Sort(leafNodes.Elements, begin, end - begin, xComparer); } else if (offset.Y > offset.Z) //Maximum variance axis is Y. { Array.Sort(leafNodes.Elements, begin, end - begin, yComparer); } else //Maximum variance axis is Z. { Array.Sort(leafNodes.Elements, begin, end - begin, zComparer); } //Find the median index. int median = (begin + end) / 2; if (median - begin >= 2) { //There are 2 or more leaf nodes remaining in the first half. The next childA will be an internal node. InternalNode newChildA = nodePool.Take(); newChildA.Reconstruct(leafNodes, begin, median); childA = newChildA; } else { //There is only 1 leaf node remaining in this half. It's a leaf node. childA = leafNodes.Elements[begin]; } if (end - median >= 2) { //There are 2 or more leaf nodes remaining in the second half. The next childB will be an internal node. InternalNode newChildB = nodePool.Take(); newChildB.Reconstruct(leafNodes, median, end); childB = newChildB; } else { //There is only 1 leaf node remaining in this half. It's a leaf node. childB = leafNodes.Elements[median]; } }
internal override bool TryToInsert(LeafNode node, out Node treeNode) { //Since we are an internal node, we know we have two children. //Regardless of what kind of nodes they are, figure out which would be a better choice to merge the new node with. //Use the path which produces the smallest 'volume.' BoundingBox mergedA, mergedB; BoundingBox.CreateMerged(ref childA.BoundingBox, ref node.BoundingBox, out mergedA); BoundingBox.CreateMerged(ref childB.BoundingBox, ref node.BoundingBox, out mergedB); Vector3 offset; float originalAVolume, originalBVolume; Vector3.Subtract(ref childA.BoundingBox.Max, ref childA.BoundingBox.Min, out offset); originalAVolume = offset.X * offset.Y * offset.Z; Vector3.Subtract(ref childB.BoundingBox.Max, ref childB.BoundingBox.Min, out offset); originalBVolume = offset.X * offset.Y * offset.Z; float mergedAVolume, mergedBVolume; Vector3.Subtract(ref mergedA.Max, ref mergedA.Min, out offset); mergedAVolume = offset.X * offset.Y * offset.Z; Vector3.Subtract(ref mergedB.Max, ref mergedB.Min, out offset); mergedBVolume = offset.X * offset.Y * offset.Z; //Could use factor increase or absolute difference if (mergedAVolume - originalAVolume < mergedBVolume - originalBVolume) { //merging A produces a better result. if (childA.IsLeaf) { InternalNode newChildA = nodePool.Take(); newChildA.BoundingBox = mergedA; newChildA.childA = childA; newChildA.childB = node; newChildA.currentVolume = mergedAVolume; childA = newChildA; treeNode = null; return(true); } childA.BoundingBox = mergedA; InternalNode internalNode = (InternalNode)childA; internalNode.currentVolume = mergedAVolume; treeNode = childA; return(false); } //merging B produces a better result. if (childB.IsLeaf) { //Target is a leaf! Return. InternalNode newChildB = nodePool.Take(); newChildB.BoundingBox = mergedB; newChildB.childA = node; newChildB.childB = childB; newChildB.currentVolume = mergedBVolume; childB = newChildB; treeNode = null; return(true); } { childB.BoundingBox = mergedB; treeNode = childB; InternalNode internalNode = (InternalNode)childB; internalNode.currentVolume = mergedBVolume; return(false); } }