Net.Like.Xue.Tokyo/Assets/Plugins/MeshCombineStudio/Scripts/Mesh/CombinedLODManager.cs

274 lines
9.8 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MeshCombineStudio
{
public class CombinedLODManager : MonoBehaviour
{
public enum LodMode { Automatic, DebugLod }
public enum LodDistanceMode { Automatic, Manual }
public bool drawGizmos = true;
public LOD[] lods;
public float[] distances;
public LodDistanceMode lodDistanceMode;
public LodMode lodMode;
public int showLod = 0;
public bool lodCulled;
public float lodCullDistance = 500;
public Vector3 octreeCenter = Vector3.zero;
public Vector3 octreeSize = new Vector3(256, 256, 256);
public int maxLevels = 4;
public bool search = true;
Cell octree;
Transform cameraMainT;
void Awake()
{
cameraMainT = Camera.main.transform;
}
void InitOctree()
{
octree = new Cell(octreeCenter, octreeSize, maxLevels);
}
private void Start()
{
if (search)
{
search = false;
InitOctree();
Search();
}
}
void Update()
{
if (octree.cellsUsed != null) Lod(lodMode);
}
public void UpdateLods(MeshCombiner meshCombiner, int lodAmount)
{
if (lods != null && lods.Length == lodAmount) return;
lods = new LOD[lodAmount];
float[] newDistances = new float[lodAmount];
for (int i = 0; i < lods.Length; i++)
{
lods[i] = new LOD();
if (lodDistanceMode == LodDistanceMode.Automatic) newDistances[i] = meshCombiner.cellSize * i;
else if (distances != null && i < distances.Length) newDistances[i] = distances[i];
}
distances = newDistances;
}
public void UpdateDistances(MeshCombiner meshCombiner)
{
if (lodDistanceMode != LodDistanceMode.Automatic) return;
for (int i = 0; i < distances.Length; i++) distances[i] = meshCombiner.cellSize * i;
}
public void Search()
{
for (int i = 0; i < lods.Length; i++)
{
lods[i].searchParent.gameObject.SetActive(true);
MeshRenderer[] mrs = lods[i].searchParent.GetComponentsInChildren<MeshRenderer>();
for (int j = 0; j < mrs.Length; j++) octree.AddMeshRenderer(mrs[j], mrs[j].transform.position, i, lods.Length);
}
}
public void ResetOctree()
{
if (octree == null) return;
octree.cells = null;
octree.cellsUsed = null;
for (int i = 0; i < lods.Length; i++)
{
if (lods[i].searchParent != null) Destroy(lods[i].searchParent.gameObject);
}
}
public void Lod(LodMode lodMode)
{
Vector3 cameraPosition = cameraMainT.position;
for (int i = 0; i < lods.Length - 1; i++)
{
lods[i].sphere.center = cameraPosition;
lods[i].sphere.radius = distances[i + 1];
}
// if (Benchmark.active) lodBenchmark.Start();
if (lodMode == LodMode.Automatic) octree.AutoLodInternal(lods, lodCulled ? lodCullDistance : -1);
else octree.LodInternal(lods, showLod);
// if (Benchmark.active) lodBenchmark.Stop();
}
private void OnDrawGizmosSelected()
{
if (drawGizmos && octree != null && octree.cells != null) octree.DrawGizmos(lods);
}
[System.Serializable]
public class LOD
{
public Transform searchParent;
public Sphere3 sphere = new Sphere3();
public LOD() { }
public LOD(Transform searchParent)
{
this.searchParent = searchParent;
}
}
public class Cell : BaseOctree.Cell
{
public Cell[] cells;
AABB3 box;
public Cell() { }
public Cell(Vector3 position, Vector3 size, int maxLevels) : base(position, size, maxLevels) { }
public void AddMeshRenderer(MeshRenderer mr, Vector3 position, int lodLevel, int lodLevels)
{
if (InsideBounds(position)) AddMeshRendererInternal(mr, position, lodLevel, lodLevels);
}
void AddMeshRendererInternal(MeshRenderer mr, Vector3 position, int lodLevel, int lodLevels)
{
if (level == maxLevels)
{
MaxCell thisCell = (MaxCell)this;
if (thisCell.mrList == null) thisCell.mrList = new List<MeshRenderer>[lodLevels];
List<MeshRenderer>[] mrList = thisCell.mrList;
if (mrList[lodLevel] == null) mrList[lodLevel] = new List<MeshRenderer>();
mrList[lodLevel].Add(mr);
thisCell.currentLod = -1;
}
else
{
bool maxCellCreated;
int index = AddCell<Cell, MaxCell>(ref cells, position, out maxCellCreated);
cells[index].box = new AABB3(cells[index].bounds.min, cells[index].bounds.max);
cells[index].AddMeshRendererInternal(mr, position, lodLevel, lodLevels);
}
}
public void AutoLodInternal(LOD[] lods, float lodCulledDistance)
{
if (level == maxLevels)
{
MaxCell thisCell = (MaxCell)this;
if (lodCulledDistance != -1)
{
float squareDistance = (bounds.center - lods[0].sphere.center).sqrMagnitude;
if (squareDistance > lodCulledDistance * lodCulledDistance)
{
if (thisCell.currentLod != -1)
{
for (int i = 0; i < lods.Length; i++)
{
for (int j = 0; j < thisCell.mrList[i].Count; j++) thisCell.mrList[i][j].enabled = false;
}
thisCell.currentLod = -1;
}
return;
}
}
for (int lodIndex = 0; lodIndex < lods.Length; lodIndex++)
{
bool intersect;
if (lodIndex < lods.Length - 1) intersect = Mathw.IntersectAABB3Sphere3(box, lods[lodIndex].sphere);
else intersect = true;
if (intersect)
{
if (thisCell.currentLod != lodIndex)
{
for (int i = 0; i < lods.Length; i++)
{
bool active = (i == lodIndex);
for (int j = 0; j < thisCell.mrList[i].Count; j++) thisCell.mrList[i][j].enabled = active;
}
thisCell.currentLod = lodIndex;
}
break;
}
}
}
else for (int i = 0; i < 8; ++i) if (cellsUsed[i]) cells[i].AutoLodInternal(lods, lodCulledDistance);
}
public void LodInternal(LOD[] lods, int lodLevel)
{
if (level == maxLevels)
{
MaxCell thisCell = (MaxCell)this;
if (thisCell.currentLod != lodLevel)
{
for (int i = 0; i < lods.Length; i++)
{
bool active = (i == lodLevel);
for (int j = 0; j < thisCell.mrList[i].Count; j++) thisCell.mrList[i][j].enabled = active;
}
thisCell.currentLod = lodLevel;
}
}
else for (int i = 0; i < 8; ++i) if (cellsUsed[i]) cells[i].LodInternal(lods, lodLevel);
} //===============================================================================================================================
public void DrawGizmos(LOD[] lods)
{
for (int i = 0; i < lods.Length; i++)
{
if (i == 0) Gizmos.color = Color.red;
else if (i == 1) Gizmos.color = Color.green;
else if (i == 2) Gizmos.color = Color.yellow;
else if (i == 3) Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(lods[i].sphere.center, lods[i].sphere.radius);
}
DrawGizmosInternal();
}
public void DrawGizmosInternal()
{
if (level == maxLevels)
{
MaxCell thisCell = (MaxCell)this;
if (thisCell.currentLod == 0) Gizmos.color = Color.red;
else if (thisCell.currentLod == 1) Gizmos.color = Color.green;
else if (thisCell.currentLod == 2) Gizmos.color = Color.yellow;
else if (thisCell.currentLod == 3) Gizmos.color = Color.blue;
Gizmos.DrawWireCube(bounds.center, bounds.size - new Vector3(0.25f, 0.25f, 0.25f));
Gizmos.color = Color.white;
}
else for (int i = 0; i < 8; ++i) if (cellsUsed[i]) cells[i].DrawGizmosInternal();
}
}
public class MaxCell : Cell
{
public List<MeshRenderer>[] mrList;
public int currentLod;
}
}
}