Singularity/Library/PackageCache/com.unity.render-pipelines..../Runtime/2D/UTess2D/Refinery.cs
2024-05-06 11:45:45 -07:00

192 lines
7.7 KiB
C#

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;
}
}
}