BStandShaderResources/OcTree1.unitypackage at master · AMikeW/BStandShaderResources (github.com)
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class DcTreeManager : MonoBehaviour
{
public DcTreeNode MainDcTreeNode;
private Camera camera;
private Plane[] plane;
void Start()
{
Renderer[] renderers = this.transform.GetComponentsInChildren<Renderer>();
List<Renderer> rendererList = new List<Renderer>();
rendererList.AddRange(renderers);
if (renderers != null && renderers.Length > 0)
{
MainDcTreeNode = new DcTreeNode(new Bounds(Vector3.zero, new Vector3(25, 25, 25)), 0);
MainDcTreeNode.GenerateChildNodes(rendererList, true);
}
else
{
Debug.LogError("一个renderers都没有");
}
camera = Camera.main;
}
private void Update()
{
MainDcTreeNode.RefreshVisiable((bounds) =>
{
plane = GeometryUtility.CalculateFrustumPlanes(this.camera);
return GeometryUtility.TestPlanesAABB(plane, bounds);
});
}
private void OnDrawGizmos()
{
if (UnityEditor.EditorApplication.isPlaying)
{
MainDcTreeNode.Draw((bounds) => { Gizmos.DrawWireCube(bounds.center, bounds.size); });
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DcTreeNode
{
static Dictionary<int, Vector3> side2Dir = new Dictionary<int, Vector3>()
{
[0] = new Vector3(-1, -1, -1).normalized, //lb+ lb代表left bottom左下 +代表正面
[1] = new Vector3(-1, 1, -1).normalized, //lt+
[2] = new Vector3(1, -1, -1).normalized, //rb+
[3] = new Vector3(1, 1, -1).normalized, //rt+
[4] = new Vector3(-1, -1, 1).normalized, //lb- 左下 背面
[5] = new Vector3(-1, 1, 1).normalized, //lt-
[6] = new Vector3(1, -1, 1).normalized, //rb-
[7] = new Vector3(1, 1, 1).normalized, //rt-
};
static Dictionary<int, Color> side2Color = new Dictionary<int, Color>()
{
[0] = Color.red, //lb+ 同上
[1] = Color.yellow, //lt+
[2] = Color.green, //rb+
[3] = Color.blue, //rt+
[4] = Color.cyan, //lb-
[5] = Color.gray, //lt-
[6] = Color.black, //rb-
[7] = Color.white, //rt-
};
private int side;
private Bounds nodeBounds;
private List<DcTreeNode> childNodes;
private List<Renderer> containRenderers;
public DcTreeNode(Bounds bounds, int side)
{
childNodes = new List<DcTreeNode>();
nodeBounds = bounds;
this.side = side;
}
public void Draw(Action<Bounds> action)
{
Gizmos.color = side2Color[side];
action(nodeBounds);
foreach (var v in childNodes)
{
v.Draw(action);
}
}
public void RefreshVisiable(Func<Bounds, bool> func)
{
bool isOn = func(nodeBounds);
if (isOn)
{
foreach (var v in childNodes)
{
v.RefreshVisiable(func);
}
}
if (containRenderers != null)
{
foreach (var v in containRenderers)
{
v.enabled = isOn;
}
}
}
/// <summary>
/// 生成8个子节点
/// </summary>
public void GenerateChildNodes(List<Renderer> renderers, bool isFirst = false)
{
containRenderers = renderers;
List<Renderer> tempAlreadyAddRenderers = new List<Renderer>();
float halfSize = this.nodeBounds.size.x * 0.5f;
float length = new Vector3(halfSize, halfSize, halfSize).magnitude / 2;
for (int i = 0; i < 8; i++)
{
Bounds bounds = new Bounds();
bounds.size = this.nodeBounds.size * 0.5f;
bounds.center = this.nodeBounds.center + side2Dir[i] * length;
List<Renderer> tempNodeRenderers = new List<Renderer>();
foreach (var v in containRenderers)
{
if (bounds.Contains(v.bounds.min) && bounds.Contains(v.bounds.max))
{
tempNodeRenderers.Add(v);
}
}
foreach (var v in tempNodeRenderers)
{
containRenderers.Remove(v);
}
DcTreeNode node;
if (isFirst)
{
node = new DcTreeNode(bounds, i);
}
else
{
node = new DcTreeNode(bounds, side);
}
childNodes.Add(node);
if (tempNodeRenderers.Count > 0)
{
node.GenerateChildNodes(tempNodeRenderers);
}
}
}
}
实现思路:生成一个适当的Bounds盒子区域进行开始八叉树遍历,每一次切割出等体积的8个子方块,必须满足至少有一个物体AABB盒完全包含在当前八叉树节点Bounds才进行切割8个子节点。
这里的切割方式是最简单无脑的所以很简单,一般都要有一些算法支持 不然会有很多空隙导致搜索变慢。(可自行优化)