示例#1
0
        void CornerBasedSplit(SpatialNode node, ISpatialObject obj, out SpatialNode splitNode)
        {
            foreach (var cornerChildren in m_CornerSplitChildren)
            {
                cornerChildren.Clear();
            }

            // separate children by their closest corners
            m_CornerSplitChildren[FindClosestCornerIndex(node, obj)].Add(obj);
            foreach (var child in node.children)
            {
                m_CornerSplitChildren[FindClosestCornerIndex(node, child)].Add(child);
            }

            // split children by closest corners along each axis
            var splitLowX  = new List <ISpatialObject>();
            var splitHighX = new List <ISpatialObject>();
            var splitLowY  = new List <ISpatialObject>();
            var splitHighY = new List <ISpatialObject>();
            var splitLowZ  = new List <ISpatialObject>();
            var splitHighZ = new List <ISpatialObject>();

            for (var i = 0; i < m_CornerSplitChildren.Length; ++i)
            {
                ((i >> 2) % 2 > 0 ? splitHighX : splitLowX).AddRange(m_CornerSplitChildren[i]);
                ((i >> 1) % 2 > 0 ? splitHighY : splitLowY).AddRange(m_CornerSplitChildren[i]);
                (i % 2 > 0 ? splitHighZ : splitLowZ).AddRange(m_CornerSplitChildren[i]);
            }

            // find the diff between the split node child counts
            var dx = Mathf.Abs(splitLowX.Count - splitHighX.Count);
            var dy = Mathf.Abs(splitLowY.Count - splitHighY.Count);
            var dz = Mathf.Abs(splitLowZ.Count - splitHighZ.Count);

            var bestSplits = new List <int>();

            if (dx <= dy && dx <= dz)
            {
                bestSplits.Add(k_X);
            }
            if (dy <= dx && dy <= dz)
            {
                bestSplits.Add(k_Y);
            }
            if (dz <= dx && dz <= dy)
            {
                bestSplits.Add(k_Z);
            }

            // handle tiebreakers if multiple equal counts
            var bestSplit = 0;

            if (bestSplits.Count == 1)
            {
                bestSplit = bestSplits[0];
            }
            else
            {
                var overlaps = new List <float>();
                var volumes  = new List <float>();
                foreach (var axis in bestSplits)
                {
                    // tiebreaker uses overlaps and volume totals
                    switch (axis)
                    {
                    case k_X:
                        CalculateMinMax(splitLowX, out var minLowX, out var maxLowX);
                        CalculateMinMax(splitHighX, out var minHighX, out var maxHighX);
                        overlaps.Add(CalculateOverlap(minLowX, maxLowX, minHighX, maxHighX));
                        volumes.Add(CalculateVolume(minLowX, maxLowX) + CalculateVolume(minHighX, maxHighX));
                        break;

                    case k_Y:
                        CalculateMinMax(splitLowY, out var minLowY, out var maxLowY);
                        CalculateMinMax(splitHighY, out var minHighY, out var maxHighY);
                        overlaps.Add(CalculateOverlap(minLowY, maxLowY, minHighY, maxHighY));
                        volumes.Add(CalculateVolume(minLowY, maxLowY) + CalculateVolume(minHighY, maxHighY));
                        break;

                    case k_Z:
                        CalculateMinMax(splitLowZ, out var minLowZ, out var maxLowZ);
                        CalculateMinMax(splitHighZ, out var minHighZ, out var maxHighZ);
                        overlaps.Add(CalculateOverlap(minLowZ, maxLowZ, minHighZ, maxHighZ));
                        volumes.Add(CalculateVolume(minLowZ, maxLowZ) + CalculateVolume(minHighZ, maxHighZ));
                        break;
                    }
                }

                var bestOverlap = float.MaxValue;
                var bestVolume  = float.MaxValue;
                for (var i = 0; i < bestSplits.Count; ++i)
                {
                    // check overlap first, if equal check total volume
                    if (overlaps[i] > bestOverlap ||
                        Mathf.Approximately(overlaps[i], bestOverlap) && volumes[i] > bestVolume)
                    {
                        continue;
                    }

                    bestSplit   = bestSplits[i];
                    bestOverlap = overlaps[i];
                    bestVolume  = volumes[i];
                }
            }

            SpatialNode smallNode = null;
            SpatialNode bigNode   = null;

            node.children.Clear();
            splitNode = new SpatialNode {
                parent = node.parent
            };

            var nodeComparer      = Comparer <ISpatialObject> .Default;
            var splitNodeComparer = Comparer <ISpatialObject> .Default;

            switch (bestSplit)
            {
            case k_X:
                node.children.AddRange(splitLowX);
                nodeComparer = m_NodeComparerX;
                splitNode.children.AddRange(splitHighX);
                splitNodeComparer = m_SplitNodeComparerX;
                break;

            case k_Y:
                node.children.AddRange(splitLowY);
                nodeComparer = m_NodeComparerY;
                splitNode.children.AddRange(splitHighY);
                splitNodeComparer = m_SplitNodeComparerY;
                break;

            case k_Z:
                node.children.AddRange(splitLowZ);
                nodeComparer = m_NodeComparerZ;
                splitNode.children.AddRange(splitHighZ);
                splitNodeComparer = m_SplitNodeComparerZ;
                break;
            }

            // if a node has too few children, sort them by distance from split axis and get ready for next step
            if (node.children.Count < m_MinPerNode && nodeComparer != null)
            {
                node.children.Sort(nodeComparer);
                smallNode = node;
                bigNode   = splitNode;
            }
            else if (splitNode.children.Count < m_MinPerNode && splitNodeComparer != null)
            {
                splitNode.children.Sort(splitNodeComparer);
                smallNode = splitNode;
                bigNode   = node;
            }

            // move closest children from big node to small node until small node has the minimum amount required
            if (smallNode != null && bigNode != null)
            {
                var amount = m_MinPerNode - smallNode.children.Count;
                var index  = bigNode.children.Count - amount;
                smallNode.children.AddRange(bigNode.children.GetRange(index, amount));
                bigNode.children.RemoveRange(index, amount);
            }

            node.AdjustBounds();
            splitNode.AdjustBounds();
            foreach (var child in splitNode.children)
            {
                if (child is SpatialNode childNode)
                {
                    childNode.parent = splitNode;
                }
            }
        }
示例#2
0
 static int FindClosestCornerIndex(SpatialNode node, ISpatialObject obj)
 {
     return((obj.center.x <= node.center.x ? 0 : k_X) +
            (obj.center.y <= node.center.y ? 0 : k_Y) +
            (obj.center.z <= node.center.z ? 0 : k_Z));
 }
示例#3
0
 void SplitNode(SpatialNode node, ISpatialObject obj, out SpatialNode splitNode)
 {
     CornerBasedSplit(node, obj, out splitNode);
 }