Files
VR-WuKong/Assets/ThirdParty/Tools/Ultimate Game Tools/AutomaticLOD/Scripts/AutomaticLOD.cs
2025-11-14 18:44:06 +08:00

2180 lines
62 KiB
C#

#define USE_WILLRENDEROBJECT_WORKAROUND
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using UltimateGameTools.MeshSimplifier;
public class AutomaticLOD : MonoBehaviour
{
[Serializable]
public enum EvalMode
{
CameraDistance,
ScreenCoverage
}
[Serializable]
public enum LevelsToGenerate
{
_1 = 1,
_2,
_3,
_4,
_5,
_6
}
[Serializable]
public enum SwitchMode
{
SwitchMesh,
SwitchGameObject,
UnityLODGroup
}
[Serializable]
public class LODLevelData
{
// Common
public float m_fScreenCoverage;
public float m_fMaxCameraDistance;
public float m_fMeshVerticesAmount; // 0.0 - 1.0
public int m_nColorEditorBarIndex;
public Mesh m_mesh;
public bool m_bUsesOriginalMesh;
// SwitchMode = SwitchGameObject / LODGroup
public GameObject m_gameObject;
public LODLevelData GetCopy()
{
LODLevelData data = new LODLevelData();
data.m_fScreenCoverage = m_fScreenCoverage;
data.m_fMaxCameraDistance = m_fMaxCameraDistance;
data.m_fMeshVerticesAmount = m_fMeshVerticesAmount;
data.m_nColorEditorBarIndex = m_nColorEditorBarIndex;
data.m_mesh = m_mesh;
data.m_bUsesOriginalMesh = m_bUsesOriginalMesh;
return data;
}
}
public static Camera UserDefinedLODCamera
{
get
{
return s_userDefinedCamera;
}
set
{
s_userDefinedCamera = value;
}
}
public SwitchMode LODSwitchMode
{
get
{
if(m_LODObjectRootPersist != null)
{
return m_LODObjectRootPersist.m_switchMode;
}
else if(m_LODObjectRoot != null)
{
return m_LODObjectRoot.m_switchMode;
}
return m_switchMode;
}
}
[HideInInspector]
public Mesh m_originalMesh = null;
[HideInInspector]
public EvalMode m_evalMode = EvalMode.ScreenCoverage;
[HideInInspector]
public bool m_bEnablePrefabUsage = false;
[HideInInspector]
public string m_strAssetPath = null;
[HideInInspector]
public float m_fMaxCameraDistance = 1000.0f;
[HideInInspector]
public int m_nColorEditorBarNewIndex = 0;
[HideInInspector]
public List<LODLevelData> m_listLODLevels;
[HideInInspector]
public AutomaticLOD m_LODObjectRoot;
[HideInInspector]
public List<AutomaticLOD> m_listDependentChildren = new List<AutomaticLOD>();
public bool m_bExpandRelevanceSpheres = true;
public RelevanceSphere[] m_aRelevanceSpheres = null;
[SerializeField]
private Simplifier m_meshSimplifier = null;
[SerializeField]
private bool m_bGenerateIncludeChildren = true;
[SerializeField]
private LevelsToGenerate m_levelsToGenerate = LevelsToGenerate._3;
[SerializeField]
private SwitchMode m_switchMode = SwitchMode.UnityLODGroup;
[SerializeField]
private bool m_bOverrideRootSettings = false;
[SerializeField, HideInInspector]
private bool m_bLODDataDirty = true;
[SerializeField, HideInInspector]
private AutomaticLOD m_LODObjectRootPersist = null; // Will persist even if you exclude it from the LOD tree
[SerializeField, HideInInspector]
private LODGroup m_LODGroup = null; // For SwitchMode == LODGroup
private bool m_bUseAutomaticCameraLODSwitch = true;
void Awake()
{
if (m_originalMesh)
{
MeshFilter meshFilter = GetComponent<MeshFilter>();
if(meshFilter != null)
{
meshFilter.sharedMesh = m_originalMesh;
}
else
{
SkinnedMeshRenderer skin = GetComponent<SkinnedMeshRenderer>();
if (skin != null)
{
skin.sharedMesh = m_originalMesh;
}
}
}
m_localCenter = transform.InverseTransformPoint(ComputeWorldCenter());
m_cachedFrameLODLevel = new Dictionary<Camera, int>();
// Compute which frames the LOD check takes place, to evenly distribute checks over time and not take much CPU
// if we have tens or hundreds of LOD objects
b_performCheck = false;
m_frameToCheck = -1;
if(IsDependent() == false)
{
// Only do this for root LOD objects
m_frameToCheck = s_currentFrameCheckIndex;
s_currentCheckIndex = s_currentCheckIndex + 1;
if(s_currentCheckIndex >= k_MaxLODChecksPerFrame)
{
s_currentCheckIndex = 0;
s_currentFrameCheckIndex++;
if(s_currentFrameCheckIndex >= k_MaxFrameCheckLoop)
{
s_currentFrameCheckIndex = 0;
}
s_checkLoopLength = Mathf.Min(s_checkLoopLength + 1, k_MaxFrameCheckLoop);
}
}
s_lastFrameComputedModulus = 0;
s_currentFrameInLoop = 0;
}
void Update()
{
// See if this frame we need to do the LOD check
if(m_bUseAutomaticCameraLODSwitch == false || LODSwitchMode == SwitchMode.UnityLODGroup)
{
return;
}
if (Time.frameCount != s_lastFrameComputedModulus && s_checkLoopLength > 0)
{
s_currentFrameInLoop = Time.frameCount % s_checkLoopLength;
s_lastFrameComputedModulus = Time.frameCount;
}
if(IsDependent() == false)
{
if(s_currentFrameInLoop == m_frameToCheck)
{
b_performCheck = true;
m_cachedFrameLODLevel.Clear();
}
else
{
b_performCheck = false;
}
}
#if USE_WILLRENDEROBJECT_WORKAROUND
Camera renderCamera = s_userDefinedCamera != null ? s_userDefinedCamera : m_renderCamera != null ? m_renderCamera : Camera.main;
if (IsDependent())
{
bool bCheck = m_LODObjectRootPersist != null ? m_LODObjectRootPersist.b_performCheck : m_LODObjectRoot.b_performCheck;
if (bCheck)
{
// Dependent object
renderCamera = s_userDefinedCamera != null ? s_userDefinedCamera : (m_LODObjectRootPersist != null && m_LODObjectRootPersist.m_renderCamera != null) ? m_LODObjectRootPersist.m_renderCamera : (m_LODObjectRoot != null && m_LODObjectRoot.m_renderCamera != null) ? m_LODObjectRoot.m_renderCamera : (m_renderCamera != null ? m_renderCamera : Camera.main);
if (bCheck && renderCamera != null)
{
int nLODLevel = m_LODObjectRootPersist != null ? m_LODObjectRootPersist.GetLODLevelUsingCamera(renderCamera) : m_LODObjectRoot.GetLODLevelUsingCamera(renderCamera);
if (nLODLevel != -1)
{
SwitchToLOD(nLODLevel, false);
}
}
}
}
else
{
if (m_originalMesh && b_performCheck && renderCamera)
{
// Root object with mesh data
int nLODLevel = GetLODLevelUsingCamera(renderCamera);
if (nLODLevel != -1)
{
SwitchToLOD(nLODLevel, false);
}
}
}
#endif
}
#if !USE_WILLRENDEROBJECT_WORKAROUND // Needs Unity bugfix: changing meshes during OnWillRenderObject
void OnWillRenderObject()
{
if(m_bUseAutomaticCameraLODSwitch == false || LODSwitchMode == SwitchMode.UnityLODGroup)
{
return;
}
#if UNITY_EDITOR
for(int i = 0; i < UnityEditor.SceneView.sceneViews.Count; ++i)
{
UnityEditor.SceneView sceneView = UnityEditor.SceneView.sceneViews[i] as UnityEditor.SceneView;
if (Camera.current == sceneView.camera)
{
return;
}
}
#endif
if (IsDependent())
{
// Dependent object
bool bCheck = m_LODObjectRootPersist != null ? m_LODObjectRootPersist.b_performCheck : m_LODObjectRoot.b_performCheck;
if(bCheck)
{
int nLODLevel = m_LODObjectRootPersist != null ? m_LODObjectRootPersist.GetLODLevelUsingCamera(Camera.current) : m_LODObjectRoot.GetLODLevelUsingCamera(Camera.current);
if (nLODLevel != -1)
{
SwitchToLOD(nLODLevel, false);
}
}
}
else
{
if (m_originalMesh && b_performCheck)
{
// Root object with mesh data
int nLODLevel = GetLODLevelUsingCamera(Camera.current);
if (nLODLevel != -1)
{
SwitchToLOD(nLODLevel, false);
}
}
}
}
#else
void OnWillRenderObject()
{
#if UNITY_EDITOR
// Is it a scene view camera?
for (int i = 0; i < UnityEditor.SceneView.sceneViews.Count; ++i)
{
UnityEditor.SceneView sceneView = UnityEditor.SceneView.sceneViews[i] as UnityEditor.SceneView;
if (Camera.current == sceneView.camera)
{
return;
}
}
if(Camera.current.enabled == false || m_originalMesh == null)
{
return;
}
#endif
m_renderCamera = Camera.current;
}
#endif
#if UNITY_EDITOR
void OnDrawGizmos()
{
if (m_LODObjectRoot != null)
{
if (m_LODObjectRoot.m_bExpandRelevanceSpheres == false)
{
return;
}
}
else
{
if (m_bExpandRelevanceSpheres == false)
{
return;
}
}
Gizmos.color = Color.red;
RelevanceSphere[] aRelevanceSpheres = m_LODObjectRoot != null ? m_LODObjectRoot.m_aRelevanceSpheres : m_aRelevanceSpheres;
if (aRelevanceSpheres == null)
{
return;
}
if(aRelevanceSpheres.Length == 0)
{
return;
}
bool bDrawVertices = false;
for (int i = 0; i < UnityEditor.Selection.gameObjects.Length; i++)
{
if (((UnityEditor.Selection.gameObjects[i] == this.gameObject) && m_LODObjectRoot == null) || ((m_LODObjectRoot != null) && (UnityEditor.Selection.gameObjects[i] == m_LODObjectRoot.gameObject)))
{
bDrawVertices = true;
}
}
if (bDrawVertices == false)
{
return;
}
Vector3[] aVerticesWorld = Simplifier.GetWorldVertices(this.gameObject);
if(aVerticesWorld == null)
{
return;
}
Matrix4x4[] aSphereMatrices = new Matrix4x4[aRelevanceSpheres.Length];
for (int nSphere = 0; nSphere < aRelevanceSpheres.Length; nSphere++)
{
aSphereMatrices[nSphere] = Matrix4x4.TRS(aRelevanceSpheres[nSphere].m_v3Position, Quaternion.Euler(aRelevanceSpheres[nSphere].m_v3Rotation), aRelevanceSpheres[nSphere].m_v3Scale).inverse;
}
for (int nVertex = 0; nVertex < aVerticesWorld.Length; nVertex++)
{
for (int nSphere = 0; nSphere < aRelevanceSpheres.Length; nSphere++)
{
if (aRelevanceSpheres[nSphere].m_bExpanded)
{
Vector3 v3VertexSphereLocal = aSphereMatrices[nSphere].MultiplyPoint(aVerticesWorld[nVertex]);
if (v3VertexSphereLocal.magnitude <= 0.5)
{
Gizmos.DrawCube(aVerticesWorld[nVertex], Vector3.one * UnityEditor.HandleUtility.GetHandleSize(aVerticesWorld[nVertex]) * 0.05f);
break;
}
}
}
}
}
#endif
public static bool HasValidMeshData(GameObject go)
{
MeshFilter meshFilter = go.GetComponent<MeshFilter>();
if (meshFilter != null)
{
return true;
}
else
{
SkinnedMeshRenderer skin = go.GetComponent<SkinnedMeshRenderer>();
if (skin != null)
{
return true;
}
}
return false;
}
public static bool IsRootOrBelongsToLODTree(AutomaticLOD automaticLOD, AutomaticLOD root)
{
if(automaticLOD == null)
{
return false;
}
return (automaticLOD.m_LODObjectRoot == null) || (automaticLOD.m_LODObjectRoot == root) || (automaticLOD == root) || (automaticLOD.m_LODObjectRoot == root.m_LODObjectRoot);
}
public int GetNumberOfLevelsToGenerate()
{
return (int)m_levelsToGenerate;
}
public bool IsGenerateIncludeChildrenActive()
{
return m_bGenerateIncludeChildren;
}
public bool IsRootAutomaticLOD()
{
return m_LODObjectRoot == null;
}
public bool HasDependentChildren()
{
return m_listDependentChildren != null && m_listDependentChildren.Count > 0;
}
public bool HasLODDataDirty()
{
return m_bLODDataDirty;
}
public bool SetLODDataDirty(bool bDirty)
{
return m_bLODDataDirty = bDirty;
}
public int GetLODLevelCount()
{
return m_listLODLevels != null ? m_listLODLevels.Count : 0;
}
public float ComputeScreenCoverage(Camera camera)
{
float fMinX = float.MaxValue;
float fMinY = float.MaxValue;
float fMaxX = float.MinValue;
float fMaxY = float.MinValue;
if(m_originalMesh)
{
if(_corners == null)
{
_corners = new Vector3[8];
}
if (_corners.Length != 8)
{
_corners = new Vector3[8];
}
BuildCornerData(ref _corners, m_originalMesh.bounds);
for(int i = 0; i < _corners.Length; i++)
{
Vector3 v3Viewport = camera.WorldToViewportPoint(transform.TransformPoint(_corners[i]));
if(v3Viewport.x < fMinX) fMinX = v3Viewport.x;
if(v3Viewport.y < fMinY) fMinY = v3Viewport.y;
if(v3Viewport.x > fMaxX) fMaxX = v3Viewport.x;
if(v3Viewport.y > fMaxY) fMaxY = v3Viewport.y;
}
}
for(int nObject = 0; nObject < m_listDependentChildren.Count; nObject++)
{
if(m_listDependentChildren[nObject] != null && m_listDependentChildren[nObject].m_originalMesh)
{
if(m_listDependentChildren[nObject]._corners == null)
{
m_listDependentChildren[nObject]._corners = new Vector3[8];
}
if (m_listDependentChildren[nObject]._corners.Length != 8)
{
m_listDependentChildren[nObject]._corners = new Vector3[8];
}
BuildCornerData(ref m_listDependentChildren[nObject]._corners, m_listDependentChildren[nObject].m_originalMesh.bounds);
for(int i = 0; i < m_listDependentChildren[nObject]._corners.Length; i++)
{
Vector3 v3Viewport = camera.WorldToViewportPoint(m_listDependentChildren[nObject].transform.TransformPoint(m_listDependentChildren[nObject]._corners[i]));
if(v3Viewport.x < fMinX) fMinX = v3Viewport.x;
if(v3Viewport.y < fMinY) fMinY = v3Viewport.y;
if(v3Viewport.x > fMaxX) fMaxX = v3Viewport.x;
if(v3Viewport.y > fMaxY) fMaxY = v3Viewport.y;
}
}
}
float fScreenCoveragePixels = (fMaxX - fMinX) * (fMaxY - fMinY);
return fScreenCoveragePixels;
}
public Vector3 ComputeWorldCenter()
{
float fMinX = float.MaxValue;
float fMinY = float.MaxValue;
float fMinZ = float.MaxValue;
float fMaxX = float.MinValue;
float fMaxY = float.MinValue;
float fMaxZ = float.MinValue;
if (m_originalMesh)
{
for (int i = 0; i < 2; i++)
{
Vector3 v3World = i == 0 ? GetComponent<Renderer>().bounds.min : GetComponent<Renderer>().bounds.max;
if (v3World.x < fMinX) fMinX = v3World.x;
if (v3World.y < fMinY) fMinY = v3World.y;
if (v3World.z < fMinZ) fMinZ = v3World.z;
if (v3World.x > fMaxX) fMaxX = v3World.x;
if (v3World.y > fMaxY) fMaxY = v3World.y;
if (v3World.z > fMaxZ) fMaxZ = v3World.z;
}
}
for (int nObject = 0; nObject < m_listDependentChildren.Count; nObject++)
{
if (m_listDependentChildren[nObject] != null && m_listDependentChildren[nObject].m_originalMesh)
{
for (int i = 0; i < 2; i++)
{
Vector3 v3World = i == 0 ? m_listDependentChildren[nObject].GetComponent<Renderer>().bounds.min : m_listDependentChildren[nObject].GetComponent<Renderer>().bounds.max;
if (v3World.x < fMinX) fMinX = v3World.x;
if (v3World.y < fMinY) fMinY = v3World.y;
if (v3World.z < fMinZ) fMinZ = v3World.z;
if (v3World.x > fMaxX) fMaxX = v3World.x;
if (v3World.y > fMaxY) fMaxY = v3World.y;
if (v3World.z > fMaxZ) fMaxZ = v3World.z;
}
}
}
Vector3 v3Min = new Vector3(fMinX, fMinY, fMinZ);
Vector3 v3Max = new Vector3(fMaxX, fMaxY, fMaxZ);
return (v3Max + v3Min) * 0.5f;
}
public float ComputeViewSpaceBounds(Vector3 v3CameraPos, Vector3 v3CameraDir, Vector3 v3CameraUp, out Vector3 v3Min, out Vector3 v3Max, out Vector3 v3Center)
{
Matrix4x4 mtxView = Matrix4x4.TRS(v3CameraPos, Quaternion.LookRotation(v3CameraDir, v3CameraUp), Vector3.one);
float fMinX = float.MaxValue;
float fMinY = float.MaxValue;
float fMinZ = float.MaxValue;
float fMaxX = float.MinValue;
float fMaxY = float.MinValue;
float fMaxZ = float.MinValue;
v3Center = mtxView.inverse.MultiplyPoint(transform.TransformPoint(Vector3.zero));
if (m_originalMesh)
{
for (int i = 0; i < 2; i++)
{
Vector3 v3World = i == 0 ? GetComponent<Renderer>().bounds.min : GetComponent<Renderer>().bounds.max;
Vector3 v3View = mtxView.inverse.MultiplyPoint(v3World);
if (v3View.x < fMinX) fMinX = v3View.x;
if (v3View.y < fMinY) fMinY = v3View.y;
if (v3View.z < fMinZ) fMinZ = v3View.z;
if (v3View.x > fMaxX) fMaxX = v3View.x;
if (v3View.y > fMaxY) fMaxY = v3View.y;
if (v3View.z > fMaxZ) fMaxZ = v3View.z;
}
}
for (int nObject = 0; nObject < m_listDependentChildren.Count; nObject++)
{
if (m_listDependentChildren[nObject] != null && m_listDependentChildren[nObject].m_originalMesh)
{
for (int i = 0; i < 2; i++)
{
Vector3 v3World = i == 0 ? m_listDependentChildren[nObject].GetComponent<Renderer>().bounds.min : m_listDependentChildren[nObject].GetComponent<Renderer>().bounds.max;
Vector3 v3View = mtxView.inverse.MultiplyPoint(v3World);
if (v3View.x < fMinX) fMinX = v3View.x;
if (v3View.y < fMinY) fMinY = v3View.y;
if (v3View.z < fMinZ) fMinZ = v3View.z;
if (v3View.x > fMaxX) fMaxX = v3View.x;
if (v3View.y > fMaxY) fMaxY = v3View.y;
if (v3View.z > fMaxZ) fMaxZ = v3View.z;
}
}
}
v3Min = new Vector3(fMinX, fMinY, fMinZ);
v3Max = new Vector3(fMaxX, fMaxY, fMaxZ);
float fViewSurfaceArea = (fMaxX - fMinX) * (fMaxY - fMinY);
return fViewSurfaceArea;
}
public void SetAutomaticCameraLODSwitch(bool bEnabled)
{
SetAutomaticCameraLODSwitchRecursive(this, this.gameObject, bEnabled);
}
private static void SetAutomaticCameraLODSwitchRecursive(AutomaticLOD root, GameObject gameObject, bool bEnabled)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if(automaticLOD != null && IsRootOrBelongsToLODTree(automaticLOD, root))
{
automaticLOD.m_bUseAutomaticCameraLODSwitch = bEnabled;
}
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
SetAutomaticCameraLODSwitchRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bEnabled);
}
}
public void SetLODLevels(List<LODLevelData> listLODLevelData, EvalMode evalMode, float fMaxCameraDistance, bool bRecurseIntoChildren)
{
m_listLODLevels = listLODLevelData;
m_fMaxCameraDistance = fMaxCameraDistance;
m_nColorEditorBarNewIndex = listLODLevelData.Count;
m_evalMode = evalMode;
m_LODObjectRoot = null;
m_LODObjectRootPersist = null;
m_listDependentChildren = new List<AutomaticLOD>();
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < transform.childCount; nChild++)
{
SetLODLevelsRecursive(this, transform.GetChild(nChild).gameObject);
}
}
}
private static void SetLODLevelsRecursive(AutomaticLOD root, GameObject gameObject)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
bool bProcess = false;
if(automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
bProcess = true;
}
}
else
{
if(HasValidMeshData(gameObject))
{
automaticLOD = gameObject.AddComponent<AutomaticLOD>();
bProcess = true;
}
}
if(bProcess && automaticLOD)
{
automaticLOD.m_fMaxCameraDistance = root.m_fMaxCameraDistance;
automaticLOD.m_nColorEditorBarNewIndex = root.m_nColorEditorBarNewIndex;
automaticLOD.m_evalMode = root.m_evalMode;
automaticLOD.m_listLODLevels = new List<LODLevelData>();
automaticLOD.m_LODObjectRoot = root;
automaticLOD.m_LODObjectRootPersist = root;
root.m_listDependentChildren.Add(automaticLOD);
for (int i = 0; i < root.m_listLODLevels.Count; i++)
{
automaticLOD.m_listLODLevels.Add(root.m_listLODLevels[i].GetCopy());
automaticLOD.m_listLODLevels[i].m_mesh = CreateNewEmptyMesh(automaticLOD);
}
}
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
SetLODLevelsRecursive(root, gameObject.transform.GetChild(nChild).gameObject);
}
if(bProcess && automaticLOD)
{
for (int i = 0; i < root.m_listLODLevels.Count; i++)
{
CheckForAdditionalLODSetup(root, automaticLOD, automaticLOD.m_listLODLevels[i], i);
}
}
}
public void AddLODLevel(int nLevel, bool bBefore, bool bCreateMesh, bool bRecurseIntoChildren)
{
AddLODLevelRecursive(this, this.gameObject, nLevel, bBefore, bCreateMesh, bRecurseIntoChildren);
}
public static void AddLODLevelRecursive(AutomaticLOD root, GameObject gameObject, int nLevel, bool bBefore, bool bCreateMesh, bool bRecurseIntoChildren)
{
if (Simplifier.Cancelled)
{
return;
}
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
bool bProcess = true;
if (automaticLOD.m_listLODLevels == null)
{
bProcess = false;
}
else
{
if (nLevel < 0 || nLevel >= automaticLOD.m_listLODLevels.Count)
{
bProcess = false;
}
}
if(bProcess)
{
LODLevelData data = new LODLevelData();
data.m_bUsesOriginalMesh = false;
data.m_gameObject = null;
if (bBefore)
{
if (nLevel == 0)
{
data.m_fScreenCoverage = automaticLOD.m_listLODLevels[0].m_fScreenCoverage;
data.m_fMaxCameraDistance = automaticLOD.m_listLODLevels[0].m_fMaxCameraDistance;
data.m_fMeshVerticesAmount = 1.0f;
data.m_nColorEditorBarIndex = automaticLOD.m_nColorEditorBarNewIndex++;
if (automaticLOD.m_listLODLevels.Count > 1)
{
automaticLOD.m_listLODLevels[0].m_fScreenCoverage = (automaticLOD.m_listLODLevels[0].m_fScreenCoverage + automaticLOD.m_listLODLevels[1].m_fScreenCoverage) / 2.0f;
automaticLOD.m_listLODLevels[0].m_fMaxCameraDistance = (automaticLOD.m_listLODLevels[0].m_fMaxCameraDistance + automaticLOD.m_listLODLevels[1].m_fMaxCameraDistance) / 2.0f;
}
else
{
automaticLOD.m_listLODLevels[0].m_fScreenCoverage *= 0.5f;
automaticLOD.m_listLODLevels[0].m_fMaxCameraDistance *= 2.0f;
if(Mathf.Approximately(automaticLOD.m_listLODLevels[0].m_fMaxCameraDistance, 0.0f))
{
automaticLOD.m_listLODLevels[0].m_fMaxCameraDistance = automaticLOD.m_fMaxCameraDistance * 0.5f;
}
}
}
else
{
data.m_fScreenCoverage = (automaticLOD.m_listLODLevels[nLevel - 1].m_fScreenCoverage + automaticLOD.m_listLODLevels[nLevel].m_fScreenCoverage) / 2.0f;
data.m_fMaxCameraDistance = (automaticLOD.m_listLODLevels[nLevel - 1].m_fMaxCameraDistance + automaticLOD.m_listLODLevels[nLevel].m_fMaxCameraDistance) / 2.0f;
data.m_fMeshVerticesAmount = (automaticLOD.m_listLODLevels[nLevel - 1].m_fMeshVerticesAmount + automaticLOD.m_listLODLevels[nLevel].m_fMeshVerticesAmount) / 2.0f;
data.m_nColorEditorBarIndex = automaticLOD.m_nColorEditorBarNewIndex++;
}
if(bCreateMesh)
{
if (data.m_mesh == null)
{
data.m_mesh = CreateNewEmptyMesh(automaticLOD);
}
}
automaticLOD.m_listLODLevels.Insert(nLevel, data);
if(bCreateMesh)
{
CheckForAdditionalLODSetup(root, automaticLOD, data, nLevel == 0 ? 0 : nLevel - 1);
}
}
else
{
int nLastLevel = automaticLOD.m_listLODLevels.Count - 1;
if (nLevel == nLastLevel)
{
data.m_fScreenCoverage = automaticLOD.m_listLODLevels[nLastLevel].m_fScreenCoverage * 0.5f;
data.m_fMaxCameraDistance = (automaticLOD.m_listLODLevels[nLastLevel].m_fMaxCameraDistance + automaticLOD.m_fMaxCameraDistance) * 0.5f;
data.m_fMeshVerticesAmount = automaticLOD.m_listLODLevels[nLastLevel].m_fMeshVerticesAmount * 0.5f;
data.m_nColorEditorBarIndex = automaticLOD.m_nColorEditorBarNewIndex++;
}
else
{
data.m_fScreenCoverage = (automaticLOD.m_listLODLevels[nLevel + 1].m_fScreenCoverage + automaticLOD.m_listLODLevels[nLevel].m_fScreenCoverage) / 2.0f;
data.m_fMaxCameraDistance = (automaticLOD.m_listLODLevels[nLevel + 1].m_fMaxCameraDistance + automaticLOD.m_listLODLevels[nLevel].m_fMaxCameraDistance) / 2.0f;
data.m_fMeshVerticesAmount = (automaticLOD.m_listLODLevels[nLevel + 1].m_fMeshVerticesAmount + automaticLOD.m_listLODLevels[nLevel].m_fMeshVerticesAmount) / 2.0f;
data.m_nColorEditorBarIndex = automaticLOD.m_nColorEditorBarNewIndex++;
}
if (bCreateMesh)
{
if (data.m_mesh == null)
{
data.m_mesh = CreateNewEmptyMesh(automaticLOD);
}
}
if (nLevel == nLastLevel)
{
automaticLOD.m_listLODLevels.Add(data);
}
else
{
automaticLOD.m_listLODLevels.Insert(nLevel + 1, data);
}
if(bCreateMesh)
{
CheckForAdditionalLODSetup(root, automaticLOD, data, nLevel == nLastLevel ? nLastLevel : nLevel + 1);
}
}
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
AddLODLevelRecursive(root, gameObject.transform.GetChild(nChild).gameObject, nLevel, bBefore, bCreateMesh, bRecurseIntoChildren);
if (Simplifier.Cancelled)
{
return;
}
}
}
}
public void RemoveLODLevel(int nLevel, bool bDeleteMesh, bool bRecurseIntoChildren)
{
RemoveLODLevelRecursive(this, this.gameObject, nLevel, bDeleteMesh, bRecurseIntoChildren);
}
public static void RemoveLODLevelRecursive(AutomaticLOD root, GameObject gameObject, int nLevel, bool bDeleteMesh, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
bool bProcess = true;
if (automaticLOD.m_listLODLevels == null)
{
bProcess = false;
}
else
{
if (nLevel < 0 || nLevel >= automaticLOD.m_listLODLevels.Count || automaticLOD.m_listLODLevels.Count == 1)
{
bProcess = false;
}
}
if (bProcess)
{
if (bDeleteMesh)
{
if (automaticLOD.m_listLODLevels[nLevel].m_mesh != null)
{
automaticLOD.m_listLODLevels[nLevel].m_mesh.Clear();
}
if(automaticLOD.m_listLODLevels[nLevel].m_gameObject != null)
{
if(Application.isEditor && Application.isPlaying == false)
{
DestroyImmediate(automaticLOD.m_listLODLevels[nLevel].m_gameObject);
}
else
{
Destroy(automaticLOD.m_listLODLevels[nLevel].m_gameObject);
}
}
}
if (nLevel == 0)
{
if(automaticLOD.m_listLODLevels.Count > 1)
{
automaticLOD.m_listLODLevels[1].m_fMaxCameraDistance = 0.0f;
automaticLOD.m_listLODLevels[1].m_fScreenCoverage = 1.0f;
}
}
automaticLOD.m_listLODLevels.RemoveAt(nLevel);
}
for(int i = 0; i < automaticLOD.m_listLODLevels.Count; i++)
{
if(automaticLOD.m_listLODLevels[i].m_gameObject != null)
{
automaticLOD.m_listLODLevels[i].m_gameObject.name = "LOD" + i;
}
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
RemoveLODLevelRecursive(root, gameObject.transform.GetChild(nChild).gameObject, nLevel, bDeleteMesh, bRecurseIntoChildren);
}
}
}
public Simplifier GetMeshSimplifier()
{
return m_meshSimplifier;
}
public void ComputeLODData(bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
{
ComputeLODDataRecursive(this, this.gameObject, bRecurseIntoChildren, progress);
}
private void ComputeLODDataRecursive(AutomaticLOD root, GameObject gameObject, bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
{
if (Simplifier.Cancelled)
{
return;
}
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if(automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
automaticLOD.FreeLODData(false);
MeshFilter meshFilter = automaticLOD.GetComponent<MeshFilter>();
if (meshFilter != null)
{
if (automaticLOD.m_originalMesh == null)
{
automaticLOD.m_originalMesh = meshFilter.sharedMesh;
}
Simplifier[] simplifiers = automaticLOD.GetComponents<Simplifier>();
for (int c = 0; c < simplifiers.Length; c++)
{
if (Application.isEditor && Application.isPlaying == false)
{
DestroyImmediate(simplifiers[c]);
}
else
{
Destroy(simplifiers[c]);
}
}
automaticLOD.m_meshSimplifier = automaticLOD.gameObject.AddComponent<Simplifier>();
automaticLOD.m_meshSimplifier.hideFlags = HideFlags.HideInInspector;
IEnumerator enumerator = automaticLOD.m_meshSimplifier.ProgressiveMesh(gameObject, automaticLOD.m_originalMesh, root.m_aRelevanceSpheres, automaticLOD.name, progress);
while (enumerator.MoveNext())
{
if (Simplifier.Cancelled)
{
return;
}
}
if (Simplifier.Cancelled)
{
return;
}
}
else
{
SkinnedMeshRenderer skin = automaticLOD.GetComponent<SkinnedMeshRenderer>();
if (skin != null)
{
if (automaticLOD.m_originalMesh == null)
{
automaticLOD.m_originalMesh = skin.sharedMesh;
}
Simplifier[] simplifiers = automaticLOD.GetComponents<Simplifier>();
for (int c = 0; c < simplifiers.Length; c++)
{
if (Application.isEditor && Application.isPlaying == false)
{
DestroyImmediate(simplifiers[c]);
}
else
{
Destroy(simplifiers[c]);
}
}
automaticLOD.m_meshSimplifier = automaticLOD.gameObject.AddComponent<Simplifier>();
automaticLOD.m_meshSimplifier.hideFlags = HideFlags.HideInInspector;
IEnumerator enumerator = automaticLOD.m_meshSimplifier.ProgressiveMesh(gameObject, automaticLOD.m_originalMesh, root.m_aRelevanceSpheres, automaticLOD.name, progress);
while(enumerator.MoveNext())
{
if(Simplifier.Cancelled)
{
return;
}
}
if (Simplifier.Cancelled)
{
return;
}
}
}
}
for (int i = 0; i < automaticLOD.m_listLODLevels.Count; i++)
{
automaticLOD.m_listLODLevels[i].m_mesh = null;
}
automaticLOD.m_bLODDataDirty = false;
}
if(bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount && Simplifier.Cancelled == false; nChild++)
{
ComputeLODDataRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren, progress);
if (Simplifier.Cancelled)
{
return;
}
}
}
}
public bool HasLODData()
{
return m_meshSimplifier != null && m_listLODLevels != null && m_listLODLevels.Count > 0;
}
public int GetLODLevelUsingCamera(Camera currentCamera)
{
// Only compute it once per camera and frame
if (m_cachedFrameLODLevel.ContainsKey(currentCamera))
{
return m_cachedFrameLODLevel[currentCamera];
}
if (m_listLODLevels == null || m_listLODLevels.Count == 0)
{
return -1;
}
float fDistanceToCamera = 0.0f;
float fScreenCoverage = 0.0f;
if (m_evalMode == EvalMode.CameraDistance)
{
Vector3 v3WorldCenter = transform.TransformPoint(m_localCenter.x, m_localCenter.y, m_localCenter.z);
fDistanceToCamera = Vector3.Distance(v3WorldCenter, currentCamera.transform.position);
}
else if(m_evalMode == EvalMode.ScreenCoverage)
{
fScreenCoverage = ComputeScreenCoverage(currentCamera);
}
int nLODLevel = 0;
for (nLODLevel = 0; nLODLevel < m_listLODLevels.Count; nLODLevel++)
{
if (nLODLevel == m_listLODLevels.Count - 1)
{
break;
}
if (m_evalMode == EvalMode.CameraDistance)
{
if (fDistanceToCamera < m_listLODLevels[nLODLevel + 1].m_fMaxCameraDistance)
{
break;
}
}
else if (m_evalMode == EvalMode.ScreenCoverage)
{
if (fScreenCoverage > m_listLODLevels[nLODLevel + 1].m_fScreenCoverage)
{
break;
}
}
}
// Set the cached value for this frame
m_cachedFrameLODLevel.Add(currentCamera, nLODLevel);
return nLODLevel;
}
public int GetCurrentLODLevel()
{
return m_nCurrentLOD;
}
public void SwitchToLOD(int nLevel, bool bRecurseIntoChildren)
{
if(m_LODGroup != null)
{
m_LODGroup.ForceLOD(nLevel);
}
else
{
SwitchToLODRecursive(this, this.gameObject, nLevel, bRecurseIntoChildren);
}
}
private static void SwitchToLODRecursive(AutomaticLOD root, GameObject gameObject, int nLODLevel, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (nLODLevel >= 0 && nLODLevel < automaticLOD.m_listLODLevels.Count && automaticLOD.m_nCurrentLOD != nLODLevel)
{
if (automaticLOD.LODSwitchMode == SwitchMode.SwitchMesh)
{
MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
if (meshFilter != null)
{
Mesh newMesh = automaticLOD.m_listLODLevels[nLODLevel].m_bUsesOriginalMesh ? automaticLOD.m_originalMesh : automaticLOD.m_listLODLevels[nLODLevel].m_mesh;
if (meshFilter.sharedMesh != newMesh)
{
meshFilter.sharedMesh = newMesh;
}
}
else
{
SkinnedMeshRenderer skin = gameObject.GetComponent<SkinnedMeshRenderer>();
if (skin != null)
{
Mesh newMesh = automaticLOD.m_listLODLevels[nLODLevel].m_bUsesOriginalMesh ? automaticLOD.m_originalMesh : automaticLOD.m_listLODLevels[nLODLevel].m_mesh;
if (skin.sharedMesh != newMesh)
{
if (newMesh != null && newMesh.vertexCount == 0)
{
// Avoid editor warning of mesh not having skinning data
if (skin.sharedMesh != null)
{
skin.sharedMesh = null;
}
}
else
{
skin.sharedMesh = newMesh;
}
}
}
}
}
else if(automaticLOD.LODSwitchMode == SwitchMode.SwitchGameObject)
{
if (gameObject.GetComponent<Renderer>() != null)
{
gameObject.GetComponent<Renderer>().enabled = automaticLOD.m_listLODLevels[nLODLevel].m_bUsesOriginalMesh;
}
for (int i = 0; i < automaticLOD.m_listLODLevels.Count; i++)
{
if(automaticLOD.m_listLODLevels[i].m_gameObject != null)
{
automaticLOD.m_listLODLevels[i].m_gameObject.SetActive(automaticLOD.m_listLODLevels[nLODLevel].m_bUsesOriginalMesh == false && i == nLODLevel && automaticLOD.gameObject.activeSelf);
}
}
}
else if(automaticLOD.LODSwitchMode == SwitchMode.UnityLODGroup)
{
return;
}
automaticLOD.m_nCurrentLOD = nLODLevel;
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
SwitchToLODRecursive(root, gameObject.transform.GetChild(nChild).gameObject, nLODLevel, true);
}
}
}
public void ComputeAllLODMeshes(bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
{
if (m_listLODLevels != null)
{
for(int i = 0; i < m_listLODLevels.Count; i++)
{
ComputeLODMeshRecursive(this, this.gameObject, i, bRecurseIntoChildren, progress);
if (Simplifier.Cancelled)
{
return;
}
}
SetupLODGroup();
}
}
public void ComputeLODMesh(int nLevel, bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
{
ComputeLODMeshRecursive(this, this.gameObject, nLevel, bRecurseIntoChildren, progress);
SetupLODGroup();
}
private static void ComputeLODMeshRecursive(AutomaticLOD root, GameObject gameObject, int nLevel, bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
{
if (Simplifier.Cancelled)
{
return;
}
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (automaticLOD.m_meshSimplifier != null)
{
if (automaticLOD.m_listLODLevels[nLevel].m_mesh)
{
automaticLOD.m_listLODLevels[nLevel].m_mesh.Clear();
}
float fAmount = automaticLOD.m_listLODLevels[nLevel].m_fMeshVerticesAmount;
if (automaticLOD.m_bOverrideRootSettings == false && automaticLOD.m_LODObjectRoot != null)
{
fAmount = automaticLOD.m_LODObjectRoot.m_listLODLevels[nLevel].m_fMeshVerticesAmount;
}
if (automaticLOD.m_listLODLevels[nLevel].m_mesh == null)
{
automaticLOD.m_listLODLevels[nLevel].m_mesh = CreateNewEmptyMesh(automaticLOD);
}
CheckForAdditionalLODSetup(root, automaticLOD, automaticLOD.m_listLODLevels[nLevel], nLevel);
int nVertexCount = Mathf.RoundToInt(fAmount * automaticLOD.m_meshSimplifier.GetOriginalMeshUniqueVertexCount());
if(nVertexCount < automaticLOD.m_meshSimplifier.GetOriginalMeshUniqueVertexCount())
{
automaticLOD.m_listLODLevels[nLevel].m_bUsesOriginalMesh = false;
IEnumerator enumerator = automaticLOD.m_meshSimplifier.ComputeMeshWithVertexCount(gameObject, automaticLOD.m_listLODLevels[nLevel].m_mesh, nVertexCount, automaticLOD.name + " LOD " + nLevel, progress);
while (enumerator.MoveNext())
{
if (Simplifier.Cancelled)
{
return;
}
}
if (Simplifier.Cancelled)
{
return;
}
}
else
{
automaticLOD.m_listLODLevels[nLevel].m_bUsesOriginalMesh = true;
if (automaticLOD.m_listLODLevels[nLevel].m_gameObject != null && automaticLOD.m_listLODLevels[nLevel].m_gameObject.GetComponent<Renderer>() != null)
{
automaticLOD.m_listLODLevels[nLevel].m_gameObject.GetComponent<Renderer>().enabled = !automaticLOD.m_listLODLevels[nLevel].m_bUsesOriginalMesh;
}
}
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
ComputeLODMeshRecursive(root, gameObject.transform.GetChild(nChild).gameObject, nLevel, bRecurseIntoChildren, progress);
if (Simplifier.Cancelled)
{
return;
}
}
}
}
public void RestoreOriginalMesh(bool bDeleteLODData, bool bRecurseIntoChildren)
{
if(m_LODGroup != null)
{
m_LODGroup.ForceLOD(-1);
}
RestoreOriginalMeshRecursive(this, this.gameObject, bDeleteLODData, bRecurseIntoChildren);
}
private static void RestoreOriginalMeshRecursive(AutomaticLOD root, GameObject gameObject, bool bDeleteLODData, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (automaticLOD.LODSwitchMode != SwitchMode.SwitchGameObject)
{
if (automaticLOD.m_originalMesh != null)
{
MeshFilter meshFilter = automaticLOD.GetComponent<MeshFilter>();
if (meshFilter != null)
{
meshFilter.sharedMesh = automaticLOD.m_originalMesh;
}
else
{
SkinnedMeshRenderer skin = automaticLOD.GetComponent<SkinnedMeshRenderer>();
if (skin != null)
{
skin.sharedMesh = automaticLOD.m_originalMesh;
}
}
}
}
automaticLOD.m_nCurrentLOD = -1;
if(automaticLOD.LODSwitchMode == SwitchMode.SwitchGameObject)
{
if (gameObject.GetComponent<Renderer>() != null)
{
gameObject.GetComponent<Renderer>().enabled = true;
}
for(int i = 0; i < automaticLOD.m_listLODLevels.Count; i++)
{
if(automaticLOD.m_listLODLevels[i].m_gameObject != null)
{
automaticLOD.m_listLODLevels[i].m_gameObject.SetActive(false);
}
}
}
if(bDeleteLODData)
{
automaticLOD.FreeLODData(false);
automaticLOD.m_listLODLevels.Clear();
automaticLOD.m_listLODLevels = null;
automaticLOD.m_listDependentChildren.Clear();
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
RestoreOriginalMeshRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bDeleteLODData, bRecurseIntoChildren);
}
}
}
public bool HasOriginalMeshActive(bool bRecurseIntoChildren)
{
return HasOriginalMeshActiveRecursive(this, this.gameObject, bRecurseIntoChildren);
}
private static bool HasOriginalMeshActiveRecursive(AutomaticLOD root, GameObject gameObject, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
bool bHasOriginalMeshActive = false;
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (automaticLOD.m_originalMesh != null)
{
MeshFilter meshFilter = automaticLOD.GetComponent<MeshFilter>();
if (meshFilter != null)
{
if(meshFilter.sharedMesh == automaticLOD.m_originalMesh)
{
bHasOriginalMeshActive = true;
}
}
else
{
SkinnedMeshRenderer skin = automaticLOD.GetComponent<SkinnedMeshRenderer>();
if (skin != null)
{
if(skin.sharedMesh == automaticLOD.m_originalMesh)
{
bHasOriginalMeshActive = true;
}
}
}
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
bHasOriginalMeshActive = bHasOriginalMeshActive || HasOriginalMeshActiveRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren);
}
}
return bHasOriginalMeshActive;
}
public bool HasVertexData(int nLevel, bool bRecurseIntoChildren)
{
return HasVertexDataRecursive(this, this.gameObject, nLevel, bRecurseIntoChildren);
}
private static bool HasVertexDataRecursive(AutomaticLOD root, GameObject gameObject, int nLevel, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (automaticLOD.m_listLODLevels[nLevel].m_bUsesOriginalMesh)
{
if(automaticLOD.m_originalMesh && automaticLOD.m_originalMesh.vertexCount > 0)
{
return true;
}
}
else if (automaticLOD.m_listLODLevels[nLevel].m_mesh && automaticLOD.m_listLODLevels[nLevel].m_mesh.vertexCount > 0)
{
return true;
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
if (HasVertexDataRecursive(root, gameObject.transform.GetChild(nChild).gameObject, nLevel, bRecurseIntoChildren))
{
return true;
}
}
}
return false;
}
public int GetOriginalVertexCount(bool bRecurseIntoChildren)
{
int nVertexCount = 0;
GetOriginalVertexCountRecursive(this, this.gameObject, ref nVertexCount, bRecurseIntoChildren);
return nVertexCount;
}
private static void GetOriginalVertexCountRecursive(AutomaticLOD root, GameObject gameObject, ref int nVertexCount, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (automaticLOD.m_originalMesh != null)
{
nVertexCount += automaticLOD.m_originalMesh.vertexCount;
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
GetOriginalVertexCountRecursive(root, gameObject.transform.GetChild(nChild).gameObject, ref nVertexCount, bRecurseIntoChildren);
}
}
}
public int GetOriginalTriangleCount(bool bRecurseIntoChildren)
{
int nTriangleCount = 0;
GetOriginalTriangleCountRecursive(this, this.gameObject, ref nTriangleCount, bRecurseIntoChildren);
return nTriangleCount;
}
private static void GetOriginalTriangleCountRecursive(AutomaticLOD root, GameObject gameObject, ref int nTriangleCount, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (automaticLOD.m_originalMesh != null)
{
nTriangleCount += automaticLOD.m_originalMesh.triangles.Length / 3;
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
GetOriginalTriangleCountRecursive(root, gameObject.transform.GetChild(nChild).gameObject, ref nTriangleCount, bRecurseIntoChildren);
}
}
}
public int GetCurrentVertexCount(bool bRecurseIntoChildren)
{
int nVertexCount = 0;
GetCurrentVertexCountRecursive(this, this.gameObject, ref nVertexCount, bRecurseIntoChildren);
return nVertexCount;
}
private static void GetCurrentVertexCountRecursive(AutomaticLOD root, GameObject gameObject, ref int nVertexCount, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
if(meshFilter != null && meshFilter.sharedMesh != null)
{
nVertexCount += meshFilter.sharedMesh.vertexCount;
}
else
{
SkinnedMeshRenderer skin = gameObject.GetComponent<SkinnedMeshRenderer>();
if (skin != null && skin.sharedMesh != null)
{
nVertexCount += skin.sharedMesh.vertexCount;
}
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
GetCurrentVertexCountRecursive(root, gameObject.transform.GetChild(nChild).gameObject, ref nVertexCount, bRecurseIntoChildren);
}
}
}
public int GetLODVertexCount(int nLevel, bool bRecurseIntoChildren)
{
int nVertexCount = 0;
GetLODVertexCountRecursive(this, this.gameObject, nLevel, ref nVertexCount, bRecurseIntoChildren);
return nVertexCount;
}
private static void GetLODVertexCountRecursive(AutomaticLOD root, GameObject gameObject, int nLevel, ref int nVertexCount, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if(automaticLOD.m_listLODLevels[nLevel].m_bUsesOriginalMesh && automaticLOD.m_originalMesh != null)
{
nVertexCount += automaticLOD.m_originalMesh.vertexCount;
}
else if (automaticLOD.m_listLODLevels[nLevel].m_mesh != null)
{
nVertexCount += automaticLOD.m_listLODLevels[nLevel].m_mesh.vertexCount;
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
GetLODVertexCountRecursive(root, gameObject.transform.GetChild(nChild).gameObject, nLevel, ref nVertexCount, bRecurseIntoChildren);
}
}
}
public int GetLODTriangleCount(int nLevel, bool bRecurseIntoChildren)
{
int nTriangleCount = 0;
GetLODTriangleCountRecursive(this, this.gameObject, nLevel, ref nTriangleCount, bRecurseIntoChildren);
return nTriangleCount;
}
private static void GetLODTriangleCountRecursive(AutomaticLOD root, GameObject gameObject, int nLevel, ref int nTriangleCount, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (automaticLOD.m_listLODLevels[nLevel].m_bUsesOriginalMesh && automaticLOD.m_originalMesh != null)
{
nTriangleCount += automaticLOD.m_originalMesh.triangles.Length / 3;
}
else if (automaticLOD.m_listLODLevels[nLevel].m_mesh != null)
{
nTriangleCount += automaticLOD.m_listLODLevels[nLevel].m_mesh.triangles.Length / 3;
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
GetLODTriangleCountRecursive(root, gameObject.transform.GetChild(nChild).gameObject, nLevel, ref nTriangleCount, bRecurseIntoChildren);
}
}
}
public void RemoveFromLODTree()
{
if (m_LODObjectRoot != null)
{
m_LODObjectRoot.m_listDependentChildren.Remove(this);
}
RestoreOriginalMesh(true, false);
}
public void FreeLODData(bool bRecurseIntoChildren)
{
FreeLODDataRecursive(this, this.gameObject, bRecurseIntoChildren);
}
private static void FreeLODDataRecursive(AutomaticLOD root, GameObject gameObject, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (automaticLOD.m_listLODLevels != null)
{
foreach (LODLevelData data in automaticLOD.m_listLODLevels)
{
if (data.m_mesh)
{
data.m_mesh.Clear();
}
if(data.m_gameObject != null)
{
if(Application.isEditor && Application.isPlaying == false)
{
DestroyImmediate(data.m_gameObject);
}
else
{
Destroy(data.m_gameObject);
}
}
data.m_bUsesOriginalMesh = false;
}
}
Simplifier[] simplifiers = automaticLOD.GetComponents<Simplifier>();
for (int c = 0; c < simplifiers.Length; c++)
{
if (Application.isEditor && Application.isPlaying == false)
{
DestroyImmediate(simplifiers[c]);
}
else
{
Destroy(simplifiers[c]);
}
}
if (automaticLOD.m_meshSimplifier != null)
{
automaticLOD.m_meshSimplifier = null;
}
if(automaticLOD.m_LODGroup != null)
{
if (Application.isEditor && Application.isPlaying == false)
{
DestroyImmediate(automaticLOD.m_LODGroup);
}
else
{
Destroy(automaticLOD.m_LODGroup);
}
}
automaticLOD.m_bLODDataDirty = true;
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
FreeLODDataRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren);
}
}
}
private static Mesh CreateNewEmptyMesh(AutomaticLOD automaticLOD)
{
if(automaticLOD.m_originalMesh == null)
{
return new Mesh();
}
Mesh meshOut = Mesh.Instantiate(automaticLOD.m_originalMesh);
meshOut.Clear();
return meshOut;
}
private static GameObject CreateBasicObjectCopy(GameObject gameObject, Mesh mesh, Transform parent)
{
GameObject newGameObject = new GameObject();
newGameObject.layer = gameObject.layer;
newGameObject.isStatic = gameObject.isStatic;
newGameObject.tag = gameObject.tag;
newGameObject.transform.parent = parent;
newGameObject.transform.localPosition = Vector3.zero;
newGameObject.transform.localRotation = Quaternion.identity;
newGameObject.transform.localScale = Vector3.one;
Component[] aComponents = gameObject.GetComponents<Component>();
for(int i = 0; i < aComponents.Length; i++)
{
Component c = aComponents[i];
if(c.GetType() == typeof(MeshRenderer) || c.GetType() == typeof(MeshFilter) || c.GetType() == typeof(SkinnedMeshRenderer))
{
CopyComponent(c, newGameObject);
}
}
MeshFilter meshFilter = newGameObject.GetComponent<MeshFilter>();
if(meshFilter != null)
{
meshFilter.sharedMesh = mesh;
}
else
{
SkinnedMeshRenderer skin = newGameObject.GetComponent<SkinnedMeshRenderer>();
if(skin != null)
{
skin.sharedMesh = mesh;
}
}
if(newGameObject.GetComponent<Renderer>() != null)
{
newGameObject.GetComponent<Renderer>().enabled = true;
}
return newGameObject;
}
private static void CheckForAdditionalLODSetup(AutomaticLOD root, AutomaticLOD automaticLOD, LODLevelData levelData, int level)
{
if(automaticLOD.LODSwitchMode == SwitchMode.SwitchGameObject || automaticLOD.LODSwitchMode == SwitchMode.UnityLODGroup)
{
if(levelData.m_gameObject != null)
{
if(Application.isEditor && Application.isPlaying == false)
{
DestroyImmediate(levelData.m_gameObject);
}
else
{
Destroy(levelData.m_gameObject);
}
}
levelData.m_gameObject = CreateBasicObjectCopy(automaticLOD.gameObject, levelData.m_mesh, automaticLOD.gameObject.transform);
levelData.m_gameObject.SetActive(automaticLOD.LODSwitchMode == SwitchMode.UnityLODGroup);
for(int i = 0; i < automaticLOD.m_listLODLevels.Count; i++)
{
if(automaticLOD.m_listLODLevels[i].m_gameObject != null)
{
automaticLOD.m_listLODLevels[i].m_gameObject.name = "LOD" + i;
automaticLOD.m_listLODLevels[i].m_gameObject.transform.SetSiblingIndex(i);
}
}
}
else
{
levelData.m_gameObject = null;
}
}
public void SetupLODGroup()
{
AutomaticLOD rootAutomaticLOD = m_LODObjectRoot == null ? this : m_LODObjectRoot;
if (rootAutomaticLOD != null && rootAutomaticLOD.m_switchMode == SwitchMode.UnityLODGroup)
{
LOD[] lods;
if (rootAutomaticLOD.m_LODGroup == null)
{
rootAutomaticLOD.m_LODGroup = rootAutomaticLOD.gameObject.GetComponent<LODGroup>();
if (rootAutomaticLOD.m_LODGroup == null)
{
rootAutomaticLOD.m_LODGroup = rootAutomaticLOD.gameObject.AddComponent<LODGroup>();
}
}
lods = rootAutomaticLOD.m_LODGroup.GetLODs();
List<List<Renderer>> renderers = new List<List<Renderer>>();
for(int i = 0; i < rootAutomaticLOD.GetNumberOfLevelsToGenerate(); ++i)
{
renderers.Add(new List<Renderer>());
}
SetupLODGroupRecursive(rootAutomaticLOD, rootAutomaticLOD.gameObject, ref renderers);
for(int i = 0; i < renderers.Count; ++i)
{
lods[i].renderers = renderers[i].ToArray();
}
for (int i = renderers.Count; i < lods.Length; ++i)
{
lods[i].renderers = null;
}
rootAutomaticLOD.m_LODGroup.SetLODs(lods);
rootAutomaticLOD.m_LODGroup.RecalculateBounds();
}
}
private static void SetupLODGroupRecursive(AutomaticLOD root, GameObject gameObject, ref List<List<Renderer>> renderers)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (automaticLOD.m_listLODLevels != null)
{
bool usesOriginalMesh = false;
for(int i = 0; i < automaticLOD.m_listLODLevels.Count; ++i)
{
LODLevelData data = automaticLOD.m_listLODLevels[i];
Renderer renderer = data.m_bUsesOriginalMesh ? gameObject.GetComponent<Renderer>() : (data.m_gameObject != null ? data.m_gameObject.GetComponent<Renderer>() : null);
if (renderer != null && renderers[i].Contains(renderer) == false)
{
renderers[i].Add(renderer);
}
if(data.m_bUsesOriginalMesh)
{
usesOriginalMesh = true;
}
}
Renderer originalRenderer = gameObject.GetComponent<Renderer>();
if (originalRenderer != null)
{
originalRenderer.enabled = usesOriginalMesh;
}
}
}
}
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
SetupLODGroupRecursive(root, gameObject.transform.GetChild(nChild).gameObject, ref renderers);
}
}
static private Component CopyComponent(Component original, GameObject destination)
{
#if UNITY_EDITOR
System.Type type = original.GetType();
var dst = destination.GetComponent(type);
if(!dst)
{
dst = destination.AddComponent(type);
}
var fields = type.GetFields();
foreach(var field in fields)
{
if(field.IsStatic)
{
continue;
}
field.SetValue(dst, field.GetValue(original));
}
var props = type.GetProperties();
foreach(var prop in props)
{
if(!prop.CanWrite || !prop.CanWrite || prop.Name == "mesh" || prop.Name == "sharedMesh" || prop.Name == "material" || prop.Name == "materials")
{
continue;
}
prop.SetValue(dst, prop.GetValue(original, null), null);
}
return dst;
#else
return null;
#endif
}
#if UNITY_EDITOR
public void DisablePrefabUsage(bool bRecurseIntoChildren)
{
DisablePrefabUsageRecursive(this, this.gameObject, bRecurseIntoChildren);
}
private static void DisablePrefabUsageRecursive(AutomaticLOD root, GameObject gameObject, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null)
{
if (IsRootOrBelongsToLODTree(automaticLOD, root))
{
if (automaticLOD.m_listLODLevels != null)
{
foreach (LODLevelData data in automaticLOD.m_listLODLevels)
{
if (data.m_mesh)
{
if(UnityEditor.AssetDatabase.IsMainAsset(data.m_mesh) || UnityEditor.AssetDatabase.IsSubAsset(data.m_mesh))
{
Mesh newMesh = Instantiate(data.m_mesh) as Mesh;
data.m_mesh = newMesh;
}
}
}
}
automaticLOD.m_strAssetPath = null;
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
DisablePrefabUsageRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren);
}
}
}
#endif
private void BuildCornerData(ref Vector3[] av3Corners, Bounds bounds)
{
av3Corners[0].x = bounds.min.x;
av3Corners[0].y = bounds.min.y;
av3Corners[0].z = bounds.min.z;
av3Corners[1].x = bounds.min.x;
av3Corners[1].y = bounds.min.y;
av3Corners[1].z = bounds.max.z;
av3Corners[2].x = bounds.min.x;
av3Corners[2].y = bounds.max.y;
av3Corners[2].z = bounds.min.z;
av3Corners[3].x = bounds.min.x;
av3Corners[3].y = bounds.max.y;
av3Corners[3].z = bounds.max.z;
av3Corners[4].x = bounds.max.x;
av3Corners[4].y = bounds.min.y;
av3Corners[4].z = bounds.min.z;
av3Corners[5].x = bounds.max.x;
av3Corners[5].y = bounds.min.y;
av3Corners[5].z = bounds.max.z;
av3Corners[6].x = bounds.max.x;
av3Corners[6].y = bounds.max.y;
av3Corners[6].z = bounds.min.z;
av3Corners[7].x = bounds.max.x;
av3Corners[7].y = bounds.max.y;
av3Corners[7].z = bounds.max.z;
}
private bool IsDependent()
{
return m_LODObjectRoot != null || m_LODObjectRootPersist != null;
}
// Optimization to distribute LOD checks over time if we have tens or hundreds of LOD objects
private const int k_MaxLODChecksPerFrame = 4;
private const int k_MaxFrameCheckLoop = 100;
private static int s_currentCheckIndex = 0;
private static int s_currentFrameCheckIndex = 0;
private static int s_checkLoopLength = 0;
private static int s_lastFrameComputedModulus = -1;
private static int s_currentFrameInLoop = -1;
// User defined camera to override the one we use for LOD activation
private static Camera s_userDefinedCamera = null;
// Instance variables
private Camera m_renderCamera = null;
private int m_nCurrentLOD = -1;
private Dictionary<Camera, int> m_cachedFrameLODLevel;
private Vector3 m_localCenter;
private Vector3[] _corners = null;
private int m_frameToCheck = 0;
private bool b_performCheck = false;
}