763 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			763 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | ||
|  | using System.Collections.Generic; | ||
|  | using System.Reflection; | ||
|  | using Unity.Profiling; | ||
|  | using Unity.Collections; | ||
|  | using Unity.Mathematics; | ||
|  | using Unity.Collections.LowLevel.Unsafe; | ||
|  | 
 | ||
|  | namespace UnityEngine.Rendering.Universal.UTess | ||
|  | { | ||
|  |     enum UEventType | ||
|  |     { | ||
|  |         EVENT_POINT = 0, | ||
|  |         EVENT_END = 1, | ||
|  |         EVENT_START = 2, | ||
|  |     }; | ||
|  | 
 | ||
|  |     struct UEvent | ||
|  |     { | ||
|  |         public float2 a; | ||
|  |         public float2 b; | ||
|  |         public int idx; | ||
|  |         public int type; | ||
|  |     }; | ||
|  | 
 | ||
|  |     struct UHull | ||
|  |     { | ||
|  |         public float2 a; | ||
|  |         public float2 b; | ||
|  |         public int idx; | ||
|  | 
 | ||
|  |         public ArraySlice<int> ilarray; | ||
|  |         public int ilcount; | ||
|  |         public ArraySlice<int> iuarray; | ||
|  |         public int iucount; | ||
|  |     }; | ||
|  | 
 | ||
|  |     struct UStar | ||
|  |     { | ||
|  |         public ArraySlice<int> points; | ||
|  |         public int pointCount; | ||
|  |     }; | ||
|  | 
 | ||
|  |     struct UBounds | ||
|  |     { | ||
|  |         public double2 min; | ||
|  |         public double2 max; | ||
|  |     }; | ||
|  | 
 | ||
|  |     struct UCircle | ||
|  |     { | ||
|  |         public float2 center; | ||
|  |         public float radius; | ||
|  |     }; | ||
|  | 
 | ||
|  |     struct UTriangle | ||
|  |     { | ||
|  |         public float2 va; | ||
|  |         public float2 vb; | ||
|  |         public float2 vc; | ||
|  |         public UCircle c; | ||
|  |         public float area; | ||
|  |         public int3 indices; | ||
|  |     }; | ||
|  | 
 | ||
|  |     struct UEncroachingSegment | ||
|  |     { | ||
|  |         public float2 a; | ||
|  |         public float2 b; | ||
|  |         public int index; | ||
|  |     } | ||
|  | 
 | ||
|  |     internal interface ICondition2<in T, in U> | ||
|  |     { | ||
|  |         bool Test(T x, U y, ref float t); | ||
|  |     } | ||
|  | 
 | ||
|  |     struct XCompare : IComparer<double> | ||
|  |     { | ||
|  |         public int Compare(double a, double b) | ||
|  |         { | ||
|  |             return (a < b) ? -1 : 1; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     unsafe struct IntersectionCompare : IComparer<int2> | ||
|  |     { | ||
|  |         public NativeArray<double2> points; | ||
|  |         public NativeArray<int2> edges; | ||
|  | 
 | ||
|  |         public fixed double xvasort[4]; | ||
|  |         public fixed double xvbsort[4]; | ||
|  | 
 | ||
|  |         public int Compare(int2 a, int2 b) | ||
|  |         { | ||
|  |             var e1a = edges[a.x]; | ||
|  |             var e1b = edges[a.y]; | ||
|  |             var e2a = edges[b.x]; | ||
|  |             var e2b = edges[b.y]; | ||
|  | 
 | ||
|  |             xvasort[0] = points[e1a.x].x; | ||
|  |             xvasort[1] = points[e1a.y].x; | ||
|  |             xvasort[2] = points[e1b.x].x; | ||
|  |             xvasort[3] = points[e1b.y].x; | ||
|  | 
 | ||
|  |             xvbsort[0] = points[e2a.x].x; | ||
|  |             xvbsort[1] = points[e2a.y].x; | ||
|  |             xvbsort[2] = points[e2b.x].x; | ||
|  |             xvbsort[3] = points[e2b.y].x; | ||
|  | 
 | ||
|  |             fixed(double* xvasortPtr = xvasort) | ||
|  |             { | ||
|  |                 ModuleHandle.InsertionSort<double, XCompare>(xvasortPtr, 0, 3, new XCompare()); | ||
|  |             } | ||
|  | 
 | ||
|  |             fixed(double* xvbsortPtr = xvbsort) | ||
|  |             { | ||
|  |                 ModuleHandle.InsertionSort<double, XCompare>(xvbsortPtr, 0, 3, new XCompare()); | ||
|  |             } | ||
|  | 
 | ||
|  |             for (int i = 0; i < 4; ++i) | ||
|  |                 if (xvasort[i] - xvbsort[i] != 0) | ||
|  |                     return xvasort[i] < xvbsort[i] ? -1 : 1; | ||
|  |             return points[e1a.x].y < points[e1a.x].y ? -1 : 1; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     struct TessEventCompare : IComparer<UEvent> | ||
|  |     { | ||
|  |         public int Compare(UEvent a, UEvent b) | ||
|  |         { | ||
|  |             float f = (a.a.x - b.a.x); | ||
|  |             if (0 != f) | ||
|  |                 return (f > 0) ? 1 : -1; | ||
|  | 
 | ||
|  |             f = (a.a.y - b.a.y); | ||
|  |             if (0 != f) | ||
|  |                 return (f > 0) ? 1 : -1; | ||
|  | 
 | ||
|  |             int i = a.type - b.type; | ||
|  |             if (0 != i) | ||
|  |                 return i; | ||
|  | 
 | ||
|  |             if (a.type != (int)UEventType.EVENT_POINT) | ||
|  |             { | ||
|  |                 float o = ModuleHandle.OrientFast(a.a, a.b, b.b); | ||
|  |                 if (0 != o) | ||
|  |                 { | ||
|  |                     return (o > 0) ? 1 : -1; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             return a.idx - b.idx; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     struct TessEdgeCompare : IComparer<int2> | ||
|  |     { | ||
|  |         public int Compare(int2 a, int2 b) | ||
|  |         { | ||
|  |             int i = a.x - b.x; | ||
|  |             if (0 != i) | ||
|  |                 return i; | ||
|  |             i = a.y - b.y; | ||
|  |             return i; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     struct TessCellCompare : IComparer<int3> | ||
|  |     { | ||
|  |         public int Compare(int3 a, int3 b) | ||
|  |         { | ||
|  |             int i = a.x - b.x; | ||
|  |             if (0 != i) | ||
|  |                 return i; | ||
|  |             i = a.y - b.y; | ||
|  |             if (0 != i) | ||
|  |                 return i; | ||
|  |             i = a.z - b.z; | ||
|  |             return i; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     struct TessJunctionCompare : IComparer<int2> | ||
|  |     { | ||
|  |         public int Compare(int2 a, int2 b) | ||
|  |         { | ||
|  |             int i = a.x - b.x; | ||
|  |             if (0 != i) | ||
|  |                 return i; | ||
|  |             i = a.y - b.y; | ||
|  |             return i; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     struct DelaEdgeCompare : IComparer<int4> | ||
|  |     { | ||
|  |         public int Compare(int4 a, int4 b) | ||
|  |         { | ||
|  |             int i = a.x - b.x; | ||
|  |             if (0 != i) | ||
|  |                 return i; | ||
|  |             i = a.y - b.y; | ||
|  |             if (0 != i) | ||
|  |                 return i; | ||
|  |             i = a.z - b.z; | ||
|  |             if (0 != i) | ||
|  |                 return i; | ||
|  |             i = a.w - b.w; | ||
|  |             return i; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     struct TessLink | ||
|  |     { | ||
|  |         internal NativeArray<int> roots; | ||
|  |         internal NativeArray<int> ranks; | ||
|  | 
 | ||
|  |         internal static TessLink CreateLink(int count, Allocator allocator) | ||
|  |         { | ||
|  |             TessLink link = new TessLink(); | ||
|  |             link.roots = new NativeArray<int>(count, allocator); | ||
|  |             link.ranks = new NativeArray<int>(count, allocator); | ||
|  | 
 | ||
|  |             for (int i = 0; i < count; ++i) | ||
|  |             { | ||
|  |                 link.roots[i] = i; | ||
|  |                 link.ranks[i] = 0; | ||
|  |             } | ||
|  |             return link; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static void DestroyLink(TessLink link) | ||
|  |         { | ||
|  |             link.ranks.Dispose(); | ||
|  |             link.roots.Dispose(); | ||
|  |         } | ||
|  | 
 | ||
|  |         internal int Find(int x) | ||
|  |         { | ||
|  |             var x0 = x; | ||
|  |             while (roots[x] != x) | ||
|  |             { | ||
|  |                 x = roots[x]; | ||
|  |             } | ||
|  |             while (roots[x0] != x) | ||
|  |             { | ||
|  |                 var y = roots[x0]; | ||
|  |                 roots[x0] = x; | ||
|  |                 x0 = y; | ||
|  |             } | ||
|  |             return x; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal void Link(int x, int y) | ||
|  |         { | ||
|  |             var xr = Find(x); | ||
|  |             var yr = Find(y); | ||
|  |             if (xr == yr) | ||
|  |             { | ||
|  |                 return; | ||
|  |             } | ||
|  |             var xd = ranks[xr]; | ||
|  |             var yd = ranks[yr]; | ||
|  |             if (xd < yd) | ||
|  |             { | ||
|  |                 roots[xr] = yr; | ||
|  |             } | ||
|  |             else if (yd < xd) | ||
|  |             { | ||
|  |                 roots[yr] = xr; | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 roots[yr] = xr; | ||
|  |                 ++ranks[xr]; | ||
|  |             } | ||
|  |         } | ||
|  |     }; | ||
|  | 
 | ||
|  |     internal struct ModuleHandle | ||
|  |     { | ||
|  |         // Max Edge Count with Subdivision allowed. This is already a very relaxed limit | ||
|  |         // and anything beyond are basically littered with numerous paths. | ||
|  |         internal static readonly  int kMaxArea = 65536; | ||
|  |         internal static readonly  int kMaxEdgeCount = 65536; | ||
|  |         internal static readonly  int kMaxIndexCount = 65536; | ||
|  |         internal static readonly  int kMaxVertexCount = 65536; | ||
|  |         internal static readonly  int kMaxTriangleCount = kMaxIndexCount / 3; | ||
|  |         internal static readonly  int kMaxRefineIterations = 48; | ||
|  |         internal static readonly  int kMaxSmoothenIterations = 256; | ||
|  |         internal static readonly  float kIncrementAreaFactor = 1.2f; | ||
|  | 
 | ||
|  |         internal static void Copy<T>(NativeArray<T> src, int srcIndex, NativeArray<T> dst, int dstIndex, int length) | ||
|  |             where T : struct | ||
|  |         { | ||
|  |             NativeArray<T>.Copy(src, srcIndex, dst, dstIndex, length); | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static void Copy<T>(NativeArray<T> src, NativeArray<T> dst, int length) | ||
|  |             where T : struct | ||
|  |         { | ||
|  |             Copy(src, 0, dst, 0, length); | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static unsafe void InsertionSort<T, U>(void* array, int lo, int hi, U comp) | ||
|  |             where T : struct where U : IComparer<T> | ||
|  |         { | ||
|  |             int i, j; | ||
|  |             T t; | ||
|  |             for (i = lo; i < hi; i++) | ||
|  |             { | ||
|  |                 j = i; | ||
|  |                 t = UnsafeUtility.ReadArrayElement<T>(array, i + 1); | ||
|  |                 while (j >= lo && comp.Compare(t, UnsafeUtility.ReadArrayElement<T>(array, j)) < 0) | ||
|  |                 { | ||
|  |                     UnsafeUtility.WriteArrayElement<T>(array, j + 1, UnsafeUtility.ReadArrayElement<T>(array, j)); | ||
|  |                     j--; | ||
|  |                 } | ||
|  |                 UnsafeUtility.WriteArrayElement<T>(array, j + 1, t); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // Search Lower Bounds | ||
|  |         internal static int GetLower<T, U, X>(NativeArray<T> values, int count, U check, X condition) | ||
|  |             where T : struct where U : struct where X : ICondition2<T, U> | ||
|  |         { | ||
|  |             int l = 0; | ||
|  |             int h = count - 1; | ||
|  |             int i = l - 1; | ||
|  |             while (l <= h) | ||
|  |             { | ||
|  |                 int m = ((int)(l + h)) >> 1; | ||
|  |                 float t = 0; | ||
|  |                 if (condition.Test(values[m], check, ref t)) | ||
|  |                 { | ||
|  |                     i = m; | ||
|  |                     l = m + 1; | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     h = m - 1; | ||
|  |                 } | ||
|  |             } | ||
|  |             return i; | ||
|  |         } | ||
|  | 
 | ||
|  |         // Search Upper Bounds | ||
|  |         internal static int GetUpper<T, U, X>(NativeArray<T> values, int count, U check, X condition) | ||
|  |             where T : struct where U : struct where X : ICondition2<T, U> | ||
|  |         { | ||
|  |             int l = 0; | ||
|  |             int h = count - 1; | ||
|  |             int i = h + 1; | ||
|  |             while (l <= h) | ||
|  |             { | ||
|  |                 int m = ((int)(l + h)) >> 1; | ||
|  |                 float t = 0; | ||
|  |                 if (condition.Test(values[m], check, ref t)) | ||
|  |                 { | ||
|  |                     i = m; | ||
|  |                     h = m - 1; | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     l = m + 1; | ||
|  |                 } | ||
|  |             } | ||
|  |             return i; | ||
|  |         } | ||
|  | 
 | ||
|  |         // Search for Equal | ||
|  |         internal static int GetEqual<T, U, X>(NativeArray<T> values, int count, U check, X condition) | ||
|  |             where T : struct where U : struct where X : ICondition2<T, U> | ||
|  |         { | ||
|  |             int l = 0; | ||
|  |             int h = count - 1; | ||
|  |             while (l <= h) | ||
|  |             { | ||
|  |                 int m = ((int)(l + h)) >> 1; | ||
|  |                 float t = 0; | ||
|  |                 condition.Test(values[m], check, ref t); | ||
|  |                 if (t == 0) | ||
|  |                 { | ||
|  |                     return m; | ||
|  |                 } | ||
|  |                 else if (t <= 0) | ||
|  |                 { | ||
|  |                     l = m + 1; | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     h = m - 1; | ||
|  |                 } | ||
|  |             } | ||
|  |             return -1; | ||
|  |         } | ||
|  | 
 | ||
|  |         // Simple Orientation test. | ||
|  |         internal static float OrientFast(float2 a, float2 b, float2 c) | ||
|  |         { | ||
|  |             float epsilon = 1.1102230246251565e-16f; | ||
|  |             float det = (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y); | ||
|  |             if (math.abs(det) < epsilon) return 0; | ||
|  |             return det; | ||
|  |         } | ||
|  | 
 | ||
|  |         // This is needed when doing PlanarGraph as it requires high precision separation of points. | ||
|  |         internal static double OrientFastDouble(double2 a, double2 b, double2 c) | ||
|  |         { | ||
|  |             double epsilon = 1.1102230246251565e-16f; | ||
|  |             double det = (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y); | ||
|  |             if (math.abs(det) < epsilon) return 0; | ||
|  |             return det; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static UCircle CircumCircle(UTriangle tri) | ||
|  |         { | ||
|  |             float xa = tri.va.x * tri.va.x; | ||
|  |             float xb = tri.vb.x * tri.vb.x; | ||
|  |             float xc = tri.vc.x * tri.vc.x; | ||
|  |             float ya = tri.va.y * tri.va.y; | ||
|  |             float yb = tri.vb.y * tri.vb.y; | ||
|  |             float yc = tri.vc.y * tri.vc.y; | ||
|  |             float c = 2f * ((tri.vb.x - tri.va.x) * (tri.vc.y - tri.va.y) - (tri.vb.y - tri.va.y) * (tri.vc.x - tri.va.x)); | ||
|  |             float x = ((tri.vc.y - tri.va.y) * (xb - xa + yb - ya) + (tri.va.y - tri.vb.y) * (xc - xa + yc - ya)) / c; | ||
|  |             float y = ((tri.va.x - tri.vc.x) * (xb - xa + yb - ya) + (tri.vb.x - tri.va.x) * (xc - xa + yc - ya)) / c; | ||
|  |             float vx = (tri.va.x - x); | ||
|  |             float vy = (tri.va.y - y); | ||
|  |             return new UCircle { center = new float2(x, y), radius = math.sqrt((vx * vx) + (vy * vy)) }; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static bool IsInsideCircle(UCircle c, float2 v) | ||
|  |         { | ||
|  |             return math.distance(v, c.center) < c.radius; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static float TriangleArea(float2 va, float2 vb, float2 vc) | ||
|  |         { | ||
|  |             float3 a = new float3(va.x, va.y, 0); | ||
|  |             float3 b = new float3(vb.x, vb.y, 0); | ||
|  |             float3 c = new float3(vc.x, vc.y, 0); | ||
|  |             float3 v = math.cross(a - b, a - c); | ||
|  |             return math.abs(v.z) * 0.5f; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static float Sign(float2 p1, float2 p2, float2 p3) | ||
|  |         { | ||
|  |             return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y); | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static bool IsInsideTriangle(float2 pt, float2 v1, float2 v2, float2 v3) | ||
|  |         { | ||
|  |             float d1, d2, d3; | ||
|  |             bool has_neg, has_pos; | ||
|  | 
 | ||
|  |             d1 = Sign(pt, v1, v2); | ||
|  |             d2 = Sign(pt, v2, v3); | ||
|  |             d3 = Sign(pt, v3, v1); | ||
|  | 
 | ||
|  |             has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0); | ||
|  |             has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0); | ||
|  | 
 | ||
|  |             return !(has_neg && has_pos); | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static bool IsInsideTriangleApproximate(float2 pt, float2 v1, float2 v2, float2 v3) | ||
|  |         { | ||
|  |             float d0, d1, d2, d3; | ||
|  |             d0 = TriangleArea(v1, v2, v3); | ||
|  |             d1 = TriangleArea(pt, v1, v2); | ||
|  |             d2 = TriangleArea(pt, v2, v3); | ||
|  |             d3 = TriangleArea(pt, v3, v1); | ||
|  |             float epsilon = 1.1102230246251565e-16f; | ||
|  |             return Mathf.Abs(d0 - (d1 + d2 + d3)) < epsilon; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static bool IsInsideCircle(float2 a, float2 b, float2 c, float2 p) | ||
|  |         { | ||
|  |             float ab = math.dot(a, a); | ||
|  |             float cd = math.dot(b, b); | ||
|  |             float ef = math.dot(c, c); | ||
|  | 
 | ||
|  |             float ax = a.x; | ||
|  |             float ay = a.y; | ||
|  |             float bx = b.x; | ||
|  |             float by = b.y; | ||
|  |             float cx = c.x; | ||
|  |             float cy = c.y; | ||
|  | 
 | ||
|  |             float circum_x = (ab * (cy - by) + cd * (ay - cy) + ef * (by - ay)) / | ||
|  |                 (ax * (cy - by) + bx * (ay - cy) + cx * (by - ay)); | ||
|  |             float circum_y = (ab * (cx - bx) + cd * (ax - cx) + ef * (bx - ax)) / | ||
|  |                 (ay * (cx - bx) + by * (ax - cx) + cy * (bx - ax)); | ||
|  | 
 | ||
|  |             float2 circum = new float2(); | ||
|  |             circum.x = circum_x / 2; | ||
|  |             circum.y = circum_y / 2; | ||
|  |             float circum_radius = math.distance(a, circum); | ||
|  |             float dist = math.distance(p, circum); | ||
|  |             return circum_radius - dist > 0.00001f; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static void BuildTriangles(NativeArray<float2> vertices, int vertexCount, NativeArray<int> indices, int indexCount, ref NativeArray<UTriangle> triangles, ref int triangleCount, ref float maxArea, ref float avgArea, ref float minArea) | ||
|  |         { | ||
|  |             // Check if there are invalid triangles or segments. | ||
|  |             for (int i = 0; i < indexCount; i += 3) | ||
|  |             { | ||
|  |                 UTriangle tri = new UTriangle(); | ||
|  |                 var i0 = indices[i + 0]; | ||
|  |                 var i1 = indices[i + 1]; | ||
|  |                 var i2 = indices[i + 2]; | ||
|  |                 tri.va = vertices[i0]; | ||
|  |                 tri.vb = vertices[i1]; | ||
|  |                 tri.vc = vertices[i2]; | ||
|  |                 tri.c = CircumCircle(tri); | ||
|  |                 tri.area = TriangleArea(tri.va, tri.vb, tri.vc); | ||
|  |                 maxArea = math.max(tri.area, maxArea); | ||
|  |                 minArea = math.min(tri.area, minArea); | ||
|  |                 avgArea = avgArea + tri.area; | ||
|  |                 triangles[triangleCount++] = tri; | ||
|  |             } | ||
|  |             avgArea = avgArea / triangleCount; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static void BuildTriangles(NativeArray<float2> vertices, int vertexCount, NativeArray<int> indices, int indexCount, ref NativeArray<UTriangle> triangles, ref int triangleCount, ref float maxArea, ref float avgArea, ref float minArea, ref float maxEdge, ref float avgEdge, ref float minEdge) | ||
|  |         { | ||
|  |             // Check if there are invalid triangles or segments. | ||
|  |             for (int i = 0; i < indexCount; i += 3) | ||
|  |             { | ||
|  |                 UTriangle tri = new UTriangle(); | ||
|  |                 var i0 = indices[i + 0]; | ||
|  |                 var i1 = indices[i + 1]; | ||
|  |                 var i2 = indices[i + 2]; | ||
|  |                 tri.va = vertices[i0]; | ||
|  |                 tri.vb = vertices[i1]; | ||
|  |                 tri.vc = vertices[i2]; | ||
|  |                 tri.c = CircumCircle(tri); | ||
|  | 
 | ||
|  |                 tri.area = TriangleArea(tri.va, tri.vb, tri.vc); | ||
|  |                 maxArea = math.max(tri.area, maxArea); | ||
|  |                 minArea = math.min(tri.area, minArea); | ||
|  |                 avgArea = avgArea + tri.area; | ||
|  | 
 | ||
|  |                 var e1 = math.distance(tri.va, tri.vb); | ||
|  |                 var e2 = math.distance(tri.vb, tri.vc); | ||
|  |                 var e3 = math.distance(tri.vc, tri.va); | ||
|  |                 maxEdge = math.max(e1, maxEdge); | ||
|  |                 maxEdge = math.max(e2, maxEdge); | ||
|  |                 maxEdge = math.max(e3, maxEdge); | ||
|  |                 minEdge = math.min(e1, minEdge); | ||
|  |                 minEdge = math.min(e2, minEdge); | ||
|  |                 minEdge = math.min(e3, minEdge); | ||
|  | 
 | ||
|  |                 avgEdge = avgEdge + e1; | ||
|  |                 avgEdge = avgEdge + e2; | ||
|  |                 avgEdge = avgEdge + e3; | ||
|  |                 triangles[triangleCount++] = tri; | ||
|  |             } | ||
|  |             avgArea = avgArea / triangleCount; | ||
|  |             avgEdge = avgEdge / indexCount; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static void BuildTrianglesAndEdges(NativeArray<float2> vertices, int vertexCount, NativeArray<int> indices, int indexCount, ref NativeArray<UTriangle> triangles, ref int triangleCount, ref NativeArray<int4> delaEdges, ref int delaEdgeCount, ref float maxArea, ref float avgArea, ref float minArea) | ||
|  |         { | ||
|  |             // Check if there are invalid triangles or segments. | ||
|  |             for (int i = 0; i < indexCount; i += 3) | ||
|  |             { | ||
|  |                 UTriangle tri = new UTriangle(); | ||
|  |                 var i0 = indices[i + 0]; | ||
|  |                 var i1 = indices[i + 1]; | ||
|  |                 var i2 = indices[i + 2]; | ||
|  |                 tri.va = vertices[i0]; | ||
|  |                 tri.vb = vertices[i1]; | ||
|  |                 tri.vc = vertices[i2]; | ||
|  |                 tri.c = CircumCircle(tri); | ||
|  |                 tri.area = TriangleArea(tri.va, tri.vb, tri.vc); | ||
|  |                 maxArea = math.max(tri.area, maxArea); | ||
|  |                 minArea = math.min(tri.area, minArea); | ||
|  |                 avgArea = avgArea + tri.area; | ||
|  |                 tri.indices = new int3(i0, i1, i2); | ||
|  | 
 | ||
|  |                 // Outputs. | ||
|  |                 delaEdges[delaEdgeCount++] = new int4(math.min(i0, i1), math.max(i0, i1), triangleCount, -1); | ||
|  |                 delaEdges[delaEdgeCount++] = new int4(math.min(i1, i2), math.max(i1, i2), triangleCount, -1); | ||
|  |                 delaEdges[delaEdgeCount++] = new int4(math.min(i2, i0), math.max(i2, i0), triangleCount, -1); | ||
|  |                 triangles[triangleCount++] = tri; | ||
|  |             } | ||
|  |             avgArea = avgArea / triangleCount; | ||
|  |         } | ||
|  | 
 | ||
|  |         static void CopyGraph(NativeArray<float2> srcPoints, int srcPointCount, ref NativeArray<float2> dstPoints, ref int dstPointCount, NativeArray<int2> srcEdges, int srcEdgeCount, ref NativeArray<int2> dstEdges, ref int dstEdgeCount) | ||
|  |         { | ||
|  |             dstEdgeCount = srcEdgeCount; | ||
|  |             dstPointCount = srcPointCount; | ||
|  |             Copy(srcEdges, dstEdges, srcEdgeCount); | ||
|  |             Copy(srcPoints, dstPoints, srcPointCount); | ||
|  |         } | ||
|  | 
 | ||
|  |         static void CopyGeometry(NativeArray<int> srcIndices, int srcIndexCount, ref NativeArray<int> dstIndices, ref int dstIndexCount, NativeArray<float2> srcVertices, int srcVertexCount, ref NativeArray<float2> dstVertices, ref int dstVertexCount) | ||
|  |         { | ||
|  |             dstIndexCount = srcIndexCount; | ||
|  |             dstVertexCount = srcVertexCount; | ||
|  |             Copy(srcIndices, dstIndices, srcIndexCount); | ||
|  |             Copy(srcVertices, dstVertices, srcVertexCount); | ||
|  |         } | ||
|  | 
 | ||
|  |         static void TransferOutput(NativeArray<int2> srcEdges, int srcEdgeCount, ref NativeArray<int2> dstEdges, ref int dstEdgeCount, NativeArray<int> srcIndices, int srcIndexCount, ref NativeArray<int> dstIndices, ref int dstIndexCount, NativeArray<float2> srcVertices, int srcVertexCount, ref NativeArray<float2> dstVertices, ref int dstVertexCount) | ||
|  |         { | ||
|  |             dstEdgeCount = srcEdgeCount; | ||
|  |             dstIndexCount = srcIndexCount; | ||
|  |             dstVertexCount = srcVertexCount; | ||
|  |             Copy(srcEdges, dstEdges, srcEdgeCount); | ||
|  |             Copy(srcIndices, dstIndices, srcIndexCount); | ||
|  |             Copy(srcVertices, dstVertices, srcVertexCount); | ||
|  |         } | ||
|  | 
 | ||
|  |         static void GraphConditioner(NativeArray<float2> points, ref NativeArray<float2> pgPoints, ref int pgPointCount, ref NativeArray<int2> pgEdges, ref int pgEdgeCount, bool resetTopology) | ||
|  |         { | ||
|  |             var min = new float2(math.INFINITY, math.INFINITY); | ||
|  |             var max = float2.zero; | ||
|  |             for (int i = 0; i < points.Length; ++i) | ||
|  |             { | ||
|  |                 min = math.min(points[i], min); | ||
|  |                 max = math.max(points[i], max); | ||
|  |             } | ||
|  | 
 | ||
|  |             var ext = (max - min); | ||
|  |             var mid = ext * 0.5f; | ||
|  |             var kNonRect = 0.0001f; | ||
|  | 
 | ||
|  |             // Construct a simple convex hull rect!. | ||
|  |             pgPointCount = resetTopology ? 0 : pgPointCount; | ||
|  |             var pc = pgPointCount; | ||
|  |             pgPoints[pgPointCount++] = new float2(min.x, min.y); pgPoints[pgPointCount++] = new float2(min.x - kNonRect, min.y + mid.y); pgPoints[pgPointCount++] = new float2(min.x, max.y); pgPoints[pgPointCount++] = new float2(min.x + mid.x, max.y + kNonRect); | ||
|  |             pgPoints[pgPointCount++] = new float2(max.x, max.y); pgPoints[pgPointCount++] = new float2(max.x + kNonRect, min.y + mid.y); pgPoints[pgPointCount++] = new float2(max.x, min.y); pgPoints[pgPointCount++] = new float2(min.x + mid.x, min.y - kNonRect); | ||
|  | 
 | ||
|  |             pgEdgeCount = 8; | ||
|  |             pgEdges[0] = new int2(pc + 0, pc + 1); pgEdges[1] = new int2(pc + 1, pc + 2); pgEdges[2] = new int2(pc + 2, pc + 3); pgEdges[3] = new int2(pc + 3, pc + 4); | ||
|  |             pgEdges[4] = new int2(pc + 4, pc + 5); pgEdges[5] = new int2(pc + 5, pc + 6); pgEdges[6] = new int2(pc + 6, pc + 7); pgEdges[7] = new int2(pc + 7, pc + 0); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Reorder vertices. | ||
|  |         static void Reorder(int startVertexCount, int index, ref NativeArray<int> indices, ref int indexCount, ref NativeArray<float2> vertices, ref int vertexCount) | ||
|  |         { | ||
|  |             var found = false; | ||
|  | 
 | ||
|  |             for (var i = 0; i < indexCount; ++i) | ||
|  |             { | ||
|  |                 if (indices[i] != index) continue; | ||
|  |                 found = true; | ||
|  |                 break; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (!found) | ||
|  |             { | ||
|  |                 vertexCount--; | ||
|  |                 vertices[index] = vertices[vertexCount]; | ||
|  |                 for (var i = 0; i < indexCount; ++i) | ||
|  |                     if (indices[i] == vertexCount) | ||
|  |                         indices[i] = index; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // Perform Sanitization. | ||
|  |         internal static void VertexCleanupConditioner(int startVertexCount, ref NativeArray<int> indices, ref int indexCount, ref NativeArray<float2> vertices, ref int vertexCount) | ||
|  |         { | ||
|  |             for (int i = startVertexCount; i < vertexCount; ++i) | ||
|  |             { | ||
|  |                 Reorder(startVertexCount, i, ref indices, ref indexCount, ref vertices, ref vertexCount); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public static float4 ConvexQuad(Allocator allocator, NativeArray<float2> points, NativeArray<int2> edges, ref NativeArray<float2> outVertices, ref int outVertexCount, ref NativeArray<int> outIndices, ref int outIndexCount, ref NativeArray<int2> outEdges, ref int outEdgeCount) | ||
|  |         { | ||
|  |             // Inputs are garbage, just early out. | ||
|  |             float4 ret = float4.zero; | ||
|  |             outEdgeCount = 0; outIndexCount = 0; outVertexCount = 0; | ||
|  |             if (points.Length < 3 || points.Length >= kMaxVertexCount) | ||
|  |                 return ret; | ||
|  | 
 | ||
|  |             // Ensure inputs form a proper PlanarGraph. | ||
|  |             int pgEdgeCount = 0, pgPointCount = 0; | ||
|  |             NativeArray<int2> pgEdges = new NativeArray<int2>(kMaxEdgeCount, allocator); | ||
|  |             NativeArray<float2> pgPoints = new NativeArray<float2>(kMaxVertexCount, allocator); | ||
|  | 
 | ||
|  |             // Valid Edges and Paths, correct the Planar Graph. If invalid create a simple convex hull rect. | ||
|  |             GraphConditioner(points, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount, true); | ||
|  |             Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref outVertices, ref outVertexCount, ref outIndices, ref outIndexCount); | ||
|  | 
 | ||
|  |             // Dispose Temp Memory. | ||
|  |             pgPoints.Dispose(); | ||
|  |             pgEdges.Dispose(); | ||
|  |             return ret; | ||
|  |         } | ||
|  | 
 | ||
|  |         public static float4 Tessellate(Allocator allocator, NativeArray<float2> points, NativeArray<int2> edges, ref NativeArray<float2> outVertices, ref int outVertexCount, ref NativeArray<int> outIndices, ref int outIndexCount, ref NativeArray<int2> outEdges, ref int outEdgeCount) | ||
|  |         { | ||
|  |             // Inputs are garbage, just early out. | ||
|  |             float4 ret = float4.zero; | ||
|  |             outEdgeCount = 0; outIndexCount = 0; outVertexCount = 0; | ||
|  |             if (points.Length < 3 || points.Length >= kMaxVertexCount) | ||
|  |                 return ret; | ||
|  | 
 | ||
|  |             // Ensure inputs form a proper PlanarGraph. | ||
|  |             bool validGraph = false, handleEdgeCase = false; | ||
|  |             int pgEdgeCount = 0, pgPointCount = 0; | ||
|  |             NativeArray<int2> pgEdges = new NativeArray<int2>(edges.Length * 8, allocator); | ||
|  |             NativeArray<float2> pgPoints = new NativeArray<float2>(points.Length * 4, allocator); | ||
|  | 
 | ||
|  |             // Valid Edges and Paths, correct the Planar Graph. If invalid create a simple convex hull rect. | ||
|  |             if (0 != edges.Length) | ||
|  |             { | ||
|  |                 validGraph = PlanarGraph.Validate(allocator, points, points.Length, edges, edges.Length, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount); | ||
|  |             } | ||
|  | 
 | ||
|  |             // Fallbacks are now handled by the Higher level packages. Enable if UTess needs to handle it. | ||
|  |             // #if UTESS_QUAD_FALLBACK | ||
|  |             //             if (!validGraph) | ||
|  |             //             { | ||
|  |             //                 pgPointCount = 0; | ||
|  |             //                 handleEdgeCase = true; | ||
|  |             //                 ModuleHandle.Copy(points, pgPoints, points.Length); | ||
|  |             //                 GraphConditioner(points, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount, false); | ||
|  |             //             } | ||
|  |             // #else | ||
|  | 
 | ||
|  |             // If its not a valid Graph simply return back input Data without triangulation instead of going through UTess (pointless wasted cpu cycles). | ||
|  |             if (!validGraph) | ||
|  |             { | ||
|  |                 outEdgeCount = edges.Length; | ||
|  |                 outVertexCount = points.Length; | ||
|  |                 ModuleHandle.Copy(edges, outEdges, edges.Length); | ||
|  |                 ModuleHandle.Copy(points, outVertices, points.Length); | ||
|  |             } | ||
|  | 
 | ||
|  |             // Do a proper Delaunay Triangulation if Inputs are valid. | ||
|  |             if (pgPointCount > 2 && pgEdgeCount > 2) | ||
|  |             { | ||
|  |                 // Tessellate does not add new points, only PG and SD does. Assuming each point creates a degenerate triangle, * 4 is more than enough. | ||
|  |                 NativeArray<int> tsIndices = new NativeArray<int>(pgPointCount * 8, allocator); | ||
|  |                 NativeArray<float2> tsVertices = new NativeArray<float2>(pgPointCount * 4, allocator); | ||
|  |                 int tsIndexCount = 0, tsVertexCount = 0; | ||
|  |                 validGraph = Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref tsVertices, ref tsVertexCount, ref tsIndices, ref tsIndexCount); | ||
|  |                 if (validGraph) | ||
|  |                 { | ||
|  |                     // Copy Out | ||
|  |                     TransferOutput(pgEdges, pgEdgeCount, ref outEdges, ref outEdgeCount, tsIndices, tsIndexCount, ref outIndices, ref outIndexCount, tsVertices, tsVertexCount, ref outVertices, ref outVertexCount); | ||
|  |                     if (handleEdgeCase == true) | ||
|  |                         outEdgeCount = 0; | ||
|  |                 } | ||
|  |                 tsVertices.Dispose(); | ||
|  |                 tsIndices.Dispose(); | ||
|  |             } | ||
|  | 
 | ||
|  |             // Dispose Temp Memory. | ||
|  |             pgPoints.Dispose(); | ||
|  |             pgEdges.Dispose(); | ||
|  |             return ret; | ||
|  |         } | ||
|  |     } | ||
|  | } |