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 pgPoints, int pgPointCount, NativeArray pgEdges, int pgEdgeCount, ref NativeArray 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 pgPoints, ref int pgPointCount, float2 newVertex, ref int nid) { nid = pgPointCount; pgPoints[nid] = newVertex; pgPointCount++; } static int FindSegment(NativeArray pgPoints, int pgPointCount, NativeArray 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 pgPoints, ref int pgPointCount, ref NativeArray 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 pgPoints, ref int pgPointCount, ref NativeArray pgEdges, ref int pgEdgeCount, ref NativeArray vertices, ref int vertexCount, ref NativeArray 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(ModuleHandle.kMaxEdgeCount, allocator); var triangles = new NativeArray(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; } } }