Files
2026-03-04 10:03:45 +08:00

485 lines
12 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace NatureManufacture.RAM
{
[ExecuteInEditMode]
public class LodManager : MonoBehaviour, ISerializationCallbackReceiver
{
public struct SVector2Int
{
public int X;
public int Y;
}
private enum SubdividePhase
{
Loading = 0,
PreSubdivide = 1,
InitArrays = 2,
DoSubdivide = 3,
FinishMesh = 4
}
[SerializeField]
private MeshFilter sourceMeshFilter;
[SerializeField]
private float refreshTime = 0.1f;
[SerializeField]
private Vector4 lodDistance = new Vector4(100f, 80f, 50f, 20f);
[SerializeField]
private Transform cameraLod;
[SerializeField]
private float trianglesRefreshRate = 200f;
[SerializeField]
private float cameraMovementDistanceCheck = 0.1f;
[SerializeField]
private int levelStart = 4;
private int currentTriangleIndex;
private float _lastRefresh = -100f;
private readonly Queue<(int, int, int, int)> subdivideData = new Queue<(int, int, int, int)>();
private SubdividePhase _currentPhase;
private Vector3 _camPosition;
private MeshRenderer _sourceMeshRenderer;
private Matrix4x4 _localToWorldMatrix;
private Bounds _objectBounds;
private Vector3 _lastCameraPosition;
private Mesh _targetMesh;
private Mesh _baseMesh;
private readonly List<Vector3> _vertices = new List<Vector3>();
private readonly List<Vector3> _normals = new List<Vector3>();
private readonly List<Vector4> _tangents = new List<Vector4>();
private readonly List<Color> _colors = new List<Color>();
private readonly List<Vector4> _uv = new List<Vector4>();
private readonly List<Vector4> _uv3 = new List<Vector4>();
private readonly List<int> _indices = new List<int>();
private Bounds _meshBounds;
private int[] _indicesBase;
private int _verticesCount;
private int _indicesCount;
private readonly Dictionary<SVector2Int, int> _newVertices = new Dictionary<SVector2Int, int>(new NmsVector2IntEqualityComparer());
private SVector2Int _test;
public MeshFilter SourceMeshFilter
{
get
{
return sourceMeshFilter;
}
set
{
sourceMeshFilter = value;
}
}
public Vector4 LODDistance
{
get
{
return lodDistance;
}
set
{
lodDistance = value;
}
}
public float RefreshTime
{
get
{
return refreshTime;
}
set
{
refreshTime = value;
}
}
public Transform CameraLod
{
get
{
if (cameraLod == null && Camera.main != null)
{
cameraLod = Camera.main.transform;
}
return cameraLod;
}
set
{
cameraLod = value;
}
}
private void OnEnable()
{
_currentPhase = SubdividePhase.Loading;
}
private void OnDisable()
{
StopAllCoroutines();
_currentPhase = SubdividePhase.Loading;
}
public void OnBeforeSerialize()
{
StopAllCoroutines();
}
public void OnAfterDeserialize()
{
_currentPhase = SubdividePhase.Loading;
}
private void PrepareMesh()
{
if (!CameraLod && Camera.main != null)
{
CameraLod = Camera.main.transform;
}
_baseMesh = SourceMeshFilter.sharedMesh;
_baseMesh.RecalculateTangents();
_meshBounds = _baseMesh.bounds;
_sourceMeshRenderer = SourceMeshFilter.GetComponent<MeshRenderer>();
_objectBounds = _sourceMeshRenderer.bounds;
ClearData();
_targetMesh = DuplicateMesh(_baseMesh);
_targetMesh.indexFormat = IndexFormat.UInt32;
_targetMesh.MarkDynamic();
GetComponent<MeshFilter>().sharedMesh = _targetMesh;
_currentPhase = SubdividePhase.PreSubdivide;
_lastRefresh = Time.realtimeSinceStartup;
_lastCameraPosition = Vector3.zero;
}
private void ClearData()
{
_vertices.Clear();
_normals.Clear();
_tangents.Clear();
_colors.Clear();
_uv.Clear();
_uv3.Clear();
_indices.Clear();
_newVertices.Clear();
}
private void Update()
{
ManageSubdivide();
}
private void ManageSubdivide(bool checkDistance = true)
{
switch (_currentPhase)
{
case SubdividePhase.Loading:
PrepareMesh();
break;
case SubdividePhase.PreSubdivide:
WaitForSubdivide(checkDistance);
break;
case SubdividePhase.InitArrays:
InitArrays();
break;
case SubdividePhase.DoSubdivide:
DoSubdivide();
break;
case SubdividePhase.FinishMesh:
FinishMesh();
break;
}
}
private void WaitForSubdivide(bool checkDistance)
{
if (!(_lastRefresh + RefreshTime < Time.realtimeSinceStartup))
{
return;
}
_lastRefresh = Time.realtimeSinceStartup;
SetPosition();
if (checkDistance && !(Mathf.Sqrt(_objectBounds.SqrDistance(_camPosition)) < lodDistance[0]))
{
return;
}
float num = Vector3.Distance(_camPosition, _lastCameraPosition);
float num2 = lodDistance[0];
for (int i = 1; i < 4; i++)
{
if (lodDistance[i] != 0f)
{
num2 = lodDistance[i];
break;
}
}
if (!checkDistance || !(num < num2 * cameraMovementDistanceCheck))
{
_lastCameraPosition = _camPosition;
_currentPhase = SubdividePhase.InitArrays;
}
}
private void SetPosition()
{
if (!Application.isPlaying || !(CameraLod == null))
{
_camPosition = CameraLod.position;
}
}
private void InitArrays()
{
if ((bool)_baseMesh)
{
_baseMesh = SourceMeshFilter.sharedMesh;
}
if (!_baseMesh)
{
Debug.Log(_baseMesh.vertices.Length);
Debug.LogError("No base mesh filter with mesh");
return;
}
_verticesCount = _vertices.Count;
_indicesCount = 0;
currentTriangleIndex = 0;
_localToWorldMatrix = base.transform.localToWorldMatrix;
_currentPhase = SubdividePhase.DoSubdivide;
}
private void FinishMesh()
{
_targetMesh.SetVertices(_vertices, 0, _verticesCount, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
if (_normals.Count > 0)
{
_targetMesh.SetNormals(_normals, 0, _verticesCount, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
}
if (_tangents.Count > 0)
{
_targetMesh.SetTangents(_tangents, 0, _verticesCount, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
}
if (_colors.Count > 0)
{
_targetMesh.SetColors(_colors, 0, _verticesCount, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
}
if (_uv.Count > 0)
{
_targetMesh.SetUVs(0, _uv, 0, _verticesCount, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
}
if (_uv3.Count > 0)
{
_targetMesh.SetUVs(3, _uv3, 0, _verticesCount, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
}
_targetMesh.SetTriangles(_indices, 0, _indicesCount, 0, calculateBounds: false);
_targetMesh.bounds = _meshBounds;
if (_sourceMeshRenderer.enabled)
{
_sourceMeshRenderer.enabled = false;
}
_currentPhase = SubdividePhase.PreSubdivide;
}
private void DoSubdivide()
{
for (int i = 0; (float)i < trianglesRefreshRate; i++)
{
if (currentTriangleIndex >= _indicesBase.Length)
{
currentTriangleIndex = 0;
_currentPhase = SubdividePhase.FinishMesh;
break;
}
int i2 = _indicesBase[currentTriangleIndex];
int i3 = _indicesBase[currentTriangleIndex + 1];
int i4 = _indicesBase[currentTriangleIndex + 2];
SubdivideTriangleIterative(_camPosition, levelStart, i2, i3, i4);
currentTriangleIndex += 3;
}
}
private void SubdivideTriangleIterative(Vector3 camPosition, int level, int i1, int i2, int i3)
{
subdivideData.Enqueue((level, i1, i2, i3));
while (subdivideData.Count > 0)
{
(int, int, int, int) tuple = subdivideData.Dequeue();
int item = tuple.Item1;
int item2 = tuple.Item2;
int item3 = tuple.Item3;
int item4 = tuple.Item4;
int num = levelStart - item;
if (num > 3)
{
num = 0;
}
float num2 = LODDistance[num];
bool flag = Vector3.Distance(_localToWorldMatrix.MultiplyPoint3x4(_vertices[item2]), camPosition) > num2;
bool flag2 = Vector3.Distance(_localToWorldMatrix.MultiplyPoint3x4(_vertices[item3]), camPosition) > num2;
bool flag3 = Vector3.Distance(_localToWorldMatrix.MultiplyPoint3x4(_vertices[item4]), camPosition) > num2;
if (!flag && !flag2 && flag3 && item > 0)
{
int newVertex = GetNewVertex4(item2, item3);
AddOrChangeIndice(item2);
AddOrChangeIndice(newVertex);
AddOrChangeIndice(item4);
AddOrChangeIndice(newVertex);
AddOrChangeIndice(item3);
AddOrChangeIndice(item4);
}
else if (flag && !flag2 && !flag3 && item > 0)
{
int newVertex2 = GetNewVertex4(item3, item4);
AddOrChangeIndice(item2);
AddOrChangeIndice(newVertex2);
AddOrChangeIndice(item4);
AddOrChangeIndice(item2);
AddOrChangeIndice(item3);
AddOrChangeIndice(newVertex2);
}
else if (!flag && flag2 && !flag3 && item > 0)
{
int newVertex3 = GetNewVertex4(item2, item4);
AddOrChangeIndice(newVertex3);
AddOrChangeIndice(item3);
AddOrChangeIndice(item4);
AddOrChangeIndice(item2);
AddOrChangeIndice(item3);
AddOrChangeIndice(newVertex3);
}
else if (flag || flag2 || item == 0)
{
AddOrChangeIndice(item2);
AddOrChangeIndice(item3);
AddOrChangeIndice(item4);
}
else if (item > 0)
{
int newVertex4 = GetNewVertex4(item2, item3);
int newVertex5 = GetNewVertex4(item3, item4);
int newVertex6 = GetNewVertex4(item4, item2);
subdivideData.Enqueue((item - 1, item2, newVertex4, newVertex6));
subdivideData.Enqueue((item - 1, item3, newVertex5, newVertex4));
subdivideData.Enqueue((item - 1, item4, newVertex6, newVertex5));
subdivideData.Enqueue((item - 1, newVertex4, newVertex5, newVertex6));
}
}
}
private void AddOrChangeIndice(int indice)
{
if (_indices.Count > _indicesCount)
{
_indices[_indicesCount] = indice;
}
else
{
_indices.Add(indice);
}
_indicesCount++;
}
private int GetNewVertex4(int i1, int i2)
{
if (i1 < i2)
{
_test.X = i1;
_test.Y = i2;
}
else
{
_test.X = i2;
_test.Y = i1;
}
if (_newVertices.TryGetValue(_test, out var value))
{
return value;
}
int count = _vertices.Count;
_newVertices.Add(_test, count);
if (_vertices.Count > _verticesCount)
{
_vertices[_verticesCount] = (_vertices[i1] + _vertices[i2]) * 0.5f;
_uv[_verticesCount] = NewUv(_uv, i1, i2);
_normals[_verticesCount] = (_normals[i1] + _normals[i2]) * 0.5f;
_tangents[_verticesCount] = (_tangents[i1] + _tangents[i2]) * 0.5f;
if (_colors.Count > 0)
{
_colors[_verticesCount] = Color.Lerp(_colors[i1], _colors[i2], 0.5f);
}
if (_uv3.Count > 0)
{
_uv3[_verticesCount] = (_uv3[i1] + _uv3[i2]) * 0.5f;
}
}
else
{
_vertices.Add((_vertices[i1] + _vertices[i2]) * 0.5f);
_normals.Add((_normals[i1] + _normals[i2]) * 0.5f);
_tangents.Add((_tangents[i1] + _tangents[i2]) * 0.5f);
_uv.Add(NewUv(_uv, i1, i2));
if (_colors.Count > 0)
{
_colors.Add(Color.Lerp(_colors[i1], _colors[i2], 0.5f));
}
if (_uv3.Count > 0)
{
_uv3.Add((_uv3[i1] + _uv3[i2]) * 0.5f);
}
}
_verticesCount++;
return count;
}
private Vector4 NewUv(List<Vector4> oldUv, int i1, int i2)
{
return (oldUv[i1] + oldUv[i2]) * 0.5f;
}
private Mesh DuplicateMesh(Mesh mesh)
{
_indicesBase = mesh.triangles;
_vertices.AddRange(mesh.vertices);
_normals.AddRange(mesh.normals);
_tangents.AddRange(mesh.tangents);
_colors.AddRange(mesh.colors);
mesh.GetUVs(0, _uv);
mesh.GetUVs(3, _uv3);
return Object.Instantiate(mesh);
}
}
}