Unity3D 八叉树划分空间和可视化

也许更好的阅读体验

成果展示

所有层
第一层

第二层

代码

OctreeNode
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OctreeNode
{
    //空间内包含的物体
    public List<GameObject> areaObjects;
    //空间中心
    public Vector3 center;
    //空间大小
    public float size;
    //子节点个数 
    private int kidCount = 8;
    //子节点
    public OctreeNode[] kids;
    //构造函数
    public OctreeNode (Vector3 center, float size)
    {
        this.center = center;
        this.size = size;
        kids = new OctreeNode[kidCount];
        areaObjects = new List<GameObject>();
    }
    #region 八个子节点中心对应的偏移方向
    public static Vector3[] centerOffset = new Vector3[8] {
        new Vector3 (-1, 1, -1),
        new Vector3 (1, 1, -1),
        new Vector3 (-1, 1, 1),
        new Vector3 (1, 1, 1),
        new Vector3 (-1, -1, -1),
        new Vector3 (1, -1, -1),
        new Vector3 (-1, -1, 1),
        new Vector3 (1, -1, 1)
    };
    #endregion

    #region 空间和内部物体管理
    //空间内物体树
    public int objectCount => areaObjects.Count;
    //把该空间画出来
    public void DrawGizmos ()
    {
        Gizmos.DrawWireCube(center, Vector3.one * size);
    }
    //判断是否包含某个点
    public bool Contains (Vector3 position)
    {
        float halfSize = size / 2.0f;
        return Mathf.Abs(position.x - center.x) <= halfSize &&
            Mathf.Abs(position.y - center.y) <= halfSize &&
            Mathf.Abs(position.z - center.z) <= halfSize;
    }
    //清理空间内物体
    public void Clear ()
    {
        areaObjects.Clear();
    }
    //添加物体
    public void AddGameObject (GameObject obj)
    {
        areaObjects.Add(obj);
    }
    #endregion

}

Orctree
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design.Serialization;
using Unity.VisualScripting;
using UnityEngine;

//可视化模式
public enum OctreeDebugMode
{
    AllDepth,
    TargetDepth
}
public class Octree : MonoBehaviour
{

    #region 物体生成和构建八叉树
    //生成物体数
    [Range(0, 500)]
    public int genCount = 500;
    //物体生成范围
    [Range(1, 300)]
    public float range = 100;
    //生成的物体
    public List<GameObject> sceneObjects;
    //Octree最大层数
    [Range(1, 8)]
    public int maxDepth = 3;
    //Octree的根节点
    public OctreeNode root;


    // Start is called before the first frame update
    void Start()
    {
        GenObjects();
        OctreePartition();
    }

    //随机生成一些cube
    private void GenObjects()
    {
        float genRange = range * 0.5f;
        sceneObjects = new List<GameObject>();

        for (int i = 0; i < genCount; ++i)
        {
            GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
            obj.transform.position = new Vector3(Random.Range(-genRange, genRange),
                Random.Range(-genRange, genRange),
                Random.Range(-genRange, genRange));
            obj.hideFlags = HideFlags.HideInHierarchy;
            sceneObjects.Add(obj);
        }
    }
    //进行八叉树划分
    private void OctreePartition ()
    {
        //设定根节点
        Vector3 origin = Vector3.one;
        root = new OctreeNode(Vector3.zero, range);
        root.areaObjects = sceneObjects;
        //往下生成八叉树 根节点层数为1
        GenerateOcetree(root, range, 2);
    }
    //递归往下生成八叉树
    private void GenerateOcetree (OctreeNode root, float range, int depth)
    {
        //depth是当前生成的层数
        if (depth > maxDepth) return;
        //下一层的大小
        float halfRange = range / 2.0f;
        //根节点偏移量
        float rootOffset = halfRange / 2.0f;
        for (int i = 0; i < 8; ++i)
        {
            Vector3 origin = root.center + OctreeNode.centerOffset[i] * rootOffset;
            root.kids[i] = new OctreeNode(origin, halfRange);
        }
        PartitionSceneObjects(root);
        for (int i = 0; i < 8; ++i)
        {
            if (root.kids[i].objectCount >= 2) GenerateOcetree(root.kids[i], halfRange, depth + 1);
        }
    }
    //把空间内物体划分给子节点
    private void PartitionSceneObjects(OctreeNode root)
    {
        foreach (GameObject obj in root.areaObjects)
        {
            foreach (OctreeNode kid in root.kids)
            {
                if (kid.Contains(obj.transform.position))
                {
                    kid.AddGameObject(obj);
                }
            }
        }
    }
    #endregion

    #region 可视化
    //是否显示八叉树
    public bool showOctree = true;
    //可视化类型
    public OctreeDebugMode octreeDebugMode;
    //可视化层数
    [Range(0, 8)]
    public int displayDepth = 3;
    //不同深度的可视化颜色
    public Color[] displayColor;
    private void OnDrawGizmos()
    {
        if (root == null) return;
        if (showOctree && displayDepth <= maxDepth)
        {
            //显示所有深度的空间范围
            if (octreeDebugMode == OctreeDebugMode.AllDepth)
            {
                DrawNode(root, 1);
            }
            //显示指定深度的空间范围(第displayDepth层)
            else if (octreeDebugMode == OctreeDebugMode.TargetDepth)
            {
                if (displayDepth > 0 && displayColor.Length >= displayDepth) 
                {
                    Color color = displayColor[displayDepth - 1];
                    color.a = 0.5f;
                    Gizmos.color = color;
                    DrawTargetDepth(root, displayDepth);
                }
            }
        }
    }
    //绘制所有节点 当前深度为depth
    private void DrawNode(OctreeNode root, int depth)
    {
        if (root == null || depth > maxDepth) return;
        Color color = displayColor[depth - 1];
        color.a = 0.5f;
        Gizmos.color = color;
        root.DrawGizmos();
        foreach (OctreeNode kid in root.kids)
        {
            DrawNode(kid, depth + 1);
        }
    }
    //绘制指定层
    private void DrawTargetDepth (OctreeNode root, int targetDepth)
    {
        --targetDepth;
        if (root == null || targetDepth < 0) return;
        Debug.Log(targetDepth);
        if (targetDepth == 0)
        {
            root.DrawGizmos();
            return;
        }
        foreach (OctreeNode kid in root.kids)
        {
            DrawTargetDepth(kid, targetDepth);
        }
    }
    #endregion
}

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的八叉树体素网格python可视化代码示例: ```python from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from octree import Octree # 创建八叉树 octree = Octree(50, 50, 50) # 添加一些体素 octree.set_voxel(25, 25, 25) octree.set_voxel(20, 20, 20) octree.set_voxel(30, 30, 30) octree.set_voxel(40, 40, 40) # 获取八叉树的数据 data = octree.get_data() # 创建一个三维坐标轴 fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # 循环遍历数据并绘制立方体 for voxel in data: if voxel: ax.scatter( voxel[0], voxel[1], voxel[2], c='b', marker='o', s=50, alpha=0.5) # 设置坐标轴范围 ax.set_xlim(0, 50) ax.set_ylim(0, 50) ax.set_zlim(0, 50) # 显示图形 plt.show() ``` 需要注意的是,这里的`Octree`类需要自己实现。你可以参考以下示例代码: ```python class Octree: def __init__(self, width, height, depth): self.width = width self.height = height self.depth = depth self.root = None def set_voxel(self, x, y, z): if not self.root: self.root = OctreeNode(0, 0, 0, self.width, self.height, self.depth) self.root.set_voxel(x, y, z) def get_data(self): if not self.root: return [] return self.root.get_data() ``` `OctreeNode`类的实现如下: ```python class OctreeNode: def __init__(self, x, y, z, width, height, depth): self.x = x self.y = y self.z = z self.width = width self.height = height self.depth = depth self.children = [None] * 8 self.value = False def get_index(self, x, y, z): index = 0 if x >= self.x + self.width / 2: index |= 4 if y >= self.y + self.height / 2: index |= 2 if z >= self.z + self.depth / 2: index |= 1 return index def get_child(self, x, y, z): index = self.get_index(x, y, z) if not self.children[index]: w = self.width / 2 h = self.height / 2 d = self.depth / 2 if index == 0: self.children[index] = OctreeNode(self.x, self.y, self.z, w, h, d) elif index == 1: self.children[index] = OctreeNode(self.x, self.y, self.z+d, w, h, d) elif index == 2: self.children[index] = OctreeNode(self.x, self.y+h, self.z, w, h, d) elif index == 3: self.children[index] = OctreeNode(self.x, self.y+h, self.z+d, w, h, d) elif index == 4: self.children[index] = OctreeNode(self.x+w, self.y, self.z, w, h, d) elif index == 5: self.children[index] = OctreeNode(self.x+w, self.y, self.z+d, w, h, d) elif index == 6: self.children[index] = OctreeNode(self.x+w, self.y+h, self.z, w, h, d) elif index == 7: self.children[index] = OctreeNode(self.x+w, self.y+h, self.z+d, w, h, d) return self.children[index] def set_voxel(self, x, y, z): if self.width == 1 and self.height == 1 and self.depth == 1: self.value = True return child = self.get_child(x, y, z) child.set_voxel(x, y, z) if all(child.value for child in self.children): self.value = True def get_data(self): if self.width == 1 and self.height == 1 and self.depth == 1: if self.value: return [(self.x, self.y, self.z)] else: return [] data = [] for child in self.children: if child: data += child.get_data() return data ``` 这里的八叉树实现是用来存储体素的,可以将其看作一个三维的布尔数组。我们可以通过`set_voxel(x, y, z)`方法将某一个体素设置为真,然后通过`get_data()`方法获取所有为真的体素的坐标。在绘制时,我们可以将每一个体素看作一个小球(蓝色圆圈)来进行可视化

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值