From f8ff2828141cd9f89508c6fa5a74c61f32c69bf0 Mon Sep 17 00:00:00 2001 From: bawar9 Date: Wed, 18 Mar 2020 13:28:46 +0500 Subject: [PATCH 1/3] Improved the model simplification algorithm by adding an option that takes into consideration the discrete curvature to the quadric error metrics in order to retain detailed features in model simplifcation. Based on a paper by Li Yao and Shihui Huang. --- Runtime/LODGenerator.cs | 2 +- Runtime/MeshSimplifier.cs | 147 ++++++++++++++++++++++++++++++- Runtime/SimplificationOptions.cs | 7 ++ 3 files changed, 154 insertions(+), 2 deletions(-) diff --git a/Runtime/LODGenerator.cs b/Runtime/LODGenerator.cs index f98778d..d1265a5 100644 --- a/Runtime/LODGenerator.cs +++ b/Runtime/LODGenerator.cs @@ -597,11 +597,11 @@ private static Mesh SimplifyMesh(Mesh mesh, float quality, SimplificationOptions meshSimplifier.PreserveBorderEdges = options.PreserveBorderEdges; meshSimplifier.PreserveUVSeamEdges = options.PreserveUVSeamEdges; meshSimplifier.PreserveUVFoldoverEdges = options.PreserveUVFoldoverEdges; + meshSimplifier.PreserveSurfaceCurvature = options.PreserveSurfaceCurvature; meshSimplifier.EnableSmartLink = options.EnableSmartLink; meshSimplifier.VertexLinkDistance = options.VertexLinkDistance; meshSimplifier.MaxIterationCount = options.MaxIterationCount; meshSimplifier.Agressiveness = options.Agressiveness; - meshSimplifier.Initialize(mesh); meshSimplifier.SimplifyMesh(quality); diff --git a/Runtime/MeshSimplifier.cs b/Runtime/MeshSimplifier.cs index 70a1cba..13219ae 100644 --- a/Runtime/MeshSimplifier.cs +++ b/Runtime/MeshSimplifier.cs @@ -410,6 +410,7 @@ public int Compare(BorderVertex x, BorderVertex y) private bool preserveBorderEdges = false; private bool preserveUVSeamEdges = false; private bool preserveUVFoldoverEdges = false; + private bool preserveSurfaceCurvature = false; private bool enableSmartLink = true; private int maxIterationCount = 100; private double agressiveness = 7.0; @@ -503,6 +504,17 @@ public bool PreserveUVFoldoverEdges set { preserveUVFoldoverEdges = value; } } + + /// + /// Gets or sets if the discrete curvature of the mesh surface be taken into account during simplification. + /// Default value: false + /// + public bool PreserveSurfaceCurvature + { + get { return preserveSurfaceCurvature; } + set { preserveSurfaceCurvature = value; } + } + /// /// Gets or sets if a feature for smarter vertex linking should be enabled, reducing artifacts in the /// decimated result at the cost of a slightly more expensive initialization by treating vertices at @@ -763,6 +775,7 @@ public MeshSimplifier(Mesh mesh) #endregion #region Private Methods + #region Initialize Vertex Attribute private void InitializeVertexAttribute(T[] attributeValues, ref ResizableArray attributeArray, string attributeName) { @@ -799,6 +812,51 @@ private double VertexError(ref SymmetricMatrix q, double x, double y, double z) + 2 * q.m5 * y * z + 2 * q.m6 * y + q.m7 * z * z + 2 * q.m8 * z + q.m9; } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private double CurvatureError(Vertex Vi, Vertex Vj) + { + //System.Diagnostics.Stopwatch w = new System.Diagnostics.Stopwatch(); + //w.Start(); + + double diffVector = (Vi.p - Vj.p).Magnitude; + + HashSet trianglesWithVi = GetTrianglesContainingVertex(Vi); + HashSet trianglesWithVj = GetTrianglesContainingVertex(Vj); + HashSet trianglesWithViOrVjOrBoth = new HashSet(trianglesWithVi); + trianglesWithViOrVjOrBoth.UnionWith(trianglesWithVj); + HashSet trianglesWithViAndVjBoth = GetTrianglesContainingBothVertices(Vi, Vj); + + + double maxDotOuter = 0; + + foreach (var triangleWithViOrVjOrBoth in trianglesWithViOrVjOrBoth) + { + double maxDotInner = 0; + + Vector3 normVecWithViOrVjOrBoth = GetTriangleNormalVector(triangleWithViOrVjOrBoth); + + foreach (var triangleWithViAndVjBoth in trianglesWithViAndVjBoth) + { + Vector3 normVecTriangleWithViAndVjBoth = GetTriangleNormalVector(triangleWithViAndVjBoth); + + double dot = Vector3.Dot(normVecWithViOrVjOrBoth, normVecTriangleWithViAndVjBoth); + + if (dot > maxDotInner) { maxDotInner = dot; } + } + + if (maxDotInner > maxDotOuter) { maxDotOuter = maxDotInner; } + + } + + //w.Stop(); + //Debug.Log("Time ellapsed = " + w.ElapsedMilliseconds); + + return diffVector * maxDotOuter; + } + + private double CalculateError(ref Vertex vert0, ref Vertex vert1, out Vector3d result) { // compute interpolated vertex @@ -813,7 +871,15 @@ private double CalculateError(ref Vertex vert0, ref Vertex vert1, out Vector3d r -1.0 / det * q.Determinant2(), // vx = A41/det(q_delta) 1.0 / det * q.Determinant3(), // vy = A42/det(q_delta) -1.0 / det * q.Determinant4()); // vz = A43/det(q_delta) - error = VertexError(ref q, result.x, result.y, result.z); + + double curvatureError = 0; + + if (preserveSurfaceCurvature) + { + curvatureError = CurvatureError(vert0, vert1); + } + + error = VertexError(ref q, result.x, result.y, result.z) + curvatureError; } else { @@ -844,6 +910,7 @@ private double CalculateError(ref Vertex vert0, ref Vertex vert1, out Vector3d r } return error; } + #endregion #region Calculate Barycentric Coordinates @@ -1675,6 +1742,84 @@ private void CalculateSubMeshOffsets() } } #endregion + + #region Traingle helper functions + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private HashSet GetTrianglesContainingVertex(Vertex toCheck) + { + int trianglesCount = toCheck.tcount; + int startIndex = toCheck.tstart; + + HashSet tris = new HashSet(); + + for (int a = startIndex; a < startIndex + trianglesCount; a++) + { + tris.Add(triangles[refs[a].tid]); + } + + return tris; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private HashSet GetTrianglesContainingBothVertices(Vertex vertex1, Vertex vertex2) + { + HashSet tris = new HashSet(); + + + int trianglesCount = vertex1.tcount; + int startIndex = vertex1.tstart; + + for (int a = startIndex; a < startIndex + trianglesCount; a++) + { + Triangle tri = triangles[refs[a].tid]; + + Vertex v1 = vertices[tri.v0]; + Vertex v2 = vertices[tri.v1]; + Vertex v3 = vertices[tri.v2]; + + int hashcode = vertex2.GetHashCode(); + + if (v1.GetHashCode().Equals(hashcode) || v2.GetHashCode().Equals(hashcode) || v3.GetHashCode().Equals(hashcode)) + { + tris.Add(tri); + } + + } + + + + return tris; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector3 GetTriangleNormalVector(Triangle triangle) + { + Vector3 normal = Vector3.zero; + + Vertex V0 = vertices[triangle.v0]; + Vertex V1 = vertices[triangle.v1]; + Vertex V2 = vertices[triangle.v2]; + + Vector3 a = new Vector3((float)V0.p.x, (float)V0.p.y, (float)V0.p.z); + Vector3 b = new Vector3((float)V1.p.x, (float)V1.p.y, (float)V1.p.z); + Vector3 c = new Vector3((float)V2.p.x, (float)V2.p.y, (float)V2.p.z); + + + Vector3 side1 = b - a; + Vector3 side2 = c - a; + + Vector3 perp = Vector3.Cross(side1, side2); + + float perpLength = perp.magnitude; + normal = perp / perpLength; + + return normal; + } + #endregion Traingle helper functions + + #endregion #region Public Methods diff --git a/Runtime/SimplificationOptions.cs b/Runtime/SimplificationOptions.cs index b2a672e..3e5723b 100644 --- a/Runtime/SimplificationOptions.cs +++ b/Runtime/SimplificationOptions.cs @@ -43,6 +43,7 @@ public struct SimplificationOptions PreserveBorderEdges = false, PreserveUVSeamEdges = false, PreserveUVFoldoverEdges = false, + PreserveSurfaceCurvature = false, EnableSmartLink = true, VertexLinkDistance = double.Epsilon, MaxIterationCount = 100, @@ -68,6 +69,12 @@ public struct SimplificationOptions [Tooltip("If the UV foldover edges should be preserved.")] public bool PreserveUVFoldoverEdges; /// + /// If the discrete curvature of the mesh surface be taken into account during simplification. Taking surface curvature into account can result in good quality mesh simplification, but it can slow the simplification process significantly. + /// Default value: false + /// + [Tooltip("If the discrete curvature of the mesh surface be taken into account during simplification. Taking surface curvature into account can result in very good quality mesh simplification, but it can slow the simplification process significantly.")] + public bool PreserveSurfaceCurvature; + /// /// If a feature for smarter vertex linking should be enabled, reducing artifacts in the /// decimated result at the cost of a slightly more expensive initialization by treating vertices at /// the same position as the same vertex while separating the attributes. From f4d836bc233cdff07f72f223b4767b698640eaa0 Mon Sep 17 00:00:00 2001 From: bawar9 Date: Sun, 22 Mar 2020 18:03:09 +0500 Subject: [PATCH 2/3] Removed some comments. Fixed some useless computations. Changed the triangle utility functions to take reference type parameters instead of value type structs. --- Runtime/MeshSimplifier.cs | 60 ++++++++++----------------------------- 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/Runtime/MeshSimplifier.cs b/Runtime/MeshSimplifier.cs index 13219ae..ddb6505 100644 --- a/Runtime/MeshSimplifier.cs +++ b/Runtime/MeshSimplifier.cs @@ -815,18 +815,15 @@ private double VertexError(ref SymmetricMatrix q, double x, double y, double z) [MethodImpl(MethodImplOptions.AggressiveInlining)] - private double CurvatureError(Vertex Vi, Vertex Vj) + private double CurvatureError(ref Vertex Vi, ref Vertex Vj) { - //System.Diagnostics.Stopwatch w = new System.Diagnostics.Stopwatch(); - //w.Start(); - double diffVector = (Vi.p - Vj.p).Magnitude; - HashSet trianglesWithVi = GetTrianglesContainingVertex(Vi); - HashSet trianglesWithVj = GetTrianglesContainingVertex(Vj); + HashSet trianglesWithVi = GetTrianglesContainingVertex(ref Vi); + HashSet trianglesWithVj = GetTrianglesContainingVertex(ref Vj); HashSet trianglesWithViOrVjOrBoth = new HashSet(trianglesWithVi); trianglesWithViOrVjOrBoth.UnionWith(trianglesWithVj); - HashSet trianglesWithViAndVjBoth = GetTrianglesContainingBothVertices(Vi, Vj); + HashSet trianglesWithViAndVjBoth = GetTrianglesContainingBothVertices(ref Vi, ref Vj); double maxDotOuter = 0; @@ -834,15 +831,15 @@ private double CurvatureError(Vertex Vi, Vertex Vj) foreach (var triangleWithViOrVjOrBoth in trianglesWithViOrVjOrBoth) { double maxDotInner = 0; - - Vector3 normVecWithViOrVjOrBoth = GetTriangleNormalVector(triangleWithViOrVjOrBoth); + + Vector3d normVecTriangleWithViOrVjOrBoth = triangleWithViOrVjOrBoth.n; foreach (var triangleWithViAndVjBoth in trianglesWithViAndVjBoth) { - Vector3 normVecTriangleWithViAndVjBoth = GetTriangleNormalVector(triangleWithViAndVjBoth); - - double dot = Vector3.Dot(normVecWithViOrVjOrBoth, normVecTriangleWithViAndVjBoth); + Vector3d normVecTriangleWithViAndVjBoth = triangleWithViAndVjBoth.n; + double dot = Vector3d.Dot(ref normVecTriangleWithViOrVjOrBoth, ref normVecTriangleWithViAndVjBoth); + if (dot > maxDotInner) { maxDotInner = dot; } } @@ -850,9 +847,6 @@ private double CurvatureError(Vertex Vi, Vertex Vj) } - //w.Stop(); - //Debug.Log("Time ellapsed = " + w.ElapsedMilliseconds); - return diffVector * maxDotOuter; } @@ -873,10 +867,10 @@ private double CalculateError(ref Vertex vert0, ref Vertex vert1, out Vector3d r -1.0 / det * q.Determinant4()); // vz = A43/det(q_delta) double curvatureError = 0; - + if (preserveSurfaceCurvature) { - curvatureError = CurvatureError(vert0, vert1); + curvatureError = CurvatureError(ref vert0, ref vert1); } error = VertexError(ref q, result.x, result.y, result.z) + curvatureError; @@ -1743,9 +1737,9 @@ private void CalculateSubMeshOffsets() } #endregion - #region Traingle helper functions + #region Triangle helper functions [MethodImpl(MethodImplOptions.AggressiveInlining)] - private HashSet GetTrianglesContainingVertex(Vertex toCheck) + private HashSet GetTrianglesContainingVertex(ref Vertex toCheck) { int trianglesCount = toCheck.tcount; int startIndex = toCheck.tstart; @@ -1762,7 +1756,7 @@ private HashSet GetTrianglesContainingVertex(Vertex toCheck) [MethodImpl(MethodImplOptions.AggressiveInlining)] - private HashSet GetTrianglesContainingBothVertices(Vertex vertex1, Vertex vertex2) + private HashSet GetTrianglesContainingBothVertices(ref Vertex vertex1, ref Vertex vertex2) { HashSet tris = new HashSet(); @@ -1793,31 +1787,7 @@ private HashSet GetTrianglesContainingBothVertices(Vertex vertex1, Ver } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector3 GetTriangleNormalVector(Triangle triangle) - { - Vector3 normal = Vector3.zero; - - Vertex V0 = vertices[triangle.v0]; - Vertex V1 = vertices[triangle.v1]; - Vertex V2 = vertices[triangle.v2]; - - Vector3 a = new Vector3((float)V0.p.x, (float)V0.p.y, (float)V0.p.z); - Vector3 b = new Vector3((float)V1.p.x, (float)V1.p.y, (float)V1.p.z); - Vector3 c = new Vector3((float)V2.p.x, (float)V2.p.y, (float)V2.p.z); - - - Vector3 side1 = b - a; - Vector3 side2 = c - a; - - Vector3 perp = Vector3.Cross(side1, side2); - - float perpLength = perp.magnitude; - normal = perp / perpLength; - - return normal; - } - #endregion Traingle helper functions + #endregion Triangle helper functions #endregion From 97b55642983694366a7fd0c8e8b7182f80a5d874 Mon Sep 17 00:00:00 2001 From: bawar9 Date: Sun, 22 Mar 2020 18:09:28 +0500 Subject: [PATCH 3/3] Removed an empty line to stay consistent with the rest of the code style. --- Runtime/MeshSimplifier.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Runtime/MeshSimplifier.cs b/Runtime/MeshSimplifier.cs index ddb6505..0ac77e3 100644 --- a/Runtime/MeshSimplifier.cs +++ b/Runtime/MeshSimplifier.cs @@ -504,7 +504,6 @@ public bool PreserveUVFoldoverEdges set { preserveUVFoldoverEdges = value; } } - /// /// Gets or sets if the discrete curvature of the mesh surface be taken into account during simplification. /// Default value: false