线性八叉树

  最近有个实际用例, 需要用到空间相关的测试, 大概如下:

  一个水体的数据, 是由很多微小的点组成的, 这些点就是体积水, 我们要表现的话, 需要从这些点中找出能代表水体表面的点, 然后划分成三角面, 组成水体表面的Mesh, 之后加个材质就能展现了.

  查找这些表面顶点的方法就比较麻烦了, 目前没有找到直接的方法, 貌似点云扫描数据的表面重建这些能有用, 没时间去看, 直接用八叉树划分空间, 然后把点数据放到相应的划分里去, 从上往下找到有点的空间, 基本就是表面了, 它的好处呢就是空间的划分可以自己设定大小, 等于是一种LOD的方法了, 这里实现了一个线性的八叉树, 可以对这种情况进行处理:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BoxOcTree<T>
{
    public static System.Func<T, Vector3> DataToPosition;

    // cache datas
    public Bounds bounds;
    public BoxOcTree<T> parent;
    public BoxOcTree<T>[] trees = null;
    public List<T> datas = null;

    public bool ContainsData(T data)
    {
        if(DataToPosition != null)
        {
            var pos = DataToPosition.Invoke(data);
            return bounds.Contains(pos);
        }
        return false;
    }

    public bool ContainsPos(Vector3 pos, bool incursive = false)
    {
        var succ = bounds.Contains(pos);
        if(incursive)
        {
            if(trees != null && trees.Length > 0)
            {
                foreach(var tree in trees)
                {
                    if(tree.ContainsPos(pos, incursive))
                    {
                        return true;
                    }
                }
            }
        }
        return succ;
    }

    public BoxOcTree<T> FindEndNode(Vector3 pos)
    {
        var succ = bounds.Contains(pos);
        if(false == succ)
        {
            return null;
        }
        if(trees != null && trees.Length > 0)
        {
            foreach(var tree in trees)
            {
                if(tree != null)
                {
                    var tagNode = tree.FindEndNode(pos);
                    if(tagNode != null)
                    {
                        return tagNode;
                    }
                }
            }
        }
        return this;
    }

    public BoxOcTree<T> PushDataToTree(T data)
    {
        var pos = BoxOcTree<T>.DataToPosition(data);
        var tree = FindEndNode(pos);
        if(tree != null)
        {
            if(tree.datas == null)
            {
                tree.datas = new List<T>();
            }
            tree.datas.Add(data);
            return tree;
        }
        return null;
    }

    public bool HasData(bool recursive)
    {
        var hasData = datas != null && datas.Count > 0;
        if(false == recursive)
        {
            return hasData;
        }
        var hasAnyData = hasData;
        if(false == hasAnyData)
        {
            if(trees != null)
            {
                for(int i = trees.Length - 1; i >= 0; i--)
                {
                    var tree = trees[i];
                    if(tree != null && tree.HasData(recursive))
                    {
                        hasAnyData = true;
                        break;
                    }
                }
            }
        }
        return hasAnyData;
    }

    public void ClearNoData(bool recursive)
    {
        if(parent == null)
        {
            return;
        }
        if(HasData(recursive) == false)
        {
            Destroy(recursive);
        }
    }

    public void Destroy(bool recursive)
    {
        if(parent != null)
        {
            var index = System.Array.IndexOf<BoxOcTree<T>>(parent.trees, this);
            if(index >= 0)
            {
                parent.trees[index] = null;

                if(recursive)
                {
                    parent.ClearNoData(recursive);
                }
            }
            parent = null;
        }
    }

    public BoxOcTree<T> GetTailTree()
    {
        if(trees != null && trees.Length > 0)
        {
            foreach(var tree in trees)
            {
                if(tree != null)
                {
                    return tree.GetTailTree();
                }
            }
        }
        return this;
    }

    public bool IsSibling(BoxOcTree<T> other)
    {
        if(other != this && other.parent != null)
        {
            return other.parent == this.parent;
        }
        return false;
    }

    public BoxOcTree<T>[] GetSiblings()
    {
        if(this.parent != null)
        {
            return this.parent.trees;
        }
        return null;
    }
}

  目前的逻辑是先创建空间划分, 然后再把数据填入, 最后可以通过删除无数据的节点, 来加速计算.  

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class BoxOcTreeUtilities
{
    public const float Epsilon = 0.0001f;

    #region Main Funcs
    public static BoxOcTree<T> Create<T>(Bounds bounds, float minSize)
    {
        var root = new BoxOcTree<T>();
        root.bounds = bounds;

        List<BoxOcTree<T>> boxOcTrees = new List<BoxOcTree<T>>();
        OcTreeSpaceAccess(bounds, minSize, (_bounds) =>
        {
            var child = Create<T>(_bounds, minSize);
            child.parent = root;
            boxOcTrees.Add(child);
        });
        root.trees = boxOcTrees.ToArray();

        return root;
    }

    public static BoxOcTree<T> FindEndNodeContainsPos<T>(BoxOcTree<T> root, Vector3 pos)
    {
        return root.FindEndNode(pos);
    }

    public static BoxOcTree<T> PushDataToTree<T>(BoxOcTree<T> root, T data, bool createNoExists = false)
    {
        var pos = BoxOcTree<T>.DataToPosition(data);
        var tree = FindEndNodeContainsPos(root, pos);
        if(tree != null)
        {
            if(tree.datas == null)
            {
                tree.datas = new List<T>();
            }
            tree.datas.Add(data);
        }
        else
        {
            if(createNoExists)
            {

            }
        }
        return tree;
    }

    public static void LoopTreeEnd<T>(BoxOcTree<T> root, System.Action<BoxOcTree<T>> access)
    {
        try
        {
            if(root != null)
            {
                if(root.trees != null && root.trees.Length > 0)
                {
                    foreach(var tree in root.trees)
                    {
                        LoopTreeEnd(tree, access);
                    }
                }
                else
                {
                    access.Invoke(root);
                }
            }
        }
        catch(System.Exception ex)
        {
            Debug.LogError(ex.ToString());
        }
    }

    public static HashSet<BoxOcTree<T>> SelectSurface<T>(BoxOcTree<T> root)
    {
        var startPoints = new HashSet<BoxOcTree<T>>();
        var center = root.bounds.center;
        var areaSize = root.bounds.size;
        var tailTree = root.GetTailTree();
        var cellSize = tailTree.bounds.size;
        var xCount = Mathf.FloorToInt(areaSize.x / cellSize.x);
        var yCount = Mathf.FloorToInt(areaSize.y / cellSize.y);
        var zCount = Mathf.FloorToInt(areaSize.z / cellSize.z);
        var xStart = (-areaSize.x + cellSize.x) * 0.5f;
        var yStart = (areaSize.y - cellSize.y) * 0.5f;
        var zStart = (-areaSize.z + cellSize.z) * 0.5f;

        for(int i = 0; i < xCount; i++)
        {
            for(int j = 0; j < zCount; j++)
            {
                for(int k = 0; k < yCount; k++)
                {
                    var x = xStart + i * cellSize.x;
                    var y = yStart - k * cellSize.y;
                    var z = zStart + j * cellSize.z;

                    var point = new Vector3(x, y, z) + center;
                    var endNode = root.FindEndNode(point);
                    if(endNode != null && endNode.HasData(false))
                    {
                        if(startPoints.Add(endNode) == false)
                        {
                            Debug.LogWarning("Exists");
                        }
                        break;
                    }
                }
            }
        }
        return startPoints;
    }

    public static Dictionary<int, HashSet<BoxOcTree<T>>> SeparateTree<T>(BoxOcTree<T> root)
    {
        Dictionary<int, HashSet<BoxOcTree<T>>> sep = new Dictionary<int, HashSet<BoxOcTree<T>>>();
        HashSet<BoxOcTree<T>> all = new HashSet<BoxOcTree<T>>();
        List<BoxOcTree<T>> cacheList = new List<BoxOcTree<T>>();
        int index = 0;
        LoopTreeEnd(root, (_node) =>
        {
            all.Add(_node);
        });
        while(all.Count > 0)
        {
            var item = all.First();
            var sibilings = item.GetSiblings();

            var openList = new HashSet<BoxOcTree<T>>();
            var closeList = sep.GetValue(index);
            foreach(var sibiling in sibilings)
            {
                if(sibiling != null)
                {
                    openList.Add(sibiling);
                }
            }

            while(openList.Count > 0)
            {
                var first = openList.First();
                if(first == null)
                {
                    Debug.LogError("First NULL...");
                }
                openList.Remove(first);
                closeList.Add(first);
                if(GetNearbyNodes(cacheList, first, root, true))
                {
                    foreach(var nearNode in cacheList)
                    {
                        if(nearNode != null)
                        {
                            if(closeList.Contains(nearNode) == false)
                            {
                                openList.Add(nearNode);
                            }
                        }
                    }
                }
            }
            all.ExceptWith(closeList);
            index++;
        }

        return sep;
    }
    #endregion

    #region Help Funcs
    public static void OcTreeSpaceAccess(Bounds bounds, float minSize, System.Action<Bounds> boundsAccess)
    {
        var minSizeDouble = minSize * 2f;
        var boundMax = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z);
        if(boundMax >= minSizeDouble)
        {
            float[] xList = new float[0];
            float[] yList = new float[0];
            float[] zList = new float[0];
            OcTreeSpaceSep(bounds.size.x, minSizeDouble, bounds.center.x, ref xList);
            OcTreeSpaceSep(bounds.size.y, minSizeDouble, bounds.center.y, ref yList);
            OcTreeSpaceSep(bounds.size.z, minSizeDouble, bounds.center.z, ref zList);
            foreach(var x in xList)
            {
                foreach(var y in yList)
                {
                    foreach(var z in zList)
                    {
                        var center = new Vector3(x, y, z);
                        var size_x = DistanceCheckTheSame(bounds.min.x, bounds.max.x, x) ? bounds.size.x : bounds.size.x * 0.5f;
                        var size_y = DistanceCheckTheSame(bounds.min.y, bounds.max.y, y) ? bounds.size.y : bounds.size.y * 0.5f;
                        var size_z = DistanceCheckTheSame(bounds.min.z, bounds.max.z, z) ? bounds.size.z : bounds.size.z * 0.5f;
                        boundsAccess.Invoke(new Bounds(center, new Vector3(size_x, size_y, size_z)));
                    }
                }
            }
        }
    }

    private static void OcTreeSpaceSep(float size, float minSize, float center, ref float[] array)
    {
        if(size >= minSize)
        {
            array = new float[2];
            array[0] = center - (size * 0.25f);
            array[1] = center + (size * 0.25f);
        }
        else
        {
            array = new float[1];
            array[0] = center;
        }
    }

    private static bool DistanceCheckTheSame(float left, float right, float value)
    {
        return Mathf.Abs(Mathf.Abs(left - value) - Mathf.Abs(right - value)) < Epsilon;
    }

    public static bool GetNearbyNodes<T>(List<BoxOcTree<T>> cacheList, BoxOcTree<T> node, BoxOcTree<T> root, bool checkTail = false)
    {
        cacheList.Clear();
        var up = node.bounds.center + Vector3.Scale(Vector3.up, node.bounds.size);
        var down = node.bounds.center - Vector3.Scale(Vector3.up, node.bounds.size);
        var left = node.bounds.center + Vector3.Scale(Vector3.left, node.bounds.size);
        var right = node.bounds.center - Vector3.Scale(Vector3.left, node.bounds.size);
        var front = node.bounds.center + Vector3.Scale(Vector3.forward, node.bounds.size);
        var back = node.bounds.center - Vector3.Scale(Vector3.forward, node.bounds.size);

        Add(ref cacheList, root.FindEndNode(up), checkTail);
        Add(ref cacheList, root.FindEndNode(down), checkTail);
        Add(ref cacheList, root.FindEndNode(left), checkTail);
        Add(ref cacheList, root.FindEndNode(right), checkTail);
        Add(ref cacheList, root.FindEndNode(front), checkTail);
        Add(ref cacheList, root.FindEndNode(back), checkTail);
        return cacheList.Count > 0;
    }

    public static bool IsTailNode<T>(BoxOcTree<T> node)
    {
        return node != null && (node.trees == null || node.trees.Length == 0);
    }

    private static void Add<T>(ref List<BoxOcTree<T>> list, BoxOcTree<T> node, bool checkTail = false)
    {
        if(node != null)
        {
            if(checkTail && IsTailNode(node) == false)
            {
                return;
            }
            if(list == null)
            {
                list = new List<BoxOcTree<T>>();
            }
            list.Add(node);
        }
    }
    #endregion

    #region Presetting
#if UNITY_EDITOR
    [UnityEditor.InitializeOnLoadMethod()]
#else
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
#endif
    public static void Init()
    {
        BoxOcTree<Vector3>.DataToPosition = (_raw) => { return _raw; };
        BoxOcTree<Transform>.DataToPosition = (_trans) => { return _trans.position; };
        BoxOcTree<GameObject>.DataToPosition = (_go) => { return _go.transform.position; };

        Debug.Log("BoxOcTreeUtilities Init");
    }
    #endregion
}

  第一版基本就是这样了.

-------------------------------------------------------------------

  第二版, 在创建八叉树根节点的时候, 不创建任何空间划分, 在需要获取最后子节点的时候再进行查找和创建, 可以节省大量的时间. 因为即使提前划分好, 在最终阶段还是要清理没有数据的多余划分的...

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BoxOcTree<T>
{
    public static System.Func<T, Vector3> DataToPosition;

    // cache datas
    public Bounds bounds;
    public BoxOcTree<T> parent;
    public BoxOcTree<T>[] trees = null;
    public List<T> datas = null;
    public int layer = 1;

    public bool ContainsData(T data)
    {
        if(DataToPosition != null)
        {
            var pos = DataToPosition.Invoke(data);
            return bounds.Contains(pos);
        }
        return false;
    }

    public bool ContainsPos(Vector3 pos)
    {
        var succ = bounds.Contains(pos);
        return succ;
    }

    public BoxOcTree<T> FindEndNode(Vector3 pos)
    {
        var succ = bounds.Contains(pos);
        if(false == succ)
        {
            return null;
        }
        if(trees != null && trees.Length > 0)
        {
            foreach(var tree in trees)
            {
                if(tree != null)
                {
                    var tagNode = tree.FindEndNode(pos);
                    if(tagNode != null)
                    {
                        return tagNode;
                    }
                }
            }
        }
        return this;
    }

    public void AddData(T data)
    {
        if(this.datas == null)
        {
            this.datas = new List<T>();
        }
        this.datas.Add(data);
    }

    public BoxOcTree<T> PushDataToTree(T data)
    {
        var pos = BoxOcTree<T>.DataToPosition(data);
        var tree = FindEndNode(pos);
        if(tree != null)
        {
            tree.AddData(data);
            return tree;
        }
        return null;
    }

    public bool HasData(bool recursive)
    {
        var hasData = datas != null && datas.Count > 0;
        if(false == recursive)
        {
            return hasData;
        }
        var hasAnyData = hasData;
        if(false == hasAnyData)
        {
            if(trees != null)
            {
                for(int i = trees.Length - 1; i >= 0; i--)
                {
                    var tree = trees[i];
                    if(tree != null && tree.HasData(recursive))
                    {
                        hasAnyData = true;
                        break;
                    }
                }
            }
        }
        return hasAnyData;
    }

    public void ClearNoData(bool recursive)
    {
        if(parent == null)
        {
            return;
        }
        if(HasData(recursive) == false)
        {
            Destroy(recursive);
        }
    }

    public void Destroy(bool recursive)
    {
        if(parent != null)
        {
            var index = System.Array.IndexOf<BoxOcTree<T>>(parent.trees, this);
            if(index >= 0)
            {
                parent.trees[index] = null;

                if(recursive)
                {
                    parent.ClearNoData(recursive);
                }
            }
            parent = null;
        }
    }

    public BoxOcTree<T> GetTailTree()
    {
        if(trees != null && trees.Length > 0)
        {
            foreach(var tree in trees)
            {
                if(tree != null)
                {
                    return tree.GetTailTree();
                }
            }
        }
        return this;
    }

    public bool IsSibling(BoxOcTree<T> other)
    {
        if(other != this && other.parent != null)
        {
            return other.parent == this.parent;
        }
        return false;
    }

    public BoxOcTree<T>[] GetSiblings()
    {
        if(this.parent != null)
        {
            return this.parent.trees;
        }
        return null;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class BoxOcTreeUtilities
{
    public const float Epsilon = 0.0001f;
    public const int TreeBufferSize = 8;

    #region Main Funcs
    public static BoxOcTree<T> Create<T>(Bounds bounds, float minSize, int currentLayer = 1)
    {
        var root = CreateEmpty<T>(bounds, null, currentLayer);
        var nextLayer = currentLayer + 1;

        List<BoxOcTree<T>> boxOcTrees = new List<BoxOcTree<T>>();
        OcTreeSpaceAccess(bounds, minSize, (_bounds) =>
        {
            var child = Create<T>(_bounds, minSize, nextLayer);
            child.parent = root;
            boxOcTrees.Add(child);
            return true;
        });
        root.trees = boxOcTrees.ToArray();

        return root;
    }

    public static BoxOcTree<T> CreateEmpty<T>(Bounds bounds, BoxOcTree<T> parent = null, int currentLayer = 1)
    {
        var root = new BoxOcTree<T>();
        root.bounds = bounds;
        root.layer = currentLayer;
        root.parent = parent;
        return root;
    }

    public static BoxOcTree<T> RequireTargetNode<T>(BoxOcTree<T> treeNode, float minSize, Vector3 targetPos)
    {
        BoxOcTree<T> targetNode = treeNode.FindEndNode(targetPos);
        if(targetNode == null)
        {
            return null;
        }

        if(targetNode != null && IsTailNode(targetNode, minSize))
        {
            return targetNode;
        }
        var nextLayer = targetNode.layer + 1;
        OcTreeSpaceAccess(targetNode.bounds, minSize, (_bounds) =>
        {
            if(_bounds.Contains(targetPos))
            {
                var child = CreateEmpty<T>(_bounds, targetNode, nextLayer);
                if(targetNode.trees == null)
                {
                    targetNode.trees = GetTreeBuffer<T>();
                }
                else if(targetNode.trees.Length != TreeBufferSize)
                {
                    var temp = targetNode.trees;
                    targetNode.trees = GetTreeBuffer<T>();
                    System.Array.Copy(temp, 0, targetNode.trees, 0, temp.Length);
                }
                for(int i = 0; i < targetNode.trees.Length; i++)
                {
                    if(targetNode.trees[i] == null)
                    {
                        targetNode.trees[i] = child;
                        break;
                    }
                }

                targetNode = RequireTargetNode(child, minSize, targetPos);
                return false;
            }
            return true;
        });

        return targetNode;
    }

    public static BoxOcTree<T> PushDataToTree<T>(BoxOcTree<T> root, T data, float minSize = 1f, bool createNoExists = true)
    {
        var pos = BoxOcTree<T>.DataToPosition(data);
        var tree = createNoExists ? RequireTargetNode(root, minSize, pos) : root.FindEndNode(pos);
        if(tree != null)
        {
            tree.AddData(data);
        }
        return tree;
    }

    public static void LoopTreeEnd<T>(BoxOcTree<T> root, System.Action<BoxOcTree<T>> access)
    {
        try
        {
            if(root != null)
            {
                if(root.trees != null && root.trees.Length > 0)
                {
                    foreach(var tree in root.trees)
                    {
                        LoopTreeEnd(tree, access);
                    }
                }
                else
                {
                    access.Invoke(root);
                }
            }
        }
        catch(System.Exception ex)
        {
            Debug.LogError(ex.ToString());
        }
    }

    public static HashSet<BoxOcTree<T>> SelectSurface<T>(BoxOcTree<T> root)
    {
        var startPoints = new HashSet<BoxOcTree<T>>();
        var center = root.bounds.center;
        var areaSize = root.bounds.size;
        var tailTree = root.GetTailTree();
        var cellSize = tailTree.bounds.size;
        var xCount = Mathf.FloorToInt(areaSize.x / cellSize.x);
        var yCount = Mathf.FloorToInt(areaSize.y / cellSize.y);
        var zCount = Mathf.FloorToInt(areaSize.z / cellSize.z);
        var xStart = (-areaSize.x + cellSize.x) * 0.5f;
        var yStart = (areaSize.y - cellSize.y) * 0.5f;
        var zStart = (-areaSize.z + cellSize.z) * 0.5f;

        for(int i = 0; i < xCount; i++)
        {
            for(int j = 0; j < zCount; j++)
            {
                for(int k = 0; k < yCount; k++)
                {
                    var x = xStart + i * cellSize.x;
                    var y = yStart - k * cellSize.y;
                    var z = zStart + j * cellSize.z;

                    var point = new Vector3(x, y, z) + center;
                    var endNode = root.FindEndNode(point);
                    if(endNode != null && endNode.HasData(false))
                    {
                        if(startPoints.Add(endNode) == false)
                        {
                            Debug.LogWarning("Exists");
                        }
                        break;
                    }
                }
            }
        }
        return startPoints;
    }

    public static Dictionary<int, HashSet<BoxOcTree<T>>> SeparateTree<T>(BoxOcTree<T> root)
    {
        Dictionary<int, HashSet<BoxOcTree<T>>> sep = new Dictionary<int, HashSet<BoxOcTree<T>>>();
        HashSet<BoxOcTree<T>> all = new HashSet<BoxOcTree<T>>();
        List<BoxOcTree<T>> cacheList = new List<BoxOcTree<T>>();
        int index = 0;
        LoopTreeEnd(root, (_node) =>
        {
            all.Add(_node);
        });
        while(all.Count > 0)
        {
            var item = all.First();
            var sibilings = item.GetSiblings();

            var openList = new HashSet<BoxOcTree<T>>();
            var closeList = sep.GetValue(index);
            foreach(var sibiling in sibilings)
            {
                if(sibiling != null)
                {
                    openList.Add(sibiling);
                }
            }

            while(openList.Count > 0)
            {
                var first = openList.First();
                if(first == null)
                {
                    Debug.LogError("First NULL...");
                }
                openList.Remove(first);
                closeList.Add(first);
                if(GetNearbyNodes(cacheList, first, root, true))
                {
                    foreach(var nearNode in cacheList)
                    {
                        if(nearNode != null)
                        {
                            if(closeList.Contains(nearNode) == false)
                            {
                                openList.Add(nearNode);
                            }
                        }
                    }
                }
            }
            all.ExceptWith(closeList);
            index++;
        }

        return sep;
    }
    #endregion

    #region Help Funcs
    public static void OcTreeSpaceAccess(Bounds bounds, float minSize, System.Func<Bounds, bool> boundsAccess)
    {
        var minSizeDouble = minSize * 2f;
        var boundMax = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z);
        if(boundMax >= minSizeDouble)
        {
            float[] xList = new float[0];
            float[] yList = new float[0];
            float[] zList = new float[0];
            OcTreeSpaceSep(bounds.size.x, minSizeDouble, bounds.center.x, ref xList);
            OcTreeSpaceSep(bounds.size.y, minSizeDouble, bounds.center.y, ref yList);
            OcTreeSpaceSep(bounds.size.z, minSizeDouble, bounds.center.z, ref zList);
            bool running = true;
            foreach(var x in xList)
            {
                foreach(var y in yList)
                {
                    foreach(var z in zList)
                    {
                        var center = new Vector3(x, y, z);
                        var size_x = DistanceCheckTheSame(bounds.min.x, bounds.max.x, x) ? bounds.size.x : bounds.size.x * 0.5f;
                        var size_y = DistanceCheckTheSame(bounds.min.y, bounds.max.y, y) ? bounds.size.y : bounds.size.y * 0.5f;
                        var size_z = DistanceCheckTheSame(bounds.min.z, bounds.max.z, z) ? bounds.size.z : bounds.size.z * 0.5f;
                        running = boundsAccess.Invoke(new Bounds(center, new Vector3(size_x, size_y, size_z)));
                        if(false == running)
                        {
                            break;
                        }
                    }
                    if(false == running)
                    {
                        break;
                    }
                }
                if(false == running)
                {
                    break;
                }
            }
        }
    }

    private static void OcTreeSpaceSep(float size, float minSize, float center, ref float[] array)
    {
        if(size >= minSize)
        {
            array = new float[2];
            array[0] = center - (size * 0.25f);
            array[1] = center + (size * 0.25f);
        }
        else
        {
            array = new float[1];
            array[0] = center;
        }
    }

    private static bool DistanceCheckTheSame(float left, float right, float value)
    {
        return Mathf.Abs(Mathf.Abs(left - value) - Mathf.Abs(right - value)) < Epsilon;
    }

    public static bool GetNearbyNodes<T>(List<BoxOcTree<T>> cacheList, BoxOcTree<T> node, BoxOcTree<T> root, bool checkTail = false)
    {
        cacheList.Clear();
        var up = node.bounds.center + Vector3.Scale(Vector3.up, node.bounds.size);
        var down = node.bounds.center - Vector3.Scale(Vector3.up, node.bounds.size);
        var left = node.bounds.center + Vector3.Scale(Vector3.left, node.bounds.size);
        var right = node.bounds.center - Vector3.Scale(Vector3.left, node.bounds.size);
        var front = node.bounds.center + Vector3.Scale(Vector3.forward, node.bounds.size);
        var back = node.bounds.center - Vector3.Scale(Vector3.forward, node.bounds.size);

        Add(ref cacheList, root.FindEndNode(up), checkTail);
        Add(ref cacheList, root.FindEndNode(down), checkTail);
        Add(ref cacheList, root.FindEndNode(left), checkTail);
        Add(ref cacheList, root.FindEndNode(right), checkTail);
        Add(ref cacheList, root.FindEndNode(front), checkTail);
        Add(ref cacheList, root.FindEndNode(back), checkTail);
        return cacheList.Count > 0;
    }

    public static bool NodeWithoutChild<T>(BoxOcTree<T> node)
    {
        return node != null && (node.trees == null || node.trees.Length == 0);
    }

    public static bool IsTailNode<T>(BoxOcTree<T> node, float minSize)
    {
        if(NodeWithoutChild(node))
        {
            var extents = node.bounds.extents;
            if(extents.x <= minSize && extents.y <= minSize && extents.z <= minSize)
            {
                return true;
            }
        }
        return false;
    }

    private static void Add<T>(ref List<BoxOcTree<T>> list, BoxOcTree<T> node, bool checkTail = false)
    {
        if(node != null)
        {
            if(checkTail && NodeWithoutChild(node) == false)
            {
                return;
            }
            if(list == null)
            {
                list = new List<BoxOcTree<T>>();
            }
            list.Add(node);
        }
    }

    public static BoxOcTree<T>[] GetTreeBuffer<T>()
    {
        return new BoxOcTree<T>[TreeBufferSize];
    }
    #endregion

    #region Presetting
#if UNITY_EDITOR
    [UnityEditor.InitializeOnLoadMethod()]
#else
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
#endif
    public static void Init()
    {
        BoxOcTree<Vector3>.DataToPosition = (_raw) => { return _raw; };
        BoxOcTree<Transform>.DataToPosition = (_trans) => { return _trans.position; };
        BoxOcTree<GameObject>.DataToPosition = (_go) => { return _go.transform.position; };

        Debug.Log("BoxOcTreeUtilities Init");
    }
    #endregion
}

 

posted @ 2022-08-17 15:34  tiancaiKG  阅读(158)  评论(0编辑  收藏  举报