Pārlūkot izejas kodu

working with polygon triangulation

Vicente Penades 6 gadi atpakaļ
vecāks
revīzija
59f2448f1a

+ 20 - 12
src/SharpGLTF.Toolkit/Geometry/PrimitiveBuilder.cs

@@ -229,28 +229,36 @@ namespace SharpGLTF.Geometry
             Guard.NotNull(points, nameof(points));
             Guard.MustBeGreaterThanOrEqualTo(points.Length, 3, nameof(points));
 
-            if (points.Length == 3)
-            {
-                AddTriangle(points[0], points[1], points[2]);
-                return;
-            }
-
+            // prepare vertices and indices
             Span<Vector3> vertices = stackalloc Vector3[points.Length];
+            Span<int> inIndices = stackalloc int[points.Length];
 
             for (int i = 0; i < vertices.Length; ++i)
             {
                 vertices[i] = points[i].Position;
+                inIndices[i] = i;
+            }
+
+            if (_Scrict) PolygonToolkit.CheckIndices(vertices, inIndices);
+            else PolygonToolkit.SanitizeIndices(vertices, ref inIndices);
+
+            if (inIndices.Length < 3) return;
+            if (inIndices.Length == 3)
+            {
+                AddTriangle(points[inIndices[0]], points[inIndices[1]], points[inIndices[2]]);
+                return;
             }
 
-            Span<int> indices = stackalloc int[(vertices.Length - 2) * 3];
+            // tessellate
+            Span<int> outIndices = stackalloc int[(vertices.Length - 2) * 3];
 
-            _Mesh._Triangulator.Triangulate(indices, vertices);
+            _Mesh._Triangulator.Triangulate(outIndices, vertices, inIndices);
 
-            for (int i = 0; i < indices.Length; i += 3)
+            for (int i = 0; i < outIndices.Length; i += 3)
             {
-                var a = points[indices[i + 0]];
-                var b = points[indices[i + 1]];
-                var c = points[indices[i + 2]];
+                var a = points[outIndices[i + 0]];
+                var b = points[outIndices[i + 1]];
+                var c = points[outIndices[i + 2]];
 
                 AddTriangle(a, b, c);
             }

+ 62 - 21
src/SharpGLTF.Toolkit/Geometry/Triangulation.cs

@@ -8,6 +8,43 @@ namespace SharpGLTF.Geometry
 
     static class PolygonToolkit
     {
+        /// <summary>
+        /// Checks if all the indices are valid
+        /// </summary>
+        /// <param name="inVertices">A list of <see cref="Vector3"/> position points.</param>
+        /// <param name="indices">A list of vertex indices pointing to <paramref name="inVertices"/>.</param>
+        public static void CheckIndices(this ReadOnlySpan<Vector3> inVertices, ReadOnlySpan<int> indices)
+        {
+            Guard.IsFalse(inVertices.IsEmpty, nameof(inVertices));
+            Guard.IsFalse(indices.IsEmpty, nameof(indices));
+
+            int a = 0;
+            while (indices.Length > 1 && a < indices.Length)
+            {
+                // first vertex
+                var aa = inVertices[indices[a]];
+
+                // check invalid values
+                Guard.IsTrue(aa._IsReal(), nameof(inVertices));
+
+                // second vertex
+                var b = a + 1; if (b >= indices.Length) b -= inVertices.Length;
+                var bb = inVertices[indices[b]];
+
+                // check collapsed points (would produce degenerated triangles)
+                Guard.IsFalse(aa == bb, nameof(indices));
+
+                // third vertex
+                var c = b + 1; if (c >= indices.Length) c -= inVertices.Length;
+                var cc = inVertices[indices[c]];
+
+                // check collapsed segments (would produce degenerated triangles)
+                Guard.IsFalse(aa == cc, nameof(indices));
+
+                a++;
+            }
+        }
+
         /// <summary>
         /// Removes the invalid points of a polygon.
         /// </summary>
@@ -32,7 +69,7 @@ namespace SharpGLTF.Geometry
                 }
 
                 // second vertex
-                var b = a + 1; if (b >= inVertices.Length) b -= inVertices.Length;
+                var b = a + 1; if (b >= indices.Length) b -= indices.Length;
                 var bb = inVertices[indices[b]];
 
                 // remove collapsed points (would produce degenerated triangles)
@@ -43,7 +80,7 @@ namespace SharpGLTF.Geometry
                 }
 
                 // third vertex
-                var c = b + 1; if (c >= inVertices.Length) c -= inVertices.Length;
+                var c = b + 1; if (c >= indices.Length) c -= indices.Length;
                 var cc = inVertices[indices[c]];
 
                 // remove collapsed segments (would produce degenerated triangles)
@@ -72,7 +109,7 @@ namespace SharpGLTF.Geometry
 
     public interface IPolygonTriangulator
     {
-        void Triangulate(Span<int> outIndices, ReadOnlySpan<Vector3> inVertices);
+        void Triangulate(Span<int> outIndices, ReadOnlySpan<Vector3> inVertices, ReadOnlySpan<int> inIndices);
     }
 
     /// <summary>
@@ -88,15 +125,19 @@ namespace SharpGLTF.Geometry
 
         public static IPolygonTriangulator Default => _Instance;
 
-        public void Triangulate(Span<int> outIndices, ReadOnlySpan<Vector3> inVertices)
+        public void Triangulate(Span<int> outIndices, ReadOnlySpan<Vector3> inVertices, ReadOnlySpan<int> inIndices)
         {
             int idx = 0;
 
-            for (int i = 2; i < inVertices.Length; ++i)
+            for (int i = 2; i < inIndices.Length; ++i)
             {
-                outIndices[idx++] = 0;
-                outIndices[idx++] = i - 1;
-                outIndices[idx++] = i;
+                var a = inIndices[idx++];
+                var b = inIndices[idx++];
+                var c = inIndices[idx++];
+
+                outIndices[a] = 0;
+                outIndices[b] = i - 1;
+                outIndices[c] = i;
             }
         }
     }
@@ -111,13 +152,13 @@ namespace SharpGLTF.Geometry
 
         public static IPolygonTriangulator Default => _Instance;
 
-        public void Triangulate(Span<int> outIndices, ReadOnlySpan<Vector3> inVertices)
+        public void Triangulate(Span<int> outIndices, ReadOnlySpan<Vector3> inVertices, ReadOnlySpan<int> inIndices)
         {
             System.Diagnostics.Debug.Assert(outIndices.Length == (inVertices.Length - 2) * 3, $"{nameof(outIndices)} has invalid length");
 
             // setup source Indices
-            Span<int> inIndices = stackalloc int[inVertices.Length];
-            for (int i = 0; i < inIndices.Length; ++i) inIndices[i] = i;
+            Span<int> tmpIndices = stackalloc int[inIndices.Length];
+            inIndices.CopyTo(tmpIndices);
 
             // define master direction
             var masterDirection = Vector3.Zero;
@@ -131,30 +172,30 @@ namespace SharpGLTF.Geometry
             int triIdx = 0;
 
             // begin clipping ears
-            while (inIndices.Length > 3)
+            while (tmpIndices.Length > 3)
             {
-                var tri = _FindEarTriangle(inIndices, inVertices, masterDirection);
+                var tri = _FindEarTriangle(inVertices, tmpIndices, masterDirection);
                 if (tri.Item1 < 0) throw new ArgumentException("failed to triangulate", nameof(inVertices));
 
-                outIndices[triIdx++] = inIndices[tri.Item1];
-                outIndices[triIdx++] = inIndices[tri.Item2];
-                outIndices[triIdx++] = inIndices[tri.Item3];
+                outIndices[triIdx++] = tmpIndices[tri.Item1];
+                outIndices[triIdx++] = tmpIndices[tri.Item2];
+                outIndices[triIdx++] = tmpIndices[tri.Item3];
 
                 // tri.Item2 is the index of the ear's triangle.
                 // remove the ear from the array:
 
-                PolygonToolkit.RemoveElementAt(ref inIndices, tri.Item2);
+                PolygonToolkit.RemoveElementAt(ref tmpIndices, tri.Item2);
             }
 
             // add last triangle.
-            outIndices[triIdx++] = inIndices[0];
-            outIndices[triIdx++] = inIndices[1];
-            outIndices[triIdx++] = inIndices[2];
+            outIndices[triIdx++] = tmpIndices[0];
+            outIndices[triIdx++] = tmpIndices[1];
+            outIndices[triIdx++] = tmpIndices[2];
 
             System.Diagnostics.Debug.Assert(outIndices.Length == triIdx, $"{nameof(outIndices)} has invalid length");
         }
 
-        private static (int, int, int) _FindEarTriangle(ReadOnlySpan<int> inIndices, ReadOnlySpan<Vector3> inVertices, Vector3 masterDirection)
+        private static (int, int, int) _FindEarTriangle(ReadOnlySpan<Vector3> inVertices, ReadOnlySpan<int> inIndices, Vector3 masterDirection)
         {
             for (int i = 0; i < inIndices.Length; ++i)
             {

+ 84 - 28
tests/SharpGLTF.Tests/Geometry/TriangulationTests.cs

@@ -13,39 +13,91 @@ namespace SharpGLTF.Geometry
     [Category("Model Authoring")]
     public class TriangulationTests
     {
-        private static readonly Vector3[] _BowTiePolygon = new[]
+        private static readonly Vector2[] _BowTiePolygon = new[]
         {
-            new Vector3(-10, -10, 0),
-            new Vector3(0, -5, 0),
-            new Vector3(10, -10, 0),
-            new Vector3(10, 10, 0),
-            new Vector3(0, 5, 0),
-            new Vector3(-10, 10, 0)
+            new Vector2(-10, -10),
+            new Vector2(0, -5),
+            new Vector2(10, -10),
+            new Vector2(10, 10),
+            new Vector2(0, 5),
+            new Vector2(-10, 10)
         };
 
-        private static readonly Vector3[] _BridgePolygon = new[]
+        private static readonly Vector2[] _BridgePolygon = new[]
         {            
-            new Vector3(-10, 2, 0),
-            new Vector3(-10, -10, 0),
-            new Vector3(-8, -10, 0),
-            new Vector3(-8, -5, 0),
-            new Vector3(-4, -1, 0),
-            new Vector3(0, 0, 0),
-            new Vector3(+4, -1, 0),
-            new Vector3(+8, -5, 0),
-            new Vector3(+8, -10, 0),
-            new Vector3(+10, -10, 0),
-            new Vector3(+10, 2, 0),
+            new Vector2(-10, 2),
+            new Vector2(-10, -10),
+            new Vector2(-8, -10),
+            new Vector2(-8, -5),
+            new Vector2(-4, -1),
+            new Vector2(0, 0),
+            new Vector2(+4, -1),
+            new Vector2(+8, -5),
+            new Vector2(+8, -10),
+            new Vector2(+10, -10),
+            new Vector2(+10, 2),
         };
 
-        private static readonly Vector3[] _CoLinearBaseTriangle = new[]
+        private static readonly Vector2[] _CoLinearBaseTriangle = new[]
         {
-            new Vector3(0,10,0),
-            new Vector3(-10,-10,0),
-            new Vector3(-5,-10,0),
-            new Vector3(0,-10,0),
-            new Vector3(5,-10,0),
-            new Vector3(10,-10,0),            
+            new Vector2(0,10),
+            new Vector2(-10,-10),
+            new Vector2(-5,-10),
+            new Vector2(0,-10),
+            new Vector2(5,-10),
+            new Vector2(10,-10),            
+        };
+
+        // polygon with a hole, connected to the outside with a "vertex bridge"
+        private static readonly Vector2[] _PoygonWithHole = new[]
+        {
+            new Vector2(0,4),
+            new Vector2(4,0),            
+
+            // inner "hole"            
+            new Vector2(2,0),
+            new Vector2(0,2),
+            new Vector2(-2,0),
+            new Vector2(0,-2),
+            new Vector2(2,0),
+
+            new Vector2(4,0),
+            new Vector2(0,-4),
+            new Vector2(-4,0),
+        };
+
+        // this is a pretty messy polygon with lots of degenerated triangles, and holes
+        private static readonly Vector2[] _SahaquielPolygon = new[]
+        {
+            new Vector2(0,0),
+
+            new Vector2(1,0),
+            new Vector2(2,1),
+            new Vector2(3,0),
+
+            new Vector2(2.5f,0.0f),
+            new Vector2(2.0f,0.5f),
+            new Vector2(1.5f,0.0f),
+            new Vector2(2.0f,-0.5f),
+            new Vector2(2.5f,0.0f),
+
+            new Vector2(2,-1),
+            new Vector2(1, 0),
+
+            new Vector2(0,0),
+            
+            new Vector2(-1,0),
+            new Vector2(-2,-1),
+            new Vector2(-3,0),
+
+            new Vector2(-2.5f,0.0f),
+            new Vector2(-2.0f,-0.5f),
+            new Vector2(-1.5f,0.0f),
+            new Vector2(-2.0f,0.5f),
+            new Vector2(-2.5f,0.0f),
+
+            new Vector2(-2,1),
+            new Vector2(-1, 0),
         };
 
         [Test]
@@ -57,12 +109,16 @@ namespace SharpGLTF.Geometry
             var mesh = VERTEX.CreateCompatibleMesh();
             mesh.Triangulator = BasicEarClippingTriangulation.Default;
 
-            for(int i=0; i < 10; ++i)
+            var polygon = _PoygonWithHole
+                .Select(item => new Vector3(item, 0))
+                .ToArray();
+
+            for (int i=0; i < 10; ++i)
             {
                 var xform = Matrix4x4.CreateFromYawPitchRoll(i, i *2, i);
                 xform = Matrix4x4.CreateTranslation(0, 0, 20) * xform;
 
-                var vertices = _BridgePolygon
+                var vertices = polygon
                     .Select(item => new VERTEX(Vector3.Transform(item,xform)) )
                     .ToArray();