using Unity.Collections; using Unity.Mathematics; namespace UnityEngine.Rendering.Universal.UTess { struct Refinery { // Old min and max are 0.5 and 0.05. We pretty much do the same with some relaxing on both ends. private static readonly float kMinAreaFactor = 0.0482f; private static readonly float kMaxAreaFactor = 0.4820f; // After doing more tests with a number of Sprites, this is area to which we can reduce to considering quality and CPU cost. private static readonly int kMaxSteinerCount = 4084; // Check if Triangle is Ok. static bool RequiresRefining(UTriangle tri, float maxArea) { // Add any further criteria later on. return (tri.area > maxArea); } static void FetchEncroachedSegments(NativeArray<float2> pgPoints, int pgPointCount, NativeArray<int2> pgEdges, int pgEdgeCount, ref NativeArray<UEncroachingSegment> encroach, ref int encroachCount, UCircle c) { for (int i = 0; i < pgEdgeCount; ++i) { var edge = pgEdges[i]; var edgeA = pgPoints[edge.x]; var edgeB = pgPoints[edge.y]; // Check if center is along the Edge. if (!math.any(c.center - edgeA) || !math.any(c.center - edgeB)) continue; // Get Radius var edgeD = edgeA - edgeB; var edgeM = (edgeA + edgeB) * 0.5f; var edgeR = math.length(edgeD) * 0.5f; if (math.length(edgeM - c.center) > edgeR) continue; UEncroachingSegment es = new UEncroachingSegment(); es.a = edgeA; es.b = edgeB; es.index = i; encroach[encroachCount++] = es; } } static void InsertVertex(ref NativeArray<float2> pgPoints, ref int pgPointCount, float2 newVertex, ref int nid) { nid = pgPointCount; pgPoints[nid] = newVertex; pgPointCount++; } static int FindSegment(NativeArray<float2> pgPoints, int pgPointCount, NativeArray<int2> pgEdges, int pgEdgeCount, UEncroachingSegment es) { for (int i = es.index; i < pgEdgeCount; ++i) { var edge = pgEdges[i]; var edgeA = pgPoints[edge.x]; var edgeB = pgPoints[edge.y]; if (math.any(edgeA - es.a) || math.any(edgeB - es.b)) continue; return i; } return -1; } static void SplitSegments(ref NativeArray<float2> pgPoints, ref int pgPointCount, ref NativeArray<int2> pgEdges, ref int pgEdgeCount, UEncroachingSegment es) { var sid = FindSegment(pgPoints, pgPointCount, pgEdges, pgEdgeCount, es); if (sid == -1) return; var edge = pgEdges[sid]; var edgeA = pgPoints[edge.x]; var edgeB = pgPoints[edge.y]; var split = (edgeA + edgeB) * 0.5f; var neid = 0; if (math.abs(edge.x - edge.y) == 1) { neid = (edge.x > edge.y) ? edge.x : edge.y; InsertVertex(ref pgPoints, ref pgPointCount, split, ref neid); // Add the split segments. var rep = pgEdges[sid]; pgEdges[sid] = new int2(rep.x, neid); for (int i = pgEdgeCount; i > (sid + 1); --i) pgEdges[i] = pgEdges[i - 1]; pgEdges[sid + 1] = new int2(neid, rep.y); pgEdgeCount++; } else { neid = pgPointCount; pgPoints[pgPointCount++] = split; pgEdges[sid] = new int2(math.max(edge.x, edge.y), neid); pgEdges[pgEdgeCount++] = new int2(math.min(edge.x, edge.y), neid); } } internal static bool Condition(Allocator allocator, float factorArea, float targetArea, ref NativeArray<float2> pgPoints, ref int pgPointCount, ref NativeArray<int2> pgEdges, ref int pgEdgeCount, ref NativeArray<float2> vertices, ref int vertexCount, ref NativeArray<int> indices, ref int indexCount, ref float maxArea) { // Process Triangles. maxArea = 0.0f; var minArea = 0.0f; var avgArea = 0.0f; var refined = false; var validGraph = true; // Temporary Stuffs. int triangleCount = 0, invalidTriangle = -1, inputPointCount = pgPointCount; var encroach = new NativeArray<UEncroachingSegment>(ModuleHandle.kMaxEdgeCount, allocator); var triangles = new NativeArray<UTriangle>(ModuleHandle.kMaxTriangleCount, allocator); ModuleHandle.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref maxArea, ref avgArea, ref minArea); factorArea = factorArea != 0 ? math.clamp(factorArea, kMinAreaFactor, kMaxAreaFactor) : factorArea; var criArea = maxArea * factorArea; criArea = math.max(criArea, targetArea); // Refine while (!refined && validGraph) { // Check if any of the Triangle is Invalid or Segment is invalid. If yes, Refine. for (int i = 0; i < triangleCount; ++i) { if (RequiresRefining(triangles[i], criArea)) { invalidTriangle = i; break; } } // Find any Segment that can be Split based on the Input Length. // todo. if (invalidTriangle != -1) { // Get all Segments that are encroached. var t = triangles[invalidTriangle]; var encroachCount = 0; FetchEncroachedSegments(pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref encroach, ref encroachCount, t.c); // Split each Encroached Segments. If no segments are encroached. Split the Triangle. if (encroachCount != 0) { for (int i = 0; i < encroachCount; ++i) { SplitSegments(ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount, encroach[i]); } } else { // Update Triangulation. var split = t.c.center; pgPoints[pgPointCount++] = split; } // Tessellate again. indexCount = 0; vertexCount = 0; validGraph = Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref vertices, ref vertexCount, ref indices, ref indexCount); // Build Internal Triangles. encroachCount = 0; triangleCount = 0; invalidTriangle = -1; if (validGraph) ModuleHandle.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref maxArea, ref avgArea, ref minArea); // More than enough Steiner points inserted. This handles all sort of weird input sprites very well (even random point cloud). if (pgPointCount - inputPointCount > kMaxSteinerCount) break; } else { refined = true; } } // Dispose off triangles.Dispose(); encroach.Dispose(); return refined; } } }