Skip to content

Commit

Permalink
feat(simplifier): added options validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Whinarn committed Feb 25, 2022
1 parent e27528b commit 55264ce
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Editor/LODGeneratorHelperEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ private void GenerateLODs()
}
catch (System.Exception ex)
{
Debug.LogException(ex);
Debug.LogException(ex, lodGeneratorHelper);
DisplayError("Failed to generate LODs!", ex.Message, "OK", lodGeneratorHelper);
}
finally
Expand Down
8 changes: 8 additions & 0 deletions Runtime/Exceptions.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 51 additions & 0 deletions Runtime/Exceptions/ValidateSimplificationOptionsException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;

namespace UnityMeshSimplifier
{
/// <summary>
/// An exception thrown when validating simplification options.
/// </summary>
public sealed class ValidateSimplificationOptionsException : Exception
{
private readonly string propertyName;

/// <summary>
/// Creates a new simplification options validation exception.
/// </summary>
/// <param name="propertyName">The property name.</param>
/// <param name="message">The exception message.</param>
public ValidateSimplificationOptionsException(string propertyName, string message)
: base(message)
{
this.propertyName = propertyName;
}

/// <summary>
/// Creates a new simplification options validation exception.
/// </summary>
/// <param name="propertyName">The property name.</param>
/// <param name="message">The exception message.</param>
/// <param name="innerException">The exception that caused the validation error.</param>
public ValidateSimplificationOptionsException(string propertyName, string message, Exception innerException)
: base(message, innerException)
{
this.propertyName = propertyName;
}

/// <summary>
/// Gets the property name that caused the validation error.
/// </summary>
public string PropertyName
{
get { return propertyName; }
}

/// <summary>
/// Gets the message of the exception.
/// </summary>
public override string Message
{
get { return base.Message + Environment.NewLine + "Property name: " + propertyName; }
}
}
}
11 changes: 11 additions & 0 deletions Runtime/Exceptions/ValidateSimplificationOptionsException.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 13 additions & 11 deletions Runtime/LODGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ public static LODGroup GenerateLODs(GameObject gameObject, LODLevel[] levels, bo
if (existingLodGroup != null)
throw new System.InvalidOperationException("The game object already appears to have a LOD Group. Please remove it first.");

MeshSimplifier.ValidateOptions(simplificationOptions);

saveAssetsPath = ValidateSaveAssetsPath(saveAssetsPath);

var lodParentGameObject = new GameObject(LODParentGameObjectName);
Expand Down Expand Up @@ -195,7 +197,7 @@ public static LODGroup GenerateLODs(GameObject gameObject, LODLevel[] levels, bo
for (int rendererIndex = 0; rendererIndex < staticRenderers.Length; rendererIndex++)
{
var renderer = staticRenderers[rendererIndex];
var levelRenderer = CreateLevelRenderer(gameObject, levelIndex, ref level, levelTransform, rendererIndex, renderer, ref simplificationOptions, saveAssetsPath);
var levelRenderer = CreateLevelRenderer(gameObject, levelIndex, level, levelTransform, rendererIndex, renderer, simplificationOptions, saveAssetsPath);
levelRenderers.Add(levelRenderer);
}
}
Expand All @@ -205,7 +207,7 @@ public static LODGroup GenerateLODs(GameObject gameObject, LODLevel[] levels, bo
for (int rendererIndex = 0; rendererIndex < skinnedRenderers.Length; rendererIndex++)
{
var renderer = skinnedRenderers[rendererIndex];
var levelRenderer = CreateLevelRenderer(gameObject, levelIndex, ref level, levelTransform, rendererIndex, renderer, ref simplificationOptions, saveAssetsPath);
var levelRenderer = CreateLevelRenderer(gameObject, levelIndex, level, levelTransform, rendererIndex, renderer, simplificationOptions, saveAssetsPath);
levelRenderers.Add(levelRenderer);
}
}
Expand Down Expand Up @@ -454,7 +456,7 @@ private static void ParentAndOffsetTransform(Transform transform, Transform pare
transform.SetParent(parentTransform, true);
}

private static Renderer CreateLevelRenderer(GameObject gameObject, int levelIndex, ref LODLevel level, Transform levelTransform, int rendererIndex, RendererInfo renderer, ref SimplificationOptions simplificationOptions, string saveAssetsPath)
private static Renderer CreateLevelRenderer(GameObject gameObject, int levelIndex, in LODLevel level, Transform levelTransform, int rendererIndex, in RendererInfo renderer, in SimplificationOptions simplificationOptions, string saveAssetsPath)
{
var mesh = renderer.mesh;

Expand All @@ -476,16 +478,16 @@ private static Renderer CreateLevelRenderer(GameObject gameObject, int levelInde
if (renderer.isStatic)
{
string rendererName = string.Format("{0:000}_static_{1}", rendererIndex, renderer.name);
return CreateStaticLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, ref level);
return CreateStaticLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, level);
}
else
{
string rendererName = string.Format("{0:000}_skinned_{1}", rendererIndex, renderer.name);
return CreateSkinnedLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, renderer.rootBone, renderer.bones, ref level);
return CreateSkinnedLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, renderer.rootBone, renderer.bones, level);
}
}

private static MeshRenderer CreateStaticLevelRenderer(string name, Transform parentTransform, Transform originalTransform, Mesh mesh, Material[] materials, ref LODLevel level)
private static MeshRenderer CreateStaticLevelRenderer(string name, Transform parentTransform, Transform originalTransform, Mesh mesh, Material[] materials, in LODLevel level)
{
var levelGameObject = new GameObject(name, typeof(MeshFilter), typeof(MeshRenderer));
var levelTransform = levelGameObject.transform;
Expand All @@ -503,11 +505,11 @@ private static MeshRenderer CreateStaticLevelRenderer(string name, Transform par

var meshRenderer = levelGameObject.GetComponent<MeshRenderer>();
meshRenderer.sharedMaterials = materials;
SetupLevelRenderer(meshRenderer, ref level);
SetupLevelRenderer(meshRenderer, level);
return meshRenderer;
}

private static SkinnedMeshRenderer CreateSkinnedLevelRenderer(string name, Transform parentTransform, Transform originalTransform, Mesh mesh, Material[] materials, Transform rootBone, Transform[] bones, ref LODLevel level)
private static SkinnedMeshRenderer CreateSkinnedLevelRenderer(string name, Transform parentTransform, Transform originalTransform, Mesh mesh, Material[] materials, Transform rootBone, Transform[] bones, in LODLevel level)
{
var levelGameObject = new GameObject(name, typeof(SkinnedMeshRenderer));
var levelTransform = levelGameObject.transform;
Expand All @@ -525,7 +527,7 @@ private static SkinnedMeshRenderer CreateSkinnedLevelRenderer(string name, Trans
skinnedMeshRenderer.sharedMaterials = materials;
skinnedMeshRenderer.rootBone = rootBone;
skinnedMeshRenderer.bones = bones;
SetupLevelRenderer(skinnedMeshRenderer, ref level);
SetupLevelRenderer(skinnedMeshRenderer, level);
return skinnedMeshRenderer;
}

Expand Down Expand Up @@ -553,7 +555,7 @@ private static Transform FindBestRootBone(Transform transform, SkinnedMeshRender
return bestBone;
}

private static void SetupLevelRenderer(Renderer renderer, ref LODLevel level)
private static void SetupLevelRenderer(Renderer renderer, in LODLevel level)
{
renderer.shadowCastingMode = level.ShadowCastingMode;
renderer.receiveShadows = level.ReceiveShadows;
Expand Down Expand Up @@ -605,7 +607,7 @@ private static void CollectChildRenderersForLOD(Transform transform, List<Render
}
}

private static Mesh SimplifyMesh(Mesh mesh, float quality, SimplificationOptions options)
private static Mesh SimplifyMesh(Mesh mesh, float quality, in SimplificationOptions options)
{
var meshSimplifier = new MeshSimplifier();
meshSimplifier.SimplificationOptions = options;
Expand Down
104 changes: 86 additions & 18 deletions Runtime/MeshSimplifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,11 @@ public sealed class MeshSimplifier
public SimplificationOptions SimplificationOptions
{
get { return this.simplificationOptions; }
set { this.simplificationOptions = value; }
set
{
ValidateOptions(value);
this.simplificationOptions = value;
}
}

/// <summary>
Expand All @@ -110,7 +114,12 @@ public SimplificationOptions SimplificationOptions
public bool PreserveBorderEdges
{
get { return simplificationOptions.PreserveBorderEdges; }
set { simplificationOptions.PreserveBorderEdges = value; }
set
{
var simplificationOptions = this.simplificationOptions;
simplificationOptions.PreserveBorderEdges = value;
SimplificationOptions = simplificationOptions;
}
}

/// <summary>
Expand All @@ -121,7 +130,12 @@ public bool PreserveBorderEdges
public bool PreserveUVSeamEdges
{
get { return simplificationOptions.PreserveUVSeamEdges; }
set { simplificationOptions.PreserveUVSeamEdges = value; }
set
{
var simplificationOptions = this.simplificationOptions;
simplificationOptions.PreserveUVSeamEdges = value;
SimplificationOptions = simplificationOptions;
}
}

/// <summary>
Expand All @@ -132,7 +146,12 @@ public bool PreserveUVSeamEdges
public bool PreserveUVFoldoverEdges
{
get { return simplificationOptions.PreserveUVFoldoverEdges; }
set { simplificationOptions.PreserveUVFoldoverEdges = value; }
set
{
var simplificationOptions = this.simplificationOptions;
simplificationOptions.PreserveUVFoldoverEdges = value;
SimplificationOptions = simplificationOptions;
}
}

/// <summary>
Expand All @@ -143,7 +162,12 @@ public bool PreserveUVFoldoverEdges
public bool PreserveSurfaceCurvature
{
get { return simplificationOptions.PreserveSurfaceCurvature; }
set { simplificationOptions.PreserveSurfaceCurvature = value; }
set
{
var simplificationOptions = this.simplificationOptions;
simplificationOptions.PreserveSurfaceCurvature = value;
SimplificationOptions = simplificationOptions;
}
}

/// <summary>
Expand All @@ -156,7 +180,12 @@ public bool PreserveSurfaceCurvature
public bool EnableSmartLink
{
get { return simplificationOptions.EnableSmartLink; }
set { simplificationOptions.EnableSmartLink = value; }
set
{
var simplificationOptions = this.simplificationOptions;
simplificationOptions.EnableSmartLink = value;
SimplificationOptions = simplificationOptions;
}
}

/// <summary>
Expand All @@ -168,7 +197,12 @@ public bool EnableSmartLink
public int MaxIterationCount
{
get { return simplificationOptions.MaxIterationCount; }
set { simplificationOptions.MaxIterationCount = value; }
set
{
var simplificationOptions = this.simplificationOptions;
simplificationOptions.MaxIterationCount = value;
SimplificationOptions = simplificationOptions;
}
}

/// <summary>
Expand All @@ -179,7 +213,12 @@ public int MaxIterationCount
public double Agressiveness
{
get { return simplificationOptions.Agressiveness; }
set { simplificationOptions.Agressiveness = value; }
set
{
var simplificationOptions = this.simplificationOptions;
simplificationOptions.Agressiveness = value;
SimplificationOptions = simplificationOptions;
}
}

/// <summary>
Expand All @@ -200,7 +239,12 @@ public bool Verbose
public double VertexLinkDistance
{
get { return simplificationOptions.VertexLinkDistance; }
set { simplificationOptions.VertexLinkDistance = value > double.Epsilon ? value : double.Epsilon; }
set
{
var simplificationOptions = this.simplificationOptions;
simplificationOptions.VertexLinkDistance = value > double.Epsilon ? value : double.Epsilon;
SimplificationOptions = simplificationOptions;
}
}

/// <summary>
Expand All @@ -212,7 +256,12 @@ public double VertexLinkDistance
public double VertexLinkDistanceSqr
{
get { return simplificationOptions.VertexLinkDistance * simplificationOptions.VertexLinkDistance; }
set { simplificationOptions.VertexLinkDistance = Math.Sqrt(value); }
set
{
var simplificationOptions = this.simplificationOptions;
simplificationOptions.VertexLinkDistance = Math.Sqrt(value);
SimplificationOptions = simplificationOptions;
}
}

/// <summary>
Expand Down Expand Up @@ -2000,13 +2049,6 @@ public void Initialize(Mesh mesh)
if (mesh == null)
throw new ArgumentNullException(nameof(mesh));

int uvComponentCount = simplificationOptions.UVComponentCount;
if (simplificationOptions.ManualUVComponentCount)
{
if (uvComponentCount < 0 || uvComponentCount > 4)
throw new InvalidOperationException("The UV component count cannot be below 0 or above 4.");
}

this.Vertices = mesh.vertices;
this.Normals = mesh.normals;
this.Tangents = mesh.tangents;
Expand All @@ -2019,7 +2061,7 @@ public void Initialize(Mesh mesh)
{
if (simplificationOptions.ManualUVComponentCount)
{
switch (uvComponentCount)
switch (simplificationOptions.UVComponentCount)
{
case 1:
case 2:
Expand Down Expand Up @@ -2248,6 +2290,32 @@ public Mesh ToMesh()
return MeshUtils.CreateMesh(vertices, indices, normals, tangents, colors, boneWeights, uvs2D, uvs3D, uvs4D, bindposes, blendShapes);
}
#endregion

#region Validate Options
/// <summary>
/// Validates simplification options.
/// Will throw an exception if the options are invalid.
/// </summary>
/// <param name="options">The simplification options to validate.</param>
/// <exception cref="ValidateSimplificationOptionsException">The exception thrown in case of invalid options.</exception>
public static void ValidateOptions(SimplificationOptions options)
{
if (options.EnableSmartLink && options.VertexLinkDistance < 0.0)
throw new ValidateSimplificationOptionsException(nameof(options.VertexLinkDistance), "The vertex link distance cannot be negative when smart linking is enabled.");

if (options.MaxIterationCount <= 0)
throw new ValidateSimplificationOptionsException(nameof(options.MaxIterationCount), "The max iteration count cannot be zero or negative, since there would be nothing for the algorithm to do.");

if (options.Agressiveness <= 0.0)
throw new ValidateSimplificationOptionsException(nameof(options.Agressiveness), "The aggressiveness has to be above zero to make sense. Recommended is around 7.");

if (options.ManualUVComponentCount)
{
if (options.UVComponentCount < 0 || options.UVComponentCount > 4)
throw new ValidateSimplificationOptionsException(nameof(options.UVComponentCount), "The UV component count cannot be below 0 or above 4 when manual UV component count is enabled.");
}
}
#endregion
#endregion
}
}

0 comments on commit 55264ce

Please sign in to comment.