This commit is contained in:
CortexCore
2024-11-13 17:47:45 +08:00
parent c4af12acd7
commit 416e3322db
208 changed files with 2591757 additions and 1497 deletions

View File

@@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace NGS.AdvancedCullingSystem
{
public abstract class BinaryTree<TNode, TData> where TNode : BinaryTreeNode
{
public BinaryTreeNode Root
{
get
{
return RootInternal;
}
}
[field: SerializeReference]
protected TNode RootInternal { get; private set; }
[field: SerializeField]
public int Height { get; private set; }
[field : SerializeField]
public float CellSize { get; private set; }
private int _maxDepth = -1;
public BinaryTree(float cellSize)
{
CellSize = Mathf.Max(cellSize, 0.1f);
}
public BinaryTree(IList<TData> datas, int maxDepth)
{
if (maxDepth <= 1)
throw new ArgumentException("Max depth should be greater than 1");
_maxDepth = maxDepth;
Vector3 min = Vector3.one * float.MaxValue;
Vector3 max = Vector3.one * float.MinValue;
foreach (var data in datas)
{
Bounds dBounds = GetBounds(data);
Vector3 dMin = dBounds.min;
Vector3 dMax = dBounds.max;
min.x = Mathf.Min(min.x, dMin.x);
min.y = Mathf.Min(min.y, dMin.y);
min.z = Mathf.Min(min.z, dMin.z);
max.x = Mathf.Max(max.x, dMax.x);
max.y = Mathf.Max(max.y, dMax.y);
max.z = Mathf.Max(max.z, dMax.z);
}
RootInternal = CreateNode(min + ((max - min) / 2), max - min + Vector3.one * 0.01f, false);
Height = 1;
foreach (var data in datas)
Add(data);
}
public void Add(TData data)
{
if (Root == null)
{
RootInternal = CreateNode(GetBounds(data).center, Vector3.one * CellSize, true);
Height = 1;
}
if (!Includes(RootInternal, data))
GrowTreeUp(data);
AddInternal(RootInternal, data, 1);
}
private TNode ExpandRoot(TNode root, TData target)
{
Bounds rootBounds = root.Bounds;
Bounds targetBounds = GetBounds(target);
Vector3 parentCenter = Vector3.zero;
Vector3 parentSize = Vector3.zero;
Vector3 childCenter = Vector3.zero;
bool rootIsLeft = false;
for (int i = 0; i < 3; i++)
{
if (targetBounds.min[i] < rootBounds.min[i])
{
parentSize = rootBounds.size;
parentSize[i] *= 2;
parentCenter = rootBounds.center;
parentCenter[i] -= rootBounds.size[i] / 2;
childCenter = rootBounds.center;
childCenter[i] -= rootBounds.size[i];
break;
}
if (targetBounds.max[i] > rootBounds.max[i])
{
parentSize = rootBounds.size;
parentSize[i] *= 2;
parentCenter = rootBounds.center;
parentCenter[i] += rootBounds.size[i] / 2;
childCenter = rootBounds.center;
childCenter[i] += rootBounds.size[i];
rootIsLeft = true;
break;
}
}
TNode parent = CreateNode(parentCenter, parentSize, false);
TNode child = CreateNode(childCenter, rootBounds.size, root.IsLeaf);
if (rootIsLeft)
SetChildsToNode(parent, RootInternal, child);
else
SetChildsToNode(parent, child, RootInternal);
return parent;
}
protected void GrowTreeUp(TData target)
{
if (Includes(RootInternal, target))
return;
RootInternal = ExpandRoot(RootInternal, target);
Height++;
GrowTreeUp(target);
}
protected void GrowTreeDown(TNode node, TData target, int depth)
{
if (node.HasChilds)
throw new Exception("GrowTreeDown::" + depth + " node already has childs");
Bounds nodeBounds = node.Bounds;
Vector3 offset;
Vector3 size;
if (nodeBounds.size.x >= nodeBounds.size.y && nodeBounds.size.x >= nodeBounds.size.z)
{
offset = new Vector3(nodeBounds.size.x / 4, 0, 0);
size = new Vector3(nodeBounds.size.x / 2, nodeBounds.size.y, nodeBounds.size.z);
}
else if (nodeBounds.size.y >= nodeBounds.size.x && nodeBounds.size.y >= nodeBounds.size.z)
{
offset = new Vector3(0, nodeBounds.size.y / 4, 0);
size = new Vector3(nodeBounds.size.x, nodeBounds.size.y / 2, nodeBounds.size.z);
}
else
{
offset = new Vector3(0, 0, nodeBounds.size.z / 4);
size = new Vector3(nodeBounds.size.x, nodeBounds.size.y, nodeBounds.size.z / 2);
}
bool isLeafs = (depth == _maxDepth) ||
(size.x <= CellSize && size.y <= CellSize && size.z <= CellSize);
TNode left = CreateNode(nodeBounds.center - offset, size, isLeafs);
TNode right = CreateNode(nodeBounds.center + offset, size, isLeafs);
SetChildsToNode(node, left, right);
if (isLeafs)
{
if (Height < depth)
Height = depth;
if (CellSize == 0)
CellSize = Mathf.Min(size.x, size.y, size.z);
return;
}
if (Intersects(left, target))
GrowTreeDown(left, target, depth + 1);
if (Intersects(right, target))
GrowTreeDown(right, target, depth + 1);
}
protected bool Intersects(TNode node, TData data)
{
return node.Bounds.Intersects(GetBounds(data));
}
protected bool Includes(TNode node, TData data)
{
return node.Bounds.Contains(GetBounds(data));
}
protected virtual void AddInternal(TNode node, TData data, int depth)
{
if (node.IsLeaf)
{
AddDataToNode(node, data);
return;
}
if (!node.HasChilds)
GrowTreeDown(node, data, depth + 1);
TNode left = (TNode)node.GetLeft();
TNode right = (TNode)node.GetRight();
if (Intersects(left, data))
AddInternal(left, data, depth + 1);
if (Intersects(right, data))
AddInternal(right, data, depth + 1);
}
protected abstract Bounds GetBounds(TData data);
protected abstract TNode CreateNode(Vector3 center, Vector3 size, bool isLeaf);
protected abstract void SetChildsToNode(TNode parent, TNode leftChild, TNode rightChild);
protected abstract void AddDataToNode(TNode node, TData data);
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace NGS.AdvancedCullingSystem
{
public class BinaryTreeDrawer
{
public Color Color { get; set; }
public void DrawTreeGizmos(BinaryTreeNode root)
{
if (root == null)
return;
Bounds bounds = root.Bounds;
Gizmos.color = Color;
Gizmos.DrawWireCube(bounds.center, bounds.size);
DrawTreeGizmos(root.GetLeft());
DrawTreeGizmos(root.GetRight());
}
}
}

View File

@@ -0,0 +1,59 @@
using UnityEngine;
namespace NGS.AdvancedCullingSystem
{
public abstract class BinaryTreeNode
{
public Vector3 Center
{
get
{
return _bounds.center;
}
}
public Vector3 Size
{
get
{
return _bounds.size;
}
}
public Bounds Bounds
{
get
{
return _bounds;
}
}
public bool HasChilds
{
get
{
return GetLeft() != null;
}
}
public bool IsLeaf
{
get
{
return _isLeaf;
}
}
[SerializeField]
private Bounds _bounds;
[SerializeField]
private bool _isLeaf;
public BinaryTreeNode(Vector3 center, Vector3 size, bool isLeaf)
{
_bounds = new Bounds(center, size);
_isLeaf = isLeaf;
}
public abstract BinaryTreeNode GetLeft();
public abstract BinaryTreeNode GetRight();
}
}

View File

@@ -0,0 +1,14 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace NGS.AdvancedCullingSystem
{
public static class BoundsHelper
{
public static bool Contains(this Bounds bounds, Bounds target)
{
return bounds.Contains(target.min) && bounds.Contains(target.max);
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace NGS.AdvancedCullingSystem
{
public static class LODGroupHelper
{
public static int Count(this LODGroup group, Func<Renderer, bool> filter)
{
LOD[] lods = group.GetLODs();
return Count(lods, filter);
}
public static int Count(this LOD[] lods, Func<Renderer, bool> filter)
{
int count = 0;
for (int i = 0; i < lods.Length; i++)
{
Renderer[] renderers = lods[i].renderers;
for (int c = 0; c < renderers.Length; c++)
{
Renderer renderer = renderers[c];
if (renderer == null)
continue;
if (filter(renderer))
count++;
}
}
return count;
}
public static bool ContainsAny(this LOD[] lods, Func<Renderer, bool> filter)
{
for (int i = 0; i < lods.Length; i++)
{
Renderer[] renderers = lods[i].renderers;
for (int c = 0; c < renderers.Length; c++)
if (filter(renderers[c]))
return true;
}
return false;
}
}
}