Commit 9039e78f authored by xujr's avatar xujr

清除模型减面插件

parent 3cb451db
fileFormatVersion: 2
guid: 499f9da9cd5f222439ab9de52c2f8eaa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: dfbc8d70721cd494a9d25c99de2af769
folderAsset: yes
timeCreated: 1434653133
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 58274acced6ba674ea529f016b296eaa
folderAsset: yes
timeCreated: 1434653165
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using UltimateGameTools.MeshSimplifier;
[CustomEditor(typeof(AutomaticLOD)), CanEditMultipleObjects]
public class AutomaticLODEditor : Editor
{
void Progress(string strTitle, string strMessage, float fT)
{
int nPercent = Mathf.RoundToInt(fT * 100.0f);
if(nPercent != s_nLastProgress || s_strLastTitle != strTitle || s_strLastMessage != strMessage)
{
s_strLastTitle = strTitle;
s_strLastMessage = strMessage;
s_nLastProgress = nPercent;
if(EditorUtility.DisplayCancelableProgressBar(strTitle, strMessage, fT))
{
Simplifier.Cancelled = true;
}
}
}
void OnEnable()
{
m_nCurrentLODResizing = -1;
m_nCurrentLODSelected = -1;
m_bPreviewingLOD = false;
Random.InitState(4);
if(s_bStaticDataLoaded == false || !s_texBlack || !s_texDarkGray || !s_texArrow || !s_texHandle || !s_texLODGroupSelected || !s_texLODGroupNotSelected)
{
LoadStaticData();
}
if (serializedObject == null)
{
return;
}
PropertyGenerateIncludeChildren = serializedObject.FindProperty("m_bGenerateIncludeChildren");
PropertyLevelsToGenerate = serializedObject.FindProperty("m_levelsToGenerate");
PropertySwitchMode = serializedObject.FindProperty("m_switchMode");
PropertyEvalMode = serializedObject.FindProperty("m_evalMode");
PropertyEnablePrefabUsage = serializedObject.FindProperty("m_bEnablePrefabUsage");
PropertyMaxCameraDistanceDistance = serializedObject.FindProperty("m_fMaxCameraDistance");
PropertyListLODLevels = serializedObject.FindProperty("m_listLODLevels");
PropertyExpandRelevanceSpheres = serializedObject.FindProperty("m_bExpandRelevanceSpheres");
PropertyRelevanceSpheres = serializedObject.FindProperty("m_aRelevanceSpheres");
PropertyOverrideRootSettings = serializedObject.FindProperty("m_bOverrideRootSettings");
PropertyLODDataDirty = serializedObject.FindProperty("m_bLODDataDirty");
m_bGenerateLODData = false;
m_bRecomputeAllMeshes = false;
m_bEnablePrefabUsage = false;
m_bDisablePrefabUsage = false;
m_bDeleteLODData = false;
m_bRemoveFromLODTree = false;
m_bSetupNewRelevanceSphere = false;
m_fPreviewLODPos = 0.0f;
EditorApplication.update -= Update;
EditorApplication.update += Update;
SetHideFlags();
}
void OnDisable()
{
RestoreOriginalMeshes();
EditorApplication.update -= Update;
}
void Update()
{
AutomaticLOD automaticLOD = target as AutomaticLOD;
// Check for changes in UnityLODGroup mode to restore the LODGroup LOD handling if a specific LOD was forced by selecting on a LOD box.
// We will check for changes in the scene view cameras
if (automaticLOD != null && automaticLOD.LODSwitchMode == AutomaticLOD.SwitchMode.UnityLODGroup && m_dicSceneViewCameraInfos != null)
{
bool restoreLODGroupHandling = false;
for (int i = 0; i < SceneView.sceneViews.Count; i++)
{
SceneView sceneView = SceneView.sceneViews[i] as SceneView;
if (m_dicSceneViewCameraInfos.ContainsKey(sceneView) == false)
{
restoreLODGroupHandling = true;
break;
}
else if (sceneView.camera != null)
{
if(sceneView.camera.transform.position != m_dicSceneViewCameraInfos[sceneView].m_v3Position ||
sceneView.pivot != m_dicSceneViewCameraInfos[sceneView].m_v3Pivot ||
sceneView.rotation != m_dicSceneViewCameraInfos[sceneView].m_qRotation ||
Mathf.Approximately(sceneView.size, m_dicSceneViewCameraInfos[sceneView].m_fSize) == false)
{
restoreLODGroupHandling = true;
break;
}
}
sceneView.pivot = m_dicSceneViewCameraInfos[sceneView].m_v3Pivot;
sceneView.rotation = m_dicSceneViewCameraInfos[sceneView].m_qRotation;
sceneView.size = m_dicSceneViewCameraInfos[sceneView].m_fSize;
}
if (restoreLODGroupHandling && !EditorApplication.isPlaying)
{
foreach (Object targetObject in targets)
{
automaticLOD = targetObject as AutomaticLOD;
automaticLOD.SwitchToLOD(-1, false);
}
m_nCurrentLODSelected = -1;
Repaint();
}
}
}
void OnSceneGUI()
{
AutomaticLOD automaticLOD = target as AutomaticLOD;
bool bDrawSpheres = true;
if (automaticLOD.m_LODObjectRoot != null)
{
if (automaticLOD.m_LODObjectRoot.m_bExpandRelevanceSpheres == false)
{
bDrawSpheres = false;
}
}
else
{
if (automaticLOD.m_bExpandRelevanceSpheres == false)
{
bDrawSpheres = false;
}
}
if (automaticLOD.m_aRelevanceSpheres != null && bDrawSpheres)
{
for (int nSphere = 0; nSphere < automaticLOD.m_aRelevanceSpheres.Length; nSphere++)
{
if (automaticLOD.m_aRelevanceSpheres[nSphere].m_bExpanded == false)
{
continue;
}
RelevanceSphere relevanceSphere = automaticLOD.m_aRelevanceSpheres[nSphere] as RelevanceSphere;
if (Tools.current == Tool.Move)
{
EditorGUI.BeginChangeCheck();
Vector3 v3Position = Handles.PositionHandle(relevanceSphere.m_v3Position, Quaternion.Euler(relevanceSphere.m_v3Rotation));
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(automaticLOD, "Move Relevance Sphere");
relevanceSphere.m_v3Position = v3Position;
automaticLOD.RestoreOriginalMesh(false, true);
m_nCurrentLODSelected = -1;
automaticLOD.SetLODDataDirty(true);
EditorUtility.SetDirty(target);
}
}
else if (Tools.current == Tool.Rotate)
{
EditorGUI.BeginChangeCheck();
Quaternion qRotation = Handles.RotationHandle(Quaternion.Euler(relevanceSphere.m_v3Rotation), relevanceSphere.m_v3Position);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(automaticLOD, "Rotate Relevance Sphere");
relevanceSphere.m_v3Rotation = qRotation.eulerAngles;
automaticLOD.RestoreOriginalMesh(false, true);
m_nCurrentLODSelected = -1;
automaticLOD.SetLODDataDirty(true);
EditorUtility.SetDirty(target);
}
}
else if (Tools.current == Tool.Scale)
{
EditorGUI.BeginChangeCheck();
Vector3 v3Scale = Handles.ScaleHandle(relevanceSphere.m_v3Scale, relevanceSphere.m_v3Position, Quaternion.Euler(relevanceSphere.m_v3Rotation), HandleUtility.GetHandleSize(relevanceSphere.m_v3Position) * 1.0f);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(automaticLOD, "Scale Relevance Sphere");
relevanceSphere.m_v3Scale = v3Scale;
automaticLOD.RestoreOriginalMesh(false, true);
m_nCurrentLODSelected = -1;
automaticLOD.SetLODDataDirty(true);
EditorUtility.SetDirty(target);
}
}
Matrix4x4 mtxHandles = Handles.matrix;
Handles.matrix = Matrix4x4.TRS(relevanceSphere.m_v3Position, Quaternion.Euler(relevanceSphere.m_v3Rotation), relevanceSphere.m_v3Scale);
Handles.color = new Color(0.0f, 0.0f, 1.0f, 0.5f);
Handles.SphereHandleCap(0, Vector3.zero, Quaternion.identity, 1.0f, Event.current.type);
Handles.matrix = mtxHandles;
}
}
Handles.color = Color.white;
if (automaticLOD.LODSwitchMode != AutomaticLOD.SwitchMode.UnityLODGroup)
{
int nLODLevel = automaticLOD.GetCurrentLODLevel();
if (nLODLevel >= 0)
{
Handles.Label(automaticLOD.transform.position, "LOD " + nLODLevel);
}
}
}
public override void OnInspectorGUI()
{
AutomaticLOD automaticLOD;
string strIncludeChildrenLabel = "Recurse Into Children";
int nAutomaticLODObjectsWithLODData = 0;
int nAutomaticLODObjectsWithoutLODData = 0;
int nLODLevels = -1;
int nButtonWidth = 200;
int nButtonWidthSmall = 150;
foreach (Object targetObject in targets)
{
automaticLOD = targetObject as AutomaticLOD;
if (automaticLOD.m_LODObjectRoot != null && targets.Length > 1)
{
EditorGUILayout.HelpBox("One or more GameObjects of the selection is not a root Automatic LOD GameObject. Only root Automatic LOD GameObjects can be edited at the same time.", MessageType.Warning);
return;
}
if (automaticLOD.HasLODData() || automaticLOD.HasDependentChildren())
{
nAutomaticLODObjectsWithLODData++;
if (nLODLevels == -1)
{
nLODLevels = automaticLOD.m_listLODLevels.Count;
}
else
{
if (automaticLOD.m_listLODLevels.Count != nLODLevels)
{
EditorGUILayout.HelpBox("All selected objects need to have the same number of LOD levels", MessageType.Warning);
return;
}
}
}
else
{
nAutomaticLODObjectsWithoutLODData++;
}
}
if (nAutomaticLODObjectsWithoutLODData > 0 && nAutomaticLODObjectsWithLODData > 0)
{
EditorGUILayout.HelpBox("One or more GameObjects of the selection has no LOD levels generated", MessageType.Warning);
return;
}
if (targets.Length > 1)
{
EditorGUILayout.HelpBox("Multiple selection", MessageType.Info);
}
serializedObject.Update();
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
Rect rectSpace = GUILayoutUtility.GetLastRect();
if (nAutomaticLODObjectsWithoutLODData > 0)
{
EditorGUILayout.PropertyField(PropertyGenerateIncludeChildren, new GUIContent(strIncludeChildrenLabel, "If checked, we will traverse the whole GameObject's hierarchy looking for meshes"));
GUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(PropertySwitchMode, new GUIContent("Switch Mode", "Use the new LODGroup for best performance and robustness. The other two are legacy methods: Mesh will create internal meshes and switch between them. GameObject will create LOD level hierarchy GameObjects."));
//GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
if (PropertySwitchMode.enumNames[PropertySwitchMode.enumValueIndex] != AutomaticLOD.SwitchMode.UnityLODGroup.ToString())
{
GUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(PropertyLevelsToGenerate, new GUIContent("Levels To Generate", "Tells how many levels to create by default, but can be changed later on"));
//GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
else
{
// LODGroup uses 4 levels by default (3 + fully culled) and there is no way to change that programatically without falling into Unity's bug where LODGroup.SetLODS() fails
for(int i = 0; i < PropertyLevelsToGenerate.enumNames.Length; ++i)
{
if(PropertyLevelsToGenerate.enumNames[i] == AutomaticLOD.LevelsToGenerate._3.ToString())
{
PropertyLevelsToGenerate.enumValueIndex = i;
break;
}
}
}
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Generate LODs", "Starts the LOD generation process"), GUILayout.Width(nButtonWidth)))
{
m_bGenerateLODData = true;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
EditorGUILayout.HelpBox("Number of LOD levels and their properties can be changed later on this panel, after pressing the \"Generate LODs\" button.\n\nSwitch Mode should have no significant speed impact, it's more a convenience option:\nSwitch Mesh will generate internal meshes and switch between them on the same GameObject. Switch GameObject will generate child GameObjects containing LOD levels and enable/disable the correct one.", MessageType.Info);
}
if (nLODLevels > 0)
{
automaticLOD = target as AutomaticLOD;
if (automaticLOD.m_LODObjectRoot == null)
{
if (automaticLOD.LODSwitchMode != AutomaticLOD.SwitchMode.UnityLODGroup)
{
EditorGUILayout.PropertyField(PropertyEvalMode, new GUIContent("LOD Switch Criteria", "Changes the criteria used to switch from one LOD to another at runtime"));
if (PropertyEvalMode.enumNames[PropertyEvalMode.enumValueIndex] == AutomaticLOD.EvalMode.CameraDistance.ToString())
{
PropertyMaxCameraDistanceDistance.floatValue = EditorGUILayout.FloatField(new GUIContent("Max Camera Distance", "Furthest camera distance to consider"), PropertyMaxCameraDistanceDistance.floatValue);
}
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(PropertyEnablePrefabUsage, new GUIContent("Enable Prefab Usage", "Will save the generated mesh assets to disk, so that this GameObject can be used as a prefab and be instantiated at runtime. Otherwise the mesh won't be available"));
if (EditorGUI.EndChangeCheck())
{
if (PropertyEnablePrefabUsage.boolValue)
{
m_bEnablePrefabUsage = true;
}
else
{
m_bDisablePrefabUsage = true;
}
}
EditorGUILayout.Space();
PropertyExpandRelevanceSpheres.boolValue = EditorGUILayout.Foldout(PropertyExpandRelevanceSpheres.boolValue, new GUIContent("Vertex Relevance Modifiers:"));
if (PropertyExpandRelevanceSpheres.boolValue)
{
EditorGUILayout.HelpBox("Use vertex relevance spheres to select which vertices should be preserved with more or less priority when simplifying the mesh.", MessageType.Info);
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Add New Sphere", "Adds a new vertex relevance sphere"), GUILayout.Width(nButtonWidthSmall)))
{
PropertyRelevanceSpheres.InsertArrayElementAtIndex(0);
m_bSetupNewRelevanceSphere = true;
PropertyLODDataDirty.boolValue = true;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
EditorGUILayout.Space();
EditorGUI.indentLevel++;
int nSphereToDelete = -1;
for (int i = 0; i < PropertyRelevanceSpheres.arraySize; i++)
{
SerializedProperty elementProperty = PropertyRelevanceSpheres.GetArrayElementAtIndex(i);
SerializedProperty elementExpanded = elementProperty.FindPropertyRelative("m_bExpanded");
SerializedProperty elementPosition = elementProperty.FindPropertyRelative("m_v3Position");
SerializedProperty elementRotation = elementProperty.FindPropertyRelative("m_v3Rotation");
SerializedProperty elementScale = elementProperty.FindPropertyRelative("m_v3Scale");
SerializedProperty elementRelevance = elementProperty.FindPropertyRelative("m_fRelevance");
elementExpanded.boolValue = EditorGUILayout.Foldout(elementExpanded.boolValue, new GUIContent("Sphere"));
if (elementExpanded.boolValue)
{
bool bWideMode = EditorGUIUtility.wideMode;
EditorGUIUtility.wideMode = true;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(elementPosition, new GUIContent("Position"));
if (EditorGUI.EndChangeCheck())
{
DeselectLODLevel();
PropertyLODDataDirty.boolValue = true;
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(elementRotation, new GUIContent("Rotation"));
if (EditorGUI.EndChangeCheck())
{
DeselectLODLevel();
PropertyLODDataDirty.boolValue = true;
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(elementScale, new GUIContent("Scale"));
if (EditorGUI.EndChangeCheck())
{
DeselectLODLevel();
PropertyLODDataDirty.boolValue = true;
}
EditorGUI.BeginChangeCheck();
elementRelevance.floatValue = EditorGUILayout.Slider(new GUIContent("Relevance", "Tells the simplification algorithm how relevant the vertices inside this sphere are. Default relevance is 0, use lower values to discard non important vertices, and higher values to keep them before others when simplifying the mesh"), elementRelevance.floatValue, -1.0f, 1.0f);
if (EditorGUI.EndChangeCheck())
{
PropertyLODDataDirty.boolValue = true;
}
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Remove Sphere", "Removes this relevance sphere"), GUILayout.Width(nButtonWidthSmall)))
{
DeselectLODLevel();
nSphereToDelete = i;
PropertyLODDataDirty.boolValue = true;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
EditorGUIUtility.wideMode = bWideMode;
}
}
if (nSphereToDelete >= 0)
{
PropertyRelevanceSpheres.DeleteArrayElementAtIndex(nSphereToDelete);
}
EditorGUI.indentLevel--;
}
EditorGUILayout.Space();
string helpBoxString = "Use the bar below to manage LOD levels:\nClick to select a LOD level and adjust parameters.";
if(automaticLOD.LODSwitchMode == AutomaticLOD.SwitchMode.UnityLODGroup)
{
helpBoxString += " Use the LODGroup component to adjust the camera ranges where each LOD level is visible";
}
else
{
helpBoxString += " Right click to add / delete new LOD levels.Drag the splitters in between to adjust when each LOD level should be visible and slide the top cursor to preview the current settings in the Scene window.";
}
EditorGUILayout.HelpBox(helpBoxString, MessageType.Info);
EditorGUILayout.Space();
int y = (int)GUILayoutUtility.GetLastRect().yMax;
GUILayout.Space(65);
DrawAndHandleLODBar(automaticLOD, y, (int)rectSpace.width, nButtonWidthSmall);
EditorGUILayout.Space();
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Recompute all levels", "Recomputes all different LOD meshes at once"), GUILayout.Width(nButtonWidth)))
{
m_bRecomputeAllMeshes = true;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Delete LOD data...", "Deletes all LOD data and restores the original GameObject's mesh"), GUILayout.Width(nButtonWidth)))
{
if (EditorUtility.DisplayDialog("Delete all LOD data and restore original mesh?", "Are you sure you want to delete all LOD data and restore the original mesh?", "Delete", "Cancel"))
{
m_bDeleteLODData = true;
}
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
else
{
GUILayout.Label("Child LOD GameObject depending on " + automaticLOD.m_LODObjectRoot.name);
EditorGUILayout.PropertyField(PropertyOverrideRootSettings, new GUIContent("Override " + automaticLOD.m_LODObjectRoot.name + " settings", "Will allow to edit this object's own parameters, instead of inheriting those of the root Automatic LOD GameObject"));
if (PropertyOverrideRootSettings.boolValue)
{
EditorGUILayout.Space();
for (int nLevel = 0; nLevel < automaticLOD.m_listLODLevels.Count; nLevel++)
{
SerializedProperty elementProperty = PropertyListLODLevels.GetArrayElementAtIndex(nLevel);
SerializedProperty elementVertexAmount = elementProperty.FindPropertyRelative("m_fMeshVerticesAmount");
EditorGUILayout.BeginHorizontal();
string strControlName = "SliderLOD" + nLevel;
GUI.SetNextControlName(strControlName);
float fVertexAmount = EditorGUILayout.Slider(new GUIContent("LOD " + nLevel + " vertex %", "The percentage of vertices from the original mesh to keep when simplifying it"), elementVertexAmount.floatValue * 100.0f, 0.0f, 100.0f);
elementVertexAmount.floatValue = Mathf.Clamp01(fVertexAmount / 100.0f);
if (GUI.GetNameOfFocusedControl() == strControlName && !EditorApplication.isPlaying)
{
automaticLOD.SwitchToLOD(nLevel, false);
}
if (GUILayout.Button(new GUIContent("Regenerate", "Recomputes this level's mesh"), GUILayout.Width(80)))
{
Simplifier.Cancelled = false;
automaticLOD = target as AutomaticLOD;
ChangeLODVertexAmount(automaticLOD, nLevel, automaticLOD.m_listLODLevels[nLevel].m_fMeshVerticesAmount, false);
if (Simplifier.Cancelled == false)
{
automaticLOD.SwitchToLOD(nLevel, false);
}
EditorUtility.ClearProgressBar();
if (Simplifier.Cancelled == false)
{
SaveMeshAssets();
}
Simplifier.Cancelled = false;
}
EditorGUILayout.EndHorizontal();
}
}
EditorGUILayout.Space();
EditorGUILayout.LabelField(new GUIContent("Mesh optimization stats:"));
EditorGUI.indentLevel += 2;
for (int nLevel = 0; nLevel < automaticLOD.m_listLODLevels.Count; nLevel++)
{
string strStats = "No mesh data";
if(automaticLOD.m_listLODLevels[nLevel].m_mesh != null && automaticLOD.m_originalMesh != null)
{
float fPercent = automaticLOD.m_listLODLevels[nLevel].m_fMeshVerticesAmount * 100.0f;
int levelTris = automaticLOD.m_listLODLevels[nLevel].m_bUsesOriginalMesh ? automaticLOD.m_originalMesh.triangles.Length / 3 : automaticLOD.m_listLODLevels[nLevel].m_mesh.triangles.Length / 3;
int totalTris = automaticLOD.m_originalMesh.triangles.Length / 3;
float fPercentTris = ((float)levelTris / (float)totalTris) * 100.0f;
if(PropertyOverrideRootSettings.boolValue == false && automaticLOD.m_LODObjectRoot.GetLODLevelCount() > nLevel)
{
fPercent = automaticLOD.m_LODObjectRoot.m_listLODLevels[nLevel].m_fMeshVerticesAmount * 100.0f;
}
strStats = string.Format("{0}/{1} Vertices ({2:0.00}%), {3}/{4} Tris ({5:0.00}%)", automaticLOD.m_listLODLevels[nLevel].m_bUsesOriginalMesh ? automaticLOD.m_originalMesh.vertexCount : automaticLOD.m_listLODLevels[nLevel].m_mesh.vertexCount, automaticLOD.m_originalMesh.vertexCount, fPercent,
levelTris, totalTris, fPercentTris);
}
EditorGUILayout.LabelField(new GUIContent("LOD " + nLevel + ": " + strStats));
}
EditorGUI.indentLevel -= 2;
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Exclude from LOD tree...", "Restores this GameObject's original mesh and excludes it from the LOD computation"), GUILayout.Width(nButtonWidth)))
{
if (EditorUtility.DisplayDialog("Remove from LOD tree?", "Are you sure you want to restore this gameobject's mesh and exclude it from the LOD logic?", "Remove", "Cancel"))
{
m_bRemoveFromLODTree = true;
}
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
}
serializedObject.ApplyModifiedProperties();
bool bRepaint = false;
if(m_bEnablePrefabUsage)
{
Simplifier.Cancelled = false;
m_bEnablePrefabUsage = false;
SaveMeshAssets();
Simplifier.Cancelled = false;
}
if(m_bDisablePrefabUsage)
{
m_bDisablePrefabUsage = false;
if (PropertyEnablePrefabUsage.boolValue == false)
{
foreach (Object targetObject in targets)
{
automaticLOD = targetObject as AutomaticLOD;
automaticLOD.DisablePrefabUsage(true);
}
}
}
if (m_bGenerateLODData && Event.current.type == EventType.Repaint)
{
m_bGenerateLODData = false;
Simplifier.Cancelled = false;
foreach (Object targetObject in targets)
{
automaticLOD = targetObject as AutomaticLOD;
if (PropertyGenerateIncludeChildren.boolValue == false)
{
if (AutomaticLOD.HasValidMeshData(automaticLOD.gameObject) == false)
{
EditorUtility.DisplayDialog("Error", "Object " + automaticLOD.name + " has no MeshFilter nor Skinned Mesh to process. Please use the \"" + strIncludeChildrenLabel + "\" parameter if you want to process the whole " + automaticLOD.name + " hierarchy for meshes", "OK");
continue;
}
}
try
{
CreateDefaultLODS(PropertyLevelsToGenerate.enumValueIndex + 1, automaticLOD, PropertyGenerateIncludeChildren.boolValue);
automaticLOD.ComputeLODData(true, Progress);
if (Simplifier.Cancelled == false)
{
automaticLOD.ComputeAllLODMeshes(true, Progress);
}
else
{
DeleteLODDataRecursive(automaticLOD.gameObject, automaticLOD.gameObject, true);
break;
}
m_nCurrentLODSelected = -1;
}
catch(System.Exception e)
{
Debug.LogError("Error generating LODs: " + e.Message + " Stack: " + e.StackTrace);
EditorUtility.ClearProgressBar();
Simplifier.Cancelled = false;
}
}
bRepaint = true;
EditorUtility.ClearProgressBar();
Simplifier.Cancelled = false;
}
if (m_bRecomputeAllMeshes && Event.current.type == EventType.Repaint)
{
m_bRecomputeAllMeshes = false;
Simplifier.Cancelled = false;
foreach (Object targetObject in targets)
{
automaticLOD = targetObject as AutomaticLOD;
try
{
int nRestoreLODLevel = -1;
if (automaticLOD.HasLODDataDirty())
{
nRestoreLODLevel = m_nCurrentLODSelected;
automaticLOD.RestoreOriginalMesh(false, true);
automaticLOD.ComputeLODData(true, Progress);
}
if (Simplifier.Cancelled == false)
{
automaticLOD.ComputeAllLODMeshes(true, Progress);
if (nRestoreLODLevel >= 0 && Simplifier.Cancelled == false)
{
automaticLOD.SwitchToLOD(nRestoreLODLevel, true);
}
if (Simplifier.Cancelled)
{
break;
}
}
else
{
DeleteLODDataRecursive(automaticLOD.gameObject, automaticLOD.gameObject, true);
break;
}
}
catch (System.Exception e)
{
Debug.LogError("Error recomputing all meshes: " + e.Message + " Stack: " + e.StackTrace);
EditorUtility.ClearProgressBar();
Simplifier.Cancelled = false;
}
}
bRepaint = true;
EditorUtility.ClearProgressBar();
if (Simplifier.Cancelled == false)
{
SaveMeshAssets();
}
Simplifier.Cancelled = false;
}
if (m_bDeleteLODData && Event.current.type == EventType.Repaint)
{
m_bDeleteLODData = false;
DeleteLODData();
bRepaint = true;
}
if (m_bRemoveFromLODTree && Event.current.type == EventType.Repaint)
{
m_bRemoveFromLODTree = false;
automaticLOD = target as AutomaticLOD;
automaticLOD.RemoveFromLODTree();
if (Application.isEditor && Application.isPlaying == false)
{
UnityEngine.Object.DestroyImmediate(automaticLOD);
}
else
{
UnityEngine.Object.Destroy(automaticLOD);
}
bRepaint = true;
}
if (m_bSetupNewRelevanceSphere)
{
m_bSetupNewRelevanceSphere = false;
foreach (Object targetObject in targets)
{
automaticLOD = targetObject as AutomaticLOD;
if (automaticLOD.m_aRelevanceSpheres != null && automaticLOD.m_aRelevanceSpheres.Length > 0)
{
automaticLOD.m_aRelevanceSpheres[0].SetDefault(automaticLOD.transform, 0.2f);
}
}
}
if (bRepaint)
{
Repaint();
}
}
Rect DrawAndHandleLODBar(AutomaticLOD automaticLOD, int y, int nTotalWidth, int nButtonWidth)
{
int nMargin = 10;
int nWidth = nTotalWidth - (nMargin * 2);
int nHeight = 30;
// Compute LOD bar positions (each entry tells where the LOD ends, 0-1);
int nLODLevels = automaticLOD.m_listLODLevels.Count;
float[] afLODPositions = new float[nLODLevels];
bool bRectLODSelected = false;
Rect rectLODSelected = new Rect();
Rect rectLODSelectedBorder = new Rect();
for (int nLOD = 0; nLOD < nLODLevels; nLOD++)
{
if (automaticLOD.m_evalMode == AutomaticLOD.EvalMode.ScreenCoverage)
{
afLODPositions[nLOD] = nLOD == nLODLevels - 1 ? 1.0f : ScreenCoverageToSlider(automaticLOD.m_listLODLevels[nLOD + 1].m_fScreenCoverage);
}
else if (automaticLOD.m_evalMode == AutomaticLOD.EvalMode.CameraDistance)
{
afLODPositions[nLOD] = nLOD == nLODLevels - 1 ? 1.0f : (automaticLOD.m_listLODLevels[nLOD + 1].m_fMaxCameraDistance / automaticLOD.m_fMaxCameraDistance);
}
}
int nPreviewBarHeight = 15;
int nPreviewVerticalMargin = 5;
int nYPreview = y;
if (automaticLOD.LODSwitchMode != AutomaticLOD.SwitchMode.UnityLODGroup)
{
// Preview bar
Rect rectPreview = new Rect(nMargin, y, nWidth, nPreviewBarHeight + nPreviewVerticalMargin);
if (m_nCurrentLODResizing == -1)
{
EditorGUIUtility.AddCursorRect(rectPreview, MouseCursor.SlideArrow);
}
int nPreviewControlID = GUIUtility.GetControlID(FocusType.Passive, rectPreview);
if (Event.current.type == EventType.MouseDown && rectPreview.Contains(Event.current.mousePosition) && Event.current.button == 0)
{
m_bPreviewingLOD = true;
m_fPreviewLODPos = Mathf.Clamp01((float)(Event.current.mousePosition.x - nMargin) / (float)rectPreview.width);
PreviewLOD(m_fPreviewLODPos, afLODPositions);
SaveSceneViewCameraInfo();
GUIUtility.hotControl = nPreviewControlID; //Lock on your custom control to prevent interaction with other controls
Event.current.Use();
}
else if (Event.current.type == EventType.MouseDrag && m_bPreviewingLOD == true)
{
m_fPreviewLODPos = Mathf.Clamp01((float)(Event.current.mousePosition.x - nMargin) / (float)rectPreview.width);
PreviewLOD(m_fPreviewLODPos, afLODPositions);
GUI.FocusControl("");
Event.current.Use();
}
else if (Event.current.rawType == EventType.MouseUp && m_bPreviewingLOD == true && Event.current.button == 0 && GUIUtility.hotControl == nPreviewControlID)
{
m_bPreviewingLOD = false;
foreach (Object targetObject in targets)
{
AutomaticLOD automaticLODSelection = targetObject as AutomaticLOD;
if (!EditorApplication.isPlaying)
{
automaticLODSelection.SwitchToLOD(m_nCurrentLODSelected == -1 ? 0 : m_nCurrentLODSelected, true);
}
}
LoadSceneViewCameraInfos();
GUIUtility.hotControl = 0; // Release lock
Event.current.Use();
HandleUtility.Repaint();
}
}
// LOD bar
y += nPreviewBarHeight + nPreviewVerticalMargin;
Rect rectBar = new Rect(nMargin - 1, y - 1, nWidth + 2, nHeight + 2);
GUI.DrawTexture(rectBar, s_texBlack);
for (int nLOD = 0; nLOD < nLODLevels; nLOD++)
{
int nLODWidth = nLOD == 0 ? Mathf.RoundToInt(afLODPositions[nLOD] * nWidth) : (Mathf.RoundToInt(afLODPositions[nLOD] * nWidth) - Mathf.RoundToInt(afLODPositions[nLOD - 1] * nWidth));
int nLODStart = nLOD == 0 ? nMargin : nMargin + Mathf.RoundToInt((afLODPositions[nLOD - 1] * nWidth));
SerializedProperty elementProperty = PropertyListLODLevels.GetArrayElementAtIndex(nLOD);
SerializedProperty elementMeshVerticesAmount = elementProperty.FindPropertyRelative("m_fMeshVerticesAmount");
Rect rect = new Rect(nLODStart, y, nLODWidth, nHeight);
int nResizeMargin = 3;
Rect resizeRect = new Rect(rect.x + rect.width - nResizeMargin, rect.y, nResizeMargin * 2, rect.height);
if (automaticLOD.LODSwitchMode != AutomaticLOD.SwitchMode.UnityLODGroup)
{
if (nLOD != afLODPositions.Length - 1 && m_bPreviewingLOD == false)
{
EditorGUIUtility.AddCursorRect(resizeRect, MouseCursor.ResizeHorizontal);
}
}
int nResizeControlID = GUIUtility.GetControlID(FocusType.Passive, resizeRect);
if (Event.current.type == EventType.MouseDown)
{
if (Event.current.button == 0)
{
if (resizeRect.Contains(Event.current.mousePosition) && nLOD != afLODPositions.Length - 1)
{
m_nCurrentLODResizing = nLOD;
GUIUtility.hotControl = nResizeControlID; //Lock on your custom control to prevent interaction with other controls
Event.current.Use();
}
else if (rect.Contains(Event.current.mousePosition))
{
m_nCurrentLODSelected = nLOD;
if (!EditorApplication.isPlaying)
{
foreach (Object targetObject in targets)
{
AutomaticLOD automaticLODSelection = targetObject as AutomaticLOD;
automaticLODSelection.SwitchToLOD(nLOD, true);
}
}
Event.current.Use();
if (automaticLOD.LODSwitchMode == AutomaticLOD.SwitchMode.UnityLODGroup)
{
// We force a specific LOD here using LODGroup. To exit this state we will monitor the scene cameras and if there is a change then restore control to LODGroup
SaveSceneViewCameraInfo();
}
}
}
}
else if (Event.current.type == EventType.MouseDrag && m_nCurrentLODResizing == nLOD && nLOD != afLODPositions.Length - 1 && automaticLOD.LODSwitchMode != AutomaticLOD.SwitchMode.UnityLODGroup)
{
if (automaticLOD.m_evalMode == AutomaticLOD.EvalMode.ScreenCoverage)
{
float fPosition = (float)(Event.current.mousePosition.x - nMargin) / (float)nWidth;
float fScreenCoverage = SliderToScreenCoverage(fPosition);
foreach (Object targetObject in targets)
{
AutomaticLOD automaticLODSelection = targetObject as AutomaticLOD;
Undo.RecordObject(automaticLODSelection, "Change Screen Coverage");
EditorUtility.SetDirty(automaticLODSelection);
automaticLODSelection.m_listLODLevels[nLOD + 1].m_fScreenCoverage = fScreenCoverage;
// Clip left
if (automaticLODSelection.m_listLODLevels[nLOD + 1].m_fScreenCoverage > automaticLODSelection.m_listLODLevels[nLOD].m_fScreenCoverage)
{
automaticLODSelection.m_listLODLevels[nLOD + 1].m_fScreenCoverage = automaticLODSelection.m_listLODLevels[nLOD].m_fScreenCoverage;
}
// Clip right
if (nLOD < nLODLevels - 2)
{
if (automaticLODSelection.m_listLODLevels[nLOD + 1].m_fScreenCoverage < automaticLODSelection.m_listLODLevels[nLOD + 2].m_fScreenCoverage)
{
automaticLODSelection.m_listLODLevels[nLOD + 1].m_fScreenCoverage = automaticLODSelection.m_listLODLevels[nLOD + 2].m_fScreenCoverage;
}
}
}
}
else if (automaticLOD.m_evalMode == AutomaticLOD.EvalMode.CameraDistance)
{
foreach (Object targetObject in targets)
{
AutomaticLOD automaticLODSelection = targetObject as AutomaticLOD;
Undo.RecordObject(automaticLODSelection, "Change Camera Distance");
EditorUtility.SetDirty(automaticLODSelection);
automaticLODSelection.m_listLODLevels[nLOD + 1].m_fMaxCameraDistance = Mathf.Clamp01(((float)(Event.current.mousePosition.x - nMargin) / (float)nWidth)) * automaticLODSelection.m_fMaxCameraDistance;
// Clip left
if (automaticLODSelection.m_listLODLevels[nLOD + 1].m_fMaxCameraDistance < automaticLODSelection.m_listLODLevels[nLOD].m_fMaxCameraDistance)
{
automaticLODSelection.m_listLODLevels[nLOD + 1].m_fMaxCameraDistance = automaticLODSelection.m_listLODLevels[nLOD].m_fMaxCameraDistance;
}
// Clip right
if (nLOD < nLODLevels - 2)
{
if (automaticLODSelection.m_listLODLevels[nLOD + 1].m_fMaxCameraDistance > automaticLODSelection.m_listLODLevels[nLOD + 2].m_fMaxCameraDistance)
{
automaticLODSelection.m_listLODLevels[nLOD + 1].m_fMaxCameraDistance = automaticLODSelection.m_listLODLevels[nLOD + 2].m_fMaxCameraDistance;
}
}
}
}
Event.current.Use();
}
else if (Event.current.type == EventType.MouseUp && automaticLOD.LODSwitchMode != AutomaticLOD.SwitchMode.UnityLODGroup)
{
if (Event.current.button == 0)
{
if (GUIUtility.hotControl == nResizeControlID)
{
GUIUtility.hotControl = 0; // Release lock
m_nCurrentLODResizing = -1;
Event.current.Use();
}
}
else if (Event.current.button == 1)
{
if (rect.Contains(Event.current.mousePosition))
{
m_nCurrentLODSelected = nLOD;
Event.current.Use();
GenericMenu contextMenu = new GenericMenu ();
contextMenu.AddItem(new GUIContent(MENUADDBEFORE), false, LODContextMenuCallback, MENUADDBEFORE);
contextMenu.AddItem(new GUIContent(MENUADDAFTER), false, LODContextMenuCallback, MENUADDAFTER);
if (automaticLOD.m_listLODLevels.Count > 1)
{
contextMenu.AddSeparator("");
contextMenu.AddItem(new GUIContent(MENUDELETE), false, LODContextMenuCallback, MENUDELETE);
}
contextMenu.ShowAsContext();
}
}
}
int nBorderMargin = 1;
Rect rectLODBorder = new Rect(rect.x - nBorderMargin, rect.y - nBorderMargin, rect.width + (nBorderMargin * 2), rect.height + (nBorderMargin * 2));
if (m_nCurrentLODSelected == nLOD)
{
int nSelectionMargin = 2;
bRectLODSelected = true;
rectLODSelected = rect;
rectLODSelectedBorder = new Rect(rect.x - nSelectionMargin, rect.y - nSelectionMargin, rect.width + (nSelectionMargin * 2), rect.height + (nSelectionMargin * 2));
GUILayout.Label("Selected LOD: " + nLOD, EditorStyles.boldLabel);
string strLabel = "";
if (automaticLOD.LODSwitchMode != AutomaticLOD.SwitchMode.UnityLODGroup)
{
if (automaticLOD.m_evalMode == AutomaticLOD.EvalMode.ScreenCoverage)
{
string strRange = string.Format("{0:0.0}% to {1:0.0}%", automaticLOD.m_listLODLevels[nLOD].m_fScreenCoverage * 100.0f, m_nCurrentLODSelected < (nLODLevels - 1) ? (automaticLOD.m_listLODLevels[nLOD + 1].m_fScreenCoverage * 100.0f) : 0.0f);
strLabel = "Rendered when the object covers " + strRange + " screen space";
}
else if (automaticLOD.m_evalMode == AutomaticLOD.EvalMode.CameraDistance)
{
string strRange = string.Format("{0:0.0} to {1:0.0}", automaticLOD.m_listLODLevels[nLOD].m_fMaxCameraDistance, m_nCurrentLODSelected < (nLODLevels - 1) ? (automaticLOD.m_listLODLevels[nLOD + 1].m_fMaxCameraDistance) : Mathf.Infinity);
strLabel = "Rendered when the camera is at a distance from " + strRange;
}
GUILayout.Label(strLabel);
}
float fVertexAmount = EditorGUILayout.Slider(new GUIContent("Total vertices %", "The percentage of vertices from the original mesh to keep when simplifying it"), elementMeshVerticesAmount.floatValue * 100.0f, 0.0f, 100.0f);
elementMeshVerticesAmount.floatValue = fVertexAmount / 100.0f;
int nLODMeshVertexCount = automaticLOD.GetLODVertexCount(nLOD, true);
int nMeshVertexCount = automaticLOD.GetOriginalVertexCount(true);
int nLODMeshTriangleCount = automaticLOD.GetLODTriangleCount(nLOD, true);
int nMeshTriangleCount = automaticLOD.GetOriginalTriangleCount(true);
float fPercentTriangles = ((float)nLODMeshTriangleCount / (float)nMeshTriangleCount) * 100.0f;
EditorGUILayout.LabelField("Vertex count: " + nLODMeshVertexCount + "/" + nMeshVertexCount);
EditorGUILayout.LabelField("Triangle count: " + nLODMeshTriangleCount + "/" + nMeshTriangleCount + string.Format(" ({0:0.00}%)", fPercentTriangles));
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
string strButton = automaticLOD.HasVertexData(nLOD, true) ? ("Recompute Level " + nLOD) : ("Compute level " + nLOD);
if (GUILayout.Button(new GUIContent(strButton, "Generates the mesh(es) corresponding to this LOD"), GUILayout.Width(nButtonWidth)))
{
Simplifier.Cancelled = false;
ChangeLODVertexAmount(automaticLOD, nLOD, automaticLOD.m_listLODLevels[nLOD].m_fMeshVerticesAmount, true);
if (Simplifier.Cancelled == false)
{
automaticLOD.SwitchToLOD(nLOD, true);
}
EditorUtility.ClearProgressBar();
if (Simplifier.Cancelled == false)
{
SaveMeshAssets();
}
Simplifier.Cancelled = false;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
if (automaticLOD.LODSwitchMode == AutomaticLOD.SwitchMode.UnityLODGroup)
{
GUI.DrawTexture(rectLODBorder, s_texBlack, ScaleMode.StretchToFill);
}
DrawLODBar(automaticLOD, rect, GetColorTexture(automaticLOD, automaticLOD.m_listLODLevels[nLOD].m_nColorEditorBarIndex), nLOD);
}
if(bRectLODSelected)
{
GUI.DrawTexture(rectLODSelectedBorder, s_texBlack, ScaleMode.StretchToFill);
DrawLODBar(automaticLOD, rectLODSelected, GetColorTexture(automaticLOD, automaticLOD.m_listLODLevels[m_nCurrentLODSelected].m_nColorEditorBarIndex), m_nCurrentLODSelected);
Rect rectSelectionArrow = new Rect(rectLODSelected.x + (rectLODSelected.width / 2) - (s_texArrow.width / 2), rectLODSelected.y + rectLODSelected.height - s_texArrow.height, s_texArrow.width, s_texArrow.height);
GUI.DrawTexture(rectSelectionArrow, s_texArrow, ScaleMode.StretchToFill);
}
if (automaticLOD.LODSwitchMode != AutomaticLOD.SwitchMode.UnityLODGroup)
{
// Preview bar
if (s_texHandle == null)
{
int nCursorSize = 10;
GUI.DrawTexture(new Rect(nMargin + Mathf.RoundToInt(nWidth * m_fPreviewLODPos) - (nCursorSize / 2), nYPreview, nCursorSize, nCursorSize), s_texBlack);
GUI.DrawTexture(new Rect(nMargin + Mathf.RoundToInt(nWidth * m_fPreviewLODPos), nYPreview, 1, nPreviewBarHeight + nPreviewVerticalMargin + nHeight), s_texBlack);
}
else
{
GUI.DrawTexture(new Rect(nMargin + Mathf.RoundToInt(nWidth * m_fPreviewLODPos) - (s_texHandle.width / 2), (nYPreview + nPreviewBarHeight + nPreviewVerticalMargin + nHeight) - s_texHandle.height, s_texHandle.width, s_texHandle.height), s_texHandle);
//GUI.Label (new Rect(nMargin + Mathf.RoundToInt(nWidth * m_fPreviewLODPos) + (s_texHandle.width / 2) + 5, nYPreview, 50, 20), new GUIContent("Preview"));
}
// Preview camera
if (m_bPreviewingLOD)
{
PreviewSceneViewCameras(automaticLOD, m_fPreviewLODPos);
for (int i = 0; i < SceneView.sceneViews.Count; i++)
{
SceneView sceneView = SceneView.sceneViews[i] as SceneView;
if (sceneView.orthographic == false)
{
sceneView.Repaint();
}
}
}
}
return rectBar;
}
void CreateDefaultLODS(int nLevels, AutomaticLOD root, bool bRecurseIntoChildren)
{
List<AutomaticLOD.LODLevelData> listLODLevels = new List<AutomaticLOD.LODLevelData>();
for (int i = 0; i < nLevels; i++)
{
AutomaticLOD.LODLevelData data = new AutomaticLOD.LODLevelData();
float oneminust = (float)(nLevels - i) / (float)nLevels;
data.m_fScreenCoverage = SliderToScreenCoverage(1.0f - oneminust);
data.m_fMaxCameraDistance = i == 0 ? 0.0f : i * 100.0f;
data.m_fMeshVerticesAmount = oneminust;
data.m_mesh = null;
data.m_bUsesOriginalMesh = false;
data.m_nColorEditorBarIndex = i;
listLODLevels.Add(data);
}
root.SetLODLevels(listLODLevels, AutomaticLOD.EvalMode.ScreenCoverage, 1000.0f, bRecurseIntoChildren);
}
void PreviewLOD(float fValue, float[] afLODPositions)
{
if(EditorApplication.isPlaying)
{
return;
}
foreach (Object targetObject in targets)
{
AutomaticLOD automaticLOD = targetObject as AutomaticLOD;
if (automaticLOD != null && automaticLOD.m_LODObjectRoot == null)
{
for (int nLOD = 0; nLOD < automaticLOD.m_listLODLevels.Count; nLOD++)
{
if (fValue < afLODPositions[nLOD])
{
automaticLOD.SwitchToLOD(nLOD, true);
break;
}
}
}
}
}
void ChangeLODVertexAmount(AutomaticLOD automaticLOD, int nLOD, float fMeshVertexAmount, bool bRecurseIntoChildren)
{
if (automaticLOD)
{
if (nLOD >= 0 && nLOD < automaticLOD.m_listLODLevels.Count)
{
automaticLOD.m_listLODLevels[nLOD].m_fMeshVerticesAmount = fMeshVertexAmount;
if(automaticLOD.HasLODDataDirty())
{
automaticLOD.RestoreOriginalMesh(false, true);
automaticLOD.ComputeLODData(true, Progress);
if(Simplifier.Cancelled)
{
return;
}
}
automaticLOD.ComputeLODMesh(nLOD, bRecurseIntoChildren, Progress);
if (Simplifier.Cancelled)
{
return;
}
automaticLOD.SwitchToLOD(nLOD, bRecurseIntoChildren);
}
}
}
void DeselectLODLevel()
{
RestoreOriginalMeshes();
m_nCurrentLODSelected = -1;
}
Texture2D GetColorTexture(AutomaticLOD automaticLOD, int nIndex)
{
if(automaticLOD.LODSwitchMode == AutomaticLOD.SwitchMode.UnityLODGroup)
{
if(m_nCurrentLODSelected == nIndex)
{
return s_texLODGroupSelected;
}
else
{
return s_texLODGroupNotSelected;
}
}
return s_aColorTextures[nIndex % s_aColorTextures.Length];
}
void LODContextMenuCallback(object parameter)
{
string strMenuItem = parameter.ToString();
Simplifier.Cancelled = false;
foreach (Object targetObject in targets)
{
AutomaticLOD automaticLOD = targetObject as AutomaticLOD;
if (automaticLOD != null)
{
if (strMenuItem == MENUADDBEFORE)
{
Undo.RecordObject(automaticLOD, "Add LOD level before");
if (automaticLOD.m_listDependentChildren != null)
{
foreach (AutomaticLOD childLOD in automaticLOD.m_listDependentChildren)
{
Undo.RecordObject(childLOD, "Add LOD level before");
}
}
automaticLOD.AddLODLevel(m_nCurrentLODSelected, true, true, true);
m_nCurrentLODSelected++;
}
else if(strMenuItem == MENUADDAFTER)
{
Undo.RecordObject(automaticLOD, "Add LOD level after");
if (automaticLOD.m_listDependentChildren != null)
{
foreach (AutomaticLOD childLOD in automaticLOD.m_listDependentChildren)
{
Undo.RecordObject(childLOD, "Add LOD level after");
}
}
automaticLOD.AddLODLevel(m_nCurrentLODSelected, false, true, true);
}
else if(strMenuItem == MENUDELETE)
{
Undo.RecordObject(automaticLOD, "Delete LOD level");
if (automaticLOD.m_listLODLevels != null && m_nCurrentLODSelected < automaticLOD.m_listLODLevels.Count)
{
if (automaticLOD.m_listLODLevels[m_nCurrentLODSelected].m_mesh != null)
{
Undo.RecordObject(automaticLOD.m_listLODLevels[m_nCurrentLODSelected].m_mesh, "Delete LOD level");
}
}
if (automaticLOD.m_listDependentChildren != null)
{
foreach (AutomaticLOD childLOD in automaticLOD.m_listDependentChildren)
{
Undo.RecordObject(childLOD, "Delete LOD level");
if (childLOD.m_listLODLevels != null && m_nCurrentLODSelected < childLOD.m_listLODLevels.Count)
{
if (childLOD.m_listLODLevels[m_nCurrentLODSelected].m_mesh != null)
{
Undo.RecordObject(childLOD.m_listLODLevels[m_nCurrentLODSelected].m_mesh, "Delete LOD level");
}
}
}
}
automaticLOD.RemoveLODLevel(m_nCurrentLODSelected, true, true);
if (m_nCurrentLODSelected > 0)
{
m_nCurrentLODSelected--;
}
}
EditorUtility.ClearProgressBar();
EditorUtility.SetDirty(automaticLOD);
}
automaticLOD.SwitchToLOD(m_nCurrentLODSelected, true);
if (Simplifier.Cancelled == false)
{
SaveMeshAssets();
}
}
Simplifier.Cancelled = false;
}
void DrawLODBar(AutomaticLOD automaticLOD, Rect rect, Texture2D tex2D, int nLevel)
{
GUI.DrawTexture(rect, GetColorTexture(automaticLOD, automaticLOD.m_listLODLevels[nLevel].m_nColorEditorBarIndex), ScaleMode.StretchToFill);
string strValue = "";
if (automaticLOD.m_evalMode == AutomaticLOD.EvalMode.ScreenCoverage)
{
float fCoverageStart = automaticLOD.m_listLODLevels[nLevel].m_fScreenCoverage * 100.0f;
float fCoverageEnd = nLevel == automaticLOD.m_listLODLevels.Count - 1 ? 0.0f : (automaticLOD.m_listLODLevels[nLevel + 1].m_fScreenCoverage * 100.0f);
strValue = Mathf.RoundToInt(fCoverageStart).ToString() + "%" + "-" + Mathf.RoundToInt(fCoverageEnd).ToString() + "% screen";
}
else if(automaticLOD.m_evalMode == AutomaticLOD.EvalMode.CameraDistance)
{
string strDistanceStart = automaticLOD.m_listLODLevels[nLevel].m_fMaxCameraDistance.ToString("F1");
string strDistanceEnd = nLevel == automaticLOD.m_listLODLevels.Count - 1 ? "Infinity" : automaticLOD.m_listLODLevels[nLevel + 1].m_fMaxCameraDistance.ToString("F1");
strValue = strDistanceStart + "-" + strDistanceEnd + " distance";
}
GUI.Label(rect, "LOD " + nLevel, EditorStyles.whiteMiniLabel);
if (automaticLOD.LODSwitchMode != AutomaticLOD.SwitchMode.UnityLODGroup)
{
Rect rectValue = new Rect(rect);
rectValue.y += 14;
GUI.Label(rectValue, strValue, EditorStyles.whiteMiniLabel);
}
}
void LoadStaticData()
{
int nColors = 9;
s_aColorTextures = new Texture2D[nColors];
s_aColorTextures[0] = CreateTexture(new Color32(170, 57, 57, 255));
s_aColorTextures[1] = CreateTexture(new Color32(170, 108, 57, 255));
s_aColorTextures[2] = CreateTexture(new Color32( 34, 102, 102, 255));
s_aColorTextures[3] = CreateTexture(new Color32( 45, 136, 45, 255));
s_aColorTextures[4] = CreateTexture(new Color32(102, 34, 98, 255));
s_aColorTextures[5] = CreateTexture(new Color32(169, 170, 57, 255));
s_aColorTextures[6] = CreateTexture(new Color32(170, 57, 166, 255));
s_aColorTextures[7] = CreateTexture(new Color32(45, 136, 200, 255));
s_aColorTextures[8] = CreateTexture(new Color32(45, 136, 98, 255));
s_texLODGroupSelected = CreateTexture(new Color32(160, 160, 160, 255));
s_texLODGroupNotSelected = CreateTexture(new Color32(120, 120, 120, 255));
s_texBlack = CreateTexture(Color.black);
s_texDarkGray = CreateTexture(new Color32(80, 80, 80, 255));
s_texArrow = LoadTexture(s_aColorsIconArrow, s_nIconArrowWidth, s_nIconArrowHeight);
s_texHandle = LoadTexture(s_aColorsIconHandle, s_nIconHandleWidth, s_nIconHandleHeight);
s_bStaticDataLoaded = true;
}
Texture2D LoadTexture(Color32[] aColors, int nWidth, int nHeight)
{
Texture2D newTexture = new Texture2D(nWidth, nHeight);
newTexture.SetPixels32(aColors);
newTexture.Apply();
return newTexture;
}
Texture2D CreateTexture(Color color)
{
int nWidth = 2;
int nHeight = 2;
Color[] aPixels = new Color[nWidth * nHeight];
for (int nPixel = 0; nPixel < aPixels.Length; ++nPixel)
{
aPixels[nPixel] = color;
}
Texture2D texResult = new Texture2D(nWidth, nHeight);
texResult.SetPixels(aPixels);
texResult.wrapMode = TextureWrapMode.Repeat;
texResult.Apply();
return texResult;
}
void RestoreOriginalMeshes()
{
foreach (Object targetObject in targets)
{
if (targetObject != null)
{
AutomaticLOD automaticLOD = targetObject as AutomaticLOD;
automaticLOD.RestoreOriginalMesh(false, true);
}
}
}
void DeleteLODData()
{
foreach (Object targetObject in targets)
{
AutomaticLOD automaticLOD = targetObject as AutomaticLOD;
if (automaticLOD.m_LODObjectRoot == null)
{
DeleteLODDataRecursive(automaticLOD.gameObject, automaticLOD.gameObject, true);
}
}
}
void DeleteLODDataRecursive(GameObject root, GameObject gameObject, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD)
{
if (automaticLOD.m_LODObjectRoot == null || automaticLOD.m_LODObjectRoot.gameObject == root)
{
automaticLOD.RestoreOriginalMesh(true, false);
if (automaticLOD.m_LODObjectRoot != null)
{
if (Application.isEditor && Application.isPlaying == false)
{
UnityEngine.Object.DestroyImmediate(automaticLOD);
}
else
{
UnityEngine.Object.Destroy(automaticLOD);
}
}
}
automaticLOD.m_aRelevanceSpheres = null;
automaticLOD.m_bEnablePrefabUsage = false;
automaticLOD.m_strAssetPath = "";
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
DeleteLODDataRecursive(root, gameObject.transform.GetChild(nChild).gameObject, true);
}
}
}
void SaveSceneViewCameraInfo()
{
AutomaticLOD automaticLOD = target as AutomaticLOD;
m_dicSceneViewCameraInfos = new Dictionary<SceneView, SceneCameraInfo>();
for (int i = 0; i < SceneView.sceneViews.Count; i++)
{
SceneView sceneView = SceneView.sceneViews[i] as SceneView;
SceneCameraInfo info = new SceneCameraInfo();
if (sceneView.camera != null)
{
info.m_v3Position = sceneView.camera.transform.position;
info.m_fDistance = Vector3.Distance(sceneView.camera.transform.position, sceneView.pivot);
info.m_fViewArea = automaticLOD.ComputeViewSpaceBounds(sceneView.camera.transform.position, (automaticLOD.transform.position - info.m_v3Position).normalized, Vector3.up, out info.m_v3ViewSpaceMin, out info.m_v3ViewSpaceMax, out info.m_v3ViewSpaceCenter);
}
info.m_v3ObjectWorldCenter = automaticLOD.ComputeWorldCenter();
info.m_v3Pivot = sceneView.pivot;
info.m_qRotation = sceneView.rotation;
info.m_fSize = sceneView.size;
m_dicSceneViewCameraInfos.Add(sceneView, info);
}
}
void PreviewSceneViewCameras(AutomaticLOD automaticLOD, float fPosition)
{
for (int i = 0; i < SceneView.sceneViews.Count; i++)
{
SceneView sceneView = SceneView.sceneViews[i] as SceneView;
if (m_dicSceneViewCameraInfos.ContainsKey(sceneView))
{
SceneCameraInfo info = m_dicSceneViewCameraInfos[sceneView];
Vector3 v3AutomaticLODLookAt = info.m_v3ObjectWorldCenter;
Vector3 v3Dir = (info.m_v3Position - v3AutomaticLODLookAt).normalized;
float fDistance = 0.0f;
if (automaticLOD.m_evalMode == AutomaticLOD.EvalMode.CameraDistance)
{
fDistance = Mathf.Max(0.05f, automaticLOD.m_fMaxCameraDistance * fPosition);
}
else if (automaticLOD.m_evalMode == AutomaticLOD.EvalMode.ScreenCoverage)
{
float fTargetArea = Mathf.Max(SliderToScreenCoverage(fPosition), 0.00001f);
float fTryDistance = Mathf.Max(info.m_v3ViewSpaceMax.x - info.m_v3ViewSpaceMin.x, info.m_v3ViewSpaceMax.y - info.m_v3ViewSpaceMin.y, info.m_v3ViewSpaceMax.z - info.m_v3ViewSpaceMin.z) * 2.0f;
if (Mathf.Approximately(fTryDistance, 0.0f) || fTryDistance < 0.0f)
{
fDistance = Vector3.Distance(sceneView.camera.transform.position, v3AutomaticLODLookAt);
}
else
{
sceneView.camera.transform.position = v3AutomaticLODLookAt + (v3Dir * fTryDistance);
sceneView.camera.transform.rotation = Quaternion.LookRotation(-v3Dir, Vector3.up);
float fIncDistance = fTryDistance * 0.2f;
float fTryCoverage = automaticLOD.ComputeScreenCoverage(sceneView.camera);
bool bTryForward = false;
int nTrySteps = 50;
for (int nTry = 0; nTry < nTrySteps; nTry++)
{
if (fTryCoverage < fTargetArea)
{
// Move forward
if(nTry > 0)
{
if (bTryForward == false)
{
// Overshoot
fIncDistance *= -0.5f;
}
bTryForward = true;
}
else
{
// Start forward
bTryForward = true;
if(fIncDistance > 0.0f)
{
fIncDistance *= -0.5f;
}
}
}
else
{
// Move backwards
if (nTry > 0)
{
// Overshoot
if (bTryForward == true)
{
fIncDistance *= -0.5f;
}
bTryForward = false;
}
else
{
// Start backwards
bTryForward = false;
if (fIncDistance < 0.0f)
{
fIncDistance *= -0.5f;
}
}
}
fTryDistance += fIncDistance;
sceneView.camera.transform.position = v3AutomaticLODLookAt + (v3Dir * fTryDistance);
sceneView.camera.transform.rotation = Quaternion.LookRotation(-v3Dir, Vector3.up);
fTryCoverage = automaticLOD.ComputeScreenCoverage(sceneView.camera);
if (nTry == nTrySteps - 1)
{
//Debug.Log("Coverage try " + nTry + ": " + fTryCoverage + " Target: " + fTargetArea + " Error: " + (Mathf.Abs(fTryCoverage - fTargetArea)) + " IncDistance " + fIncDistance + " TryDistance " + fTryDistance);
}
}
fDistance = fTryDistance;
}
}
sceneView.LookAtDirect(v3AutomaticLODLookAt + (v3Dir * (fDistance - info.m_fDistance)), Quaternion.LookRotation(-v3Dir, Vector3.up));
}
}
}
void LoadSceneViewCameraInfos()
{
if(m_dicSceneViewCameraInfos == null)
{
return;
}
for (int i = 0; i < SceneView.sceneViews.Count; i++)
{
SceneView sceneView = SceneView.sceneViews[i] as SceneView;
if (m_dicSceneViewCameraInfos.ContainsKey(sceneView) && sceneView.orthographic == false)
{
if(sceneView.camera != null)
{
sceneView.camera.transform.position = m_dicSceneViewCameraInfos[sceneView].m_v3Position;
}
sceneView.pivot = m_dicSceneViewCameraInfos[sceneView].m_v3Pivot;
sceneView.rotation = m_dicSceneViewCameraInfos[sceneView].m_qRotation;
sceneView.size = m_dicSceneViewCameraInfos[sceneView].m_fSize;
}
}
}
void SaveMeshAssets()
{
try
{
foreach (Object targetObject in targets)
{
AutomaticLOD automaticLOD = targetObject as AutomaticLOD;
GameObject gameObject = automaticLOD.gameObject;
if (automaticLOD.m_LODObjectRoot == null && automaticLOD.m_bEnablePrefabUsage)
{
string strMeshAssetPath = automaticLOD.m_strAssetPath;
if (string.IsNullOrEmpty(strMeshAssetPath))
{
strMeshAssetPath = UnityEditor.EditorUtility.SaveFilePanelInProject("Save mesh asset(s)", "mesh_" + gameObject.name + gameObject.GetInstanceID().ToString() + ".asset", "asset", "Please enter a file name to save the mesh asset(s) to");
if (strMeshAssetPath.Length == 0)
{
return;
}
automaticLOD.m_strAssetPath = strMeshAssetPath;
}
int nCounter = 0;
SaveMeshAssetsRecursive(gameObject, gameObject, strMeshAssetPath, true, System.IO.File.Exists(strMeshAssetPath), ref nCounter);
}
}
}
catch (System.Exception e)
{
Debug.LogError("Error saving LOD assets to disk: " + e.Message + " Stack: " + e.StackTrace);
EditorUtility.ClearProgressBar();
Simplifier.Cancelled = false;
}
EditorUtility.ClearProgressBar();
UnityEditor.AssetDatabase.Refresh();
Simplifier.Cancelled = false;
}
bool SaveMeshAssetsRecursive(GameObject root, GameObject gameObject, string strFile, bool bRecurseIntoChildren, bool bAssetAlreadyCreated, ref int nProgressElementsCounter)
{
if (gameObject == null || Simplifier.Cancelled)
{
return bAssetAlreadyCreated;
}
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD != null && automaticLOD.HasLODData() && (automaticLOD.m_LODObjectRoot == null || automaticLOD.m_LODObjectRoot.gameObject == root))
{
int nTotalProgressElements = automaticLOD.m_LODObjectRoot != null ? (automaticLOD.m_LODObjectRoot.m_listDependentChildren.Count + 1) : 1;
nTotalProgressElements *= automaticLOD.m_listLODLevels != null ? automaticLOD.m_listLODLevels.Count : 0;
for (int nLOD = 0; nLOD < automaticLOD.m_listLODLevels.Count; nLOD++)
{
if (automaticLOD.m_listLODLevels[nLOD].m_mesh != null && AutomaticLOD.HasValidMeshData(automaticLOD.gameObject))
{
float fT = (float)nProgressElementsCounter / (float)nTotalProgressElements;
Progress("Saving meshes to asset file", automaticLOD.name + " LOD " + nLOD, fT);
if (Simplifier.Cancelled)
{
return bAssetAlreadyCreated;
}
if (bAssetAlreadyCreated == false && UnityEditor.AssetDatabase.Contains(automaticLOD.m_listLODLevels[nLOD].m_mesh) == false)
{
UnityEditor.AssetDatabase.CreateAsset(automaticLOD.m_listLODLevels[nLOD].m_mesh, strFile);
bAssetAlreadyCreated = true;
}
else
{
if (UnityEditor.AssetDatabase.Contains(automaticLOD.m_listLODLevels[nLOD].m_mesh) == false)
{
UnityEditor.AssetDatabase.AddObjectToAsset(automaticLOD.m_listLODLevels[nLOD].m_mesh, strFile);
UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(automaticLOD.m_listLODLevels[nLOD].m_mesh));
}
}
nProgressElementsCounter++;
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
bAssetAlreadyCreated = SaveMeshAssetsRecursive(root, gameObject.transform.GetChild(nChild).gameObject, strFile, bRecurseIntoChildren, bAssetAlreadyCreated, ref nProgressElementsCounter);
}
}
return bAssetAlreadyCreated;
}
void SetHideFlags()
{
foreach (Object targetObject in targets)
{
AutomaticLOD automaticLOD = targetObject as AutomaticLOD;
if (automaticLOD.m_LODObjectRoot == null)
{
SetHideFlagsRecursive(automaticLOD.gameObject, automaticLOD.gameObject, true);
}
}
}
void SetHideFlagsRecursive(GameObject root, GameObject gameObject, bool bRecurseIntoChildren)
{
AutomaticLOD automaticLOD = gameObject.GetComponent<AutomaticLOD>();
if (automaticLOD && automaticLOD.GetMeshSimplifier())
{
automaticLOD.GetMeshSimplifier().hideFlags = HideFlags.HideInInspector;
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
SetHideFlagsRecursive(root, gameObject.transform.GetChild(nChild).gameObject, true);
}
}
}
float SliderToScreenCoverage(float fSlider)
{
return Mathf.Pow(1.0f - fSlider, COVERAGEPOWER);
}
float ScreenCoverageToSlider(float fCoverage)
{
return 1.0f - Mathf.Pow(fCoverage, 1.0f / COVERAGEPOWER);
}
class SceneCameraInfo
{
public Vector3 m_v3ObjectWorldCenter;
public Vector3 m_v3Position;
public Vector3 m_v3Pivot;
public Quaternion m_qRotation;
public float m_fSize;
public float m_fDistance;
public Vector3 m_v3ViewSpaceMin;
public Vector3 m_v3ViewSpaceMax;
public Vector3 m_v3ViewSpaceCenter;
public float m_fViewArea;
}
const string MENUADDBEFORE = "Add new/Before";
const string MENUADDAFTER = "Add new/After";
const string MENUDELETE = "Delete";
const float COVERAGEPOWER = 6.0f;
Dictionary<SceneView, SceneCameraInfo> m_dicSceneViewCameraInfos = null;
int m_nCurrentLODResizing;
int m_nCurrentLODSelected;
bool m_bPreviewingLOD;
float m_fPreviewLODPos;
bool m_bGenerateLODData = false;
bool m_bRecomputeAllMeshes = false;
bool m_bEnablePrefabUsage = false;
bool m_bDisablePrefabUsage = false;
bool m_bDeleteLODData = false;
bool m_bRemoveFromLODTree = false;
bool m_bSetupNewRelevanceSphere = false;
SerializedProperty PropertyGenerateIncludeChildren;
SerializedProperty PropertyLevelsToGenerate;
SerializedProperty PropertySwitchMode;
SerializedProperty PropertyEvalMode;
SerializedProperty PropertyEnablePrefabUsage;
SerializedProperty PropertyMaxCameraDistanceDistance;
SerializedProperty PropertyListLODLevels;
SerializedProperty PropertyExpandRelevanceSpheres;
SerializedProperty PropertyRelevanceSpheres;
SerializedProperty PropertyOverrideRootSettings;
SerializedProperty PropertyLODDataDirty;
static bool s_bStaticDataLoaded = false;
static Texture2D[] s_aColorTextures = null;
static Texture2D s_texBlack = null;
static Texture2D s_texDarkGray = null;
static Texture2D s_texLODGroupSelected = null;
static Texture2D s_texLODGroupNotSelected = null;
static Texture2D s_texHandle = null;
static Texture2D s_texArrow = null;
static int s_nIconArrowWidth = 20;
static int s_nIconArrowHeight = 10;
static int s_nIconHandleWidth = 13;
static int s_nIconHandleHeight = 47;
static Color32[] s_aColorsIconArrow = new Color32[] { new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 128), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 64), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0), new Color32(255, 255, 255, 0) };
static Color32[] s_aColorsIconHandle = new Color32[] { new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 112), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 112), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 191), new Color32(125, 125, 123, 255), new Color32(39, 39, 39, 191), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(39, 39, 39, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 1), new Color32(36, 36, 36, 194), new Color32(114, 114, 112, 255), new Color32(36, 36, 36, 195), new Color32(0, 0, 0, 2), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 2), new Color32(0, 0, 0, 18), new Color32(28, 28, 28, 206), new Color32(80, 80, 79, 255), new Color32(27, 27, 27, 208), new Color32(0, 0, 0, 23), new Color32(0, 0, 0, 3), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 3), new Color32(0, 0, 0, 22), new Color32(0, 0, 0, 70), new Color32(36, 36, 36, 228), new Color32(132, 132, 130, 255), new Color32(49, 48, 48, 232), new Color32(0, 0, 0, 78), new Color32(0, 0, 0, 27), new Color32(0, 0, 0, 4), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 4), new Color32(0, 0, 0, 26), new Color32(0, 0, 0, 77), new Color32(48, 48, 48, 160), new Color32(136, 136, 135, 251), new Color32(138, 138, 136, 255), new Color32(142, 141, 140, 253), new Color32(64, 65, 64, 176), new Color32(0, 0, 0, 85), new Color32(0, 0, 0, 31), new Color32(0, 0, 0, 5), new Color32(0, 0, 0, 0), new Color32(0, 0, 0, 5), new Color32(0, 0, 0, 31), new Color32(0, 0, 0, 85), new Color32(62, 62, 61, 174), new Color32(142, 142, 141, 251), new Color32(136, 136, 135, 255), new Color32(133, 133, 131, 255), new Color32(135, 135, 133, 255), new Color32(144, 144, 143, 253), new Color32(78, 78, 78, 187), new Color32(0, 0, 0, 91), new Color32(0, 0, 0, 34), new Color32(0, 0, 0, 6), new Color32(0, 0, 0, 22), new Color32(0, 0, 0, 85), new Color32(79, 79, 79, 187), new Color32(148, 147, 146, 253), new Color32(139, 138, 137, 255), new Color32(136, 136, 134, 255), new Color32(135, 136, 134, 255), new Color32(136, 135, 134, 255), new Color32(137, 138, 136, 255), new Color32(146, 146, 145, 255), new Color32(91, 91, 91, 197), new Color32(0, 0, 0, 90), new Color32(0, 0, 0, 24), new Color32(0, 0, 0, 41), new Color32(103, 103, 103, 186), new Color32(150, 150, 149, 255), new Color32(141, 141, 140, 255), new Color32(139, 139, 138, 255), new Color32(139, 139, 138, 255), new Color32(139, 139, 138, 255), new Color32(139, 139, 138, 255), new Color32(139, 139, 138, 255), new Color32(140, 141, 140, 255), new Color32(150, 150, 149, 255), new Color32(113, 113, 112, 196), new Color32(0, 0, 0, 42), new Color32(0, 0, 0, 48), new Color32(160, 159, 159, 255), new Color32(145, 145, 144, 255), new Color32(142, 143, 142, 255), new Color32(142, 143, 142, 255), new Color32(143, 142, 142, 255), new Color32(143, 142, 142, 255), new Color32(143, 142, 142, 255), new Color32(142, 142, 142, 255), new Color32(143, 143, 142, 255), new Color32(145, 145, 144, 255), new Color32(159, 160, 159, 255), new Color32(0, 0, 0, 48), new Color32(0, 0, 0, 48), new Color32(159, 159, 159, 255), new Color32(146, 147, 146, 255), new Color32(146, 147, 146, 255), new Color32(147, 147, 146, 255), new Color32(146, 146, 146, 255), new Color32(147, 147, 146, 255), new Color32(147, 146, 146, 255), new Color32(147, 146, 146, 255), new Color32(146, 146, 146, 255), new Color32(146, 147, 146, 255), new Color32(159, 160, 159, 255), new Color32(0, 0, 0, 48), new Color32(0, 0, 0, 48), new Color32(162, 162, 162, 255), new Color32(150, 150, 150, 255), new Color32(150, 150, 150, 255), new Color32(150, 150, 150, 255), new Color32(150, 150, 150, 255), new Color32(150, 150, 150, 255), new Color32(150, 150, 150, 255), new Color32(150, 150, 150, 255), new Color32(150, 150, 150, 255), new Color32(150, 150, 150, 255), new Color32(162, 162, 162, 255), new Color32(0, 0, 0, 48), new Color32(0, 0, 0, 48), new Color32(165, 166, 166, 255), new Color32(153, 154, 154, 255), new Color32(153, 154, 153, 255), new Color32(153, 153, 154, 255), new Color32(154, 153, 154, 255), new Color32(153, 153, 154, 255), new Color32(154, 154, 153, 255), new Color32(153, 154, 154, 255), new Color32(154, 153, 154, 255), new Color32(154, 153, 154, 255), new Color32(166, 166, 166, 255), new Color32(0, 0, 0, 48), new Color32(0, 0, 0, 48), new Color32(169, 169, 169, 255), new Color32(157, 157, 157, 255), new Color32(157, 157, 158, 255), new Color32(157, 156, 158, 255), new Color32(157, 157, 157, 255), new Color32(157, 157, 158, 255), new Color32(157, 157, 157, 255), new Color32(157, 157, 157, 255), new Color32(156, 156, 158, 255), new Color32(157, 157, 157, 255), new Color32(169, 169, 169, 255), new Color32(0, 0, 0, 48), new Color32(0, 0, 0, 48), new Color32(171, 171, 172, 255), new Color32(160, 160, 161, 255), new Color32(160, 160, 161, 255), new Color32(160, 160, 161, 255), new Color32(160, 160, 160, 255), new Color32(160, 160, 160, 255), new Color32(160, 160, 161, 255), new Color32(160, 160, 161, 255), new Color32(160, 159, 160, 255), new Color32(159, 160, 160, 255), new Color32(171, 171, 171, 255), new Color32(0, 0, 0, 48), new Color32(0, 0, 0, 36), new Color32(173, 173, 174, 255), new Color32(162, 162, 163, 255), new Color32(162, 162, 163, 255), new Color32(162, 162, 163, 255), new Color32(162, 162, 163, 255), new Color32(163, 162, 163, 255), new Color32(162, 163, 163, 255), new Color32(162, 162, 163, 255), new Color32(163, 162, 163, 255), new Color32(163, 162, 163, 255), new Color32(173, 174, 174, 255), new Color32(0, 0, 0, 36), new Color32(0, 0, 0, 12), new Color32(183, 183, 184, 255), new Color32(175, 175, 176, 255), new Color32(175, 175, 176, 255), new Color32(175, 175, 176, 255), new Color32(175, 175, 176, 255), new Color32(175, 175, 176, 255), new Color32(175, 175, 176, 255), new Color32(175, 175, 176, 255), new Color32(175, 175, 176, 255), new Color32(175, 175, 176, 255), new Color32(183, 183, 184, 255), new Color32(0, 0, 0, 12) };
static int s_nLastProgress = -1;
static string s_strLastTitle = "";
static string s_strLastMessage = "";
}
fileFormatVersion: 2
guid: d15024d8291bfb3408ecafe326e6f235
timeCreated: 1434653206
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: b175cfc6af1e0bd45991da714cbc9044
folderAsset: yes
timeCreated: 1434653153
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
#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;
}
fileFormatVersion: 2
guid: e90785789c206fa4e951b02cff2c61c8
timeCreated: 1434653219
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 20010eb6c3f6b69439477a35ca442bb4
folderAsset: yes
timeCreated: 1438107001
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 0c8a425c8b220db479d7e4c9ad92543c
folderAsset: yes
timeCreated: 1438107042
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using UltimateGameTools.MeshSimplifier;
[CustomEditor(typeof(MeshSimplify)), CanEditMultipleObjects]
public class MeshSimplifyEditor : Editor
{
void Progress(string strTitle, string strMessage, float fT)
{
int nPercent = Mathf.RoundToInt(fT * 100.0f);
if(nPercent != s_nLastProgress || s_strLastTitle != strTitle || s_strLastMessage != strMessage)
{
s_strLastTitle = strTitle;
s_strLastMessage = strMessage;
s_nLastProgress = nPercent;
if(EditorUtility.DisplayCancelableProgressBar(strTitle, strMessage, fT))
{
Simplifier.Cancelled = true;
}
}
}
void OnEnable()
{
PropertyGenerateIncludeChildren = serializedObject.FindProperty("m_bGenerateIncludeChildren");
PropertyEnablePrefabUsage = serializedObject.FindProperty("m_bEnablePrefabUsage");
PropertyExpandRelevanceSpheres = serializedObject.FindProperty("m_bExpandRelevanceSpheres");
PropertyRelevanceSpheres = serializedObject.FindProperty("m_aRelevanceSpheres");
PropertyOverrideRootSettings = serializedObject.FindProperty("m_bOverrideRootSettings");
PropertyVertexAmount = serializedObject.FindProperty("m_fVertexAmount");
PropertyUseEdgeLength = serializedObject.FindProperty("m_bUseEdgeLength");
PropertyUseCurvature = serializedObject.FindProperty("m_bUseCurvature");
PropertyProtectTexture = serializedObject.FindProperty("m_bProtectTexture");
PropertyLockBorder = serializedObject.FindProperty("m_bLockBorder");
PropertyDataDirty = serializedObject.FindProperty("m_bDataDirty");
PropertyExcludedFromTree = serializedObject.FindProperty("m_bExcludedFromTree");
m_bComputeMesh = false;
m_bEnablePrefabUsage = false;
m_bDisablePrefabUsage = false;
m_bDeleteData = false;
m_bRemoveFromTree = false;
m_bSetupNewRelevanceSphere = false;
SetHideFlags();
}
void OnDisable()
{
if(m_bPreviewOriginalMesh)
{
foreach (Object targetObject in targets)
{
if (targetObject != null)
{
MeshSimplify meshSimplify = targetObject as MeshSimplify;
meshSimplify.AssignSimplifiedMesh(true);
}
}
}
}
void OnSceneGUI()
{
MeshSimplify meshSimplify = target as MeshSimplify;
bool bDrawSpheres = true;
if (meshSimplify.m_meshSimplifyRoot != null)
{
if (meshSimplify.m_meshSimplifyRoot.m_bExpandRelevanceSpheres == false)
{
bDrawSpheres = false;
}
}
else
{
if (meshSimplify.m_bExpandRelevanceSpheres == false)
{
bDrawSpheres = false;
}
}
if (meshSimplify.m_aRelevanceSpheres != null && bDrawSpheres)
{
for (int nSphere = 0; nSphere < meshSimplify.m_aRelevanceSpheres.Length; nSphere++)
{
if (meshSimplify.m_aRelevanceSpheres[nSphere].m_bExpanded == false)
{
continue;
}
RelevanceSphere relevanceSphere = meshSimplify.m_aRelevanceSpheres[nSphere] as RelevanceSphere;
if (Tools.current == Tool.Move)
{
EditorGUI.BeginChangeCheck();
Vector3 v3Position = Handles.PositionHandle(relevanceSphere.m_v3Position, Quaternion.Euler(relevanceSphere.m_v3Rotation));
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(meshSimplify, "Move Relevance Sphere");
relevanceSphere.m_v3Position = v3Position;
meshSimplify.RestoreOriginalMesh(false, true);
meshSimplify.SetDataDirty(true);
EditorUtility.SetDirty(target);
}
}
else if (Tools.current == Tool.Rotate)
{
EditorGUI.BeginChangeCheck();
Quaternion qRotation = Handles.RotationHandle(Quaternion.Euler(relevanceSphere.m_v3Rotation), relevanceSphere.m_v3Position);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(meshSimplify, "Rotate Relevance Sphere");
relevanceSphere.m_v3Rotation = qRotation.eulerAngles;
meshSimplify.RestoreOriginalMesh(false, true);
meshSimplify.SetDataDirty(true);
EditorUtility.SetDirty(target);
}
}
else if (Tools.current == Tool.Scale)
{
EditorGUI.BeginChangeCheck();
Vector3 v3Scale = Handles.ScaleHandle(relevanceSphere.m_v3Scale, relevanceSphere.m_v3Position, Quaternion.Euler(relevanceSphere.m_v3Rotation), HandleUtility.GetHandleSize(relevanceSphere.m_v3Position) * 1.0f);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(meshSimplify, "Scale Relevance Sphere");
relevanceSphere.m_v3Scale = v3Scale;
meshSimplify.RestoreOriginalMesh(false, true);
meshSimplify.SetDataDirty(true);
EditorUtility.SetDirty(target);
}
}
if(Event.current.type == EventType.Repaint)
{
Matrix4x4 mtxHandles = Handles.matrix;
Handles.matrix = Matrix4x4.TRS(relevanceSphere.m_v3Position, Quaternion.Euler(relevanceSphere.m_v3Rotation), relevanceSphere.m_v3Scale);
Handles.color = new Color(0.0f, 0.0f, 1.0f, 0.5f);
Handles.SphereHandleCap(0, Vector3.zero, Quaternion.identity, 1.0f, EventType.Repaint);
Handles.matrix = mtxHandles;
}
}
}
}
public override void OnInspectorGUI()
{
MeshSimplify meshSimplify;
string strIncludeChildrenLabel = "Recurse Into Children";
int nButtonWidth = 200;
int nButtonWidthSmall = 130;
foreach (Object targetObject in targets)
{
meshSimplify = targetObject as MeshSimplify;
if (meshSimplify.m_meshSimplifyRoot != null && targets.Length > 1)
{
EditorGUILayout.HelpBox("One or more GameObjects of the selection is not a root MeshSimplify GameObject. Only root MeshSimplify GameObjects can be edited at the same time.", MessageType.Warning);
return;
}
}
if (targets.Length > 1)
{
EditorGUILayout.HelpBox("Multiple selection", MessageType.Info);
}
serializedObject.Update();
EditorGUILayout.Space();
meshSimplify = target as MeshSimplify;
if (meshSimplify.m_meshSimplifyRoot == null)
{
EditorGUILayout.PropertyField(PropertyGenerateIncludeChildren, new GUIContent(strIncludeChildrenLabel, "If checked, we will traverse the whole GameObject's hierarchy looking for meshes"));
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(PropertyEnablePrefabUsage, new GUIContent("Enable Prefab Usage", "Will save the generated mesh assets to disk, so that this GameObject can be used as a prefab and be instantiated at runtime. Otherwise the mesh won't be available"));
if (EditorGUI.EndChangeCheck())
{
if (PropertyEnablePrefabUsage.boolValue)
{
m_bEnablePrefabUsage = true;
}
else
{
m_bDisablePrefabUsage = true;
}
}
}
else
{
if (PropertyExcludedFromTree.boolValue)
{
GUILayout.Label("Object has been excluded from mesh simplification.");
}
else
{
GUILayout.Label("Child MeshSimplify GameObject depending on " + meshSimplify.m_meshSimplifyRoot.name);
EditorGUILayout.PropertyField(PropertyOverrideRootSettings, new GUIContent("Override " + meshSimplify.m_meshSimplifyRoot.name + " settings", "Will allow to edit this object's own parameters, instead of inheriting those of the root Automatic LOD GameObject"));
}
}
if (meshSimplify.m_meshSimplifyRoot == null || (PropertyOverrideRootSettings.boolValue == true && PropertyExcludedFromTree.boolValue == false))
{
bool bIsOverriden = PropertyOverrideRootSettings.boolValue == true;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(PropertyUseEdgeLength, new GUIContent("Use Edge Length", "Will take edge length into consideration when simplifying the mesh. Edges with higher length will be more likely to be kept"));
if (EditorGUI.EndChangeCheck())
{
PropertyDataDirty.boolValue = true;
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(PropertyUseCurvature, new GUIContent("Use Curvature", "Will take the angle between triangles into consideration. Edges with smaller angles between two triangles will be more likely to be kept when simplifying the mesh."));
if (EditorGUI.EndChangeCheck())
{
PropertyDataDirty.boolValue = true;
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(PropertyProtectTexture, new GUIContent("Protect Texture", "Will try to keep mapping integrity during the process of mesh simplification"));
if (EditorGUI.EndChangeCheck())
{
PropertyDataDirty.boolValue = true;
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(PropertyLockBorder, new GUIContent("Keep Borders", "Will try to keep those vertices that form an object's border"));
if (EditorGUI.EndChangeCheck())
{
PropertyDataDirty.boolValue = true;
}
EditorGUILayout.Space();
float fVertexAmount = EditorGUILayout.Slider(new GUIContent("Vertex %", "The percentage of vertices from the original mesh to keep when simplifying it"), PropertyVertexAmount.floatValue * 100.0f, 0.0f, 100.0f);
PropertyVertexAmount.floatValue = Mathf.Clamp01(fVertexAmount / 100.0f);
int nSimplifiedMeshVertexCount = meshSimplify.GetSimplifiedVertexCount(bIsOverriden == false);
int nMeshVertexCount = meshSimplify.GetOriginalVertexCount(bIsOverriden == false);
EditorGUILayout.LabelField("Vertex count: " + nSimplifiedMeshVertexCount + "/" + nMeshVertexCount);
int nSimplifiedMeshTriangleCount = meshSimplify.GetSimplifiedTriangleCount(bIsOverriden == false);
int nMeshTriangleCount = meshSimplify.GetOriginalTriangleCount(bIsOverriden == false);
EditorGUILayout.LabelField("Triangle count: " + nSimplifiedMeshTriangleCount + "/" + nMeshTriangleCount);
//EditorGUILayout.LabelField("Child nodes: " + (meshSimplify.m_listDependentChildren == null ? 0 : meshSimplify.m_listDependentChildren.Count));
EditorGUILayout.Space();
EditorGUI.BeginChangeCheck();
m_bPreviewOriginalMesh = EditorGUILayout.Toggle(new GUIContent("Preview Original Mesh", "Allows to quickly switch between viewing the simplified mesh and the original mesh to check the reduction quality"), m_bPreviewOriginalMesh);
if (EditorGUI.EndChangeCheck())
{
if (m_bPreviewOriginalMesh)
{
meshSimplify.RestoreOriginalMesh(false, bIsOverriden == false);
}
else
{
meshSimplify.AssignSimplifiedMesh(bIsOverriden == false);
}
}
EditorGUILayout.Space();
}
if (meshSimplify.m_meshSimplifyRoot == null)
{
PropertyExpandRelevanceSpheres.boolValue = EditorGUILayout.Foldout(PropertyExpandRelevanceSpheres.boolValue, new GUIContent("Vertex Relevance Modifiers:"));
if (PropertyExpandRelevanceSpheres.boolValue)
{
EditorGUILayout.HelpBox("Use vertex relevance spheres to select which vertices should be preserved with more or less priority when simplifying the mesh.", MessageType.Info);
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Add New Sphere", "Adds a new vertex relevance sphere"), GUILayout.Width(nButtonWidthSmall)))
{
PropertyRelevanceSpheres.InsertArrayElementAtIndex(0);
PropertyDataDirty.boolValue = true;
m_bSetupNewRelevanceSphere = true;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
EditorGUILayout.Space();
EditorGUI.indentLevel++;
int nSphereToDelete = -1;
for (int i = 0; i < PropertyRelevanceSpheres.arraySize; i++)
{
SerializedProperty elementProperty = PropertyRelevanceSpheres.GetArrayElementAtIndex(i);
SerializedProperty elementExpanded = elementProperty.FindPropertyRelative("m_bExpanded");
SerializedProperty elementPosition = elementProperty.FindPropertyRelative("m_v3Position");
SerializedProperty elementRotation = elementProperty.FindPropertyRelative("m_v3Rotation");
SerializedProperty elementScale = elementProperty.FindPropertyRelative("m_v3Scale");
SerializedProperty elementRelevance = elementProperty.FindPropertyRelative("m_fRelevance");
elementExpanded.boolValue = EditorGUILayout.Foldout(elementExpanded.boolValue, new GUIContent("Sphere"));
if (elementExpanded.boolValue)
{
bool bWideMode = EditorGUIUtility.wideMode;
EditorGUIUtility.wideMode = true;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(elementPosition, new GUIContent("Position"));
if (EditorGUI.EndChangeCheck())
{
PropertyDataDirty.boolValue = true;
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(elementRotation, new GUIContent("Rotation"));
if (EditorGUI.EndChangeCheck())
{
PropertyDataDirty.boolValue = true;
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(elementScale, new GUIContent("Scale"));
if (EditorGUI.EndChangeCheck())
{
PropertyDataDirty.boolValue = true;
}
EditorGUI.BeginChangeCheck();
elementRelevance.floatValue = EditorGUILayout.Slider(new GUIContent("Relevance", "Tells the simplification algorithm how relevant the vertices inside this sphere are. Default relevance is 0, use lower values to discard non important vertices, and higher values to keep them before others when simplifying the mesh"), elementRelevance.floatValue, -1.0f, 1.0f);
if (EditorGUI.EndChangeCheck())
{
PropertyDataDirty.boolValue = true;
}
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Remove Sphere", "Removes this simplification sphere"), GUILayout.Width(nButtonWidthSmall)))
{
nSphereToDelete = i;
PropertyDataDirty.boolValue = true;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
EditorGUIUtility.wideMode = bWideMode;
}
}
if (nSphereToDelete >= 0)
{
PropertyRelevanceSpheres.DeleteArrayElementAtIndex(nSphereToDelete);
}
EditorGUI.indentLevel--;
}
EditorGUILayout.Space();
}
if (meshSimplify.m_meshSimplifyRoot == null || (PropertyOverrideRootSettings.boolValue == true && PropertyExcludedFromTree.boolValue == false))
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Compute mesh", "Starts the mesh simplification process and assigns the GameObject the new simplified mesh"), GUILayout.Width(nButtonWidth)))
{
m_bComputeMesh = true;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Restore Original Mesh...", "Deletes the simplified data and restores the original mesh"), GUILayout.Width(nButtonWidth)))
{
if (EditorUtility.DisplayDialog("Delete all data and restore original mesh?", "Are you sure you want to delete all data and restore the original mesh?", "Delete", "Cancel"))
{
m_bDeleteData = true;
}
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
if (meshSimplify.m_meshSimplifyRoot != null && PropertyExcludedFromTree.boolValue == false)
{
EditorGUILayout.Space();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Exclude from tree...", "Restores this GameObject's original mesh and excludes it from the mesh simplification tree"), GUILayout.Width(nButtonWidth)))
{
if (EditorUtility.DisplayDialog("Remove from tree?", "Are you sure you want to restore this gameobject's mesh and exclude it from the tree?", "Remove", "Cancel"))
{
m_bRemoveFromTree = true;
}
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
serializedObject.ApplyModifiedProperties();
bool bRepaint = false;
if(m_bEnablePrefabUsage)
{
m_bEnablePrefabUsage = false;
SaveMeshAssets();
}
if(m_bDisablePrefabUsage)
{
m_bDisablePrefabUsage = false;
if (PropertyEnablePrefabUsage.boolValue == false)
{
foreach (Object targetObject in targets)
{
meshSimplify = targetObject as MeshSimplify;
meshSimplify.DisablePrefabUsage(true);
}
}
}
if (m_bComputeMesh && Event.current.type == EventType.Repaint)
{
m_bComputeMesh = false;
Simplifier.Cancelled = false;
foreach (Object targetObject in targets)
{
meshSimplify = targetObject as MeshSimplify;
if (meshSimplify.HasData() == false)
{
if (PropertyGenerateIncludeChildren.boolValue == false)
{
if (MeshSimplify.HasValidMeshData(meshSimplify.gameObject) == false)
{
EditorUtility.DisplayDialog("Error", "Object " + meshSimplify.name + " has no MeshFilter nor Skinned Mesh to process. Please use the \"" + strIncludeChildrenLabel + "\" parameter if you want to process the whole " + meshSimplify.name + " hierarchy for meshes", "OK");
continue;
}
}
}
try
{
if (meshSimplify.HasDataDirty() || meshSimplify.HasData() == false || meshSimplify.HasNonMeshSimplifyGameObjectsInTree())
{
meshSimplify.RestoreOriginalMesh(true, meshSimplify.m_meshSimplifyRoot == null);
meshSimplify.ComputeData(meshSimplify.m_meshSimplifyRoot == null, Progress);
if (Simplifier.Cancelled)
{
meshSimplify.RestoreOriginalMesh(true, meshSimplify.m_meshSimplifyRoot == null);
break;
}
}
meshSimplify.ComputeMesh(meshSimplify.m_meshSimplifyRoot == null, Progress);
if (Simplifier.Cancelled)
{
break;
}
meshSimplify.AssignSimplifiedMesh(meshSimplify.m_meshSimplifyRoot == null);
if (meshSimplify.m_strAssetPath != null && meshSimplify.m_bEnablePrefabUsage)
{
SaveMeshAssets();
}
}
catch (System.Exception e)
{
Debug.LogError("Error generating mesh: " + e.Message + " Stack: " + e.StackTrace);
EditorUtility.ClearProgressBar();
Simplifier.Cancelled = false;
}
Simplifier.Cancelled = false;
}
bRepaint = true;
EditorUtility.ClearProgressBar();
}
if (m_bDeleteData && Event.current.type == EventType.Repaint)
{
m_bDeleteData = false;
foreach (Object targetObject in targets)
{
meshSimplify = targetObject as MeshSimplify;
meshSimplify.RestoreOriginalMesh(true, meshSimplify.m_meshSimplifyRoot == null);
RemoveChildMeshSimplifyComponents(meshSimplify);
}
bRepaint = true;
}
if (m_bRemoveFromTree && Event.current.type == EventType.Repaint)
{
m_bRemoveFromTree = false;
meshSimplify = target as MeshSimplify;
meshSimplify.RemoveFromTree();
if (Application.isEditor && Application.isPlaying == false)
{
UnityEngine.Object.DestroyImmediate(meshSimplify);
}
else
{
UnityEngine.Object.Destroy(meshSimplify);
}
bRepaint = true;
}
if (m_bSetupNewRelevanceSphere)
{
m_bSetupNewRelevanceSphere = false;
foreach (Object targetObject in targets)
{
meshSimplify = targetObject as MeshSimplify;
if (meshSimplify.m_aRelevanceSpheres != null && meshSimplify.m_aRelevanceSpheres.Length > 0)
{
meshSimplify.m_aRelevanceSpheres[0].SetDefault(meshSimplify.transform, 0.0f);
}
}
}
if (bRepaint)
{
Repaint();
}
}
void RemoveChildMeshSimplifyComponents(MeshSimplify meshSimplify)
{
RemoveChildMeshSimplifyComponentsRecursive(meshSimplify.gameObject, meshSimplify.gameObject, true);
}
void RemoveChildMeshSimplifyComponentsRecursive(GameObject root, GameObject gameObject, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null && meshSimplify.m_meshSimplifyRoot != null)
{
if (Application.isEditor && Application.isPlaying == false)
{
UnityEngine.Object.DestroyImmediate(meshSimplify);
}
else
{
UnityEngine.Object.Destroy(meshSimplify);
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
RemoveChildMeshSimplifyComponentsRecursive(root, gameObject.transform.GetChild(nChild).gameObject, true);
}
}
}
void SaveMeshAssets()
{
try
{
foreach (Object targetObject in targets)
{
MeshSimplify meshSimplify = targetObject as MeshSimplify;
GameObject gameObject = meshSimplify.gameObject;
if (meshSimplify.m_meshSimplifyRoot == null && meshSimplify.m_bEnablePrefabUsage)
{
string strMeshAssetPath = meshSimplify.m_strAssetPath;
if (string.IsNullOrEmpty(strMeshAssetPath))
{
//Debug.Log("Showing file selection panel");
strMeshAssetPath = UnityEditor.EditorUtility.SaveFilePanelInProject("Save mesh asset(s)", "mesh_" + gameObject.name + gameObject.GetInstanceID().ToString() + ".asset", "asset", "Please enter a file name to save the mesh asset(s) to");
if (strMeshAssetPath.Length == 0)
{
//Debug.LogWarning("strMeshAssetPath.Length == 0. User cancelled?");
return;
}
//Debug.Log("User selected " + strMeshAssetPath + " using panel.");
meshSimplify.m_strAssetPath = strMeshAssetPath;
}
int nCounter = 0;
//Debug.Log("Saving files to " + strMeshAssetPath + ". Exists previously?: " + System.IO.File.Exists(strMeshAssetPath));
SaveMeshAssetsRecursive(gameObject, gameObject, strMeshAssetPath, true, System.IO.File.Exists(strMeshAssetPath), ref nCounter);
}
}
}
catch (System.Exception e)
{
Debug.LogError("Error saving mesh assets to disk: " + e.Message + " Stack: " + e.StackTrace);
EditorUtility.ClearProgressBar();
Simplifier.Cancelled = false;
}
EditorUtility.ClearProgressBar();
UnityEditor.AssetDatabase.Refresh();
Simplifier.Cancelled = false;
}
bool SaveMeshAssetsRecursive(GameObject root, GameObject gameObject, string strFile, bool bRecurseIntoChildren, bool bAssetAlreadyCreated, ref int nProgressElementsCounter)
{
if(gameObject == null || Simplifier.Cancelled)
{
return bAssetAlreadyCreated;
}
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null && meshSimplify.HasData() && (meshSimplify.m_meshSimplifyRoot == null || meshSimplify.m_meshSimplifyRoot.gameObject == root))
{
int nTotalProgressElements = meshSimplify.m_meshSimplifyRoot != null ? (meshSimplify.m_meshSimplifyRoot.m_listDependentChildren.Count + 1) : 1;
if (meshSimplify.m_simplifiedMesh != null && MeshSimplify.HasValidMeshData(meshSimplify.gameObject))
{
float fT = (float)nProgressElementsCounter / (float)nTotalProgressElements;
Progress("Saving meshes to asset file", meshSimplify.name, fT);
if (Simplifier.Cancelled)
{
return bAssetAlreadyCreated;
}
if (bAssetAlreadyCreated == false && UnityEditor.AssetDatabase.Contains(meshSimplify.m_simplifiedMesh) == false)
{
//Debug.Log("Creating asset " + meshSimplify.m_simplifiedMesh.name);
UnityEditor.AssetDatabase.CreateAsset(meshSimplify.m_simplifiedMesh, strFile);
bAssetAlreadyCreated = true;
}
else
{
if (UnityEditor.AssetDatabase.Contains(meshSimplify.m_simplifiedMesh) == false)
{
//Debug.Log("Adding asset " + meshSimplify.m_simplifiedMesh.name);
UnityEditor.AssetDatabase.AddObjectToAsset(meshSimplify.m_simplifiedMesh, strFile);
UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(meshSimplify.m_simplifiedMesh));
}
}
nProgressElementsCounter++;
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
bAssetAlreadyCreated = SaveMeshAssetsRecursive(root, gameObject.transform.GetChild(nChild).gameObject, strFile, bRecurseIntoChildren, bAssetAlreadyCreated, ref nProgressElementsCounter);
}
}
return bAssetAlreadyCreated;
}
void SetHideFlags()
{
foreach (Object targetObject in targets)
{
MeshSimplify meshSimplify = targetObject as MeshSimplify;
if (meshSimplify.m_meshSimplifyRoot == null)
{
SetHideFlagsRecursive(meshSimplify.gameObject, meshSimplify.gameObject, true);
}
}
}
void SetHideFlagsRecursive(GameObject root, GameObject gameObject, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify && meshSimplify.GetMeshSimplifier())
{
meshSimplify.GetMeshSimplifier().hideFlags = HideFlags.HideInInspector;
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
SetHideFlagsRecursive(root, gameObject.transform.GetChild(nChild).gameObject, true);
}
}
}
bool m_bComputeMesh = false;
bool m_bEnablePrefabUsage = false;
bool m_bDisablePrefabUsage = false;
bool m_bDeleteData = false;
bool m_bRemoveFromTree = false;
bool m_bSetupNewRelevanceSphere = false;
bool m_bPreviewOriginalMesh = false;
SerializedProperty PropertyGenerateIncludeChildren;
SerializedProperty PropertyEnablePrefabUsage;
SerializedProperty PropertyExpandRelevanceSpheres;
SerializedProperty PropertyRelevanceSpheres;
SerializedProperty PropertyOverrideRootSettings;
SerializedProperty PropertyVertexAmount;
SerializedProperty PropertyUseEdgeLength;
SerializedProperty PropertyUseCurvature;
SerializedProperty PropertyProtectTexture;
SerializedProperty PropertyLockBorder;
SerializedProperty PropertyDataDirty;
SerializedProperty PropertyExcludedFromTree;
static int s_nLastProgress = -1;
static string s_strLastTitle = "";
static string s_strLastMessage = "";
}
fileFormatVersion: 2
guid: a52cb74794833b64492fa4b27f8cb797
timeCreated: 1438106980
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: f6becbe5563e9ef40b1d2ddab96d5dec
folderAsset: yes
timeCreated: 1438107038
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using UltimateGameTools.MeshSimplifier;
public class MeshSimplify : MonoBehaviour
{
public bool RecurseIntoChildren
{
get
{
return m_bGenerateIncludeChildren;
}
}
public Simplifier MeshSimplifier
{
get
{
return m_meshSimplifier;
}
set
{
m_meshSimplifier = value;
}
}
[HideInInspector]
public Mesh m_originalMesh = null;
[HideInInspector]
public Mesh m_simplifiedMesh = null;
[HideInInspector]
public bool m_bEnablePrefabUsage = false;
[HideInInspector]
public float m_fVertexAmount = 1.0f;
[HideInInspector]
public string m_strAssetPath = null;
[HideInInspector]
public MeshSimplify m_meshSimplifyRoot;
[HideInInspector]
public List<MeshSimplify> m_listDependentChildren = new List<MeshSimplify>();
[HideInInspector]
public bool m_bExpandRelevanceSpheres = true;
public RelevanceSphere[] m_aRelevanceSpheres = null;
[SerializeField, HideInInspector]
private Simplifier m_meshSimplifier = null;
[SerializeField, HideInInspector]
private bool m_bGenerateIncludeChildren = true;
[SerializeField, HideInInspector]
private bool m_bOverrideRootSettings = false;
[SerializeField, HideInInspector]
private bool m_bUseEdgeLength = true, m_bUseCurvature = true, m_bProtectTexture = true, m_bLockBorder = true;
[SerializeField, HideInInspector]
private bool m_bDataDirty = true;
[SerializeField, HideInInspector]
private bool m_bExcludedFromTree = false;
#if UNITY_EDITOR
public void OnDrawGizmos()
{
if (m_meshSimplifyRoot != null)
{
if (m_meshSimplifyRoot.m_bExpandRelevanceSpheres == false)
{
return;
}
}
else
{
if (m_bExpandRelevanceSpheres == false)
{
return;
}
}
Gizmos.color = Color.red;
RelevanceSphere[] aRelevanceSpheres = m_meshSimplifyRoot != null ? m_meshSimplifyRoot.m_aRelevanceSpheres : m_aRelevanceSpheres;
if (aRelevanceSpheres == null)
{
return;
}
bool bDrawVertices = false;
for (int i = 0; i < UnityEditor.Selection.gameObjects.Length; i++)
{
if (((UnityEditor.Selection.gameObjects[i] == this.gameObject) && m_meshSimplifyRoot == null) || ((m_meshSimplifyRoot != null) && (UnityEditor.Selection.gameObjects[i] == m_meshSimplifyRoot.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 IsRootOrBelongsToTree(MeshSimplify meshSimplify, MeshSimplify root)
{
if (meshSimplify == null)
{
return false;
}
return (meshSimplify.m_bExcludedFromTree == false) && ((meshSimplify.m_meshSimplifyRoot == null) || (meshSimplify.m_meshSimplifyRoot == root) || (meshSimplify == root) || (meshSimplify.m_meshSimplifyRoot == root.m_meshSimplifyRoot));
}
public bool IsGenerateIncludeChildrenActive()
{
return m_bGenerateIncludeChildren;
}
public bool HasDependentChildren()
{
return m_listDependentChildren != null && m_listDependentChildren.Count > 0;
}
public bool HasDataDirty()
{
return m_bDataDirty;
}
public bool SetDataDirty(bool bDirty)
{
return m_bDataDirty = bDirty;
}
public bool HasNonMeshSimplifyGameObjectsInTree()
{
return HasNonMeshSimplifyGameObjectsInTreeRecursive(this, this.gameObject);
}
private bool HasNonMeshSimplifyGameObjectsInTreeRecursive(MeshSimplify root, GameObject gameObject)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify == null && HasValidMeshData(gameObject))
{
return true;
}
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
if(HasNonMeshSimplifyGameObjectsInTreeRecursive(root, gameObject.transform.GetChild(nChild).gameObject))
{
return true;
}
}
return false;
}
public void ConfigureSimplifier()
{
m_meshSimplifier.UseEdgeLength = (m_meshSimplifyRoot != null && m_bOverrideRootSettings == false) ? m_meshSimplifyRoot.m_bUseEdgeLength : m_bUseEdgeLength;
m_meshSimplifier.UseCurvature = (m_meshSimplifyRoot != null && m_bOverrideRootSettings == false) ? m_meshSimplifyRoot.m_bUseCurvature : m_bUseCurvature;
m_meshSimplifier.ProtectTexture = (m_meshSimplifyRoot != null && m_bOverrideRootSettings == false) ? m_meshSimplifyRoot.m_bProtectTexture : m_bProtectTexture;
m_meshSimplifier.LockBorder = (m_meshSimplifyRoot != null && m_bOverrideRootSettings == false) ? m_meshSimplifyRoot.m_bLockBorder : m_bLockBorder;
}
public Simplifier GetMeshSimplifier()
{
return m_meshSimplifier;
}
public void ComputeData(bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
{
ComputeDataRecursive(this, this.gameObject, bRecurseIntoChildren, progress);
}
private static void ComputeDataRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify == null && root.m_bGenerateIncludeChildren)
{
if (HasValidMeshData(gameObject))
{
meshSimplify = gameObject.AddComponent<MeshSimplify>();
meshSimplify.m_meshSimplifyRoot = root;
root.m_listDependentChildren.Add(meshSimplify);
}
}
if(meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
meshSimplify.FreeData(false);
MeshFilter meshFilter = meshSimplify.GetComponent<MeshFilter>();
if (meshFilter != null && meshFilter.sharedMesh != null)
{
if (meshFilter.sharedMesh.vertexCount > 0)
{
if (meshSimplify.m_originalMesh == null)
{
meshSimplify.m_originalMesh = meshFilter.sharedMesh;
}
Simplifier[] simplifiers = meshSimplify.GetComponents<Simplifier>();
for (int c = 0; c < simplifiers.Length; c++)
{
if (Application.isEditor && Application.isPlaying == false)
{
DestroyImmediate(simplifiers[c]);
}
else
{
Destroy(simplifiers[c]);
}
}
meshSimplify.m_meshSimplifier = meshSimplify.gameObject.AddComponent<Simplifier>();
meshSimplify.m_meshSimplifier.hideFlags = HideFlags.HideInInspector;
meshSimplify.ConfigureSimplifier();
IEnumerator enumerator = meshSimplify.m_meshSimplifier.ProgressiveMesh(gameObject, meshSimplify.m_originalMesh, root.m_aRelevanceSpheres, meshSimplify.name, progress);
while (enumerator.MoveNext())
{
if (Simplifier.Cancelled)
{
return;
}
}
if (Simplifier.Cancelled)
{
return;
}
}
}
else
{
SkinnedMeshRenderer skin = meshSimplify.GetComponent<SkinnedMeshRenderer>();
if (skin != null)
{
if (skin.sharedMesh.vertexCount > 0)
{
if (meshSimplify.m_originalMesh == null)
{
meshSimplify.m_originalMesh = skin.sharedMesh;
}
Simplifier[] simplifiers = meshSimplify.GetComponents<Simplifier>();
for (int c = 0; c < simplifiers.Length; c++)
{
if (Application.isEditor && Application.isPlaying == false)
{
DestroyImmediate(simplifiers[c]);
}
else
{
Destroy(simplifiers[c]);
}
}
meshSimplify.m_meshSimplifier = meshSimplify.gameObject.AddComponent<Simplifier>();
meshSimplify.m_meshSimplifier.hideFlags = HideFlags.HideInInspector;
meshSimplify.ConfigureSimplifier();
IEnumerator enumerator = meshSimplify.m_meshSimplifier.ProgressiveMesh(gameObject, meshSimplify.m_originalMesh, root.m_aRelevanceSpheres, meshSimplify.name, progress);
while (enumerator.MoveNext())
{
if (Simplifier.Cancelled)
{
return;
}
}
if (Simplifier.Cancelled)
{
return;
}
}
}
}
meshSimplify.m_bDataDirty = false;
}
}
if(bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
ComputeDataRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren, progress);
if (Simplifier.Cancelled)
{
return;
}
}
}
}
public bool HasData()
{
return (m_meshSimplifier != null && m_simplifiedMesh != null) || (m_listDependentChildren != null && m_listDependentChildren.Count != 0);
}
public bool HasSimplifiedMesh()
{
return m_simplifiedMesh != null && m_simplifiedMesh.vertexCount > 0;
}
public void ComputeMesh(bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
{
ComputeMeshRecursive(this, this.gameObject, bRecurseIntoChildren, progress);
}
private static void ComputeMeshRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren, Simplifier.ProgressDelegate progress = null)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_meshSimplifier != null)
{
if (meshSimplify.m_simplifiedMesh)
{
meshSimplify.m_simplifiedMesh.Clear();
}
float fAmount = meshSimplify.m_fVertexAmount;
if (meshSimplify.m_bOverrideRootSettings == false && meshSimplify.m_meshSimplifyRoot != null)
{
fAmount = meshSimplify.m_meshSimplifyRoot.m_fVertexAmount;
}
if (meshSimplify.m_simplifiedMesh == null)
{
meshSimplify.m_simplifiedMesh = CreateNewEmptyMesh(meshSimplify);
}
meshSimplify.ConfigureSimplifier();
IEnumerator enumerator = meshSimplify.m_meshSimplifier.ComputeMeshWithVertexCount(gameObject, meshSimplify.m_simplifiedMesh, Mathf.RoundToInt(fAmount * meshSimplify.m_meshSimplifier.GetOriginalMeshUniqueVertexCount()), meshSimplify.name + " Simplified", progress);
while (enumerator.MoveNext())
{
if (Simplifier.Cancelled)
{
return;
}
}
if (Simplifier.Cancelled)
{
return;
}
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
ComputeMeshRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren, progress);
if (Simplifier.Cancelled)
{
return;
}
}
}
}
public void AssignSimplifiedMesh(bool bRecurseIntoChildren)
{
AssignSimplifiedMeshRecursive(this, this.gameObject, bRecurseIntoChildren);
}
private static void AssignSimplifiedMeshRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_simplifiedMesh != null)
{
MeshFilter meshFilter = meshSimplify.GetComponent<MeshFilter>();
if (meshFilter != null)
{
meshFilter.sharedMesh = meshSimplify.m_simplifiedMesh;
}
else
{
SkinnedMeshRenderer skin = meshSimplify.GetComponent<SkinnedMeshRenderer>();
if (skin != null)
{
skin.sharedMesh = meshSimplify.m_simplifiedMesh;
}
}
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
AssignSimplifiedMeshRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren);
}
}
}
public void RestoreOriginalMesh(bool bDeleteData, bool bRecurseIntoChildren)
{
RestoreOriginalMeshRecursive(this, this.gameObject, bDeleteData, bRecurseIntoChildren);
}
private static void RestoreOriginalMeshRecursive(MeshSimplify root, GameObject gameObject, bool bDeleteData, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_originalMesh != null)
{
MeshFilter meshFilter = meshSimplify.GetComponent<MeshFilter>();
if (meshFilter != null)
{
meshFilter.sharedMesh = meshSimplify.m_originalMesh;
}
else
{
SkinnedMeshRenderer skin = meshSimplify.GetComponent<SkinnedMeshRenderer>();
if (skin != null)
{
skin.sharedMesh = meshSimplify.m_originalMesh;
}
}
}
if (bDeleteData)
{
meshSimplify.FreeData(false);
meshSimplify.m_listDependentChildren.Clear();
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
RestoreOriginalMeshRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bDeleteData, bRecurseIntoChildren);
}
}
}
public bool HasOriginalMeshActive(bool bRecurseIntoChildren)
{
return HasOriginalMeshActiveRecursive(this, this.gameObject, bRecurseIntoChildren);
}
private static bool HasOriginalMeshActiveRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
bool bHasOriginalMeshActive = false;
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_originalMesh != null)
{
MeshFilter meshFilter = meshSimplify.GetComponent<MeshFilter>();
if (meshFilter != null)
{
if(meshFilter.sharedMesh == meshSimplify.m_originalMesh)
{
bHasOriginalMeshActive = true;
}
}
else
{
SkinnedMeshRenderer skin = meshSimplify.GetComponent<SkinnedMeshRenderer>();
if (skin != null)
{
if(skin.sharedMesh == meshSimplify.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(bool bRecurseIntoChildren)
{
return HasVertexDataRecursive(this, this.gameObject, bRecurseIntoChildren);
}
private static bool HasVertexDataRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_simplifiedMesh && meshSimplify.m_simplifiedMesh.vertexCount > 0)
{
return true;
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
if (HasVertexDataRecursive(root, gameObject.transform.GetChild(nChild).gameObject, 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(MeshSimplify root, GameObject gameObject, ref int nVertexCount, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_originalMesh != null)
{
nVertexCount += meshSimplify.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(MeshSimplify root, GameObject gameObject, ref int nTriangleCount, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_originalMesh != null)
{
nTriangleCount += meshSimplify.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 GetSimplifiedVertexCount(bool bRecurseIntoChildren)
{
int nVertexCount = 0;
GetSimplifiedVertexCountRecursive(this, this.gameObject, ref nVertexCount, bRecurseIntoChildren);
return nVertexCount;
}
private static void GetSimplifiedVertexCountRecursive(MeshSimplify root, GameObject gameObject, ref int nVertexCount, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_simplifiedMesh != null)
{
nVertexCount += meshSimplify.m_simplifiedMesh.vertexCount;
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
GetSimplifiedVertexCountRecursive(root, gameObject.transform.GetChild(nChild).gameObject, ref nVertexCount, bRecurseIntoChildren);
}
}
}
public int GetSimplifiedTriangleCount(bool bRecurseIntoChildren)
{
int nTriangleCount = 0;
GetSimplifiedTriangleCountRecursive(this, this.gameObject, ref nTriangleCount, bRecurseIntoChildren);
return nTriangleCount;
}
private static void GetSimplifiedTriangleCountRecursive(MeshSimplify root, GameObject gameObject, ref int nTriangleCount, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_simplifiedMesh != null)
{
nTriangleCount += meshSimplify.m_simplifiedMesh.triangles.Length / 3;
}
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
GetSimplifiedTriangleCountRecursive(root, gameObject.transform.GetChild(nChild).gameObject, ref nTriangleCount, bRecurseIntoChildren);
}
}
}
public void RemoveFromTree()
{
if (m_meshSimplifyRoot != null)
{
m_meshSimplifyRoot.m_listDependentChildren.Remove(this);
}
RestoreOriginalMesh(true, false);
m_bExcludedFromTree = true;
}
public void FreeData(bool bRecurseIntoChildren)
{
FreeDataRecursive(this, this.gameObject, bRecurseIntoChildren);
}
private static void FreeDataRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_simplifiedMesh)
{
meshSimplify.m_simplifiedMesh.Clear();
}
Simplifier[] simplifiers = gameObject.GetComponents<Simplifier>();
for (int c = 0; c < simplifiers.Length; c++)
{
if (Application.isEditor && Application.isPlaying == false)
{
DestroyImmediate(simplifiers[c]);
}
else
{
Destroy(simplifiers[c]);
}
}
meshSimplify.m_bDataDirty = true;
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
FreeDataRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren);
}
}
}
private static Mesh CreateNewEmptyMesh(MeshSimplify meshSimplify)
{
if(meshSimplify.m_originalMesh == null)
{
return new Mesh();
}
Mesh meshOut = Mesh.Instantiate(meshSimplify.m_originalMesh);
meshOut.Clear();
return meshOut;
}
#if UNITY_EDITOR
public void DisablePrefabUsage(bool bRecurseIntoChildren)
{
DisablePrefabUsageRecursive(this, this.gameObject, bRecurseIntoChildren);
}
private static void DisablePrefabUsageRecursive(MeshSimplify root, GameObject gameObject, bool bRecurseIntoChildren)
{
MeshSimplify meshSimplify = gameObject.GetComponent<MeshSimplify>();
if (meshSimplify != null)
{
if (IsRootOrBelongsToTree(meshSimplify, root))
{
if (meshSimplify.m_simplifiedMesh)
{
if (UnityEditor.AssetDatabase.IsMainAsset(meshSimplify.m_simplifiedMesh) || UnityEditor.AssetDatabase.IsSubAsset(meshSimplify.m_simplifiedMesh))
{
Mesh newMesh = Instantiate(meshSimplify.m_simplifiedMesh) as Mesh;
meshSimplify.m_simplifiedMesh = newMesh;
}
}
meshSimplify.m_strAssetPath = null;
}
}
if (bRecurseIntoChildren)
{
for (int nChild = 0; nChild < gameObject.transform.childCount; nChild++)
{
DisablePrefabUsageRecursive(root, gameObject.transform.GetChild(nChild).gameObject, bRecurseIntoChildren);
}
}
}
#endif
}
fileFormatVersion: 2
guid: 100bcc9db4f7fc64e95ecc1abca8c97d
timeCreated: 1438106980
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UltimateGameTools
{
namespace MeshSimplifier
{
/// <summary>
/// Class that will take a Mesh as input and will build internal data to identify which vertices are repeated due to
/// different vertex data (UV, vertex colors etc).
/// </summary>
[Serializable]
public class MeshUniqueVertices
{
#region Public types
/////////////////////////////////////////////////////////////////////////////////////////////////
// Public types
/////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// A list of vertex indices. We use this in order to be able to serialize a list of lists.
/// </summary>
[Serializable]
public class ListIndices
{
public ListIndices()
{
m_listIndices = new List<int>();
}
public List<int> m_listIndices;
}
/// <summary>
/// Our serializable version of Unity's BoneWeight
/// </summary>
[Serializable]
public class SerializableBoneWeight
{
public SerializableBoneWeight(BoneWeight boneWeight)
{
_boneIndex0 = boneWeight.boneIndex0;
_boneIndex1 = boneWeight.boneIndex1;
_boneIndex2 = boneWeight.boneIndex2;
_boneIndex3 = boneWeight.boneIndex3;
_boneWeight0 = boneWeight.weight0;
_boneWeight1 = boneWeight.weight1;
_boneWeight2 = boneWeight.weight2;
_boneWeight3 = boneWeight.weight3;
}
public BoneWeight ToBoneWeight()
{
BoneWeight boneWeight = new BoneWeight();
boneWeight.boneIndex0 = _boneIndex0;
boneWeight.boneIndex1 = _boneIndex1;
boneWeight.boneIndex2 = _boneIndex2;
boneWeight.boneIndex3 = _boneIndex3;
boneWeight.weight0 = _boneWeight0;
boneWeight.weight1 = _boneWeight1;
boneWeight.weight2 = _boneWeight2;
boneWeight.weight3 = _boneWeight3;
return boneWeight;
}
public int _boneIndex0;
public int _boneIndex1;
public int _boneIndex2;
public int _boneIndex3;
public float _boneWeight0;
public float _boneWeight1;
public float _boneWeight2;
public float _boneWeight3;
}
/// <summary>
/// Vertex that is has a unique position in space.
/// </summary>
public class UniqueVertex
{
// Overrides from Object
public override bool Equals(object obj)
{
UniqueVertex uniqueVertex = obj as UniqueVertex;
return (uniqueVertex.m_nFixedX == m_nFixedX) && (uniqueVertex.m_nFixedY == m_nFixedY) && (uniqueVertex.m_nFixedZ == m_nFixedZ);
}
public override int GetHashCode()
{
return m_nFixedX + (m_nFixedY << 2) + (m_nFixedZ << 4);
}
// Constructor
public UniqueVertex(Vector3 v3Vertex)
{
FromVertex(v3Vertex);
}
// Public methods
public Vector3 ToVertex()
{
return new Vector3(FixedToCoord(m_nFixedX), FixedToCoord(m_nFixedY), FixedToCoord(m_nFixedZ));
}
// Comparison operators
public static bool operator ==(UniqueVertex a, UniqueVertex b)
{
return a.Equals(b);
}
public static bool operator !=(UniqueVertex a, UniqueVertex b)
{
return !a.Equals(b);
}
// Private methods/vars
private void FromVertex(Vector3 vertex)
{
m_nFixedX = CoordToFixed(vertex.x);
m_nFixedY = CoordToFixed(vertex.y);
m_nFixedZ = CoordToFixed(vertex.z);
}
private int CoordToFixed(float fCoord)
{
int nInteger = Mathf.FloorToInt(fCoord);
int nRemainder = Mathf.FloorToInt((fCoord - nInteger) * fDecimalMultiplier);
return nInteger << 16 | nRemainder;
}
private float FixedToCoord(int nFixed)
{
float fRemainder = (nFixed & 0xFFFF) / fDecimalMultiplier;
float fInteger = nFixed >> 16;
return fInteger + fRemainder;
}
// Private vars
private int m_nFixedX, m_nFixedY, m_nFixedZ;
private const float fDecimalMultiplier = 100000.0f;
}
#endregion
#region Public properties
/////////////////////////////////////////////////////////////////////////////////////////////////
// Public properties
/////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// For each submesh, a list of faces. ListIndices has 3 indices for each face.
/// Each index is a vertex in ListVertices.
/// </summary>
public ListIndices[] SubmeshesFaceList
{
get
{
return m_aFaceList;
}
}
/// <summary>
/// Our list of vertices. Vertices are unique, so no vertex shares the same position in space.
/// </summary>
public List<Vector3> ListVertices
{
get
{
return m_listVertices;
}
}
/// <summary>
/// Our list of vertices in world space.
/// Vertices are unique, so no vertex shares the same position in space.
/// </summary>
public List<Vector3> ListVerticesWorld
{
get
{
return m_listVerticesWorld;
}
}
/// <summary>
/// Our list of vertex bone weights
/// </summary>
public List<SerializableBoneWeight> ListBoneWeights
{
get
{
return m_listBoneWeights;
}
}
#endregion // Public properties
#region Public methods
/////////////////////////////////////////////////////////////////////////////////////////////////
// Public methods
/////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Takes a Mesh as input and will build a new list of faces and vertices. The vertex list will
/// have no vertices sharing position in 3D space. The input mesh may have them, since often
/// a vertex will have different mapping coordinates for each of the faces that share it.
/// </summary>
/// <param name="sourceMesh"></param>
/// <param name="av3VerticesWorld"</param>
public void BuildData(Mesh sourceMesh, Vector3[] av3VerticesWorld)
{
Vector3[] av3Vertices = sourceMesh.vertices;
BoneWeight[] aBoneWeights = sourceMesh.boneWeights;
Dictionary<UniqueVertex, RepeatedVertexList> dicUniqueVertex2RepeatedVertexList = new Dictionary<UniqueVertex, RepeatedVertexList>();
m_listVertices = new List<Vector3>();
m_listVerticesWorld = new List<Vector3>();
m_listBoneWeights = new List<SerializableBoneWeight>();
m_aFaceList = new ListIndices[sourceMesh.subMeshCount];
for (int nSubMesh = 0; nSubMesh < sourceMesh.subMeshCount; nSubMesh++)
{
m_aFaceList[nSubMesh] = new ListIndices();
int[] anFaces = sourceMesh.GetTriangles(nSubMesh);
for (int i = 0; i < anFaces.Length; i++)
{
UniqueVertex vertex = new UniqueVertex(av3Vertices[anFaces[i]]);
if (dicUniqueVertex2RepeatedVertexList.ContainsKey(vertex))
{
dicUniqueVertex2RepeatedVertexList[vertex].Add(new RepeatedVertex(i / 3, anFaces[i]));
m_aFaceList[nSubMesh].m_listIndices.Add(dicUniqueVertex2RepeatedVertexList[vertex].UniqueIndex);
}
else
{
int nNewUniqueIndex = m_listVertices.Count;
dicUniqueVertex2RepeatedVertexList.Add(vertex, new RepeatedVertexList(nNewUniqueIndex, new RepeatedVertex(i / 3, anFaces[i])));
m_listVertices.Add(av3Vertices[anFaces[i]]);
m_listVerticesWorld.Add(av3VerticesWorld[anFaces[i]]);
m_aFaceList[nSubMesh].m_listIndices.Add(nNewUniqueIndex);
if(aBoneWeights != null && aBoneWeights.Length > 0)
{
m_listBoneWeights.Add(new SerializableBoneWeight(aBoneWeights[anFaces[i]]));
}
}
}
}
//Debug.Log("In: " + av3Vertices.Length + " vertices. Out: " + m_listVertices.Count + " vertices.");
}
#endregion // Public methods
#region Private types
/////////////////////////////////////////////////////////////////////////////////////////////////
// Private types
/////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Vertex that has the same position in space as another one, but different vertex data (UV, color...).
/// </summary>
private class RepeatedVertex
{
// Public properties
/// <summary>
/// Face it belongs to. This will be the same index in the source mesh as in the internal created face list.
/// </summary>
public int FaceIndex
{
get
{
return _nFaceIndex;
}
}
/// <summary>
/// Position in the original vertex array.
/// </summary>
public int OriginalVertexIndex
{
get
{
return _nOriginalVertexIndex;
}
}
// Constructor
public RepeatedVertex(int nFaceIndex, int nOriginalVertexIndex)
{
_nFaceIndex = nFaceIndex;
_nOriginalVertexIndex = nOriginalVertexIndex;
}
// Private vars
private int _nFaceIndex;
private int _nOriginalVertexIndex;
}
/// <summary>
/// List of vertices that have the same position in space but different vertex data (UV, color...).
/// </summary>
private class RepeatedVertexList
{
// Public properties
/// <summary>
/// Unique vertex index in our array for this list.
/// </summary>
public int UniqueIndex
{
get
{
return m_nUniqueIndex;
}
}
// Public methods
public RepeatedVertexList(int nUniqueIndex, RepeatedVertex repeatedVertex)
{
m_nUniqueIndex = nUniqueIndex;
m_listRepeatedVertices = new List<RepeatedVertex>();
m_listRepeatedVertices.Add(repeatedVertex);
}
public void Add(RepeatedVertex repeatedVertex)
{
m_listRepeatedVertices.Add(repeatedVertex);
}
// Private vars
private int m_nUniqueIndex;
private List<RepeatedVertex> m_listRepeatedVertices;
}
#endregion // Private types
#region Private vars
/////////////////////////////////////////////////////////////////////////////////////////////////
// Private vars
/////////////////////////////////////////////////////////////////////////////////////////////////
[SerializeField]
private List<Vector3> m_listVertices;
[SerializeField]
private List<Vector3> m_listVerticesWorld;
[SerializeField]
private List<SerializableBoneWeight> m_listBoneWeights;
[SerializeField]
private ListIndices[] m_aFaceList;
#endregion // Private vars
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: c9aa0aceed4b66242b713cf6b09e6a9f
timeCreated: 1434653219
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UltimateGameTools.MeshSimplifier;
using UnityEngine;
[RequireComponent(typeof(MeshSimplify))]
public class RuntimeMeshSimplifier : MonoBehaviour
{
public string ProgressTitle
{
get
{
return m_strLastTitle;
}
}
public string ProgressMessage
{
get
{
return m_strLastMessage;
}
}
public int ProgressPercent
{
get
{
return m_nLastProgress;
}
}
public bool Finished
{
get
{
return m_bFinished;
}
}
public void Simplify(float percent)
{
if (m_bFinished == false)
{
StartCoroutine(ComputeMeshWithVertices(Mathf.Clamp01(percent / 100.0f)));
}
}
private void Awake()
{
m_selectedMeshSimplify = GetComponent<MeshSimplify>();
m_objectMaterials = new Dictionary<GameObject, Material[]>();
AddMaterials(m_selectedMeshSimplify.gameObject, m_objectMaterials);
m_bFinished = false;
}
private void AddMaterials(GameObject theGameObject, Dictionary<GameObject, Material[]> dicMaterials)
{
Renderer theRenderer = theGameObject.GetComponent<Renderer>();
if (theRenderer != null && theRenderer.sharedMaterials != null && (MeshSimplify.HasValidMeshData(theGameObject) || theGameObject.GetComponent<MeshSimplify>() != null))
{
dicMaterials.Add(theGameObject, theRenderer.sharedMaterials);
}
if (m_selectedMeshSimplify.RecurseIntoChildren)
{
for (int i = 0; i < theGameObject.transform.childCount; i++)
{
AddMaterials(theGameObject.transform.GetChild(i).gameObject, dicMaterials);
}
}
}
private void Progress(string strTitle, string strMessage, float fT)
{
int nPercent = Mathf.RoundToInt(fT * 100.0f);
if (nPercent != m_nLastProgress || m_strLastTitle != strTitle || m_strLastMessage != strMessage)
{
m_strLastTitle = strTitle;
m_strLastMessage = strMessage;
m_nLastProgress = nPercent;
//Debug.Log(strTitle + " " + strMessage + " " + nPercent);
}
}
private IEnumerator ComputeMeshWithVertices(float fAmount)
{
Simplifier.CoroutineFrameMiliseconds = 20;
foreach (KeyValuePair<GameObject, Material[]> pair in m_objectMaterials)
{
MeshSimplify meshSimplify = pair.Key.GetComponent<MeshSimplify>();
MeshFilter meshFilter = pair.Key.GetComponent<MeshFilter>();
SkinnedMeshRenderer skin = pair.Key.GetComponent<SkinnedMeshRenderer>();
if(meshSimplify == null)
{
meshSimplify = pair.Key.AddComponent<MeshSimplify>();
meshSimplify.m_meshSimplifyRoot = m_selectedMeshSimplify;
m_selectedMeshSimplify.m_listDependentChildren.Add(meshSimplify);
}
if(meshSimplify.MeshSimplifier == null)
{
meshSimplify.MeshSimplifier = meshSimplify.gameObject.AddComponent<Simplifier>();
meshSimplify.MeshSimplifier.hideFlags = HideFlags.HideInInspector;
meshSimplify.ConfigureSimplifier();
}
if (meshSimplify && MeshSimplify.HasValidMeshData(pair.Key))
{
Mesh newMesh = null;
if (meshFilter != null)
{
newMesh = Mesh.Instantiate(meshFilter.sharedMesh);
}
else if (skin != null)
{
newMesh = Mesh.Instantiate(skin.sharedMesh);
}
if (meshSimplify.HasData() == false)
{
meshSimplify.GetMeshSimplifier().CoroutineEnded = false;
StartCoroutine(meshSimplify.GetMeshSimplifier().ProgressiveMesh(pair.Key, meshFilter != null ? meshFilter.sharedMesh : skin.sharedMesh, null, meshSimplify.name, Progress));
while (meshSimplify.GetMeshSimplifier().CoroutineEnded == false)
{
yield return null;
}
}
if (meshSimplify.GetMeshSimplifier() != null)
{
meshSimplify.GetMeshSimplifier().CoroutineEnded = false;
StartCoroutine(meshSimplify.GetMeshSimplifier().ComputeMeshWithVertexCount(pair.Key, newMesh, Mathf.RoundToInt(fAmount * meshSimplify.GetMeshSimplifier().GetOriginalMeshUniqueVertexCount()), meshSimplify.name, Progress));
while (meshSimplify.GetMeshSimplifier().CoroutineEnded == false)
{
yield return null;
}
if (meshFilter != null)
{
meshFilter.mesh = newMesh;
}
else if (skin != null)
{
skin.sharedMesh = newMesh;
}
meshSimplify.m_simplifiedMesh = newMesh;
}
}
}
m_bFinished = true;
}
private Dictionary<GameObject, Material[]> m_objectMaterials;
private MeshSimplify m_selectedMeshSimplify;
private bool m_bFinished = false;
private Mesh m_newMesh;
private int m_nLastProgress = -1;
private string m_strLastTitle = "";
private string m_strLastMessage = "";
}
fileFormatVersion: 2
guid: 10b6bfc6d6d946449bd6178082a26d5e
timeCreated: 1496044654
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace UltimateGameTools
{
namespace MeshSimplifier
{
[Serializable]
public class RelevanceSphere
{
public RelevanceSphere()
{
m_v3Scale = Vector3.one;
}
public void SetDefault(Transform target, float fRelevance)
{
m_bExpanded = true;
m_v3Position = target.position + Vector3.up;
m_v3Rotation = target.rotation.eulerAngles;
m_v3Scale = Vector3.one;
m_fRelevance = fRelevance;
}
public bool m_bExpanded;
public Vector3 m_v3Position;
public Vector3 m_v3Rotation;
public Vector3 m_v3Scale;
public float m_fRelevance;
}
public class Simplifier : MonoBehaviour
{
#region Types
/////////////////////////////////////////////////////////////////////////////////////////////////
// Types
/////////////////////////////////////////////////////////////////////////////////////////////////
public delegate void ProgressDelegate(string strTitle, string strProgressMessage, float fT);
public static bool Cancelled
{
get;
set;
}
public static int CoroutineFrameMiliseconds
{
get
{
return m_nCoroutineFrameMiliseconds;
}
set
{
m_nCoroutineFrameMiliseconds = value;
}
}
#endregion // Types
#region Properties
public bool CoroutineEnded
{
get;
set;
}
public bool UseEdgeLength
{
get
{
return m_bUseEdgeLength;
}
set
{
m_bUseEdgeLength = value;
}
}
public bool UseCurvature
{
get
{
return m_bUseCurvature;
}
set
{
m_bUseCurvature = value;
}
}
public bool ProtectTexture
{
get
{
return m_bProtectTexture;
}
set
{
m_bProtectTexture = value;
}
}
public bool LockBorder
{
get
{
return m_bLockBorder;
}
set
{
m_bLockBorder = value;
}
}
#endregion // Properties
#region Public methods
/////////////////////////////////////////////////////////////////////////////////////////////////
// Public methods
/////////////////////////////////////////////////////////////////////////////////////////////////
public IEnumerator ProgressiveMesh(GameObject gameObject, Mesh sourceMesh, RelevanceSphere[] aRelevanceSpheres, string strProgressDisplayObjectName = "", ProgressDelegate progress = null)
{
m_meshOriginal = sourceMesh;
Vector3[] aVerticesWorld = GetWorldVertices(gameObject);
if (aVerticesWorld == null)
{
CoroutineEnded = true;
yield break;
}
m_listVertexMap = new List<int>();
m_listVertexPermutationBack = new List<int>();
m_listVertices = new List<Vertex>();
m_aListTriangles = new TriangleList[m_meshOriginal.subMeshCount];
if (progress != null)
{
progress("Preprocessing mesh: " + strProgressDisplayObjectName, "Building unique vertex data", 1.0f);
if (Simplifier.Cancelled)
{
CoroutineEnded = true;
yield break;
}
}
m_meshUniqueVertices = new MeshUniqueVertices();
m_meshUniqueVertices.BuildData(m_meshOriginal, aVerticesWorld);
m_nOriginalMeshVertexCount = m_meshUniqueVertices.ListVertices.Count;
m_fOriginalMeshSize = Mathf.Max(m_meshOriginal.bounds.size.x, m_meshOriginal.bounds.size.y, m_meshOriginal.bounds.size.z);
m_listHeap = new List<Vertex>(m_meshUniqueVertices.ListVertices.Count);
for (int i = 0; i < m_meshUniqueVertices.ListVertices.Count; i++)
{
m_listVertexMap.Add(-1);
m_listVertexPermutationBack.Add(-1);
}
Vector2[] av2Mapping = m_meshOriginal.uv;
AddVertices(m_meshUniqueVertices.ListVertices, m_meshUniqueVertices.ListVerticesWorld, m_meshUniqueVertices.ListBoneWeights);
for (int nSubMesh = 0; nSubMesh < m_meshOriginal.subMeshCount; nSubMesh++)
{
int[] anIndices = m_meshOriginal.GetTriangles(nSubMesh);
m_aListTriangles[nSubMesh] = new TriangleList();
AddFaceListSubMesh(nSubMesh, m_meshUniqueVertices.SubmeshesFaceList[nSubMesh].m_listIndices, anIndices, av2Mapping);
}
if(Application.isEditor && !Application.isPlaying)
{
IEnumerator enumerator = ComputeAllEdgeCollapseCosts(strProgressDisplayObjectName, gameObject.transform, aRelevanceSpheres, progress);
while (enumerator.MoveNext())
{
if (Simplifier.Cancelled)
{
CoroutineEnded = true;
yield break;
}
}
}
else
{
yield return StartCoroutine(ComputeAllEdgeCollapseCosts(strProgressDisplayObjectName, gameObject.transform, aRelevanceSpheres, progress));
}
int nVertices = m_listVertices.Count;
Stopwatch sw = Stopwatch.StartNew();
while (m_listVertices.Count > 0)
{
if (progress != null && ((m_listVertices.Count & 0xFF) == 0))
{
progress("Preprocessing mesh: " + strProgressDisplayObjectName, "Collapsing edges", 1.0f - ((float)m_listVertices.Count / (float)nVertices));
if(Cancelled)
{
CoroutineEnded = true;
yield break;
}
}
if (sw.ElapsedMilliseconds > CoroutineFrameMiliseconds && CoroutineFrameMiliseconds > 0)
{
yield return null;
sw = Stopwatch.StartNew();
}
Vertex mn = MinimumCostEdge();
m_listVertexPermutationBack[m_listVertices.Count - 1] = mn.m_nID;
m_listVertexMap[mn.m_nID] = mn.m_collapse != null ? mn.m_collapse.m_nID : -1;
Collapse(mn, mn.m_collapse, true, gameObject.transform, aRelevanceSpheres);
}
UnityEngine.Profiling.Profiler.EndSample();
m_listHeap.Clear();
CoroutineEnded = true;
}
public IEnumerator ComputeMeshWithVertexCount(GameObject gameObject, Mesh meshOut, int nVertices, string strProgressDisplayObjectName = "", ProgressDelegate progress = null)
{
if (GetOriginalMeshUniqueVertexCount() == -1)
{
CoroutineEnded = true;
yield break;
}
if (nVertices < 3)
{
CoroutineEnded = true;
yield break;
}
if (nVertices >= GetOriginalMeshUniqueVertexCount())
{
// Original vertex count requested
meshOut.triangles = new int[0];
meshOut.subMeshCount = m_meshOriginal.subMeshCount;
meshOut.vertices = m_meshOriginal.vertices;
meshOut.normals = m_meshOriginal.normals;
meshOut.tangents = m_meshOriginal.tangents;
meshOut.uv = m_meshOriginal.uv;
meshOut.uv2 = m_meshOriginal.uv2;
meshOut.colors32 = m_meshOriginal.colors32;
meshOut.boneWeights = m_meshOriginal.boneWeights;
meshOut.bindposes = m_meshOriginal.bindposes;
meshOut.triangles = m_meshOriginal.triangles;
meshOut.subMeshCount = m_meshOriginal.subMeshCount;
for (int nSubMesh = 0; nSubMesh < m_meshOriginal.subMeshCount; nSubMesh++)
{
meshOut.SetTriangles(m_meshOriginal.GetTriangles(nSubMesh), nSubMesh);
}
meshOut.name = gameObject.name + " simplified mesh";
CoroutineEnded = true;
yield break;
}
m_listVertices = new List<Vertex>();
m_aListTriangles = new TriangleList[m_meshOriginal.subMeshCount];
List<Vertex> listVertices = new List<Vertex>();
AddVertices(m_meshUniqueVertices.ListVertices, m_meshUniqueVertices.ListVerticesWorld, m_meshUniqueVertices.ListBoneWeights);
for (int i = 0; i < m_listVertices.Count; i++)
{
m_listVertices[i].m_collapse = (m_listVertexMap[i] == -1) ? null : m_listVertices[m_listVertexMap[i]];
listVertices.Add(m_listVertices[m_listVertexPermutationBack[i]]);
}
Vector2[] av2Mapping = m_meshOriginal.uv;
for (int nSubMesh = 0; nSubMesh < m_meshOriginal.subMeshCount; nSubMesh++)
{
int[] anIndices = m_meshOriginal.GetTriangles(nSubMesh);
m_aListTriangles[nSubMesh] = new TriangleList();
AddFaceListSubMesh(nSubMesh, m_meshUniqueVertices.SubmeshesFaceList[nSubMesh].m_listIndices, anIndices, av2Mapping);
}
int nTotalVertices = listVertices.Count;
Stopwatch sw = Stopwatch.StartNew();
while (listVertices.Count > nVertices)
{
if (progress != null)
{
float fT = 1.0f;
if (nTotalVertices != nVertices && ((listVertices.Count & 0xFF) == 0))
{
fT = 1.0f - ((float)(listVertices.Count - nVertices) / (float)(nTotalVertices - nVertices));
progress("Simplifying mesh: " + strProgressDisplayObjectName, "Collapsing edges", fT);
if (Cancelled)
{
CoroutineEnded = true;
yield break;
}
}
}
Vertex mn = listVertices[listVertices.Count - 1];
listVertices.RemoveAt(listVertices.Count - 1);
Collapse(mn, mn.m_collapse, false, null, null);
if (sw.ElapsedMilliseconds > CoroutineFrameMiliseconds && CoroutineFrameMiliseconds > 0)
{
yield return null;
sw = Stopwatch.StartNew();
}
}
Vector3[] av3Vertices = new Vector3[m_listVertices.Count];
for (int i = 0; i < m_listVertices.Count; i++)
{
m_listVertices[i].m_nID = i; // reassign id's
av3Vertices[i] = m_listVertices[i].m_v3Position;
}
if (Application.isEditor && !Application.isPlaying)
{
IEnumerator enumerator = ConsolidateMesh(gameObject, m_meshOriginal, meshOut, m_aListTriangles, av3Vertices, strProgressDisplayObjectName, progress);
while (enumerator.MoveNext())
{
if (Simplifier.Cancelled)
{
CoroutineEnded = true;
yield break;
}
}
}
else
{
yield return StartCoroutine(ConsolidateMesh(gameObject, m_meshOriginal, meshOut, m_aListTriangles, av3Vertices, strProgressDisplayObjectName, progress));
}
CoroutineEnded = true;
}
public int GetOriginalMeshUniqueVertexCount()
{
return m_nOriginalMeshVertexCount;
}
public int GetOriginalMeshTriangleCount()
{
return m_meshOriginal.triangles.Length / 3;
}
public static Vector3[] GetWorldVertices(GameObject gameObject)
{
Vector3[] aVertices = null;
SkinnedMeshRenderer skin = gameObject.GetComponent<SkinnedMeshRenderer>();
MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
if (skin != null)
{
if (skin.sharedMesh == null)
{
return null;
}
aVertices = skin.sharedMesh.vertices;
BoneWeight[] aBoneWeights = skin.sharedMesh.boneWeights;
Matrix4x4[] aBindPoses = skin.sharedMesh.bindposes;
Transform[] aBones = skin.bones;
if (aVertices == null || aBoneWeights == null || aBindPoses == null || aBones == null)
{
return null;
}
if(aBoneWeights.Length == 0 || aBindPoses.Length == 0 || aBones.Length == 0)
{
return null;
}
for (int nVertex = 0; nVertex < aVertices.Length; nVertex++)
{
BoneWeight bw = aBoneWeights[nVertex];
Vector3 v3World = Vector3.zero;
Vector3 v3LocalVertex;
if (Math.Abs(bw.weight0) > 0.00001f)
{
v3LocalVertex = aBindPoses[bw.boneIndex0].MultiplyPoint3x4(aVertices[nVertex]);
v3World += aBones[bw.boneIndex0].transform.localToWorldMatrix.MultiplyPoint3x4(v3LocalVertex) * bw.weight0;
}
if (Math.Abs(bw.weight1) > 0.00001f)
{
v3LocalVertex = aBindPoses[bw.boneIndex1].MultiplyPoint3x4(aVertices[nVertex]);
v3World += aBones[bw.boneIndex1].transform.localToWorldMatrix.MultiplyPoint3x4(v3LocalVertex) * bw.weight1;
}
if (Math.Abs(bw.weight2) > 0.00001f)
{
v3LocalVertex = aBindPoses[bw.boneIndex2].MultiplyPoint3x4(aVertices[nVertex]);
v3World += aBones[bw.boneIndex2].transform.localToWorldMatrix.MultiplyPoint3x4(v3LocalVertex) * bw.weight2;
}
if (Math.Abs(bw.weight3) > 0.00001f)
{
v3LocalVertex = aBindPoses[bw.boneIndex3].MultiplyPoint3x4(aVertices[nVertex]);
v3World += aBones[bw.boneIndex3].transform.localToWorldMatrix.MultiplyPoint3x4(v3LocalVertex) * bw.weight3;
}
aVertices[nVertex] = v3World;
}
}
else if(meshFilter != null)
{
if (meshFilter.sharedMesh == null)
{
return null;
}
aVertices = meshFilter.sharedMesh.vertices;
if (aVertices == null)
{
return null;
}
for (int nVertex = 0; nVertex < aVertices.Length; nVertex++)
{
aVertices[nVertex] = gameObject.transform.TransformPoint(aVertices[nVertex]);
}
}
return aVertices;
}
#endregion // Public methods
#region Private methods
/////////////////////////////////////////////////////////////////////////////////////////////////
// Private methods
/////////////////////////////////////////////////////////////////////////////////////////////////
IEnumerator ConsolidateMesh(GameObject gameObject, Mesh meshIn, Mesh meshOut, TriangleList[] aListTriangles, Vector3[] av3Vertices, string strProgressDisplayObjectName = "", ProgressDelegate progress = null)
{
Vector3[] av3NormalsIn = meshIn.normals;
Vector4[] av4TangentsIn = meshIn.tangents;
Vector2[] av2Mapping1In = meshIn.uv;
Vector2[] av2Mapping2In = meshIn.uv2;
Color[] acolColorsIn = meshIn.colors;
Color32[] aColors32In = meshIn.colors32;
List<List<int>> listlistIndicesOut = new List<List<int>>();
List<Vector3> listVerticesOut = new List<Vector3>();
List<Vector3> listNormalsOut = new List<Vector3>();
List<Vector4> listTangentsOut = new List<Vector4>();
List<Vector2> listMapping1Out = new List<Vector2>();
List<Vector2> listMapping2Out = new List<Vector2>();
List<Color32> listColors32Out = new List<Color32>();
List<BoneWeight> listBoneWeightsOut = new List<BoneWeight>();
Dictionary<VertexDataHash, int> dicVertexDataHash2Index = new Dictionary<VertexDataHash, int>(new VertexDataHashComparer());
bool bUV1 = av2Mapping1In != null && av2Mapping1In.Length > 0;
bool bUV2 = av2Mapping2In != null && av2Mapping2In.Length > 0;
bool bNormal = av3NormalsIn != null && av3NormalsIn.Length > 0;
bool bTangent = av4TangentsIn != null && av4TangentsIn.Length > 0;
Stopwatch sw = Stopwatch.StartNew();
for(int nSubMesh = 0; nSubMesh < aListTriangles.Length; nSubMesh++)
{
List<int> listIndicesOut = new List<int>();
string strMesh = aListTriangles.Length > 1 ? ("Consolidating submesh " + (nSubMesh + 1)) : "Consolidating mesh";
for (int i = 0; i < aListTriangles[nSubMesh].m_listTriangles.Count; i++)
{
if (progress != null && ((i & 0xFF) == 0))
{
float fT = aListTriangles[nSubMesh].m_listTriangles.Count == 1 ? 1.0f : ((float)i / (float)(aListTriangles[nSubMesh].m_listTriangles.Count - 1));
progress("Simplifying mesh: " + strProgressDisplayObjectName, strMesh, fT);
if (Cancelled)
{
yield break;
}
}
if (sw.ElapsedMilliseconds > CoroutineFrameMiliseconds && CoroutineFrameMiliseconds > 0)
{
yield return null;
sw = Stopwatch.StartNew();
}
for (int v = 0; v < 3; v++)
{
int nMappingIndex = aListTriangles[nSubMesh].m_listTriangles[i].IndicesUV[v];
int nVertexIndex = aListTriangles[nSubMesh].m_listTriangles[i].Indices[v];
bool bColor = false;
Vector3 v3Vertex = aListTriangles[nSubMesh].m_listTriangles[i].Vertices[v].m_v3Position;
Vector3 v3Normal = bNormal ? av3NormalsIn [nVertexIndex] : Vector3.zero;
Vector4 v4Tangent = bTangent ? av4TangentsIn[nVertexIndex] : Vector4.zero;
Vector2 uv1 = bUV1 ? av2Mapping1In[nMappingIndex] : Vector2.zero;
Vector2 uv2 = bUV2 ? av2Mapping2In[nVertexIndex] : Vector2.zero;
Color32 color32 = new Color32(0, 0, 0, 0);
if(acolColorsIn != null && acolColorsIn.Length > 0)
{
color32 = acolColorsIn[nVertexIndex];
bColor = true;
}
else if(aColors32In != null && aColors32In.Length > 0)
{
color32 = aColors32In[nVertexIndex];
bColor = true;
}
VertexDataHash vdata = new VertexDataHash(v3Vertex, v3Normal, uv1, uv2, color32);
if (dicVertexDataHash2Index.ContainsKey(vdata))
{
// Already exists -> Index
listIndicesOut.Add(dicVertexDataHash2Index[vdata]);
}
else
{
// Does not exist -> Create + index
dicVertexDataHash2Index.Add(vdata, listVerticesOut.Count);
listVerticesOut.Add(vdata.Vertex);
if (bNormal) listNormalsOut.Add(v3Normal);
if (bUV1) listMapping1Out.Add(uv1);
if (bUV2) listMapping2Out.Add(uv2);
if (bTangent) listTangentsOut.Add(v4Tangent);
if (bColor) listColors32Out.Add(color32);
if (aListTriangles[nSubMesh].m_listTriangles[i].Vertices[v].m_bHasBoneWeight)
{
listBoneWeightsOut.Add(aListTriangles[nSubMesh].m_listTriangles[i].Vertices[v].m_boneWeight);
}
listIndicesOut.Add(listVerticesOut.Count - 1);
}
}
}
listlistIndicesOut.Add(listIndicesOut);
}
meshOut.triangles = new int[0];
meshOut.vertices = listVerticesOut.ToArray();
meshOut.normals = listNormalsOut.Count > 0 ? listNormalsOut.ToArray() : null;
meshOut.tangents = listTangentsOut.Count > 0 ? listTangentsOut.ToArray() : null;
meshOut.uv = listMapping1Out.Count > 0 ? listMapping1Out.ToArray() : null;
meshOut.uv2 = listMapping2Out.Count > 0 ? listMapping2Out.ToArray() : null;
meshOut.colors32 = listColors32Out.Count > 0 ? listColors32Out.ToArray() : null;
meshOut.boneWeights = listBoneWeightsOut.Count > 0 ? listBoneWeightsOut.ToArray() : null;
meshOut.bindposes = meshIn.bindposes;
meshOut.subMeshCount = listlistIndicesOut.Count;
for (int nSubMesh = 0; nSubMesh < listlistIndicesOut.Count; nSubMesh++)
{
meshOut.SetTriangles(listlistIndicesOut[nSubMesh].ToArray(), nSubMesh);
}
meshOut.name = gameObject.name + " simplified mesh";
;
progress("Simplifying mesh: " + strProgressDisplayObjectName, "Mesh consolidation done", 1.0f);
}
int MapVertex(int nVertex, int nMax)
{
if (nMax <= 0)
{
return 0;
}
while (nVertex >= nMax)
{
nVertex = m_listVertexMap[nVertex];
}
return nVertex;
}
float ComputeEdgeCollapseCost(Vertex u, Vertex v, float fRelevanceBias)
{
bool bUseEdgeLength = m_bUseEdgeLength;
bool bUseCurvature = m_bUseCurvature;
bool bProtectTexture = m_bProtectTexture;
bool bLockBorder = m_bLockBorder;
int i;
float fEdgeLength = bUseEdgeLength ? (Vector3.Magnitude(v.m_v3Position - u.m_v3Position) / m_fOriginalMeshSize) : 1.0f;
float fCurvature = 0.001f;
List<Triangle> sides = new List<Triangle>();
for (i = 0; i < u.m_listFaces.Count; i++)
{
if (u.m_listFaces[i].HasVertex(v))
{
sides.Add(u.m_listFaces[i]);
}
}
if(bUseCurvature)
{
for (i = 0; i < u.m_listFaces.Count; i++)
{
float fMinCurv = 1.0f;
for (int j = 0; j < sides.Count; j++)
{
float dotprod = Vector3.Dot(u.m_listFaces[i].Normal, sides[j].Normal);
fMinCurv = Mathf.Min(fMinCurv, (1.0f - dotprod) / 2.0f);
}
fCurvature = Mathf.Max(fCurvature, fMinCurv);
}
}
if (u.IsBorder() && sides.Count > 1)
{
fCurvature = 1.0f;
}
if (bProtectTexture)
{
bool bNoMatch = true;
for (i = 0; i < u.m_listFaces.Count; i++)
{
for (int j = 0; j < sides.Count; j++)
{
if (u.m_listFaces[i].HasUVData == false)
{
bNoMatch = false;
break;
}
if (u.m_listFaces[i].TexAt(u) == sides[j].TexAt(u))
{
bNoMatch = false;
}
}
}
if (bNoMatch)
{
fCurvature = 1.0f;
}
}
if (bLockBorder && u.IsBorder())
{
fCurvature = MAX_VERTEX_COLLAPSE_COST;
}
fCurvature += fRelevanceBias;
return fEdgeLength * fCurvature;
}
void ComputeEdgeCostAtVertex(Vertex v, Transform transform, RelevanceSphere[] aRelevanceSpheres)
{
if (v.m_listNeighbors.Count == 0)
{
v.m_collapse = null;
v.m_fObjDist = -0.01f;
return;
}
v.m_fObjDist = MAX_VERTEX_COLLAPSE_COST;
v.m_collapse = null;
float fRelevanceBias = 0.0f;
if (aRelevanceSpheres != null)
{
for (int nSphere = 0; nSphere < aRelevanceSpheres.Length; nSphere++)
{
Matrix4x4 mtxSphere = Matrix4x4.TRS(aRelevanceSpheres[nSphere].m_v3Position, Quaternion.Euler(aRelevanceSpheres[nSphere].m_v3Rotation), aRelevanceSpheres[nSphere].m_v3Scale);
Vector3 v3World = v.m_v3PositionWorld;
Vector3 v3Local = mtxSphere.inverse.MultiplyPoint(v3World);
if (v3Local.magnitude <= 0.5f)
{
// Inside
fRelevanceBias = aRelevanceSpheres[nSphere].m_fRelevance;
}
}
}
for (int i = 0; i < v.m_listNeighbors.Count; i++)
{
float dist = ComputeEdgeCollapseCost(v, v.m_listNeighbors[i], fRelevanceBias);
if (v.m_collapse == null || dist < v.m_fObjDist)
{
v.m_collapse = v.m_listNeighbors[i];
v.m_fObjDist = dist;
}
}
}
IEnumerator ComputeAllEdgeCollapseCosts(string strProgressDisplayObjectName, Transform transform, RelevanceSphere[] aRelevanceSpheres, ProgressDelegate progress = null)
{
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < m_listVertices.Count; i++)
{
if (progress != null && ((i & 0xFF) == 0))
{
progress("Preprocessing mesh: " + strProgressDisplayObjectName, "Computing edge collapse cost", m_listVertices.Count == 1 ? 1.0f : ((float)i / (m_listVertices.Count - 1.0f)));
if(Cancelled)
{
yield break;
}
}
if (sw.ElapsedMilliseconds > CoroutineFrameMiliseconds && CoroutineFrameMiliseconds > 0)
{
yield return null;
sw = Stopwatch.StartNew();
}
ComputeEdgeCostAtVertex(m_listVertices[i], transform, aRelevanceSpheres);
HeapAdd(m_listVertices[i]);
}
}
void Collapse(Vertex u, Vertex v, bool bRecompute, Transform transform, RelevanceSphere[] aRelevanceSpheres)
{
if (v == null)
{
u.Destructor(this);
return;
}
int i;
List<Vertex> tmp = new List<Vertex>();
for (i = 0; i < u.m_listNeighbors.Count; i++)
{
tmp.Add(u.m_listNeighbors[i]);
}
List<Triangle> sides = new List<Triangle>();
for (i = 0; i < u.m_listFaces.Count; i++)
{
if (u.m_listFaces[i].HasVertex(v))
{
sides.Add(u.m_listFaces[i]);
}
}
// update texture mapping
for (i = 0; i < u.m_listFaces.Count; i++)
{
int j;
if (u.m_listFaces[i].HasVertex(v))
{
continue;
}
if (u.m_listFaces[i].HasUVData)
{
for (j = 0; j < sides.Count; j++)
{
if (u.m_listFaces[i].TexAt(u) == sides[j].TexAt(u))
{
u.m_listFaces[i].SetTexAt(u, sides[j].TexAt(v));
break; // only change tex coords once!
}
}
}
// Added support for color or 2nd uv here:
/*
for (j = 0; j < sides.Count; j++)
{
if (u.m_listFaces[i].VertexColorAt(u) == sides[j].VertexColorAt(u))
{
u.m_listFaces[i].SetVertexColorAt(u, sides[j].VertexColorAt(v));
break; // only change tex coords once!
}
}
*/
}
// Delete triangles on edge uv
for (i = u.m_listFaces.Count - 1; i >= 0; i--)
{
if(i < u.m_listFaces.Count && i >= 0 && u.m_listFaces[i].HasVertex(v))
{
u.m_listFaces[i].Destructor(this);
}
}
// Update remaining triangles to have v instead of u
for (i = u.m_listFaces.Count - 1; i >= 0; i--)
{
u.m_listFaces[i].ReplaceVertex(u, v);
}
u.Destructor(this);
// Recompute the edge collapse costs for neighboring vertices
if (bRecompute)
{
for (i = 0; i < tmp.Count; i++)
{
ComputeEdgeCostAtVertex(tmp[i], transform, aRelevanceSpheres);
HeapSortUp(tmp[i].m_nHeapSpot);
HeapSortDown(tmp[i].m_nHeapSpot);
}
}
}
void AddVertices(List<Vector3> listVertices, List<Vector3> listVerticesWorld, List<MeshUniqueVertices.SerializableBoneWeight> listBoneWeights)
{
bool bHasBoneWeights = listBoneWeights != null && listBoneWeights.Count > 0;
for (int i = 0; i < listVertices.Count; i++)
{
new Vertex(this, listVertices[i], listVerticesWorld[i], bHasBoneWeights, bHasBoneWeights ? listBoneWeights[i].ToBoneWeight() : new BoneWeight(), i);
}
}
void AddFaceListSubMesh(int nSubMesh, List<int> listTriangles, int[] anIndices, Vector2[] v2Mapping)
{
bool bUVData = false;
if (v2Mapping != null)
{
if (v2Mapping.Length > 0)
{
bUVData = true;
}
}
for (int i = 0; i < listTriangles.Count / 3; i++)
{
Triangle tri = new Triangle(this, nSubMesh,
m_listVertices[listTriangles[i * 3]], m_listVertices[listTriangles[i * 3 + 1]], m_listVertices[listTriangles[i * 3 + 2]],
bUVData, anIndices[i * 3], anIndices[i * 3 + 1], anIndices[i * 3 + 2]);
ShareUV(v2Mapping, tri);
}
}
void ShareUV(Vector2[] aMapping, Triangle t)
{
if (t.HasUVData == false)
{
return;
}
// It so happens that neighboring faces that share vertices
// sometimes share uv coordinates at those verts but have
// their own entries in the tex vert list
if (aMapping == null || aMapping.Length == 0)
{
return;
}
for (int i = 0; i < 3; i++)
{
int nCurrentVert = i;
for (int j = 0; j < t.Vertices[nCurrentVert].m_listFaces.Count; j++)
{
Triangle n = t.Vertices[nCurrentVert].m_listFaces[j];
if (t == n)
{
continue;
}
int tx1 = t.TexAt(t.Vertices[nCurrentVert]);
int tx2 = n.TexAt(t.Vertices[nCurrentVert]);
if (tx1 == tx2)
{
continue;
}
Vector2 uv1 = aMapping[tx1];
Vector2 uv2 = aMapping[tx2];
if (uv1 == uv2)
{
t.SetTexAt(t.Vertices[nCurrentVert], tx2);
}
}
}
}
Vertex MinimumCostEdge()
{
// Find the edge that when collapsed will affect model the least.
// This funtion actually returns a Vertex, the second vertex
// of the edge (collapse candidate) is stored in the vertex data.
Vertex hp = HeapPop();
return hp;
}
float HeapValue(int i)
{
if (i < 0 || i >= m_listHeap.Count)
{
return 9999999999999.9f;
}
if (m_listHeap [i] == null)
{
return 9999999999999.9f;
}
return m_listHeap[i].m_fObjDist;
}
void HeapSortUp(int k)
{
int k2;
while (HeapValue(k) < HeapValue((k2 = (k - 1) / 2)))
{
Vertex tmp = m_listHeap[k];
m_listHeap[k] = m_listHeap[k2];
m_listHeap[k].m_nHeapSpot = k;
m_listHeap[k2] = tmp;
m_listHeap[k2].m_nHeapSpot = k2;
k = k2;
}
}
void HeapSortDown(int k)
{
int k2;
if (k == -1)
{
return;
}
while (HeapValue(k) > HeapValue((k2 = (k + 1) * 2)) || HeapValue(k) > HeapValue((k2 - 1)))
{
k2 = (HeapValue(k2) < HeapValue(k2 - 1)) ? k2 : k2 - 1;
Vertex tmp = m_listHeap[k];
m_listHeap[k] = m_listHeap[k2];
m_listHeap[k].m_nHeapSpot = k;
m_listHeap[k2] = tmp;
if (tmp != null) m_listHeap[k2].m_nHeapSpot = k2;
k = k2;
}
}
void HeapAdd(Vertex v)
{
int k = m_listHeap.Count;
m_listHeap.Add(v);
v.m_nHeapSpot = k;
HeapSortUp(k);
}
Vertex HeapPop()
{
Vertex rv = m_listHeap[0];
rv.m_nHeapSpot = -1;
m_listHeap[0] = null;
HeapSortDown(0);
return rv;
}
#endregion Private methods
#region Private types
/////////////////////////////////////////////////////////////////////////////////////////////////
// Private types
/////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Stores vertex and mapping information.
/// </summary>
private class Triangle
{
public Vertex[] Vertices
{
get
{
return m_aVertices;
}
}
public bool HasUVData
{
get
{
return m_bUVData;
}
}
public int[] IndicesUV
{
get
{
return m_aUV;
}
}
public Vector3 Normal
{
get
{
return m_v3Normal;
}
}
public int[] Indices
{
get
{
return m_aIndices;
}
}
private Vertex[] m_aVertices;
private bool m_bUVData;
private int[] m_aUV;
private int[] m_aIndices;
private Vector3 m_v3Normal;
private int m_nSubMesh;
public Triangle(Simplifier simplifier, int nSubMesh, Vertex v0, Vertex v1, Vertex v2, bool bUVData, int nIndex1, int nIndex2, int nIndex3)
{
m_aVertices = new Vertex[3];
m_aUV = new int[3];
m_aIndices = new int[3];
m_aVertices[0] = v0;
m_aVertices[1] = v1;
m_aVertices[2] = v2;
m_nSubMesh = nSubMesh;
m_bUVData = bUVData;
if (m_bUVData)
{
m_aUV[0] = nIndex1;
m_aUV[1] = nIndex2;
m_aUV[2] = nIndex3;
}
m_aIndices[0] = nIndex1;
m_aIndices[1] = nIndex2;
m_aIndices[2] = nIndex3;
ComputeNormal();
simplifier.m_aListTriangles[nSubMesh].m_listTriangles.Add(this);
for (int i = 0; i < 3; i++)
{
m_aVertices[i].m_listFaces.Add(this);
for (int j = 0; j < 3; j++)
{
if (i != j)
{
if (m_aVertices[i].m_listNeighbors.Contains(m_aVertices[j]) == false)
{
m_aVertices[i].m_listNeighbors.Add(m_aVertices[j]);
}
}
}
}
}
public void Destructor(Simplifier simplifier)
{
int i;
simplifier.m_aListTriangles[m_nSubMesh].m_listTriangles.Remove(this);
for (i = 0; i < 3; i++)
{
if (m_aVertices[i] != null)
{
m_aVertices[i].m_listFaces.Remove(this);
}
}
for (i = 0; i < 3; i++)
{
int i2 = (i + 1) % 3;
if (m_aVertices[i] == null || m_aVertices[i2] == null) continue;
m_aVertices[i].RemoveIfNonNeighbor(m_aVertices[i2]);
m_aVertices[i2].RemoveIfNonNeighbor(m_aVertices[i]);
}
}
public bool HasVertex(Vertex v)
{
return (v == m_aVertices[0] || v == m_aVertices[1] || v == m_aVertices[2]);
}
public void ComputeNormal()
{
Vector3 v0 = m_aVertices[0].m_v3Position;
Vector3 v1 = m_aVertices[1].m_v3Position;
Vector3 v2 = m_aVertices[2].m_v3Position;
m_v3Normal = Vector3.Cross((v1 - v0), (v2 - v1));
if (m_v3Normal.magnitude == 0.0f) return;
m_v3Normal = m_v3Normal.normalized;
}
public int TexAt(Vertex vertex)
{
for (int i = 0; i < 3; i++)
{
if (m_aVertices[i] == vertex)
{
return m_aUV[i];
}
}
UnityEngine.Debug.LogError("TexAt(): Vertex not found");
return 0;
}
public int TexAt(int i)
{
return m_aUV[i];
}
public void SetTexAt(Vertex vertex, int uv)
{
for (int i = 0; i < 3; i++)
{
if (m_aVertices[i] == vertex)
{
m_aUV[i] = uv;
return;
}
}
UnityEngine.Debug.LogError("SetTexAt(): Vertex not found");
}
public void SetTexAt(int i, int uv)
{
m_aUV[i] = uv;
}
public void ReplaceVertex(Vertex vold, Vertex vnew)
{
if (vold == m_aVertices[0])
{
m_aVertices[0] = vnew;
}
else if (vold == m_aVertices[1])
{
m_aVertices[1] = vnew;
}
else
{
m_aVertices[2] = vnew;
}
int i;
vold.m_listFaces.Remove(this);
vnew.m_listFaces.Add(this);
for (i = 0; i < 3; i++)
{
vold.RemoveIfNonNeighbor(m_aVertices[i]);
m_aVertices[i].RemoveIfNonNeighbor(vold);
}
for (i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (i != j)
{
if (m_aVertices[i].m_listNeighbors.Contains(m_aVertices[j]) == false)
{
m_aVertices[i].m_listNeighbors.Add(m_aVertices[j]);
}
}
}
}
ComputeNormal();
}
};
/// <summary>
/// A list of triangles. We encapsulate this as a class to be able to serialize a List of TriangleLists if we need to.
/// Unity doesn't serialize a list of lists or an array of lists.
/// </summary>
private class TriangleList
{
public TriangleList()
{
m_listTriangles = new List<Triangle>();
}
public List<Triangle> m_listTriangles;
}
/// <summary>
/// Stores topology information and edge collapsing information.
/// </summary>
private class Vertex
{
public Vector3 m_v3Position;
public Vector3 m_v3PositionWorld;
public bool m_bHasBoneWeight;
public BoneWeight m_boneWeight;
public int m_nID; // Place of vertex in original list
public List<Vertex> m_listNeighbors; // Adjacent vertices
public List<Triangle> m_listFaces; // Adjacent triangles
public float m_fObjDist; // Cached cost of collapsing edge
public Vertex m_collapse; // Candidate vertex for collapse
public int m_nHeapSpot; // Heap spot, for optimization purposes.
public Vertex(Simplifier simplifier, Vector3 v, Vector3 v3World, bool bHasBoneWeight, BoneWeight boneWeight, int nID)
{
m_v3Position = v;
m_v3PositionWorld = v3World;
m_bHasBoneWeight = bHasBoneWeight;
m_boneWeight = boneWeight;
this.m_nID = nID;
m_listNeighbors = new List<Vertex>();
m_listFaces = new List<Triangle>();
simplifier.m_listVertices.Add(this);
}
public void Destructor(Simplifier simplifier)
{
while (m_listNeighbors.Count > 0)
{
m_listNeighbors[0].m_listNeighbors.Remove(this);
if (m_listNeighbors.Count > 0)
{
m_listNeighbors.RemoveAt(0);
}
}
simplifier.m_listVertices.Remove(this);
}
public void RemoveIfNonNeighbor(Vertex n)
{
if (!m_listNeighbors.Contains(n))
{
return;
}
for (int i = 0; i < m_listFaces.Count; i++)
{
if (m_listFaces[i].HasVertex(n))
{
return;
}
}
m_listNeighbors.Remove(n);
}
public bool IsBorder()
{
int i, j;
for (i = 0; i < m_listNeighbors.Count; i++)
{
int nCount = 0;
for (j = 0; j < m_listFaces.Count; j++)
{
if (m_listFaces[j].HasVertex(m_listNeighbors[i]))
{
nCount++;
}
}
if (nCount == 1)
{
return true;
}
}
return false;
}
};
class VertexDataHashComparer : IEqualityComparer<VertexDataHash>
{
public bool Equals(VertexDataHash a, VertexDataHash b)
{
return ((a.UV1 == b.UV1) && (a.UV2 == b.UV2) && (a.Vertex == b.Vertex) && //(a.Normal == b.Normal) &&
(a.Color.r == b.Color.r) && (a.Color.g == b.Color.g) && (a.Color.b == b.Color.b) && (a.Color.a == b.Color.a));
}
public int GetHashCode(VertexDataHash vdata)
{
return vdata.GetHashCode();
}
}
/// <summary>
/// Stores vertex data information. Also allows to compare two different objects of this type to
/// know when two vertices share or not the same data.
/// </summary>
class VertexDataHash
{
public Vector3 Vertex
{
get
{
return _v3Vertex;
}
}
public Vector3 Normal
{
get
{
return _v3Normal;
}
}
public Vector2 UV1
{
get
{
return _v2Mapping1;
}
}
public Vector2 UV2
{
get
{
return _v2Mapping2;
}
}
public Color32 Color
{
get
{
return _color;
}
}
public VertexDataHash(Vector3 v3Vertex, Vector3 v3Normal, Vector2 v2Mapping1, Vector2 v2Mapping2, Color32 color)
{
_v3Vertex = v3Vertex;
_v3Normal = v3Normal;
_v2Mapping1 = v2Mapping1;
_v2Mapping2 = v2Mapping2;
_color = color;
_uniqueVertex = new MeshUniqueVertices.UniqueVertex(v3Vertex);
//_uniqueNormal = new MeshUniqueVertices.UniqueVertex(v3Normal);
}
public override bool Equals(object obj)
{
VertexDataHash v = obj as VertexDataHash;
return ((v._v2Mapping1 == _v2Mapping1) && (v._v2Mapping2 == _v2Mapping2) && (v._v3Vertex == _v3Vertex) && //&& (v._v3Normal == _v3Normal) &&
(v._color.r == _color.r) && (v._color.g == _color.g) && (v._color.b == _color.b) && (v._color.a == _color.a));
}
public override int GetHashCode()
{
return _uniqueVertex.GetHashCode();// +_uniqueNormal.GetHashCode();
}
// Public static
public static bool operator ==(VertexDataHash a, VertexDataHash b)
{
return a.Equals(b);
}
public static bool operator !=(VertexDataHash a, VertexDataHash b)
{
return !a.Equals(b);
}
private Vector3 _v3Vertex;
private Vector3 _v3Normal;
private Vector2 _v2Mapping1;
private Vector2 _v2Mapping2;
private Color32 _color;
private MeshUniqueVertices.UniqueVertex _uniqueVertex;
//private MeshUniqueVertices.UniqueVertex _uniqueNormal;
}
#endregion // Private types
#region Private vars
/////////////////////////////////////////////////////////////////////////////////////////////////
// Private vars
/////////////////////////////////////////////////////////////////////////////////////////////////
private static int m_nCoroutineFrameMiliseconds = 0;
private const float MAX_VERTEX_COLLAPSE_COST = 10000000.0f;
private List<Vertex> m_listVertices;
private List<Vertex> m_listHeap;
private TriangleList[] m_aListTriangles;
[SerializeField, HideInInspector]
private int m_nOriginalMeshVertexCount = -1;
[SerializeField, HideInInspector]
private float m_fOriginalMeshSize = 1.0f;
[SerializeField, HideInInspector]
private List<int> m_listVertexMap;
[SerializeField, HideInInspector]
private List<int> m_listVertexPermutationBack;
[SerializeField, HideInInspector]
private MeshUniqueVertices m_meshUniqueVertices;
[SerializeField, HideInInspector]
private Mesh m_meshOriginal;
[SerializeField, HideInInspector]
bool m_bUseEdgeLength = true, m_bUseCurvature = true, m_bProtectTexture = true, m_bLockBorder = true;
#endregion // Private vars
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 90d922b6196aab34697b544f28239ee2
timeCreated: 1434653219
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment