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..0ac77e3 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,16 @@ 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 +774,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 +811,45 @@ 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(ref Vertex Vi, ref Vertex Vj) + { + double diffVector = (Vi.p - Vj.p).Magnitude; + + HashSet trianglesWithVi = GetTrianglesContainingVertex(ref Vi); + HashSet trianglesWithVj = GetTrianglesContainingVertex(ref Vj); + HashSet trianglesWithViOrVjOrBoth = new HashSet(trianglesWithVi); + trianglesWithViOrVjOrBoth.UnionWith(trianglesWithVj); + HashSet trianglesWithViAndVjBoth = GetTrianglesContainingBothVertices(ref Vi, ref Vj); + + + double maxDotOuter = 0; + + foreach (var triangleWithViOrVjOrBoth in trianglesWithViOrVjOrBoth) + { + double maxDotInner = 0; + + Vector3d normVecTriangleWithViOrVjOrBoth = triangleWithViOrVjOrBoth.n; + + foreach (var triangleWithViAndVjBoth in trianglesWithViAndVjBoth) + { + Vector3d normVecTriangleWithViAndVjBoth = triangleWithViAndVjBoth.n; + + double dot = Vector3d.Dot(ref normVecTriangleWithViOrVjOrBoth, ref normVecTriangleWithViAndVjBoth); + + if (dot > maxDotInner) { maxDotInner = dot; } + } + + if (maxDotInner > maxDotOuter) { maxDotOuter = maxDotInner; } + + } + + return diffVector * maxDotOuter; + } + + private double CalculateError(ref Vertex vert0, ref Vertex vert1, out Vector3d result) { // compute interpolated vertex @@ -813,7 +864,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(ref vert0, ref vert1); + } + + error = VertexError(ref q, result.x, result.y, result.z) + curvatureError; } else { @@ -844,6 +903,7 @@ private double CalculateError(ref Vertex vert0, ref Vertex vert1, out Vector3d r } return error; } + #endregion #region Calculate Barycentric Coordinates @@ -1675,6 +1735,60 @@ private void CalculateSubMeshOffsets() } } #endregion + + #region Triangle helper functions + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private HashSet GetTrianglesContainingVertex(ref 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(ref Vertex vertex1, ref 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; + } + + + #endregion Triangle 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.