Files
Cielonos/Assets/OtherPlugins/AutoLOD/Scripts/Editor/AutoLOD.cs
SoulliesOfficial f7af60351b 阶段性完成
2025-12-08 05:27:53 -05:00

936 lines
43 KiB
C#
Raw Blame History

using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
namespace AutoLOD.MeshDecimator
{
using Utilities;
public class AutoLODWindow : EditorWindow
{
private enum RendererType
{
Default,
Skinned,
Unhandled,
Unknown
}
public AutoLODProperties commonSettings;
public List<AutoLODProperties> targets;
string currentObjectName;
private Vector2 scrollPosition;
private bool showPreview;
PreviewRenderUtility previewRender;
float previewFov = 30f;
private Quaternion preview3dRotation;
private Quaternion previewLight3dRotation;
private GameObject previewObj;
private GameObject previewObjWireframe;
private float previewReductionRate = 2f;
private int previewIndex = -1;
private int previewSourceFaceCount;
private int previewTargetFaceCount;
private int previewActualFaceCount;
private Material wireframeMaterial;
private Vector3 previewPivotOffset = Vector3.zero;
private bool wireframeOnly = false;
private float wireframeOpacity = 0.5f;
private Color wireframeColor = Color.black;
private IMeshDecimator previewMeshDecimator;
private MeshDecimatorBackend previewBackend = MeshDecimatorBackend.Fast;
[MenuItem("Window/AutoLOD/MeshDecimator Documentation")]
public static void ShowOnlineHelp()
{
Help.BrowseURL("https://leochaumartin.com/wiki/index.php/AutoLOD");
}
[MenuItem("Window/AutoLOD/MeshDecimator")]
public static void ShowWindow()
{
EditorWindow win = EditorWindow.GetWindow(typeof(AutoLODWindow));
win.titleContent = new GUIContent("AutoLOD", Resources.Load<Texture>("Icon_AutoLOD"));
win.minSize = new Vector2(284, 380);
}
[MenuItem("CONTEXT/MeshRenderer/AutoLOD...")]
[MenuItem("CONTEXT/SkinnedMeshRenderer/AutoLOD...")]
public static void ContextMenu(MenuCommand command)
{
Renderer rend = (Renderer)command.context;
AutoLODWindow win = EditorWindow.GetWindow(typeof(AutoLODWindow)) as AutoLODWindow;
win.titleContent = new GUIContent("AutoLOD", Resources.Load<Texture>("Icon_AutoLOD"));
win.minSize = new Vector2(284, 380);
AutoLODProperties newObject = CreateInstance<AutoLODProperties>();
newObject._target = rend;
if (win.targets == null)
win.targets = new List<AutoLODProperties>();
win.targets.Add(newObject);
}
public void OnEnable()
{
previewRender = new PreviewRenderUtility(true);
previewRender.camera.fieldOfView = previewFov;
previewRender.camera.nearClipPlane = 0.001f;
previewRender.camera.farClipPlane = 10000f;
previewRender.camera.transform.LookAt(Vector3.zero);
previewRender.camera.clearFlags = CameraClearFlags.SolidColor;
previewRender.camera.backgroundColor = new Color(0.19f, 0.19f, 0.19f);
if (GraphicsSettings.currentRenderPipeline != null && GraphicsSettings.currentRenderPipeline.name.Contains("HD"))
{
wireframeOnly = true;
}
previewObj = new GameObject();
previewObj.hideFlags = HideFlags.HideAndDontSave;
previewObj.AddComponent<MeshFilter>();
previewObj.AddComponent<MeshRenderer>();
previewObj.transform.position = Vector3.zero;
previewObj.transform.rotation = Quaternion.identity;
previewObjWireframe = new GameObject();
previewObjWireframe.hideFlags = HideFlags.HideAndDontSave;
previewObjWireframe.AddComponent<MeshFilter>();
previewObjWireframe.AddComponent<MeshRenderer>();
previewObjWireframe.transform.position = Vector3.zero;
previewObjWireframe.transform.rotation = Quaternion.identity;
previewRender.AddSingleGO(previewObj);
previewRender.AddSingleGO(previewObjWireframe);
preview3dRotation = Quaternion.identity;
previewLight3dRotation = Quaternion.identity;
wireframeMaterial = new Material(Shader.Find("AutoLOD/Wireframe"));
wireframeMaterial.hideFlags = HideFlags.HideAndDontSave;
wireframeMaterial.SetColor("_Color", wireframeColor);
wireframeMaterial.SetFloat("_Opacity", wireframeOpacity);
previewBackend = MeshDecimatorBackend.Fast;
previewMeshDecimator = new CFastMeshDecimator();
previewMeshDecimator.Initialize();
}
public void OnDisable()
{
previewRender.Cleanup();
}
private void UpdatePreviewReductionRate()
{
if (previewIndex != -1 && previewIndex < targets.Count && targets[previewIndex]._target != null)
{
GameObject source = targets[previewIndex]._target.gameObject;
RendererType rtype = GetRendererType(source);
switch (rtype)
{
case RendererType.Default:
{
previewSourceFaceCount = source.GetComponent<MeshFilter>().sharedMesh.triangles.Length / 3;
previewTargetFaceCount = Mathf.RoundToInt(previewSourceFaceCount / previewReductionRate);
UnityEngine.Mesh decimatedMesh = previewMeshDecimator.DecimateMesh(source.GetComponent<MeshFilter>().sharedMesh, previewTargetFaceCount, false);
previewActualFaceCount = decimatedMesh.triangles.Length / 3;
previewObj.GetComponent<MeshFilter>().sharedMesh = decimatedMesh;
previewObjWireframe.GetComponent<MeshFilter>().sharedMesh = decimatedMesh;
}
break;
case RendererType.Skinned:
{
previewSourceFaceCount = source.GetComponent<SkinnedMeshRenderer>().sharedMesh.triangles.Length / 3;
previewTargetFaceCount = Mathf.RoundToInt(previewSourceFaceCount / previewReductionRate);
UnityEngine.Mesh decimatedMesh = previewMeshDecimator.DecimateMesh(source.GetComponent<SkinnedMeshRenderer>().sharedMesh, previewTargetFaceCount, false);
previewActualFaceCount = decimatedMesh.triangles.Length / 3;
previewObj.GetComponent<MeshFilter>().sharedMesh = decimatedMesh;
previewObjWireframe.GetComponent<MeshFilter>().sharedMesh = decimatedMesh;
}
break;
}
}
}
private void UpdatePreviewObject()
{
if (previewIndex != -1 && previewIndex < targets.Count && targets[previewIndex]._target != null)
{
GameObject source = targets[previewIndex]._target.gameObject;
RendererType rtype = GetRendererType(source);
switch (rtype)
{
case RendererType.Default:
{
previewObj.GetComponent<MeshFilter>().sharedMesh = source.GetComponent<MeshFilter>().sharedMesh;
Material[] sharedMaterials = new Material[source.GetComponent<MeshRenderer>().sharedMaterials.Length];
Material[] sharedWireframeMaterials = new Material[source.GetComponent<MeshRenderer>().sharedMaterials.Length];
for (int i = 0; i < sharedMaterials.Length; ++i)
{
sharedMaterials[i] = source.GetComponent<MeshRenderer>().sharedMaterials[i];
sharedWireframeMaterials[i] = wireframeMaterial;
}
previewObj.GetComponent<MeshRenderer>().sharedMaterials = sharedMaterials;
previewObjWireframe.GetComponent<MeshRenderer>().sharedMaterials = sharedWireframeMaterials;
previewRender.camera.transform.position = Vector3.back * source.GetComponent<MeshFilter>().sharedMesh.bounds.size.magnitude * 2;
previewRender.camera.transform.LookAt(Vector3.zero);
previewPivotOffset = -source.GetComponent<MeshFilter>().sharedMesh.bounds.center;
}
break;
case RendererType.Skinned:
{
previewObj.GetComponent<MeshFilter>().sharedMesh = source.GetComponent<SkinnedMeshRenderer>().sharedMesh;
Material[] sharedMaterials = new Material[source.GetComponent<SkinnedMeshRenderer>().sharedMaterials.Length];
Material[] sharedWireframeMaterials = new Material[source.GetComponent<SkinnedMeshRenderer>().sharedMaterials.Length];
for (int i = 0; i < sharedMaterials.Length; ++i)
{
sharedMaterials[i] = source.GetComponent<SkinnedMeshRenderer>().sharedMaterials[i];
sharedWireframeMaterials[i] = wireframeMaterial;
}
previewObj.GetComponent<MeshRenderer>().sharedMaterials = sharedMaterials;
previewObjWireframe.GetComponent<MeshRenderer>().sharedMaterials = sharedWireframeMaterials;
previewRender.camera.transform.position = Vector3.back * source.GetComponent<SkinnedMeshRenderer>().sharedMesh.bounds.size.magnitude * 2;
previewRender.camera.transform.LookAt(Vector3.zero);
previewPivotOffset = -source.GetComponent<SkinnedMeshRenderer>().sharedMesh.bounds.center;
}
break;
default:
break;
}
previewObj.transform.position = preview3dRotation * previewPivotOffset;
previewObjWireframe.transform.position = preview3dRotation * previewPivotOffset;
previewObjWireframe.transform.position += previewRender.camera.transform.position * 0.01f;
UpdatePreviewReductionRate();
}
else
{
ResetPreviewObject();
}
}
private void ResetPreviewObject()
{
previewIndex = -1;
previewSourceFaceCount = 0;
previewTargetFaceCount = 0;
previewActualFaceCount = 0;
previewObj.GetComponent<MeshFilter>().sharedMesh = null;
previewObj.GetComponent<MeshRenderer>().sharedMaterial = null;
previewObjWireframe.GetComponent<MeshFilter>().sharedMesh = null;
previewObjWireframe.GetComponent<MeshRenderer>().sharedMaterial = null;
}
void OnGUI()
{
if (targets == null)
targets = new List<AutoLODProperties>();
if (commonSettings == null)
commonSettings = CreateInstance<AutoLODProperties>();
AutoLODEditorUtility.InitializeStyle();
EditorGUI.DrawRect(new Rect(0, 0, Screen.width, Screen.height), new Color(0f, 0f, 0f, 0.156f));
GUILayout.Label(Resources.Load<Texture>("Logo_AutoLOD"), AutoLODEditorUtility.centeredStyle, GUILayout.Height(32f));
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical();
SerializedProperty property;
List<int> indexesToRemove = new List<int>();
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("Targets", AutoLODEditorUtility.titleStyle);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
Rect myRect = GUILayoutUtility.GetRect(0, 16, GUILayout.Height(24), GUILayout.Width(156), GUILayout.ExpandWidth(true));
GUI.skin.box = AutoLODEditorUtility.dropBoxStyle;
GUI.SetNextControlName("DragDropBox");
GUI.Box(myRect, "Drag and Drop renderers here", AutoLODEditorUtility.dropBoxStyle);
EditorGUILayout.EndHorizontal();
if (GUILayout.Button(EditorGUIUtility.IconContent("CreateAddNew"), GUILayout.Width(30), GUILayout.Height(30)))
{
targets.Add(CreateInstance<AutoLODProperties>());
}
EditorGUILayout.EndHorizontal();
if (targets.Count > 0)
EditorGUILayout.Separator();
if (myRect.Contains(Event.current.mousePosition))
{
if (Event.current.type == EventType.DragUpdated)
{
GUI.FocusControl("DragDropBox");
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
Event.current.Use();
}
else if (Event.current.type == EventType.DragPerform)
{
for (int i = 0; i < DragAndDrop.objectReferences.Length; i++)
{
if ((DragAndDrop.objectReferences[i] as GameObject).GetComponent<Renderer>() != null)
{
bool found = false;
for (int j = 0; j < targets.Count; ++j)
{
if (targets[j]._target == null)
{
targets[j]._target = (DragAndDrop.objectReferences[i] as GameObject).GetComponent<Renderer>();
found = true;
break;
}
}
if (!found)
{
targets.Add(CreateInstance<AutoLODProperties>());
targets[targets.Count - 1]._target = (DragAndDrop.objectReferences[i] as GameObject).GetComponent<Renderer>();
}
}
}
Event.current.Use();
}
}
else
{
if (GUI.GetNameOfFocusedControl() == "DragDropBox")
{
GUI.FocusControl(null);
}
}
Editor commonEditor = Editor.CreateEditor(commonSettings);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUIStyle.none, GUI.skin.verticalScrollbar);
for (int i = 0; i < targets.Count; ++i)
{
if (targets[i] == null)
targets[i] = CreateInstance<AutoLODProperties>();
string name;
name = targets[i]._target != null ? targets[i]._target.name : "Target " + i;
EditorGUILayout.BeginHorizontal();
if (name.Length > 13)
name = name.Substring(0, 13) + "...";
if (targets[i]._customSettings)
name += " [custom]";
targets[i]._foldout = EditorGUILayout.BeginFoldoutHeaderGroup(targets[i]._foldout, name, AutoLODEditorUtility.subHeaderStyle);
bool hasRenderer = targets[i]._target != null;
Editor subEditor = Editor.CreateEditor(targets[i]);
property = subEditor.serializedObject.FindProperty("_target");
EditorGUILayout.PropertyField(property, new GUIContent(""), GUILayout.Width(32), GUILayout.ExpandWidth(true));
EditorGUILayout.Space(2, false);
bool isPreview = i == previewIndex;
if (GUILayout.Toggle(isPreview, EditorGUIUtility.IconContent("d_ViewToolOrbit"), "Button", GUILayout.Width(24), GUILayout.Height(18)))
{
if (previewIndex == -1)
{
if (!this.docked)
{
Rect winRect = position;
winRect.width = winRect.height * 1.33f;
position = winRect;
}
}
previewIndex = i;
if (!isPreview)
UpdatePreviewObject();
}
else
{
if (isPreview)
{
if (!this.docked)
{
Rect winRect = position;
winRect.width = minSize.x;
position = winRect;
}
ResetPreviewObject();
}
}
EditorGUILayout.Space(2, false);
if (GUILayout.Button(EditorGUIUtility.IconContent("d_Toolbar Minus@2x"), GUILayout.Width(24), GUILayout.Height(18)))
indexesToRemove.Add(i);
EditorGUILayout.EndHorizontal();
if (targets[i]._foldout)
{
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
if (subEditor)
{
property = subEditor.serializedObject.FindProperty("_customSettings");
EditorGUILayout.PropertyField(property);
if (targets[i]._customSettings)
{
AutoLODEditorUtility.DrawPropertiesPanel(targets[i], subEditor, out bool needsRepaint);
if (needsRepaint)
Repaint();
}
}
EditorGUILayout.EndVertical();
}
subEditor.serializedObject.ApplyModifiedProperties();
if (previewIndex == i)
{
if (!hasRenderer && targets[i]._target != null)
UpdatePreviewObject();
if (hasRenderer && targets[i]._target == null)
ResetPreviewObject();
}
EditorGUILayout.EndFoldoutHeaderGroup();
EditorGUILayout.Separator();
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
indexesToRemove.Reverse();
foreach (int index in indexesToRemove)
{
targets.RemoveAt(index);
if (index == previewIndex)
{
ResetPreviewObject();
}
if (index < previewIndex)
previewIndex--;
}
EditorGUILayout.Separator();
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("Common Settings", AutoLODEditorUtility.titleStyle);
EditorGUILayout.Separator();
{
AutoLODEditorUtility.DrawPropertiesPanel(commonSettings, commonEditor, out bool needsRepaint);
if (needsRepaint)
Repaint();
}
commonEditor.serializedObject.ApplyModifiedProperties();
EditorGUILayout.Separator();
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("Generate LOD", Resources.Load<Texture>("Icon_AutoLOD")), GUILayout.Height(42), GUILayout.Width(225)))
{
GenerateLOD();
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.EndVertical();
showPreview = previewIndex > -1;
if (showPreview)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.ExpandWidth(true));
GUILayout.Label("Preview Area", AutoLODEditorUtility.titleStyle);
EditorGUILayout.Separator();
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Reduction Rate");
float tmpPreviewReductionRate = EditorGUILayout.Slider(previewReductionRate, 1f, 32f);
if (tmpPreviewReductionRate != previewReductionRate)
{
previewReductionRate = tmpPreviewReductionRate;
UpdatePreviewReductionRate();
}
MeshDecimatorBackend tmpPreviewBackend = (MeshDecimatorBackend)EditorGUILayout.EnumPopup(previewBackend, GUILayout.Width(100));
if(tmpPreviewBackend != previewBackend)
{
previewBackend = tmpPreviewBackend;
switch (previewBackend)
{
case MeshDecimatorBackend.HighQuality:
previewMeshDecimator = new CQualityMeshDecimator();
break;
case MeshDecimatorBackend.Fast:
default:
previewMeshDecimator = new CFastMeshDecimator();
break;
}
previewMeshDecimator.Initialize();
UpdatePreviewReductionRate();
}
EditorGUILayout.EndHorizontal();
Rect preview3dRect = GUILayoutUtility.GetRect(1, 1, GUILayout.MaxWidth(Screen.width - 280), GUILayout.ExpandWidth(true), GUILayout.MaxHeight(Screen.width - 280), GUILayout.ExpandHeight(true));
EditorGUI.DrawRect(preview3dRect, Color.black);
if (previewRender != null && previewRender.camera != null)
{
previewObj.SetActive(!wireframeOnly);
previewRender.BeginStaticPreview(preview3dRect);
previewRender.Render(true);
EditorGUI.DrawPreviewTexture(preview3dRect, previewRender.EndStaticPreview());
EditorGUI.LabelField(preview3dRect, string.Format("Source faces: {0}\nTarget faces: {1}\nActual faces: {2}", previewSourceFaceCount, previewTargetFaceCount, previewActualFaceCount), EditorStyles.miniBoldLabel);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Wireframe Only", GUILayout.Width(100f));
wireframeOnly = EditorGUILayout.Toggle(wireframeOnly, GUILayout.Width(24f));
float tmpWireframeOpacity = EditorGUILayout.Slider(wireframeOpacity, 0f, 1f, GUILayout.ExpandWidth(true));
if (tmpWireframeOpacity != wireframeOpacity)
{
wireframeOpacity = tmpWireframeOpacity;
wireframeMaterial.SetFloat("_Opacity", wireframeOpacity);
}
Color tmpColor = EditorGUILayout.ColorField(wireframeColor, GUILayout.Width(56));
if(tmpColor != wireframeColor)
{
wireframeColor = tmpColor;
wireframeMaterial.SetColor("_Color", wireframeColor);
}
EditorGUILayout.EndHorizontal();
}
if (Event.current.type == EventType.ScrollWheel && preview3dRect.Contains(Event.current.mousePosition))
{
float delta = Event.current.delta.y;
previewFov += delta / 5f;
previewFov = Mathf.Clamp(previewFov, 1f, 90f);
previewRender.camera.fieldOfView = previewFov;
Event.current.Use();
}
if (Event.current.type == EventType.MouseDrag && preview3dRect.Contains(Event.current.mousePosition))
{
if (Event.current.button == 0)
{
preview3dRotation = Quaternion.Euler(-Event.current.delta.y / preview3dRect.height * previewFov / 30f * 180f, 0f, 0f) *
preview3dRotation *
Quaternion.Euler(0f, -Event.current.delta.x / preview3dRect.width * previewFov / 30f * 180f, 0f);
previewObj.transform.rotation = preview3dRotation;
previewObj.transform.position = preview3dRotation * previewPivotOffset;
previewObjWireframe.transform.rotation = preview3dRotation;
previewObjWireframe.transform.position = preview3dRotation * previewPivotOffset;
previewObjWireframe.transform.position += previewRender.camera.transform.position * 0.01f;
Event.current.Use();
}
if (Event.current.button == 1)
{
previewLight3dRotation = Quaternion.Euler(-Event.current.delta.y / preview3dRect.height * previewFov / 30f * 180f, 0f, 0f) *
previewLight3dRotation *
Quaternion.Euler(0f, -Event.current.delta.x / preview3dRect.width * previewFov / 30f * 180f, 0f);
previewRender.lights[0].transform.rotation = previewLight3dRotation;
Event.current.Use();
}
}
EditorGUILayout.EndVertical();
}
GUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
EditorGUILayout.Separator();
GUILayout.BeginHorizontal();
AutoLODEditorUtility.smallFont.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
if (GUILayout.Button(EditorGUIUtility.IconContent("_Help"), GUILayout.Height(20)))
{
Help.BrowseURL("https://leochaumartin.com/wiki/index.php/AutoLOD");
}
if (GUILayout.Button(Resources.Load<Texture>("discord"), GUILayout.Height(20), GUILayout.Width(24)))
{
Help.BrowseURL("https://discord.gg/kYwzdvAt8q");
}
if (GUILayout.Button(Resources.Load<Texture>("youtube"), GUILayout.Height(20), GUILayout.Width(24)))
{
Help.BrowseURL("https://www.youtube.com/channel/UCTGysKJUd9Njaxqju4-c6_w");
}
if (GUILayout.Button(Resources.Load<Texture>("twitter"), GUILayout.Height(20), GUILayout.Width(24)))
{
Help.BrowseURL("https://twitter.com/LeoChaumartin");
}
if (GUILayout.Button(new GUIContent(Resources.Load<Texture>("mail"), "support@leochaumartin.com"), GUILayout.Height(20), GUILayout.Width(24)))
{
Help.BrowseURL("mailto:chaumartinleo@gmail.com");
}
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
GUILayout.Label("v5.5.0 ", AutoLODEditorUtility.smallFont, GUILayout.Height(8));
GUILayout.BeginHorizontal();
GUILayout.Label("<22> 2020-2025 ", AutoLODEditorUtility.smallFont, GUILayout.Height(8));
Rect logoRect = GUILayoutUtility.GetLastRect();
logoRect.x -= 42;
logoRect.height = 15;
logoRect.y -= 4;
GUI.Label(logoRect, Resources.Load<Texture>("Logo_AutoLOD"));
GUILayout.EndHorizontal();
GUILayout.Label("L<>o Chaumartin ", AutoLODEditorUtility.smallFont, GUILayout.Height(8));
GUILayout.EndVertical();
GUILayout.EndHorizontal();
EditorGUILayout.Separator();
}
void ReportProgress(string message, float value)
{
string prefix = currentObjectName != "" ? currentObjectName + " - " : "";
EditorUtility.DisplayProgressBar("AutoLOD", prefix + message, value);
}
void ReportDecimationStatus(int iteration, int start, int current, int end)
{
if (end > start)
{
string message = "Blendshapes Interpolation: This process can take a long time";
ReportProgress(message, 1f);
}
else
{
double progress = 1.0 - (current - Mathf.Min(start, end)) / (double)Mathf.Abs(start - end);
string message = "AutoLOD Decimation Process: " + ((int)(100 * progress)).ToString() + "%";
ReportProgress(message, (float)progress);
}
}
RendererType GetRendererType(GameObject go)
{
if (go.GetComponent<ReflectionProbe>())
return RendererType.Unhandled;
if (go.GetComponent<SkinnedMeshRenderer>())
return RendererType.Skinned;
if (go.GetComponent<Renderer>())
{
if (go.GetComponent<MeshFilter>())
return RendererType.Default;
return RendererType.Unhandled;
}
return RendererType.Unknown;
}
private void ProcessMeshRenderer(AutoLODProperties properties, int count)
{
GameObject go = properties._target.gameObject;
float currentProgress = 0f;
Undo.RecordObject(go, "Source GameObject modified");
currentObjectName = go.name + "_LOD0";
ReportProgress("Initialization...", currentProgress);
GameObject detailedMesh = (GameObject)Instantiate(go, go.transform.position, go.transform.rotation);
Undo.RegisterCreatedObjectUndo(detailedMesh, "Created LOD0");
foreach (MonoBehaviour mb in detailedMesh.GetComponents<MonoBehaviour>())
DestroyImmediate(mb);
// Preventing from children duplication
Transform[] children = detailedMesh.GetComponentsInChildren<Transform>();
foreach (Transform child in children)
if (child.gameObject != detailedMesh)
Undo.DestroyObjectImmediate(child.gameObject);
GameObject parentGo = go;
Undo.SetTransformParent(detailedMesh.transform, parentGo.transform, "LOD0 New Parent");
Undo.RecordObject(detailedMesh.transform, "LOD0 scale)");
detailedMesh.transform.localScale = Vector3.one;
LODGroup lodGroup = Undo.AddComponent<LODGroup>(parentGo);
Undo.DestroyObjectImmediate(parentGo.GetComponent<MeshFilter>());
Undo.DestroyObjectImmediate(parentGo.GetComponent<MeshRenderer>());
if (parentGo.GetComponent<Collider>())
{
Undo.DestroyObjectImmediate(parentGo.GetComponent<Collider>());
}
LOD[] lods = new LOD[properties._lodLevels];
UnityEngine.Mesh lodMesh;
lodMesh = detailedMesh.GetComponent<MeshFilter>().sharedMesh;
IMeshDecimator meshDecimator;
switch (properties._backend)
{
case MeshDecimatorBackend.HighQuality:
meshDecimator = new CQualityMeshDecimator();
break;
case MeshDecimatorBackend.Fast:
default:
meshDecimator = new CFastMeshDecimator();
break;
}
meshDecimator.Initialize();
meshDecimator.statusReportInvoker += ReportDecimationStatus;
//Using the existing mesh as LOD0
{
LOD lod = new LOD
{
renderers = new Renderer[1] { detailedMesh.GetComponent<Renderer>() },
screenRelativeTransitionHeight = (properties._lodLevels == 1 ? properties._relativeHeightCulling : properties._performance)
};
lods[0] = lod;
Undo.RecordObject(detailedMesh, "LOD0 Optimized Name");
detailedMesh.name = go.name + "_LOD0";
}
currentProgress = (float)(count * properties._lodLevels) / (float)(targets.Count * properties._lodLevels);
for (int l = 1; l < properties._lodLevels; ++l)
{
currentObjectName = go.name + "_LOD" + l;
GameObject clone = new GameObject(go.name + "_LOD" + l);
Undo.RegisterCreatedObjectUndo(clone, "Created LOD0");
int triangleTargetCount = (int)(lodMesh.triangles.Length / (3 * properties._reductionRate));
lodMesh = meshDecimator.DecimateMesh(lodMesh, triangleTargetCount, false);
lodMesh.name = go.name + "_LOD" + l;
if (properties._flatShading)
{
AutoLODMeshUtility.Smooth2FlatShading(lodMesh);
}
AssetDatabase.CreateAsset(lodMesh, AssetDatabase.GenerateUniqueAssetPath("Assets/" + properties._filePath + "/" + go.name + "_LOD" + l.ToString() + ".asset"));
clone.transform.parent = parentGo.transform;
clone.transform.localPosition = Vector3.zero;
clone.transform.localRotation = Quaternion.identity;
clone.transform.localScale = Vector3.one;
clone.AddComponent<MeshFilter>().sharedMesh = lodMesh;
clone.AddComponent<MeshRenderer>().sharedMaterials = detailedMesh.GetComponent<Renderer>().sharedMaterials;
LOD lod = new LOD
{
renderers = new Renderer[1] { clone.GetComponent<Renderer>() },
};
if (l < properties._lodLevels - 1)
{
if (Mathf.Pow(properties._performance, l + 1) > properties._relativeHeightCulling)
lod.screenRelativeTransitionHeight = Mathf.Pow(properties._performance, l + 1);
else
{
lod.screenRelativeTransitionHeight = (lods[l - 1].screenRelativeTransitionHeight + properties._relativeHeightCulling) / 2f;
}
}
else
lod.screenRelativeTransitionHeight = properties._relativeHeightCulling;
lods[l] = lod;
currentProgress = (float)(count * properties._lodLevels + l) / (float)(targets.Count * properties._lodLevels);
}
AssetDatabase.SaveAssets();
lodGroup.SetLODs(lods);
lodGroup.animateCrossFading = true;
lodGroup.fadeMode = LODFadeMode.CrossFade;
LODGroup.crossFadeAnimationDuration = 0.1f;
}
private void ProcessSkinnedMeshRenderer(AutoLODProperties properties, int count)
{
GameObject go = properties._target.gameObject;
float currentProgress = 0f;
Undo.RecordObject(go, "Source GameObject modified");
currentObjectName = go.name + "_LOD0";
ReportProgress("Initialization...", currentProgress);
GameObject detailedMesh = (GameObject)Instantiate(go, go.transform.position, go.transform.rotation);
foreach (MonoBehaviour mb in detailedMesh.GetComponents<MonoBehaviour>())
DestroyImmediate(mb);
bool hasBlendshapes = go.GetComponent<SkinnedMeshRenderer>().sharedMesh.blendShapeCount > 0;
detailedMesh.GetComponent<SkinnedMeshRenderer>().sharedMesh = go.GetComponent<SkinnedMeshRenderer>().sharedMesh;
Undo.RegisterCreatedObjectUndo(detailedMesh, "Created LOD0");
// Preventing from children duplication
Transform[] children = detailedMesh.GetComponentsInChildren<Transform>();
foreach (Transform child in children)
if (child.gameObject != detailedMesh)
Undo.DestroyObjectImmediate(child.gameObject);
GameObject parentGo = go;
Undo.SetTransformParent(detailedMesh.transform, parentGo.transform, "LOD0 New Parent");
Undo.RecordObject(detailedMesh.transform, "LOD0 scale)");
detailedMesh.transform.localScale = Vector3.one;
Transform bones = detailedMesh.GetComponent<SkinnedMeshRenderer>().rootBone;
detailedMesh.GetComponent<SkinnedMeshRenderer>().rootBone = null;
detailedMesh.GetComponent<SkinnedMeshRenderer>().rootBone = bones;
LODGroup lodGroup = Undo.AddComponent<LODGroup>(parentGo);
Undo.DestroyObjectImmediate(parentGo.GetComponent<SkinnedMeshRenderer>());
bool hasCollider = false;
if (parentGo.GetComponent<Collider>())
{
Undo.DestroyObjectImmediate(parentGo.GetComponent<Collider>());
hasCollider = true;
}
LOD[] lods = new LOD[properties._lodLevels];
UnityEngine.Mesh lodMesh;
lodMesh = detailedMesh.GetComponent<SkinnedMeshRenderer>().sharedMesh;
Matrix4x4[] bindposes = lodMesh.bindposes;
IMeshDecimator meshDecimator;
switch (properties._backend)
{
case MeshDecimatorBackend.HighQuality:
meshDecimator = new CQualityMeshDecimator();
break;
case MeshDecimatorBackend.Fast:
default:
meshDecimator = new CFastMeshDecimator();
break;
}
meshDecimator.Initialize();
meshDecimator.statusReportInvoker += ReportDecimationStatus;
//Using the existing mesh as LOD0
{
LOD lod = new LOD
{
renderers = new Renderer[1] { detailedMesh.GetComponent<SkinnedMeshRenderer>() },
screenRelativeTransitionHeight = (properties._lodLevels == 1 ? properties._relativeHeightCulling : properties._performance)
};
lods[0] = lod;
Undo.RecordObject(detailedMesh, "LOD0 Optimized Name");
detailedMesh.name = go.name + "_LOD0";
}
currentProgress = (float)(count * properties._lodLevels) / (float)(targets.Count * properties._lodLevels);
for (int l = 1; l < properties._lodLevels; ++l)
{
currentObjectName = go.name + "_LOD" + l;
GameObject clone = new GameObject(go.name + "_LOD" + l);
Undo.RegisterCreatedObjectUndo(clone, "Created LOD0");
int triangleTargetCount = (int)(lodMesh.triangles.Length / (3 * properties._reductionRate));
lodMesh = meshDecimator.DecimateMesh(lodMesh, triangleTargetCount, hasBlendshapes);
lodMesh.name = go.name + "_LOD" + l;
if (properties._flatShading)
{
AutoLODMeshUtility.Smooth2FlatShading(lodMesh);
}
lodMesh.bindposes = bindposes;
AssetDatabase.CreateAsset(lodMesh, AssetDatabase.GenerateUniqueAssetPath("Assets/" + properties._filePath + "/" + go.name + "_LOD" + l.ToString() + ".asset"));
clone.transform.parent = parentGo.transform;
clone.transform.localPosition = Vector3.zero;
clone.transform.localRotation = Quaternion.identity;
clone.transform.localScale = Vector3.one;
clone.AddComponent<SkinnedMeshRenderer>().bones = detailedMesh.GetComponent<SkinnedMeshRenderer>().bones;
clone.GetComponent<SkinnedMeshRenderer>().sharedMesh = lodMesh;
clone.GetComponent<SkinnedMeshRenderer>().sharedMaterials = detailedMesh.GetComponent<SkinnedMeshRenderer>().sharedMaterials;
if (hasCollider)
{
clone.AddComponent<MeshCollider>().sharedMesh = clone.GetComponent<SkinnedMeshRenderer>().sharedMesh;
}
LOD lod = new LOD
{
renderers = new Renderer[1] { clone.GetComponent<Renderer>() },
};
if (l < properties._lodLevels - 1)
{
if (Mathf.Pow(properties._performance, l + 1) > properties._relativeHeightCulling)
lod.screenRelativeTransitionHeight = Mathf.Pow(properties._performance, l + 1);
else
{
lod.screenRelativeTransitionHeight = (lods[l - 1].screenRelativeTransitionHeight + properties._relativeHeightCulling) / 2f;
}
}
else
lod.screenRelativeTransitionHeight = properties._relativeHeightCulling;
lods[l] = lod;
currentProgress = (float)(count * properties._lodLevels + l) / (float)(targets.Count * properties._lodLevels);
}
AssetDatabase.SaveAssets();
lodGroup.SetLODs(lods);
lodGroup.animateCrossFading = true;
lodGroup.fadeMode = LODFadeMode.CrossFade;
LODGroup.crossFadeAnimationDuration = 0.1f;
}
public void GenerateLOD()
{
int count = 0;
Undo.IncrementCurrentGroup();
Undo.SetCurrentGroupName("AutoLOD");
foreach (AutoLODProperties a in targets)
{
if (a._target == null)
continue;
if (a._target.gameObject.scene.name == null)
{
Debug.LogWarning(a._target.name + " is a Prefab. You must instantiate the prefab in a scene and work on the instance instead. Ignoring this object.");
continue;
}
if (!a._customSettings)
{
a.Apply(commonSettings);
}
if (!Directory.Exists(Application.dataPath + "/" + a._filePath))
{
Directory.CreateDirectory(Application.dataPath + "/" + a._filePath);
}
GameObject go = a._target.gameObject;
RendererType meshType = GetRendererType(go);
switch (meshType)
{
case RendererType.Default:
{
ProcessMeshRenderer(a, count);
break;
}
case RendererType.Skinned:
{
ProcessSkinnedMeshRenderer(a, count);
break;
}
case RendererType.Unhandled:
{
Debug.LogWarning(go.name + ": This renderer is not handled yet. Ignoring this object.");
break;
}
case RendererType.Unknown:
default:
{
Debug.LogWarning(go.name + ": No valid renderer found. Ignoring this object.");
break;
}
}
count++;
}
targets.Clear();
ResetPreviewObject();
EditorUtility.ClearProgressBar();
}
}
}