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. outEdgeCount = 0; outIndexCount = 0; outVertexCount = 0; if (points.Length == 0 || points.Length >= kMaxVertexCount) { return(float4.zero); } // Ensure inputs form a proper PlanarGraph. bool validGraph = false; int pgEdgeCount = 0, pgPointCount = 0; float4 ret = float4.zero; 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. validGraph = PlanarGraph.Validate(allocator, points, points.Length, edges, edges.Length, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount); if (!validGraph) { GraphConditioner(points, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount); } // Do a proper Delaunay Triangulation. int tsIndexCount = 0, tsVertexCount = 0; NativeArray <int> tsIndices = new NativeArray <int>(kMaxIndexCount, allocator); NativeArray <float2> tsVertices = new NativeArray <float2>(kMaxVertexCount, allocator); validGraph = Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref tsVertices, ref tsVertexCount, ref tsIndices, ref tsIndexCount); if (!validGraph) { // Create a simplex convex hull rect. GraphConditioner(points, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount); tsIndexCount = 0; tsVertexCount = 0; Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref tsVertices, ref tsVertexCount, ref tsIndices, ref tsIndexCount); } // Copy Out TransferOutput(pgEdges, pgEdgeCount, ref outEdges, ref outEdgeCount, tsIndices, tsIndexCount, ref outIndices, ref outIndexCount, tsVertices, tsVertexCount, ref outVertices, ref outVertexCount); // Dispose Temp Memory. tsVertices.Dispose(); tsIndices.Dispose(); pgPoints.Dispose(); pgEdges.Dispose(); return(ret); }
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 refined = false; var validGraph = true; // Temporary Stuffs. int triangleCount = 0, invalidTriangle = -1, inputPointCount = pgPointCount; var encroach = new NativeArray <UEncroachingSegment>(UTess.kMaxEdgeCount, allocator); var triangles = new NativeArray <UTriangle>(UTess.kMaxTriangleCount, allocator); UTess.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref maxArea); 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) { UTess.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref maxArea); } // 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); }
public static float4 Subdivide(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, float areaFactor, float targetArea, int refineIterations, int smoothenIterations) { // Inputs are garbage, just early out. float4 ret = float4.zero; outEdgeCount = 0; outIndexCount = 0; outVertexCount = 0; if (points.Length < 3 || points.Length >= kMaxVertexCount || 0 == edges.Length) { return(ret); } // Do a proper Delaunay Triangulation. int tsIndexCount = 0, tsVertexCount = 0; NativeArray <int> tsIndices = new NativeArray <int>(kMaxIndexCount, allocator); NativeArray <float2> tsVertices = new NativeArray <float2>(kMaxVertexCount, allocator); var validGraph = Tessellator.Tessellate(allocator, points, points.Length, edges, edges.Length, ref tsVertices, ref tsVertexCount, ref tsIndices, ref tsIndexCount); // Refinement and Smoothing. bool refined = false; bool refinementRequired = (targetArea != 0 || areaFactor != 0); if (validGraph && refinementRequired) { // Do Refinement until success. float maxArea = 0; float incArea = 0; int rfEdgeCount = 0, rfPointCount = 0, rfIndexCount = 0, rfVertexCount = 0; NativeArray <int2> rfEdges = new NativeArray <int2>(kMaxEdgeCount, allocator); NativeArray <float2> rfPoints = new NativeArray <float2>(kMaxVertexCount, allocator); NativeArray <int> rfIndices = new NativeArray <int>(kMaxIndexCount, allocator); NativeArray <float2> rfVertices = new NativeArray <float2>(kMaxVertexCount, allocator); ret.x = 0; refineIterations = Math.Min(refineIterations, kMaxRefineIterations); if (targetArea != 0) { // Increment for Iterations. incArea = (targetArea / 10); while (targetArea < kMaxArea && refineIterations > 0) { // Do Mesh Refinement. CopyGraph(points, points.Length, ref rfPoints, ref rfPointCount, edges, edges.Length, ref rfEdges, ref rfEdgeCount); CopyGeometry(tsIndices, tsIndexCount, ref rfIndices, ref rfIndexCount, tsVertices, tsVertexCount, ref rfVertices, ref rfVertexCount); refined = Refinery.Condition(allocator, areaFactor, targetArea, ref rfPoints, ref rfPointCount, ref rfEdges, ref rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount, ref maxArea); if (refined && rfIndexCount > rfPointCount) { // Copy Out ret.x = areaFactor; TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount); break; } refined = false; targetArea = targetArea + incArea; refineIterations--; } } else if (areaFactor != 0) { // Increment for Iterations. areaFactor = math.lerp(0.1f, 0.54f, (areaFactor - 0.05f) / 0.45f); // Specific to Animation. incArea = (areaFactor / 10); while (areaFactor < 0.8f && refineIterations > 0) { // Do Mesh Refinement. CopyGraph(points, points.Length, ref rfPoints, ref rfPointCount, edges, edges.Length, ref rfEdges, ref rfEdgeCount); CopyGeometry(tsIndices, tsIndexCount, ref rfIndices, ref rfIndexCount, tsVertices, tsVertexCount, ref rfVertices, ref rfVertexCount); refined = Refinery.Condition(allocator, areaFactor, targetArea, ref rfPoints, ref rfPointCount, ref rfEdges, ref rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount, ref maxArea); if (refined && rfIndexCount > rfPointCount) { // Copy Out ret.x = areaFactor; TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount); break; } refined = false; areaFactor = areaFactor + incArea; refineIterations--; } } if (refined) { // Sanitize generated geometry data. var preSmoothen = outVertexCount; if (ret.x != 0) { VertexCleanupConditioner(tsVertexCount, ref rfIndices, ref rfIndexCount, ref rfVertices, ref rfVertexCount); } // Smoothen. At this point only vertex relocation is allowed, not vertex addition/removal. // Note: Only refined mesh contains Steiner points and we only smoothen these points. ret.y = 0; smoothenIterations = math.clamp(smoothenIterations, 0, kMaxSmoothenIterations); while (smoothenIterations > 0) { var smoothen = Smoothen.Condition(allocator, ref rfPoints, rfPointCount, rfEdges, rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount); if (!smoothen) { break; } // Copy Out ret.y = (float)(smoothenIterations); TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount); smoothenIterations--; } // Sanitize generated geometry data. var postSmoothen = outVertexCount; if (ret.y != 0) { VertexCleanupConditioner(tsVertexCount, ref outIndices, ref outIndexCount, ref outVertices, ref outVertexCount); } } rfVertices.Dispose(); rfIndices.Dispose(); rfPoints.Dispose(); rfEdges.Dispose(); } // Refinement failed but Graph succeeded. if (validGraph && !refined) { // Copy Out TransferOutput(edges, edges.Length, ref outEdges, ref outEdgeCount, tsIndices, tsIndexCount, ref outIndices, ref outIndexCount, tsVertices, tsVertexCount, ref outVertices, ref outVertexCount); } // Dispose Temp Memory. tsVertices.Dispose(); tsIndices.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>(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. 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) { int tsIndexCount = 0, tsVertexCount = 0; NativeArray <int> tsIndices = new NativeArray <int>(kMaxIndexCount, allocator); NativeArray <float2> tsVertices = new NativeArray <float2>(kMaxVertexCount, allocator); 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); }
// Perform Voronoi based Smoothing. Does not add/remove points but merely relocates internal vertices so they are uniform distributed. public static bool Condition(Allocator allocator, ref NativeArray <float2> pgPoints, int pgPointCount, NativeArray <int2> pgEdges, int pgEdgeCount, ref NativeArray <float2> vertices, ref int vertexCount, ref NativeArray <int> indices, ref int indexCount) { // Build Triangles and Edges. float maxArea = 0, cmpArea = 0; bool polygonCentroid = true, validGraph = true; int triangleCount = 0, delaEdgeCount = 0, affectingEdgeCount = 0; var triangles = new NativeArray <UTriangle>(indexCount / 3, allocator); var delaEdges = new NativeArray <int4>(indexCount, allocator); var voronoiEdges = new NativeArray <int4>(indexCount, allocator); var connectedTri = new NativeArray <int4>(vertexCount, allocator); var voronoiCheck = new NativeArray <int>(indexCount, allocator); var affectsEdges = new NativeArray <int>(indexCount, allocator); var triCentroids = new NativeArray <int>(vertexCount, allocator); UTess.BuildTrianglesAndEdges(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref delaEdges, ref delaEdgeCount, ref maxArea); var refinedEdges = new NativeArray <int4>(delaEdgeCount, allocator); // Sort the Delaunay Edges. unsafe { UTess.InsertionSort <int4, DelaEdgeCompare>( NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(delaEdges), 0, delaEdgeCount - 1, new DelaEdgeCompare()); } // TrimEdges. Update Triangle Info for Shared Edges and remove Duplicates. RefineEdges(ref refinedEdges, ref delaEdges, ref delaEdgeCount, ref voronoiEdges); // Now for each point, generate Voronoi diagram. for (int i = 0; i < vertexCount; ++i) { // Try moving this to Centroid of the Voronoi Polygon. GetAffectingEdges(i, delaEdges, delaEdgeCount, ref affectsEdges, ref voronoiCheck, ref affectingEdgeCount); var bounded = affectingEdgeCount != 0; // Check for Boundedness for (int j = 0; j < affectingEdgeCount; ++j) { // Edge Index. var ei = affectsEdges[j]; if (delaEdges[ei].z == -1 || delaEdges[ei].w == -1) { bounded = false; break; } } // If this is bounded point, relocate to Voronoi Diagram's Centroid if (bounded) { polygonCentroid = ConnectTriangles(ref connectedTri, ref affectsEdges, ref voronoiCheck, voronoiEdges, affectingEdgeCount); if (!polygonCentroid) { break; } float2 point = float2.zero; float area = 0, distance = 0; for (int k = 0; k < affectingEdgeCount; ++k) { CentroidByPolygon(connectedTri[k], triangles, ref point, ref area, ref distance); } point /= (3 * area); pgPoints[i] = point; } } // Do Delaunay Again. int srcIndexCount = indexCount, srcVertexCount = vertexCount; indexCount = 0; vertexCount = 0; triangleCount = 0; if (polygonCentroid) { validGraph = Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref vertices, ref vertexCount, ref indices, ref indexCount); if (validGraph) { UTess.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref cmpArea); } } // Cleanup. triangles.Dispose(); delaEdges.Dispose(); refinedEdges.Dispose(); voronoiCheck.Dispose(); voronoiEdges.Dispose(); affectsEdges.Dispose(); triCentroids.Dispose(); connectedTri.Dispose(); return(validGraph && srcIndexCount == indexCount && srcVertexCount == vertexCount && (cmpArea < maxArea * kAreaTolerance)); }
public static float4 Subdivide(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, float areaFactor, float targetArea, int smoothenCount) { // Inputs are garbage, just early out. outEdgeCount = 0; outIndexCount = 0; outVertexCount = 0; if (points.Length == 0) { return(float4.zero); } // Do a proper Delaunay Triangulation. float4 ret = float4.zero; int tsIndexCount = 0, tsVertexCount = 0; NativeArray <int> tsIndices = new NativeArray <int>(kMaxIndexCount, allocator); NativeArray <float2> tsVertices = new NativeArray <float2>(kMaxVertexCount, allocator); var validGraph = Tessellator.Tessellate(allocator, points, points.Length, edges, edges.Length, ref tsVertices, ref tsVertexCount, ref tsIndices, ref tsIndexCount); // Refinement and Smoothing. bool refined = false; bool refinementRequired = (targetArea != 0 || areaFactor != 0); if (validGraph && refinementRequired) { // Do Refinement until success. float maxArea = 0; int rfEdgeCount = 0, rfPointCount = 0, rfIndexCount = 0, rfVertexCount = 0; NativeArray <int2> rfEdges = new NativeArray <int2>(kMaxEdgeCount, allocator); NativeArray <float2> rfPoints = new NativeArray <float2>(kMaxVertexCount, allocator); NativeArray <int> rfIndices = new NativeArray <int>(kMaxIndexCount, allocator); NativeArray <float2> rfVertices = new NativeArray <float2>(kMaxVertexCount, allocator); if (targetArea != 0) { while (targetArea < kMaxArea) { // Do Mesh Refinement. CopyGraph(points, points.Length, ref rfPoints, ref rfPointCount, edges, edges.Length, ref rfEdges, ref rfEdgeCount); CopyGeometry(tsIndices, tsIndexCount, ref rfIndices, ref rfIndexCount, tsVertices, tsVertexCount, ref rfVertices, ref rfVertexCount); refined = Refinery.Condition(allocator, areaFactor, targetArea, ref rfPoints, ref rfPointCount, ref rfEdges, ref rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount, ref maxArea); if (refined && rfIndexCount > rfPointCount) { // Copy Out ret.x = areaFactor; TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount); break; } refined = false; targetArea = targetArea * 2.0f; } } else if (areaFactor != 0) { while (areaFactor < 0.8f) { // Do Mesh Refinement. CopyGraph(points, points.Length, ref rfPoints, ref rfPointCount, edges, edges.Length, ref rfEdges, ref rfEdgeCount); CopyGeometry(tsIndices, tsIndexCount, ref rfIndices, ref rfIndexCount, tsVertices, tsVertexCount, ref rfVertices, ref rfVertexCount); refined = Refinery.Condition(allocator, areaFactor, targetArea, ref rfPoints, ref rfPointCount, ref rfEdges, ref rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount, ref maxArea); if (refined && rfIndexCount > rfPointCount) { // Copy Out ret.x = areaFactor; TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount); break; } refined = false; areaFactor = areaFactor * 2.0f; } } if (refined) { // Smoothen. At this point only vertex relocation is allowed, not vertex addition/removal. // Note: Only refined mesh contains Steiner points and we only smoothen these points. smoothenCount = math.clamp(smoothenCount, 0, kMaxSmoothenIterations); while (smoothenCount > 0) { var smoothen = Smoothen.Condition(allocator, ref rfPoints, rfPointCount, rfEdges, rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount); if (!smoothen) { break; } // Copy Out ret.y = (float)(smoothenCount); TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount); smoothenCount--; } } rfVertices.Dispose(); rfIndices.Dispose(); rfPoints.Dispose(); rfEdges.Dispose(); } // Refinement failed but Graph succeeded. if (validGraph && !refined) { // Copy Out TransferOutput(edges, edges.Length, ref outEdges, ref outEdgeCount, tsIndices, tsIndexCount, ref outIndices, ref outIndexCount, tsVertices, tsVertexCount, ref outVertices, ref outVertexCount); } // Dispose Temp Memory. tsVertices.Dispose(); tsIndices.Dispose(); return(ret); }