diff --git a/Editor/LODGeneratorHelperEditor.cs b/Editor/LODGeneratorHelperEditor.cs
index 38b4b26..9eb46c0 100644
--- a/Editor/LODGeneratorHelperEditor.cs
+++ b/Editor/LODGeneratorHelperEditor.cs
@@ -35,6 +35,8 @@ namespace UnityMeshSimplifier.Editor
[CustomEditor(typeof(LODGeneratorHelper))]
internal sealed class LODGeneratorHelperEditor : UnityEditor.Editor
{
+ private const string LodGeneratorPresetFieldName = "lodGeneratorPreset";
+ private const string CustomizeSettingsFieldName = "customizeSettings";
private const string FadeModeFieldName = "fadeMode";
private const string AnimateCrossFadingFieldName = "animateCrossFading";
private const string AutoCollectRenderersFieldName = "autoCollectRenderers";
@@ -54,6 +56,8 @@ internal sealed class LODGeneratorHelperEditor : UnityEditor.Editor
private const float RendererButtonWidth = 60f;
private const float RemoveRendererButtonSize = 20f;
+ private SerializedProperty lodGeneratorPresetProperty = null;
+ private SerializedProperty customizeSettingsProperty = null;
private SerializedProperty fadeModeProperty = null;
private SerializedProperty animateCrossFadingProperty = null;
private SerializedProperty autoCollectRenderersProperty = null;
@@ -82,6 +86,8 @@ internal sealed class LODGeneratorHelperEditor : UnityEditor.Editor
private void OnEnable()
{
+ lodGeneratorPresetProperty = serializedObject.FindProperty(LodGeneratorPresetFieldName);
+ customizeSettingsProperty = serializedObject.FindProperty(CustomizeSettingsFieldName);
fadeModeProperty = serializedObject.FindProperty(FadeModeFieldName);
animateCrossFadingProperty = serializedObject.FindProperty(AnimateCrossFadingFieldName);
autoCollectRenderersProperty = serializedObject.FindProperty(AutoCollectRenderersFieldName);
@@ -128,17 +134,7 @@ private void DrawGeneratedView()
private void DrawNotGeneratedView()
{
- EditorGUILayout.PropertyField(fadeModeProperty);
- var fadeMode = (LODFadeMode)fadeModeProperty.intValue;
-
- bool hasCrossFade = (fadeMode == LODFadeMode.CrossFade || fadeMode == LODFadeMode.SpeedTree);
- if (hasCrossFade)
- {
- EditorGUILayout.PropertyField(animateCrossFadingProperty);
- }
-
EditorGUILayout.PropertyField(autoCollectRenderersProperty);
- DrawSimplificationOptions();
bool newOverrideSaveAssetsPath = EditorGUILayout.Toggle(overrideSaveAssetsPathContent, overrideSaveAssetsPath);
if (newOverrideSaveAssetsPath != overrideSaveAssetsPath)
@@ -159,6 +155,36 @@ private void DrawNotGeneratedView()
}
}
+ EditorGUI.BeginDisabledGroup(customizeSettingsProperty.boolValue == true);
+ {
+ EditorGUILayout.ObjectField(lodGeneratorPresetProperty, typeof(LODGeneratorPreset));
+ }
+ EditorGUI.EndDisabledGroup();
+
+ EditorGUILayout.PropertyField(customizeSettingsProperty);
+
+ EditorGUI.BeginDisabledGroup(customizeSettingsProperty.boolValue == false);
+ {
+ EditorGUILayout.PropertyField(fadeModeProperty);
+ }
+ EditorGUI.EndDisabledGroup();
+ var fadeMode = (LODFadeMode)fadeModeProperty.intValue;
+
+ bool hasCrossFade = (fadeMode == LODFadeMode.CrossFade || fadeMode == LODFadeMode.SpeedTree);
+
+ EditorGUI.BeginDisabledGroup(customizeSettingsProperty.boolValue == false);
+ {
+ if (hasCrossFade)
+ {
+ EditorGUILayout.PropertyField(animateCrossFadingProperty);
+ }
+ }
+ EditorGUI.EndDisabledGroup();
+
+ EditorGUI.BeginDisabledGroup(customizeSettingsProperty.boolValue == false);
+ DrawSimplificationOptions();
+ EditorGUI.EndDisabledGroup();
+
if (settingsExpanded == null || settingsExpanded.Length != levelsProperty.arraySize)
{
var newSettingsExpanded = new bool[levelsProperty.arraySize];
@@ -175,10 +201,14 @@ private void DrawNotGeneratedView()
DrawLevel(levelIndex, levelProperty, hasCrossFade);
}
- if (GUILayout.Button(createLevelButtonContent))
+ EditorGUI.BeginDisabledGroup(customizeSettingsProperty.boolValue == false);
{
- CreateLevel();
+ if (GUILayout.Button(createLevelButtonContent))
+ {
+ CreateLevel();
+ }
}
+ EditorGUI.EndDisabledGroup();
if (GUILayout.Button(generateLODButtonContent))
{
@@ -209,73 +239,78 @@ private void DrawSimplificationOptions()
private void DrawLevel(int index, SerializedProperty levelProperty, bool hasCrossFade)
{
- EditorGUILayout.BeginVertical(EditorStyles.helpBox);
- EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
- GUILayout.Label(string.Format("Level {0}", index + 1), EditorStyles.boldLabel);
+ var renderersProperty = levelProperty.FindPropertyRelative(LevelRenderersFieldName);
- var previousBackgroundColor = GUI.backgroundColor;
- GUI.backgroundColor = removeColor;
- if (GUILayout.Button(deleteLevelButtonContent, GUILayout.Width(RemoveLevelButtonSize)))
+ EditorGUI.BeginDisabledGroup(customizeSettingsProperty.boolValue == false);
{
- DeleteLevel(index);
- }
- GUI.backgroundColor = previousBackgroundColor;
- EditorGUILayout.EndHorizontal();
+ EditorGUILayout.BeginVertical(EditorStyles.helpBox);
+ EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
+ GUILayout.Label(string.Format("Level {0}", index + 1), EditorStyles.boldLabel);
- ++EditorGUI.indentLevel;
-
- var screenRelativeHeightProperty = levelProperty.FindPropertyRelative(LevelScreenRelativeHeightFieldName);
- EditorGUILayout.PropertyField(screenRelativeHeightProperty);
-
- var qualityProperty = levelProperty.FindPropertyRelative(LevelQualityFieldName);
- EditorGUILayout.PropertyField(qualityProperty);
+ var previousBackgroundColor = GUI.backgroundColor;
+ GUI.backgroundColor = removeColor;
+ if (GUILayout.Button(deleteLevelButtonContent, GUILayout.Width(RemoveLevelButtonSize)))
+ {
+ DeleteLevel(index);
+ }
+ GUI.backgroundColor = previousBackgroundColor;
+ EditorGUILayout.EndHorizontal();
- bool animateCrossFading = (hasCrossFade ? animateCrossFadingProperty.boolValue : false);
- settingsExpanded[index] = EditorGUILayout.Foldout(settingsExpanded[index], settingsContent);
- if (settingsExpanded[index])
- {
++EditorGUI.indentLevel;
- var combineMeshesProperty = levelProperty.FindPropertyRelative(LevelCombineMeshesFieldName);
- EditorGUILayout.PropertyField(combineMeshesProperty);
+ var screenRelativeHeightProperty = levelProperty.FindPropertyRelative(LevelScreenRelativeHeightFieldName);
+ EditorGUILayout.PropertyField(screenRelativeHeightProperty);
- if (combineMeshesProperty.boolValue)
- {
- var combineSubMeshesProperty = levelProperty.FindPropertyRelative(LevelCombineSubMeshesFieldName);
- EditorGUILayout.PropertyField(combineSubMeshesProperty);
- }
+ var qualityProperty = levelProperty.FindPropertyRelative(LevelQualityFieldName);
+ EditorGUILayout.PropertyField(qualityProperty);
- var childProperties = levelProperty.GetChildProperties();
- foreach (var childProperty in childProperties)
+ bool animateCrossFading = (hasCrossFade ? animateCrossFadingProperty.boolValue : false);
+ settingsExpanded[index] = EditorGUILayout.Foldout(settingsExpanded[index], settingsContent);
+ if (settingsExpanded[index])
{
- if (string.Equals(childProperty.name, LevelScreenRelativeHeightFieldName) || string.Equals(childProperty.name, LevelQualityFieldName) ||
- string.Equals(childProperty.name, LevelCombineMeshesFieldName) || string.Equals(childProperty.name, LevelCombineSubMeshesFieldName) ||
- string.Equals(childProperty.name, LevelRenderersFieldName))
+ ++EditorGUI.indentLevel;
+
+ var combineMeshesProperty = levelProperty.FindPropertyRelative(LevelCombineMeshesFieldName);
+ EditorGUILayout.PropertyField(combineMeshesProperty);
+
+ if (combineMeshesProperty.boolValue)
{
- continue;
+ var combineSubMeshesProperty = levelProperty.FindPropertyRelative(LevelCombineSubMeshesFieldName);
+ EditorGUILayout.PropertyField(combineSubMeshesProperty);
}
- else if ((!hasCrossFade || !animateCrossFading) && string.Equals(childProperty.name, LevelFadeTransitionWidthFieldName))
+
+ var childProperties = levelProperty.GetChildProperties();
+ foreach (var childProperty in childProperties)
{
- continue;
+ if (string.Equals(childProperty.name, LevelScreenRelativeHeightFieldName) || string.Equals(childProperty.name, LevelQualityFieldName) ||
+ string.Equals(childProperty.name, LevelCombineMeshesFieldName) || string.Equals(childProperty.name, LevelCombineSubMeshesFieldName) ||
+ string.Equals(childProperty.name, LevelRenderersFieldName))
+ {
+ continue;
+ }
+ else if ((!hasCrossFade || !animateCrossFading) && string.Equals(childProperty.name, LevelFadeTransitionWidthFieldName))
+ {
+ continue;
+ }
+
+ EditorGUILayout.PropertyField(childProperty, true);
}
- EditorGUILayout.PropertyField(childProperty, true);
+ --EditorGUI.indentLevel;
}
- --EditorGUI.indentLevel;
- }
-
- // Remove any null renderers
- var renderersProperty = levelProperty.FindPropertyRelative(LevelRenderersFieldName);
- for (int rendererIndex = renderersProperty.arraySize - 1; rendererIndex >= 0; rendererIndex--)
- {
- var rendererProperty = renderersProperty.GetArrayElementAtIndex(rendererIndex);
- var renderer = rendererProperty.objectReferenceValue as Renderer;
- if (renderer == null)
+ // Remove any null renderers
+ for (int rendererIndex = renderersProperty.arraySize - 1; rendererIndex >= 0; rendererIndex--)
{
- renderersProperty.DeleteArrayElementAtIndex(rendererIndex);
+ var rendererProperty = renderersProperty.GetArrayElementAtIndex(rendererIndex);
+ var renderer = rendererProperty.objectReferenceValue as Renderer;
+ if (renderer == null)
+ {
+ renderersProperty.DeleteArrayElementAtIndex(rendererIndex);
+ }
}
}
+ EditorGUI.EndDisabledGroup();
bool autoCollectRenderers = autoCollectRenderersProperty.boolValue;
if (!autoCollectRenderers)
@@ -509,6 +544,7 @@ private void GenerateLODs()
try
{
EditorUtility.DisplayProgressBar("Generating LODs", "Generating LODs...", 0f);
+ lodGeneratorHelper?.TryUpdateSettingsFromPreset();
var lodGroup = LODGenerator.GenerateLODs(lodGeneratorHelper);
if (lodGroup != null)
{
@@ -631,7 +667,7 @@ where go.transform.IsChildOf(ourTransform)
if (prefabGameObjects.Any())
{
- EditorUtility.DisplayDialog("Invalid GameObjects", "Some objects are not children of the LODGenerator GameObject," +
+ EditorUtility.DisplayDialog("Invalid GameObjects", "Some objects are not children of the LODGenerator GameObject," +
" as well as being part of a prefab. They will not be added.", "OK");
}
#endif
diff --git a/Editor/LODGeneratorPresetEditor.cs b/Editor/LODGeneratorPresetEditor.cs
new file mode 100644
index 0000000..32ba14a
--- /dev/null
+++ b/Editor/LODGeneratorPresetEditor.cs
@@ -0,0 +1,235 @@
+#region License
+/*
+MIT License
+
+Copyright(c) 2017-2020 Mattias Edlund
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#endregion
+
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+
+namespace UnityMeshSimplifier.Editor
+{
+ [CustomEditor(typeof(LODGeneratorPreset))]
+ internal sealed class LODGeneratorPresetEditor : UnityEditor.Editor
+ {
+ private const string FadeModeFieldName = "fadeMode";
+ private const string AnimateCrossFadingFieldName = "animateCrossFading";
+ private const string SimplificationOptionsFieldName = "simplificationOptions";
+ private const string LevelsFieldName = "levels";
+ private const string LevelScreenRelativeHeightFieldName = "screenRelativeTransitionHeight";
+ private const string LevelFadeTransitionWidthFieldName = "fadeTransitionWidth";
+ private const string LevelQualityFieldName = "quality";
+ private const string LevelCombineMeshesFieldName = "combineMeshes";
+ private const string LevelCombineSubMeshesFieldName = "combineSubMeshes";
+ private const string LevelRenderersFieldName = "renderers";
+ private const string SimplificationOptionsEnableSmartLinkFieldName = "EnableSmartLink";
+ private const string SimplificationOptionsVertexLinkDistanceFieldName = "VertexLinkDistance";
+ private const float RemoveLevelButtonSize = 20f;
+
+ private SerializedProperty fadeModeProperty = null;
+ private SerializedProperty animateCrossFadingProperty = null;
+ private SerializedProperty simplificationOptionsProperty = null;
+ private SerializedProperty levelsProperty = null;
+
+ private bool[] settingsExpanded = null;
+ private LODGeneratorPreset lodGeneratorPreset = null;
+
+ private static readonly GUIContent createLevelButtonContent = new GUIContent("Create Level", "Creates a new LOD level.");
+ private static readonly GUIContent deleteLevelButtonContent = new GUIContent("X", "Deletes this LOD level.");
+ private static readonly GUIContent settingsContent = new GUIContent("Settings", "The settings for the LOD level.");
+ private static readonly Color removeColor = new Color(1f, 0.6f, 0.6f, 1f);
+
+ private void OnEnable()
+ {
+ fadeModeProperty = serializedObject.FindProperty(FadeModeFieldName);
+ animateCrossFadingProperty = serializedObject.FindProperty(AnimateCrossFadingFieldName);
+ simplificationOptionsProperty = serializedObject.FindProperty(SimplificationOptionsFieldName);
+ levelsProperty = serializedObject.FindProperty(LevelsFieldName);
+
+ lodGeneratorPreset = target as LODGeneratorPreset;
+ }
+
+ public override void OnInspectorGUI()
+ {
+ serializedObject.UpdateIfRequiredOrScript();
+
+ DrawView();
+
+ serializedObject.ApplyModifiedProperties();
+ }
+
+ private void DrawView()
+ {
+ EditorGUILayout.PropertyField(fadeModeProperty);
+ var fadeMode = (LODFadeMode)fadeModeProperty.intValue;
+
+ bool hasCrossFade = (fadeMode == LODFadeMode.CrossFade || fadeMode == LODFadeMode.SpeedTree);
+ if (hasCrossFade)
+ {
+ EditorGUILayout.PropertyField(animateCrossFadingProperty);
+ }
+
+ DrawSimplificationOptions();
+
+ if (settingsExpanded == null || settingsExpanded.Length != levelsProperty.arraySize)
+ {
+ var newSettingsExpanded = new bool[levelsProperty.arraySize];
+ if (settingsExpanded != null)
+ {
+ System.Array.Copy(settingsExpanded, 0, newSettingsExpanded, 0, Mathf.Min(settingsExpanded.Length, newSettingsExpanded.Length));
+ }
+ settingsExpanded = newSettingsExpanded;
+ }
+
+ for (int levelIndex = 0; levelIndex < levelsProperty.arraySize; levelIndex++)
+ {
+ var levelProperty = levelsProperty.GetArrayElementAtIndex(levelIndex);
+ DrawLevel(levelIndex, levelProperty, hasCrossFade);
+ }
+
+ if (GUILayout.Button(createLevelButtonContent))
+ {
+ CreateLevel();
+ }
+ }
+
+ private void DrawSimplificationOptions()
+ {
+ if (EditorGUILayout.PropertyField(simplificationOptionsProperty, false))
+ {
+ ++EditorGUI.indentLevel;
+
+ var enableSmartLinkProperty = simplificationOptionsProperty.FindPropertyRelative(SimplificationOptionsEnableSmartLinkFieldName);
+
+ var childProperties = simplificationOptionsProperty.GetChildProperties();
+ foreach (var childProperty in childProperties)
+ {
+ if (!enableSmartLinkProperty.boolValue && string.Equals(childProperty.name, SimplificationOptionsVertexLinkDistanceFieldName))
+ continue;
+
+ EditorGUILayout.PropertyField(childProperty, true);
+ }
+
+ --EditorGUI.indentLevel;
+ }
+ }
+
+ private void DrawLevel(int index, SerializedProperty levelProperty, bool hasCrossFade)
+ {
+ EditorGUILayout.BeginVertical(EditorStyles.helpBox);
+ EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
+ GUILayout.Label(string.Format("Level {0}", index + 1), EditorStyles.boldLabel);
+
+ var previousBackgroundColor = GUI.backgroundColor;
+ GUI.backgroundColor = removeColor;
+ if (GUILayout.Button(deleteLevelButtonContent, GUILayout.Width(RemoveLevelButtonSize)))
+ {
+ DeleteLevel(index);
+ }
+ GUI.backgroundColor = previousBackgroundColor;
+ EditorGUILayout.EndHorizontal();
+
+ ++EditorGUI.indentLevel;
+
+ var screenRelativeHeightProperty = levelProperty.FindPropertyRelative(LevelScreenRelativeHeightFieldName);
+ EditorGUILayout.PropertyField(screenRelativeHeightProperty);
+
+ var qualityProperty = levelProperty.FindPropertyRelative(LevelQualityFieldName);
+ EditorGUILayout.PropertyField(qualityProperty);
+
+ bool animateCrossFading = (hasCrossFade ? animateCrossFadingProperty.boolValue : false);
+ settingsExpanded[index] = EditorGUILayout.Foldout(settingsExpanded[index], settingsContent);
+ if (settingsExpanded[index])
+ {
+ ++EditorGUI.indentLevel;
+
+ var combineMeshesProperty = levelProperty.FindPropertyRelative(LevelCombineMeshesFieldName);
+ EditorGUILayout.PropertyField(combineMeshesProperty);
+
+ if (combineMeshesProperty.boolValue)
+ {
+ var combineSubMeshesProperty = levelProperty.FindPropertyRelative(LevelCombineSubMeshesFieldName);
+ EditorGUILayout.PropertyField(combineSubMeshesProperty);
+ }
+
+ var childProperties = levelProperty.GetChildProperties();
+ foreach (var childProperty in childProperties)
+ {
+ if (string.Equals(childProperty.name, LevelScreenRelativeHeightFieldName) || string.Equals(childProperty.name, LevelQualityFieldName) ||
+ string.Equals(childProperty.name, LevelCombineMeshesFieldName) || string.Equals(childProperty.name, LevelCombineSubMeshesFieldName) ||
+ string.Equals(childProperty.name, LevelRenderersFieldName))
+ {
+ continue;
+ }
+ else if ((!hasCrossFade || !animateCrossFading) && string.Equals(childProperty.name, LevelFadeTransitionWidthFieldName))
+ {
+ continue;
+ }
+
+ EditorGUILayout.PropertyField(childProperty, true);
+ }
+
+ --EditorGUI.indentLevel;
+ }
+
+ --EditorGUI.indentLevel;
+ EditorGUILayout.EndVertical();
+ }
+
+ private void CreateLevel()
+ {
+ int newIndex = levelsProperty.arraySize;
+ levelsProperty.InsertArrayElementAtIndex(newIndex);
+ var newLevelProperty = levelsProperty.GetArrayElementAtIndex(newIndex);
+ var lastLevelProperty = (newIndex > 0 ? levelsProperty.GetArrayElementAtIndex(newIndex - 1) : null);
+ var newScreenRelativeHeightProperty = newLevelProperty.FindPropertyRelative(LevelScreenRelativeHeightFieldName);
+ var newQualityProperty = newLevelProperty.FindPropertyRelative(LevelQualityFieldName);
+
+ if (lastLevelProperty != null)
+ {
+ var lastScreenRelativeHeightProperty = lastLevelProperty.FindPropertyRelative(LevelScreenRelativeHeightFieldName);
+ var lastQualityProperty = lastLevelProperty.FindPropertyRelative(LevelQualityFieldName);
+ newScreenRelativeHeightProperty.floatValue = lastScreenRelativeHeightProperty.floatValue * 0.5f;
+ newQualityProperty.floatValue = lastQualityProperty.floatValue * 0.65f;
+ }
+ else
+ {
+ newScreenRelativeHeightProperty.floatValue = 0.6f;
+ newQualityProperty.floatValue = 1f;
+ }
+
+ serializedObject.ApplyModifiedProperties();
+ GUIUtility.ExitGUI();
+ }
+
+ private void DeleteLevel(int index)
+ {
+ levelsProperty.DeleteArrayElementAtIndex(index);
+ serializedObject.ApplyModifiedProperties();
+ GUIUtility.ExitGUI();
+ }
+ }
+}
diff --git a/Editor/LODGeneratorPresetEditor.cs.meta b/Editor/LODGeneratorPresetEditor.cs.meta
new file mode 100644
index 0000000..4c546d1
--- /dev/null
+++ b/Editor/LODGeneratorPresetEditor.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: ba0c107493a0497aa0f6f0fc37b308da
+timeCreated: 1658437406
\ No newline at end of file
diff --git a/Runtime/Components/LODGeneratorHelper.cs b/Runtime/Components/LODGeneratorHelper.cs
index f25f679..ce57e5e 100644
--- a/Runtime/Components/LODGeneratorHelper.cs
+++ b/Runtime/Components/LODGeneratorHelper.cs
@@ -35,6 +35,12 @@ namespace UnityMeshSimplifier
public sealed class LODGeneratorHelper : MonoBehaviour
{
#region Fields
+ [SerializeField, Tooltip("The LOD Generator preset to use.")]
+ private LODGeneratorPreset lodGeneratorPreset = null;
+
+ [SerializeField, Tooltip("Whether to enable customization of preset-derived generation settings.")]
+ private bool customizeSettings = true;
+
[SerializeField, Tooltip("The fade mode used by the created LOD group.")]
private LODFadeMode fadeMode = LODFadeMode.None;
[SerializeField, Tooltip("If the cross-fading should be animated by time.")]
@@ -57,13 +63,41 @@ public sealed class LODGeneratorHelper : MonoBehaviour
#endregion
#region Properties
+ ///
+ /// Gets or sets a LOD generator preset. Presets can be used to drive simplification options and levels in a sharable way.
+ ///
+ public LODGeneratorPreset LodGeneratorPreset
+ {
+ get { return lodGeneratorPreset; }
+ set { lodGeneratorPreset = value; }
+ }
+
+ ///
+ /// Gets or sets if the simplification options and levels should be customizable, versus driven by the specified preset.
+ ///
+ public bool CustomizeSettings
+ {
+ get { return customizeSettings; }
+ set { customizeSettings = value; }
+ }
+
///
/// Gets or sets the fade mode used by the created LOD group.
///
public LODFadeMode FadeMode
{
get { return fadeMode; }
- set { fadeMode = value; }
+ set
+ {
+ if (!customizeSettings)
+ {
+ fadeMode = value;
+ }
+ else
+ {
+ WarnDisabledCustomization();
+ }
+ }
}
///
@@ -73,7 +107,17 @@ public LODFadeMode FadeMode
public bool AnimateCrossFading
{
get { return animateCrossFading; }
- set { animateCrossFading = value; }
+ set
+ {
+ if (!customizeSettings)
+ {
+ animateCrossFading = value;
+ }
+ else
+ {
+ WarnDisabledCustomization();
+ }
+ }
}
///
@@ -91,7 +135,17 @@ public bool AutoCollectRenderers
public SimplificationOptions SimplificationOptions
{
get { return simplificationOptions; }
- set { simplificationOptions = value; }
+ set
+ {
+ if (!customizeSettings)
+ {
+ simplificationOptions = value;
+ }
+ else
+ {
+ WarnDisabledCustomization();
+ }
+ }
}
///
@@ -110,7 +164,17 @@ public string SaveAssetsPath
public LODLevel[] Levels
{
get { return levels; }
- set { levels = value; }
+ set
+ {
+ if (!customizeSettings)
+ {
+ levels = value;
+ }
+ else
+ {
+ WarnDisabledCustomization();
+ }
+ }
}
///
@@ -124,49 +188,60 @@ public bool IsGenerated
#region Unity Events
private void Reset()
+ {
+ autoCollectRenderers = true;
+ ResetPresetDerivedSettings();
+ }
+
+ private void OnValidate()
+ {
+ TryUpdateSettingsFromPreset();
+ }
+ #endregion
+
+ private void WarnDisabledCustomization()
+ {
+ Debug.LogWarning($"Attempted to set a preset-driven property on a {typeof(LODGeneratorHelper)} while customization is disabled. Enable customization first.");
+ }
+
+ private void ResetPresetDerivedSettings()
{
fadeMode = LODFadeMode.None;
animateCrossFading = false;
- autoCollectRenderers = true;
simplificationOptions = SimplificationOptions.Default;
+ levels = LODLevel.GetDefaultLevels();
+ }
- levels = new LODLevel[]
+ public void TryUpdateSettingsFromPreset()
+ {
+ // Don't stomp customized settings
+ if (customizeSettings)
{
- new LODLevel(0.5f, 1f)
- {
- CombineMeshes = false,
- CombineSubMeshes = false,
- SkinQuality = SkinQuality.Auto,
- ShadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On,
- ReceiveShadows = true,
- SkinnedMotionVectors = true,
- LightProbeUsage = UnityEngine.Rendering.LightProbeUsage.BlendProbes,
- ReflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.BlendProbes,
- },
- new LODLevel(0.17f, 0.65f)
- {
- CombineMeshes = true,
- CombineSubMeshes = false,
- SkinQuality = SkinQuality.Auto,
- ShadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On,
- ReceiveShadows = true,
- SkinnedMotionVectors = true,
- LightProbeUsage = UnityEngine.Rendering.LightProbeUsage.BlendProbes,
- ReflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Simple
- },
- new LODLevel(0.02f, 0.4225f)
- {
- CombineMeshes = true,
- CombineSubMeshes = true,
- SkinQuality = SkinQuality.Bone2,
- ShadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off,
- ReceiveShadows = false,
- SkinnedMotionVectors = false,
- LightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off,
- ReflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off
- }
- };
+ return;
+ }
+
+ // Retain copy of levels so any specified renderers can survive reset
+ LODLevel[] previousLevels = (LODLevel[])levels.Clone();
+
+ // Copy settings from preset, or use defaults if no preset specified
+ if (lodGeneratorPreset != null)
+ {
+ fadeMode = lodGeneratorPreset.FadeMode;
+ animateCrossFading = lodGeneratorPreset.AnimateCrossFading;
+ simplificationOptions = lodGeneratorPreset.SimplificationOptions;
+ levels = (LODLevel[])lodGeneratorPreset.Levels.Clone();
+ }
+ else
+ {
+ ResetPresetDerivedSettings();
+ }
+
+ // Copy specified renderers over
+ int rendererCopyCount = Mathf.Min(levels.Length, previousLevels.Length);
+ for(int idx = 0; idx < rendererCopyCount; idx++)
+ {
+ levels[idx].Renderers = (Renderer[])previousLevels[idx].Renderers.Clone();
+ }
}
- #endregion
}
}
diff --git a/Runtime/LODGeneratorPreset.cs b/Runtime/LODGeneratorPreset.cs
new file mode 100644
index 0000000..3cc8c2f
--- /dev/null
+++ b/Runtime/LODGeneratorPreset.cs
@@ -0,0 +1,97 @@
+#region License
+/*
+MIT License
+
+Copyright(c) 2017-2020 Mattias Edlund
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#endregion
+
+using UnityEngine;
+
+namespace UnityMeshSimplifier
+{
+ [CreateAssetMenu(
+ fileName = "LOD Preset",
+ menuName = "Mesh Simplifier/LOD Preset")]
+ public sealed class LODGeneratorPreset : ScriptableObject
+ {
+ [SerializeField, Tooltip("The fade mode used by the created LOD group.")]
+ private LODFadeMode fadeMode = LODFadeMode.None;
+
+ [SerializeField, Tooltip("If the cross-fading should be animated by time.")]
+ private bool animateCrossFading = false;
+
+ [SerializeField, Tooltip("The simplification options.")]
+ private SimplificationOptions simplificationOptions = SimplificationOptions.Default;
+
+ [SerializeField, Tooltip("The LOD levels.")]
+ private LODLevel[] levels = null;
+
+ #region Properties
+ ///
+ /// Gets or sets the fade mode used by the created LOD group.
+ ///
+ public LODFadeMode FadeMode
+ {
+ get { return fadeMode; }
+ set { fadeMode = value; }
+ }
+
+ ///
+ /// Gets or sets if the cross-fading should be animated by time. The animation duration
+ /// is specified globally as crossFadeAnimationDuration.
+ ///
+ public bool AnimateCrossFading
+ {
+ get { return animateCrossFading; }
+ set { animateCrossFading = value; }
+ }
+
+ ///
+ /// Gets or sets the simplification options.
+ ///
+ public SimplificationOptions SimplificationOptions
+ {
+ get { return simplificationOptions; }
+ set { simplificationOptions = value; }
+ }
+
+ ///
+ /// Gets or sets the LOD levels for this preset.
+ ///
+ public LODLevel[] Levels
+ {
+ get { return levels; }
+ set { levels = value; }
+ }
+ #endregion
+
+ #region Unity Events
+ private void Reset()
+ {
+ fadeMode = LODFadeMode.None;
+ animateCrossFading = false;
+ simplificationOptions = SimplificationOptions.Default;
+ levels = LODLevel.GetDefaultLevels();
+ }
+ #endregion
+ }
+}
diff --git a/Runtime/LODGeneratorPreset.cs.meta b/Runtime/LODGeneratorPreset.cs.meta
new file mode 100644
index 0000000..d2d0700
--- /dev/null
+++ b/Runtime/LODGeneratorPreset.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: dfe269aae04c466699460584399ceb56
+timeCreated: 1658438456
\ No newline at end of file
diff --git a/Runtime/LODLevel.cs b/Runtime/LODLevel.cs
index 6318bd5..cad0df6 100644
--- a/Runtime/LODLevel.cs
+++ b/Runtime/LODLevel.cs
@@ -34,7 +34,7 @@ namespace UnityMeshSimplifier
/// A LOD (level of detail) level.
///
[Serializable]
- public struct LODLevel
+ public struct LODLevel : IEquatable
{
#region Fields
[SerializeField, Range(0f, 1f), Tooltip("The screen relative height to use for the transition.")]
@@ -242,5 +242,84 @@ public LODLevel(float screenRelativeTransitionHeight, float fadeTransitionWidth,
this.reflectionProbeUsage = ReflectionProbeUsage.BlendProbes;
}
#endregion
+
+ #region IEquatable implementation
+ public bool Equals(LODLevel other)
+ {
+ return screenRelativeTransitionHeight == other.screenRelativeTransitionHeight &&
+ fadeTransitionWidth == other.fadeTransitionWidth &&
+ quality == other.quality &&
+ combineMeshes == other.combineMeshes &&
+ combineSubMeshes == other.combineSubMeshes &&
+ skinQuality == other.skinQuality &&
+ shadowCastingMode == other.shadowCastingMode &&
+ receiveShadows == other.receiveShadows &&
+ motionVectorGenerationMode == other.motionVectorGenerationMode &&
+ skinnedMotionVectors == other.skinnedMotionVectors &&
+ lightProbeUsage == other.lightProbeUsage &&
+ reflectionProbeUsage == other.reflectionProbeUsage;
+ }
+ public override bool Equals(object obj) => obj is LODLevel other && Equals(other);
+ public override int GetHashCode()
+ {
+ return (screenRelativeTransitionHeight,
+ fadeTransitionWidth,
+ quality,
+ combineMeshes,
+ combineSubMeshes,
+ skinQuality,
+ shadowCastingMode,
+ receiveShadows,
+ motionVectorGenerationMode,
+ skinnedMotionVectors,
+ lightProbeUsage,
+ reflectionProbeUsage).GetHashCode();
+ }
+ #endregion
+
+ ///
+ /// Gets default LOD levels.
+ ///
+ /// Default LOD levels.
+ public static LODLevel[] GetDefaultLevels()
+ {
+ // TODO: Expose default levels as a project setting, rather than hard-coding values.
+ return new LODLevel[]
+ {
+ new LODLevel(0.5f, 1f)
+ {
+ CombineMeshes = false,
+ CombineSubMeshes = false,
+ SkinQuality = SkinQuality.Auto,
+ ShadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On,
+ ReceiveShadows = true,
+ SkinnedMotionVectors = true,
+ LightProbeUsage = UnityEngine.Rendering.LightProbeUsage.BlendProbes,
+ ReflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.BlendProbes,
+ },
+ new LODLevel(0.17f, 0.65f)
+ {
+ CombineMeshes = true,
+ CombineSubMeshes = false,
+ SkinQuality = SkinQuality.Auto,
+ ShadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On,
+ ReceiveShadows = true,
+ SkinnedMotionVectors = true,
+ LightProbeUsage = UnityEngine.Rendering.LightProbeUsage.BlendProbes,
+ ReflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Simple
+ },
+ new LODLevel(0.02f, 0.4225f)
+ {
+ CombineMeshes = true,
+ CombineSubMeshes = true,
+ SkinQuality = SkinQuality.Bone2,
+ ShadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off,
+ ReceiveShadows = false,
+ SkinnedMotionVectors = false,
+ LightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off,
+ ReflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off
+ }
+ };
+ }
}
}
diff --git a/Runtime/SimplificationOptions.cs b/Runtime/SimplificationOptions.cs
index 25bab3f..6313bf7 100644
--- a/Runtime/SimplificationOptions.cs
+++ b/Runtime/SimplificationOptions.cs
@@ -35,7 +35,7 @@ namespace UnityMeshSimplifier
///
[Serializable]
[StructLayout(LayoutKind.Auto)]
- public struct SimplificationOptions
+ public struct SimplificationOptions : IEquatable
{
///
/// The default simplification options.
@@ -118,5 +118,35 @@ public struct SimplificationOptions
///
[Range(0, 4), Tooltip("The UV component count. The same UV component count will be used on all UV channels.")]
public int UVComponentCount;
+
+ #region IEquatable implementation
+ public bool Equals(SimplificationOptions other)
+ {
+ return PreserveBorderEdges == other.PreserveBorderEdges &&
+ PreserveUVSeamEdges == other.PreserveUVSeamEdges &&
+ PreserveUVFoldoverEdges == other.PreserveUVFoldoverEdges &&
+ PreserveSurfaceCurvature == other.PreserveSurfaceCurvature &&
+ EnableSmartLink == other.EnableSmartLink &&
+ VertexLinkDistance == other.VertexLinkDistance &&
+ MaxIterationCount == other.MaxIterationCount &&
+ Agressiveness == other.Agressiveness &&
+ ManualUVComponentCount == other.ManualUVComponentCount &&
+ UVComponentCount == other.UVComponentCount;
+ }
+ public override bool Equals(object obj) => obj is SimplificationOptions other && Equals(other);
+ public override int GetHashCode()
+ {
+ return (PreserveBorderEdges,
+ PreserveUVSeamEdges,
+ PreserveUVFoldoverEdges,
+ PreserveSurfaceCurvature,
+ EnableSmartLink,
+ VertexLinkDistance,
+ MaxIterationCount,
+ Agressiveness,
+ ManualUVComponentCount,
+ UVComponentCount).GetHashCode();
+ }
+ #endregion
}
}