938 lines
44 KiB
C#
938 lines
44 KiB
C#
|
using System;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using Unity.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using UnityEngine.U2D;
|
||
|
using UnityEngine.Rendering.Universal.UTess;
|
||
|
using Unity.Collections.LowLevel.Unsafe;
|
||
|
using Unity.Mathematics;
|
||
|
using Unity.Burst;
|
||
|
|
||
|
|
||
|
|
||
|
namespace UnityEngine.Rendering.Universal
|
||
|
{
|
||
|
|
||
|
[BurstCompile]
|
||
|
internal class ShadowUtility
|
||
|
{
|
||
|
internal const int k_AdditionalVerticesPerEdge = 4;
|
||
|
internal const int k_VerticesPerTriangle = 3;
|
||
|
internal const int k_TrianglesPerEdge = 3;
|
||
|
internal const int k_MinimumEdges = 3;
|
||
|
internal const int k_SafeSize = 40;
|
||
|
|
||
|
public enum ProjectionType
|
||
|
{
|
||
|
ProjectionNone = -1,
|
||
|
ProjectionHard = 0,
|
||
|
ProjectionSoftLeft = 1,
|
||
|
ProjectionSoftRight = 3,
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
internal struct ShadowMeshVertex
|
||
|
{
|
||
|
internal Vector3 position; // stores: xy: position z: projection type w: soft shadow value (0 is fully shadowed)
|
||
|
internal Vector4 tangent; // stores: xy: contraction dir zw: other edge position
|
||
|
|
||
|
internal ShadowMeshVertex(ProjectionType inProjectionType, Vector2 inEdgePosition0, Vector2 inEdgePosition1)
|
||
|
{
|
||
|
position.x = inEdgePosition0.x;
|
||
|
position.y = inEdgePosition0.y;
|
||
|
position.z = 0;
|
||
|
tangent.x = (int)inProjectionType;
|
||
|
tangent.y = 0;
|
||
|
tangent.z = inEdgePosition1.x;
|
||
|
tangent.w = inEdgePosition1.y;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
internal struct RemappingInfo
|
||
|
{
|
||
|
public int count;
|
||
|
public int index;
|
||
|
public int v0Offset;
|
||
|
public int v1Offset;
|
||
|
|
||
|
public void Initialize()
|
||
|
{
|
||
|
count = 0;
|
||
|
index = -1;
|
||
|
v0Offset = 0;
|
||
|
v1Offset = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static VertexAttributeDescriptor[] m_VertexLayout = new VertexAttributeDescriptor[]
|
||
|
{
|
||
|
new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
|
||
|
new VertexAttributeDescriptor(VertexAttribute.Tangent, VertexAttributeFormat.Float32, 4),
|
||
|
};
|
||
|
|
||
|
|
||
|
unsafe static int GetNextShapeStart(int currentShape, int* inShapeStartingEdgePtr, int inShapeStartingEdgeLength, int maxValue)
|
||
|
{
|
||
|
// Make sure we are in the bounds of the shapes we have. Also make sure our starting edge isn't negative
|
||
|
return ((currentShape + 1 < inShapeStartingEdgeLength) && (inShapeStartingEdgePtr[currentShape + 1] >= 0)) ? inShapeStartingEdgePtr[currentShape + 1] : maxValue;
|
||
|
}
|
||
|
|
||
|
|
||
|
[BurstCompile]
|
||
|
static internal void CalculateProjectionInfo(ref NativeArray<Vector3> inVertices, ref NativeArray<ShadowEdge> inEdges, ref NativeArray<int> inShapeStartingEdge, ref NativeArray<bool> inShapeIsClosedArray, ref NativeArray<Vector2> outProjectionInfo)
|
||
|
{
|
||
|
unsafe
|
||
|
{
|
||
|
Vector3* inVerticesPtr = (Vector3*)inVertices.m_Buffer;
|
||
|
ShadowEdge* inEdgesPtr = (ShadowEdge*)inEdges.m_Buffer;
|
||
|
int* inShapeStartingEdgePtr = (int *)inShapeStartingEdge.m_Buffer;
|
||
|
bool* inShapeIsClosedArrayPtr = (bool*)inShapeIsClosedArray.m_Buffer;
|
||
|
Vector2* outProjectionInfoPtr = (Vector2*)outProjectionInfo.m_Buffer;
|
||
|
|
||
|
Vector2 tmpVec2 = new Vector2(); // So we don't call the constructor
|
||
|
|
||
|
int inEdgesLength = inEdges.Length;
|
||
|
int inShapeStartingEdgeLength = inShapeStartingEdge.Length;
|
||
|
int inVerticesLength = inVertices.Length;
|
||
|
|
||
|
int currentShape = 0;
|
||
|
int shapeStart = 0;
|
||
|
int nextShapeStart = GetNextShapeStart(currentShape, inShapeStartingEdgePtr, inShapeStartingEdgeLength, inEdgesLength);
|
||
|
int shapeSize = nextShapeStart;
|
||
|
|
||
|
for (int i = 0; i < inEdgesLength; i++)
|
||
|
{
|
||
|
if (i == nextShapeStart)
|
||
|
{
|
||
|
currentShape++;
|
||
|
shapeStart = nextShapeStart;
|
||
|
nextShapeStart = GetNextShapeStart(currentShape, inShapeStartingEdgePtr, inShapeStartingEdgeLength, inEdgesLength);
|
||
|
shapeSize = nextShapeStart - shapeStart;
|
||
|
}
|
||
|
|
||
|
int nextEdgeIndex = (i - shapeStart + 1) % shapeSize + shapeStart;
|
||
|
int prevEdgeIndex = (i - shapeStart + shapeSize - 1) % shapeSize + shapeStart;
|
||
|
|
||
|
|
||
|
int v0 = inEdgesPtr[i].v0;
|
||
|
int v1 = inEdgesPtr[i].v1;
|
||
|
|
||
|
int prev1 = inEdgesPtr[prevEdgeIndex].v0;
|
||
|
int next0 = inEdgesPtr[nextEdgeIndex].v1;
|
||
|
|
||
|
|
||
|
tmpVec2.x = inVerticesPtr[v0].x;
|
||
|
tmpVec2.y = inVerticesPtr[v0].y;
|
||
|
Vector2 startPt = tmpVec2;
|
||
|
|
||
|
tmpVec2.x = inVerticesPtr[v1].x;
|
||
|
tmpVec2.y = inVerticesPtr[v1].y;
|
||
|
Vector2 endPt = tmpVec2;
|
||
|
|
||
|
|
||
|
tmpVec2.x = inVerticesPtr[prev1].x;
|
||
|
tmpVec2.y = inVerticesPtr[prev1].y;
|
||
|
Vector2 prevPt = tmpVec2;
|
||
|
|
||
|
tmpVec2.x = inVerticesPtr[next0].x;
|
||
|
tmpVec2.y = inVerticesPtr[next0].y;
|
||
|
Vector2 nextPt = tmpVec2;
|
||
|
|
||
|
// Original Vertex
|
||
|
outProjectionInfoPtr[v0] = endPt;
|
||
|
|
||
|
// Hard Shadows
|
||
|
int additionalVerticesStart = k_AdditionalVerticesPerEdge * i + inVerticesLength;
|
||
|
outProjectionInfoPtr[additionalVerticesStart] = endPt;
|
||
|
outProjectionInfoPtr[additionalVerticesStart + 1] = startPt;
|
||
|
|
||
|
// Soft Triangles
|
||
|
outProjectionInfoPtr[additionalVerticesStart + 2] = endPt;
|
||
|
outProjectionInfoPtr[additionalVerticesStart + 3] = endPt;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
[BurstCompile]
|
||
|
static internal void CalculateVertices(ref NativeArray<Vector3> inVertices, ref NativeArray<ShadowEdge> inEdges, ref NativeArray<Vector2> inEdgeOtherPoints, ref NativeArray<ShadowMeshVertex> outMeshVertices)
|
||
|
{
|
||
|
unsafe
|
||
|
{
|
||
|
Vector3* inVerticesPtr = (Vector3*)inVertices.m_Buffer;
|
||
|
ShadowEdge* inEdgesPtr = (ShadowEdge*)inEdges.m_Buffer;
|
||
|
Vector2* inEdgeOtherPointsPtr = (Vector2*)inEdgeOtherPoints.m_Buffer;
|
||
|
ShadowMeshVertex* outMeshVerticesPtr = (ShadowMeshVertex*)outMeshVertices.m_Buffer;
|
||
|
|
||
|
Vector2 tmpVec2 = new Vector2(); // So we don't call the constructor
|
||
|
|
||
|
int inEdgesLength = inEdges.Length;
|
||
|
int inVerticesLength = inVertices.Length;
|
||
|
|
||
|
|
||
|
for (int i = 0; i < inVerticesLength; i++)
|
||
|
{
|
||
|
tmpVec2.x = inVerticesPtr[i].x;
|
||
|
tmpVec2.y = inVerticesPtr[i].y;
|
||
|
ShadowMeshVertex originalShadowMesh = new ShadowMeshVertex(ProjectionType.ProjectionNone, tmpVec2, inEdgeOtherPointsPtr[i]);
|
||
|
outMeshVerticesPtr[i] = originalShadowMesh;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < inEdgesLength; i++)
|
||
|
{
|
||
|
int v0 = inEdgesPtr[i].v0;
|
||
|
int v1 = inEdgesPtr[i].v1;
|
||
|
|
||
|
|
||
|
tmpVec2.x = inVerticesPtr[v0].x;
|
||
|
tmpVec2.y = inVerticesPtr[v0].y;
|
||
|
Vector2 pt0 = tmpVec2;
|
||
|
|
||
|
tmpVec2.x = inVerticesPtr[v1].x;
|
||
|
tmpVec2.y = inVerticesPtr[v1].y;
|
||
|
Vector2 pt1 = tmpVec2;
|
||
|
|
||
|
int additionalVerticesStart = k_AdditionalVerticesPerEdge * i + inVerticesLength;
|
||
|
ShadowMeshVertex additionalVertex0 = new ShadowMeshVertex(ProjectionType.ProjectionHard, pt0, inEdgeOtherPointsPtr[additionalVerticesStart]);
|
||
|
ShadowMeshVertex additionalVertex1 = new ShadowMeshVertex(ProjectionType.ProjectionHard, pt1, inEdgeOtherPointsPtr[additionalVerticesStart + 1]);
|
||
|
ShadowMeshVertex additionalVertex2 = new ShadowMeshVertex(ProjectionType.ProjectionSoftLeft, pt0, inEdgeOtherPointsPtr[additionalVerticesStart + 2]);
|
||
|
ShadowMeshVertex additionalVertex3 = new ShadowMeshVertex(ProjectionType.ProjectionSoftRight, pt0, inEdgeOtherPointsPtr[additionalVerticesStart + 3]);
|
||
|
|
||
|
outMeshVerticesPtr[additionalVerticesStart] = additionalVertex0;
|
||
|
outMeshVerticesPtr[additionalVerticesStart + 1] = additionalVertex1;
|
||
|
outMeshVerticesPtr[additionalVerticesStart + 2] = additionalVertex2;
|
||
|
outMeshVerticesPtr[additionalVerticesStart + 3] = additionalVertex3;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[BurstCompile]
|
||
|
static internal void CalculateTriangles(ref NativeArray<Vector3> inVertices, ref NativeArray<ShadowEdge> inEdges, ref NativeArray<int> inShapeStartingEdge, ref NativeArray<bool> inShapeIsClosedArray, ref NativeArray<int> outMeshIndices)
|
||
|
{
|
||
|
unsafe
|
||
|
{
|
||
|
ShadowEdge* inEdgesPtr = (ShadowEdge*)inEdges.m_Buffer;
|
||
|
int* inShapeStartingEdgePtr = (int*)inShapeStartingEdge.m_Buffer;
|
||
|
int* outMeshIndicesPtr = (int*)outMeshIndices.m_Buffer;
|
||
|
|
||
|
int inEdgesLength = inEdges.Length;
|
||
|
int inShapeStartingEdgeLength = inShapeStartingEdge.Length;
|
||
|
int inVerticesLength = inVertices.Length;
|
||
|
|
||
|
int meshIndex = 0;
|
||
|
for (int shapeIndex = 0; shapeIndex < inShapeStartingEdgeLength; shapeIndex++)
|
||
|
{
|
||
|
int startingIndex = inShapeStartingEdgePtr[shapeIndex];
|
||
|
if (startingIndex < 0)
|
||
|
return;
|
||
|
|
||
|
int endIndex = inEdgesLength;
|
||
|
if ((shapeIndex + 1) < inShapeStartingEdgeLength && inShapeStartingEdgePtr[shapeIndex + 1] > -1)
|
||
|
endIndex = inShapeStartingEdgePtr[shapeIndex + 1];
|
||
|
|
||
|
//// Hard Shadow Geometry
|
||
|
int prevEdge = endIndex - 1;
|
||
|
for (int i = startingIndex; i < endIndex; i++)
|
||
|
{
|
||
|
int v0 = inEdgesPtr[i].v0;
|
||
|
int v1 = inEdgesPtr[i].v1;
|
||
|
|
||
|
int additionalVerticesStart = k_AdditionalVerticesPerEdge * i + inVerticesLength;
|
||
|
|
||
|
// Add a degenerate rectangle
|
||
|
outMeshIndicesPtr[meshIndex++] = (ushort)v0;
|
||
|
outMeshIndicesPtr[meshIndex++] = (ushort)additionalVerticesStart;
|
||
|
outMeshIndicesPtr[meshIndex++] = (ushort)(additionalVerticesStart + 1);
|
||
|
outMeshIndicesPtr[meshIndex++] = (ushort)(additionalVerticesStart + 1);
|
||
|
outMeshIndicesPtr[meshIndex++] = (ushort)v1;
|
||
|
outMeshIndicesPtr[meshIndex++] = (ushort)v0;
|
||
|
|
||
|
prevEdge = i;
|
||
|
}
|
||
|
|
||
|
// Soft Shadow Geometry
|
||
|
for (int i = startingIndex; i < endIndex; i++)
|
||
|
//int i = 0;
|
||
|
{
|
||
|
int v0 = inEdgesPtr[i].v0;
|
||
|
int v1 = inEdgesPtr[i].v1;
|
||
|
|
||
|
int additionalVerticesStart = k_AdditionalVerticesPerEdge * i + inVerticesLength;
|
||
|
|
||
|
// We also need 1 more triangles for soft shadows (3 indices)
|
||
|
outMeshIndicesPtr[meshIndex++] = (ushort)v0;
|
||
|
outMeshIndicesPtr[meshIndex++] = (ushort)additionalVerticesStart + 2;
|
||
|
outMeshIndicesPtr[meshIndex++] = (ushort)additionalVerticesStart + 3;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[BurstCompile]
|
||
|
static internal void CalculateLocalBounds(ref NativeArray<Vector3> inVertices, out Bounds retBounds)
|
||
|
{
|
||
|
if (inVertices.Length <= 0)
|
||
|
{
|
||
|
retBounds = new Bounds(Vector3.zero, Vector3.zero);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
Vector2 minVec = Vector2.positiveInfinity;
|
||
|
Vector2 maxVec = Vector2.negativeInfinity;
|
||
|
|
||
|
unsafe
|
||
|
{
|
||
|
Vector3* inVerticesPtr = (Vector3*)inVertices.m_Buffer;
|
||
|
int inVerticesLength = inVertices.Length;
|
||
|
|
||
|
// Add outline vertices
|
||
|
for (int i = 0; i < inVerticesLength; i++)
|
||
|
{
|
||
|
Vector2 vertex = new Vector2(inVerticesPtr[i].x, inVerticesPtr[i].y);
|
||
|
|
||
|
minVec = Vector2.Min(minVec, vertex);
|
||
|
maxVec = Vector2.Max(maxVec, vertex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
retBounds = new Bounds { max = maxVec, min = minVec };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[BurstCompile]
|
||
|
static void GenerateInteriorMesh(ref NativeArray<ShadowMeshVertex> inVertices, ref NativeArray<int> inIndices, ref NativeArray<ShadowEdge> inEdges, out NativeArray<ShadowMeshVertex> outVertices, out NativeArray<int> outIndices, out int outStartIndex, out int outIndexCount)
|
||
|
{
|
||
|
int inEdgeCount = inEdges.Length;
|
||
|
|
||
|
// Do tessellation
|
||
|
NativeArray<int2> tessInEdges = new NativeArray<int2>(inEdgeCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||
|
NativeArray<float2> tessInVertices = new NativeArray<float2>(inEdgeCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||
|
|
||
|
for (int i = 0; i < inEdgeCount; i++)
|
||
|
{
|
||
|
int2 edge = new int2(inEdges[i].v0, inEdges[i].v1);
|
||
|
tessInEdges[i] = edge;
|
||
|
|
||
|
int index = edge.x;
|
||
|
tessInVertices[index] = new float2(inVertices[index].position.x, inVertices[index].position.y);
|
||
|
}
|
||
|
|
||
|
NativeArray<int> tessOutIndices = new NativeArray<int>(tessInVertices.Length * 8, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||
|
NativeArray<float2> tessOutVertices = new NativeArray<float2>(tessInVertices.Length * 4, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||
|
NativeArray<int2> tessOutEdges = new NativeArray<int2>(tessInEdges.Length * 4, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||
|
int tessOutVertexCount = 0;
|
||
|
int tessOutIndexCount = 0;
|
||
|
int tessOutEdgeCount = 0;
|
||
|
|
||
|
UTess.ModuleHandle.Tessellate(Allocator.Persistent, tessInVertices, tessInEdges, ref tessOutVertices, ref tessOutVertexCount, ref tessOutIndices, ref tessOutIndexCount, ref tessOutEdges, ref tessOutEdgeCount);
|
||
|
|
||
|
int indexOffset = inIndices.Length;
|
||
|
int vertexOffset = inVertices.Length;
|
||
|
int totalOutVertices = tessOutVertexCount + inVertices.Length;
|
||
|
int totalOutIndices = tessOutIndexCount + inIndices.Length;
|
||
|
outVertices = new NativeArray<ShadowMeshVertex>(totalOutVertices, Allocator.Persistent, NativeArrayOptions.ClearMemory);
|
||
|
outIndices = new NativeArray<int>(totalOutIndices, Allocator.Persistent, NativeArrayOptions.ClearMemory);
|
||
|
|
||
|
// Copy vertices
|
||
|
for (int i = 0; i < inVertices.Length; i++)
|
||
|
outVertices[i] = inVertices[i];
|
||
|
|
||
|
for (int i = 0; i < tessOutVertexCount; i++)
|
||
|
{
|
||
|
float2 tessVertex = tessOutVertices[i];
|
||
|
ShadowMeshVertex vertex = new ShadowMeshVertex(ProjectionType.ProjectionNone, tessVertex, Vector2.zero);
|
||
|
outVertices[i + vertexOffset] = vertex;
|
||
|
}
|
||
|
|
||
|
// Copy indices
|
||
|
for (int i = 0; i < inIndices.Length; i++)
|
||
|
outIndices[i] = inIndices[i];
|
||
|
|
||
|
// Copy and remap indices
|
||
|
for (int i = 0; i < tessOutIndexCount; i++)
|
||
|
{
|
||
|
outIndices[i + indexOffset] = tessOutIndices[i] + vertexOffset;
|
||
|
}
|
||
|
|
||
|
outStartIndex = indexOffset;
|
||
|
outIndexCount = tessOutIndexCount;
|
||
|
|
||
|
tessInEdges.Dispose();
|
||
|
tessInVertices.Dispose();
|
||
|
tessOutIndices.Dispose();
|
||
|
tessOutVertices.Dispose();
|
||
|
tessOutEdges.Dispose();
|
||
|
}
|
||
|
|
||
|
//inEdges is expected to be contiguous
|
||
|
static public Bounds GenerateShadowMesh(Mesh mesh, NativeArray<Vector3> inVertices, NativeArray<ShadowEdge> inEdges, NativeArray<int> inShapeStartingEdge, NativeArray<bool> inShapeIsClosedArray, bool allowContraction, bool fill, ShadowShape2D.OutlineTopology topology)
|
||
|
{
|
||
|
// Setup our buffers
|
||
|
int meshVertexCount = inVertices.Length + k_AdditionalVerticesPerEdge * inEdges.Length; // Each vertex will have a duplicate that can be extruded.
|
||
|
int meshIndexCount = inEdges.Length * k_VerticesPerTriangle * k_TrianglesPerEdge; // There are two triangles per edge making a degenerate rectangle (0 area)
|
||
|
|
||
|
NativeArray<Vector2> meshProjectionInfo = new NativeArray<Vector2>(meshVertexCount, Allocator.Persistent);
|
||
|
NativeArray<int> meshIndices = new NativeArray<int>(meshIndexCount, Allocator.Persistent);
|
||
|
NativeArray<ShadowMeshVertex> meshVertices = new NativeArray<ShadowMeshVertex>(meshVertexCount, Allocator.Persistent);
|
||
|
|
||
|
CalculateProjectionInfo(ref inVertices, ref inEdges, ref inShapeStartingEdge, ref inShapeIsClosedArray, ref meshProjectionInfo);
|
||
|
CalculateVertices(ref inVertices, ref inEdges, ref meshProjectionInfo, ref meshVertices);
|
||
|
CalculateTriangles(ref inVertices, ref inEdges, ref inShapeStartingEdge, ref inShapeIsClosedArray, ref meshIndices);
|
||
|
|
||
|
NativeArray<ShadowMeshVertex> finalVertices;
|
||
|
NativeArray<int> finalIndices;
|
||
|
int fillSubmeshStartIndex = 0;
|
||
|
int fillSubmeshIndexCount = 0;
|
||
|
|
||
|
if (fill) // This has limited utility at the moment as contraction is not calculated. More work will need to be done to generalize this
|
||
|
{
|
||
|
GenerateInteriorMesh(ref meshVertices, ref meshIndices, ref inEdges, out finalVertices, out finalIndices, out fillSubmeshStartIndex, out fillSubmeshIndexCount);
|
||
|
meshVertices.Dispose();
|
||
|
meshIndices.Dispose();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
finalVertices = meshVertices;
|
||
|
finalIndices = meshIndices;
|
||
|
}
|
||
|
|
||
|
// Set the mesh data
|
||
|
mesh.SetVertexBufferParams(finalVertices.Length, m_VertexLayout);
|
||
|
mesh.SetVertexBufferData<ShadowMeshVertex>(finalVertices, 0, 0, finalVertices.Length);
|
||
|
mesh.SetIndexBufferParams(finalIndices.Length, IndexFormat.UInt32);
|
||
|
mesh.SetIndexBufferData<int>(finalIndices, 0, 0, finalIndices.Length);
|
||
|
|
||
|
mesh.SetSubMesh(0, new SubMeshDescriptor(0, finalIndices.Length));
|
||
|
mesh.subMeshCount = 1;
|
||
|
|
||
|
meshProjectionInfo.Dispose();
|
||
|
finalVertices.Dispose();
|
||
|
finalIndices.Dispose();
|
||
|
|
||
|
CalculateLocalBounds(ref inVertices, out Bounds retLocalBound);
|
||
|
return retLocalBound;
|
||
|
}
|
||
|
|
||
|
|
||
|
[BurstCompile]
|
||
|
static public void CalculateEdgesFromLines(ref NativeArray<int> indices, out NativeArray<ShadowEdge> outEdges, out NativeArray<int> outShapeStartingEdge, out NativeArray<bool> outShapeIsClosedArray)
|
||
|
{
|
||
|
unsafe
|
||
|
{
|
||
|
int numOfEdges = indices.Length >> 1;
|
||
|
NativeArray<int> tempShapeStartIndices = new NativeArray<int>(numOfEdges, Allocator.Persistent);
|
||
|
NativeArray<bool> tempShapeIsClosedArray = new NativeArray<bool>(numOfEdges, Allocator.Persistent);
|
||
|
|
||
|
int* indicesPtr = (int*)indices.m_Buffer;
|
||
|
int* tempShapeStartIndicesPtr = (int*)tempShapeStartIndices.m_Buffer;
|
||
|
bool* tempShapeIsClosedArrayPtr = (bool*)tempShapeIsClosedArray.m_Buffer;
|
||
|
|
||
|
int indicesLength = indices.Length;
|
||
|
|
||
|
// Find the shape starting indices and allow contraction
|
||
|
int shapeCount = 0;
|
||
|
int shapeStart = indicesPtr[0];
|
||
|
int lastIndex = indicesPtr[0];
|
||
|
bool closedShapeFound = false;
|
||
|
tempShapeStartIndicesPtr[0] = 0;
|
||
|
|
||
|
for (int i = 0; i < indicesLength; i += 2)
|
||
|
{
|
||
|
if (closedShapeFound)
|
||
|
{
|
||
|
shapeStart = indicesPtr[i];
|
||
|
tempShapeIsClosedArrayPtr[shapeCount] = true;
|
||
|
tempShapeStartIndicesPtr[++shapeCount] = i >> 1;
|
||
|
closedShapeFound = false;
|
||
|
}
|
||
|
else if (indicesPtr[i] != lastIndex)
|
||
|
{
|
||
|
tempShapeIsClosedArrayPtr[shapeCount] = false;
|
||
|
tempShapeStartIndicesPtr[++shapeCount] = i >> 1;
|
||
|
shapeStart = indicesPtr[i];
|
||
|
}
|
||
|
|
||
|
if (shapeStart == indicesPtr[i + 1])
|
||
|
closedShapeFound = true;
|
||
|
|
||
|
lastIndex = indicesPtr[i + 1];
|
||
|
}
|
||
|
|
||
|
tempShapeIsClosedArrayPtr[shapeCount++] = closedShapeFound;
|
||
|
|
||
|
// Copy the our data to a smaller array
|
||
|
outShapeStartingEdge = new NativeArray<int>(shapeCount, Allocator.Persistent);
|
||
|
outShapeIsClosedArray = new NativeArray<bool>(shapeCount, Allocator.Persistent);
|
||
|
int* outShapeStartingEdgePtr = (int*)outShapeStartingEdge.m_Buffer;
|
||
|
bool* outShapeIsClosedArrayPtr = (bool*)outShapeIsClosedArray.m_Buffer;
|
||
|
|
||
|
for (int i = 0; i < shapeCount; i++)
|
||
|
{
|
||
|
outShapeStartingEdgePtr[i] = tempShapeStartIndicesPtr[i];
|
||
|
outShapeIsClosedArrayPtr[i] = tempShapeIsClosedArrayPtr[i];
|
||
|
}
|
||
|
tempShapeStartIndices.Dispose();
|
||
|
tempShapeIsClosedArray.Dispose();
|
||
|
|
||
|
// Add edges
|
||
|
outEdges = new NativeArray<ShadowEdge>(numOfEdges, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||
|
ShadowEdge* outEdgesPtr = (ShadowEdge*)outEdges.m_Buffer;
|
||
|
for (int i = 0; i < numOfEdges; i++)
|
||
|
{
|
||
|
int indicesIndex = i << 1;
|
||
|
int v0Index = indicesPtr[indicesIndex];
|
||
|
int v1Index = indicesPtr[indicesIndex + 1];
|
||
|
|
||
|
outEdgesPtr[i] = new ShadowEdge(v0Index, v1Index);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
[BurstCompile]
|
||
|
static internal void GetVertexReferenceStats(ref NativeArray<Vector3> vertices, ref NativeArray<ShadowEdge> edges, int vertexCount, out bool hasReusedVertices, out int newVertexCount, out NativeArray<RemappingInfo> remappingInfo)
|
||
|
{
|
||
|
unsafe
|
||
|
{
|
||
|
int edgeCount = edges.Length;
|
||
|
|
||
|
newVertexCount = 0;
|
||
|
hasReusedVertices = false;
|
||
|
remappingInfo = new NativeArray<RemappingInfo>(vertexCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||
|
|
||
|
RemappingInfo* remappingInfoPtr = (RemappingInfo*)remappingInfo.GetUnsafePtr();
|
||
|
ShadowEdge* edgesPtr = (ShadowEdge*)edges.GetUnsafePtr();
|
||
|
|
||
|
// Clear the remapping info
|
||
|
for (int i = 0; i < vertexCount; i++)
|
||
|
remappingInfoPtr[i].Initialize();
|
||
|
|
||
|
// Process v0
|
||
|
for (int i = 0; i < edgeCount; i++)
|
||
|
{
|
||
|
int v0 = edgesPtr[i].v0;
|
||
|
remappingInfoPtr[v0].count = remappingInfoPtr[v0].count + 1;
|
||
|
if (remappingInfoPtr[v0].count > 1)
|
||
|
hasReusedVertices = true;
|
||
|
|
||
|
newVertexCount++;
|
||
|
}
|
||
|
|
||
|
// Process v1
|
||
|
for (int i = 0; i < edgeCount; i++)
|
||
|
{
|
||
|
int v1 = edgesPtr[i].v1;
|
||
|
if (remappingInfoPtr[v1].count == 0) // This is an open shape
|
||
|
{
|
||
|
remappingInfoPtr[v1].count = 1;
|
||
|
newVertexCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Find the starts of the new indices..
|
||
|
int startPos = 0;
|
||
|
for (int i=0;i<vertexCount;i++)
|
||
|
{
|
||
|
// Leave the other indices -1 for easier validation testing
|
||
|
if (remappingInfoPtr[i].count > 0)
|
||
|
{
|
||
|
remappingInfoPtr[i].index = startPos;
|
||
|
startPos += remappingInfoPtr[i].count;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static public bool IsTriangleReversed(NativeArray<Vector3> vertices, int idx0, int idx1, int idx2)
|
||
|
{
|
||
|
Vector3 v0 = vertices[idx0];
|
||
|
Vector3 v1 = vertices[idx1];
|
||
|
Vector3 v2 = vertices[idx2];
|
||
|
|
||
|
float twiceArea = (v0.x * v1.y + v1.x * v2.y + v2.x * v0.y) - (v0.y * v1.x + v1.y * v2.x + v2.y * v0.x);
|
||
|
return Mathf.Sign(twiceArea) >= 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
[BurstCompile]
|
||
|
static public void CalculateEdgesFromTriangles(ref NativeArray<Vector3> vertices, ref NativeArray<int> indices, bool duplicatesVertices, out NativeArray<Vector3> newVertices, out NativeArray<ShadowEdge> outEdges, out NativeArray<int> outShapeStartingEdge, out NativeArray<bool> outShapeIsClosedArray)
|
||
|
{
|
||
|
unsafe
|
||
|
{
|
||
|
// Run clipper to calculate edges..
|
||
|
Clipper2D.Solution solution = new Clipper2D.Solution();
|
||
|
Clipper2D.ExecuteArguments executeArguments = new Clipper2D.ExecuteArguments(Clipper2D.InitOptions.ioDefault, Clipper2D.ClipType.ctUnion);
|
||
|
|
||
|
int triangleCount = indices.Length / 3;
|
||
|
NativeArray<Vector2> points = new NativeArray<Vector2>(indices.Length, Allocator.Persistent);
|
||
|
NativeArray<int> pathSizes = new NativeArray<int>(triangleCount, Allocator.Persistent);
|
||
|
NativeArray<Clipper2D.PathArguments> pathArguments = new NativeArray<Clipper2D.PathArguments>(triangleCount, Allocator.Persistent);
|
||
|
|
||
|
// Pointers to our native arrays for performance in editor
|
||
|
Vector2* pointsPtr = (Vector2*)points.GetUnsafePtr<Vector2>();
|
||
|
int* pathSizesPtr = (int*)pathSizes.GetUnsafePtr<int>();
|
||
|
Clipper2D.PathArguments* pathArgumentsPtr = (Clipper2D.PathArguments*)pathArguments.GetUnsafePtr<Clipper2D.PathArguments>();
|
||
|
Vector3* verticesPtr = (Vector3*)vertices.GetUnsafePtr<Vector3>();
|
||
|
|
||
|
// Copy input data for Clipper2D.Execute
|
||
|
Clipper2D.PathArguments sharedPathArg = new Clipper2D.PathArguments(Clipper2D.PolyType.ptSubject, true);
|
||
|
for (int i = 0; i < triangleCount; i++)
|
||
|
{
|
||
|
pathSizesPtr[i] = 3;
|
||
|
pathArgumentsPtr[i] = sharedPathArg;
|
||
|
|
||
|
int pointOffset = 3 * i;
|
||
|
pointsPtr[pointOffset] = verticesPtr[indices[pointOffset]];
|
||
|
pointsPtr[pointOffset + 1] = verticesPtr[indices[pointOffset + 1]];
|
||
|
pointsPtr[pointOffset + 2] = verticesPtr[indices[pointOffset + 2]];
|
||
|
}
|
||
|
|
||
|
Clipper2D.Execute(ref solution, points, pathSizes, pathArguments, executeArguments, Allocator.Persistent);
|
||
|
|
||
|
// Cleanup execute inputs because we have necessary data in our solution
|
||
|
points.Dispose();
|
||
|
pathSizes.Dispose();
|
||
|
pathArguments.Dispose();
|
||
|
|
||
|
// Copy solution to outputs
|
||
|
int pointLen = solution.points.Length;
|
||
|
int shapeCount = solution.pathSizes.Length;
|
||
|
newVertices = new NativeArray<Vector3>(pointLen, Allocator.Persistent);
|
||
|
outEdges = new NativeArray<ShadowEdge>(pointLen, Allocator.Persistent);
|
||
|
outShapeStartingEdge = new NativeArray<int>(shapeCount, Allocator.Persistent);
|
||
|
outShapeIsClosedArray = new NativeArray<bool>(shapeCount, Allocator.Persistent);
|
||
|
|
||
|
// More pointers for edtor time perfomance
|
||
|
int* solutionPathSizesPtr = (int*)solution.pathSizes.GetUnsafePtr<int>();
|
||
|
Vector2* solutionPointsPtr = (Vector2*)solution.points.GetUnsafePtr<Vector2>();
|
||
|
|
||
|
Vector3* newVerticesPtr = (Vector3*)newVertices.GetUnsafePtr<Vector3>();
|
||
|
ShadowEdge* outEdgesPtr = (ShadowEdge*)outEdges.GetUnsafePtr<ShadowEdge>();
|
||
|
int* outShapeStartingEdgePtr = (int*)outShapeStartingEdge.GetUnsafePtr<int>();
|
||
|
bool* outShapeIsClosedArrayPtr = (bool*)outShapeIsClosedArray.GetUnsafePtr<bool>();
|
||
|
|
||
|
// Copy output data from the solution
|
||
|
int nextStart = 0;
|
||
|
for (int shapeIndex = 0; shapeIndex < shapeCount; shapeIndex++)
|
||
|
{
|
||
|
// Copy shape info to outputs
|
||
|
int curStart = nextStart;
|
||
|
int curPathSize = solutionPathSizesPtr[shapeIndex];
|
||
|
outShapeStartingEdgePtr[shapeIndex] = nextStart;
|
||
|
nextStart += curPathSize;
|
||
|
|
||
|
// Copy vertices and edges to outputs;
|
||
|
int previousVertex = nextStart - 1;
|
||
|
for (int pointIndex = curStart; pointIndex < nextStart; pointIndex++)
|
||
|
{
|
||
|
newVerticesPtr[pointIndex] = solutionPointsPtr[pointIndex];
|
||
|
outEdgesPtr[pointIndex] = new ShadowEdge(previousVertex, pointIndex);
|
||
|
previousVertex = pointIndex;
|
||
|
}
|
||
|
|
||
|
// All shapes are closed since they are created from triangles
|
||
|
outShapeIsClosedArrayPtr[shapeIndex] = true;
|
||
|
}
|
||
|
|
||
|
// Cleanup solution
|
||
|
solution.Dispose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
[BurstCompile]
|
||
|
static public void ReverseWindingOrder(ref NativeArray<int> inShapeStartingEdge, ref NativeArray<ShadowEdge> inOutSortedEdges)
|
||
|
{
|
||
|
for (int shapeIndex = 0; shapeIndex < inShapeStartingEdge.Length; shapeIndex++)
|
||
|
{
|
||
|
int startingIndex = inShapeStartingEdge[shapeIndex];
|
||
|
if (startingIndex < 0)
|
||
|
return;
|
||
|
|
||
|
int endIndex = inOutSortedEdges.Length;
|
||
|
if ((shapeIndex + 1) < inShapeStartingEdge.Length && inShapeStartingEdge[shapeIndex + 1] > -1)
|
||
|
endIndex = inShapeStartingEdge[shapeIndex + 1];
|
||
|
|
||
|
// Reverse the winding order
|
||
|
int count = (endIndex - startingIndex);
|
||
|
for (int i = 0; i < (count >> 1); i++)
|
||
|
{
|
||
|
int edgeAIndex = startingIndex + i;
|
||
|
int edgeBIndex = startingIndex + count - 1 - i;
|
||
|
|
||
|
ShadowEdge edgeA = inOutSortedEdges[edgeAIndex];
|
||
|
ShadowEdge edgeB = inOutSortedEdges[edgeBIndex];
|
||
|
|
||
|
edgeA.Reverse();
|
||
|
edgeB.Reverse();
|
||
|
|
||
|
inOutSortedEdges[edgeAIndex] = edgeB;
|
||
|
inOutSortedEdges[edgeBIndex] = edgeA;
|
||
|
}
|
||
|
|
||
|
bool isOdd = (count & 1) == 1;
|
||
|
if (isOdd)
|
||
|
{
|
||
|
int edgeAIndex = startingIndex + (count >> 1);
|
||
|
ShadowEdge edgeA = inOutSortedEdges[edgeAIndex];
|
||
|
|
||
|
edgeA.Reverse();
|
||
|
inOutSortedEdges[edgeAIndex] = edgeA;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int GetClosedPathCount(ref NativeArray<int> inShapeStartingEdge, ref NativeArray<bool> inShapeIsClosedArray)
|
||
|
{
|
||
|
int count = 0;
|
||
|
for(int i=0;i<inShapeStartingEdge.Length;i++)
|
||
|
{
|
||
|
if (inShapeStartingEdge[i] < 0)
|
||
|
break;
|
||
|
|
||
|
count++;
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void GetPathInfo(NativeArray<ShadowEdge> inEdges, NativeArray<int> inShapeStartingEdge, NativeArray<bool> inShapeIsClosedArray, out int closedPathArrayCount, out int closedPathsCount, out int openPathArrayCount, out int openPathsCount)
|
||
|
{
|
||
|
closedPathArrayCount = 0;
|
||
|
openPathArrayCount = 0;
|
||
|
closedPathsCount = 0;
|
||
|
openPathsCount = 0;
|
||
|
|
||
|
for (int i = 0; i < inShapeStartingEdge.Length; i++)
|
||
|
{
|
||
|
// If this shape starting edge is invalid stop..
|
||
|
if (inShapeStartingEdge[i] < 0)
|
||
|
break;
|
||
|
|
||
|
|
||
|
int start = inShapeStartingEdge[i];
|
||
|
int end = (i < (inShapeStartingEdge.Length - 1 )) && (inShapeStartingEdge[i + 1] != -1) ? inShapeStartingEdge[i + 1] : inEdges.Length;
|
||
|
int edges = end - start;
|
||
|
if (inShapeIsClosedArray[i])
|
||
|
{
|
||
|
closedPathArrayCount += edges + 1;
|
||
|
closedPathsCount++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
openPathArrayCount += edges + 1;
|
||
|
openPathsCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
[BurstCompile]
|
||
|
static public void ClipEdges(ref NativeArray<Vector3> inVertices, ref NativeArray<ShadowEdge> inEdges, ref NativeArray<int> inShapeStartingEdge, ref NativeArray<bool> inShapeIsClosedArray, float contractEdge, out NativeArray<Vector3> outVertices, out NativeArray<ShadowEdge> outEdges, out NativeArray<int> outShapeStartingEdge)
|
||
|
{
|
||
|
unsafe
|
||
|
{
|
||
|
Allocator k_ClippingAllocator = Allocator.Persistent;
|
||
|
int k_Precision = 65536;
|
||
|
|
||
|
int closedPathCount;
|
||
|
int closedPathArrayCount;
|
||
|
int openPathCount;
|
||
|
int openPathArrayCount;
|
||
|
GetPathInfo(inEdges, inShapeStartingEdge, inShapeIsClosedArray, out closedPathArrayCount, out closedPathCount, out openPathArrayCount, out openPathCount);
|
||
|
|
||
|
NativeArray<Clipper2D.PathArguments> clipperPathArguments = new NativeArray<Clipper2D.PathArguments>(closedPathCount, k_ClippingAllocator, NativeArrayOptions.ClearMemory);
|
||
|
NativeArray<int> closedPathSizes = new NativeArray<int>(closedPathCount, k_ClippingAllocator);
|
||
|
NativeArray<Vector2> closedPath = new NativeArray<Vector2>(closedPathArrayCount, k_ClippingAllocator);
|
||
|
NativeArray<int> openPathSizes = new NativeArray<int>(openPathCount, k_ClippingAllocator);
|
||
|
NativeArray<Vector2> openPath = new NativeArray<Vector2>(openPathArrayCount, k_ClippingAllocator);
|
||
|
|
||
|
Clipper2D.PathArguments* clipperPathArgumentsPtr = (Clipper2D.PathArguments*)clipperPathArguments.m_Buffer;
|
||
|
int* closedPathSizesPtr = (int*)closedPathSizes.m_Buffer;
|
||
|
Vector2* closedPathPtr = (Vector2*)closedPath.m_Buffer;
|
||
|
int* openPathSizesPtr = (int*)openPathSizes.m_Buffer;
|
||
|
Vector2* openPathPtr = (Vector2*)openPath.m_Buffer;
|
||
|
|
||
|
int* inShapeStartingEdgePtr = (int*)inShapeStartingEdge.m_Buffer;
|
||
|
bool* inShapeIsClosedArrayPtr = (bool*)inShapeIsClosedArray.m_Buffer;
|
||
|
Vector3* inVerticesPtr = (Vector3*)inVertices.m_Buffer;
|
||
|
ShadowEdge* inEdgesPtr = (ShadowEdge*)inEdges.m_Buffer;
|
||
|
|
||
|
|
||
|
int inEdgesLength = inEdges.Length;
|
||
|
|
||
|
Vector2 tmpVec2 = new Vector2(); // So we don't call the constructor
|
||
|
Vector3 tmpVec3 = Vector3.zero;
|
||
|
|
||
|
|
||
|
// Seperate out our closed and open shapes. Closed shapes will go through clipper. Open shapes will just be copied.
|
||
|
int closedPathArrayIndex = 0;
|
||
|
int closedPathSizesIndex = 0;
|
||
|
int openPathArrayIndex = 0;
|
||
|
int openPathSizesIndex = 0;
|
||
|
int totalPathCount = closedPathCount + openPathCount;
|
||
|
for (int shapeStartIndex = 0; (shapeStartIndex < totalPathCount); shapeStartIndex++)
|
||
|
{
|
||
|
int currentShapeStart = inShapeStartingEdgePtr[shapeStartIndex];
|
||
|
int nextShapeStart = (shapeStartIndex + 1) < (totalPathCount) ? inShapeStartingEdgePtr[shapeStartIndex + 1] : inEdgesLength;
|
||
|
int numberOfEdges = nextShapeStart - currentShapeStart;
|
||
|
|
||
|
// If we have a closed shape then add it to our path and path sizes.
|
||
|
if (inShapeIsClosedArrayPtr[shapeStartIndex])
|
||
|
{
|
||
|
closedPathSizesPtr[closedPathSizesIndex] = numberOfEdges + 1;
|
||
|
clipperPathArgumentsPtr[closedPathSizesIndex] = new Clipper2D.PathArguments(Clipper2D.PolyType.ptSubject, true);
|
||
|
closedPathSizesIndex++;
|
||
|
|
||
|
for (int i = 0; i < numberOfEdges; i++)
|
||
|
{
|
||
|
Vector3 vec3 = inVerticesPtr[inEdgesPtr[i + currentShapeStart].v0];
|
||
|
tmpVec2.x = vec3.x;
|
||
|
tmpVec2.y = vec3.y;
|
||
|
closedPathPtr[closedPathArrayIndex++] = tmpVec2;
|
||
|
}
|
||
|
|
||
|
closedPathPtr[closedPathArrayIndex++] = inVerticesPtr[inEdgesPtr[numberOfEdges + currentShapeStart - 1].v1];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
openPathSizesPtr[openPathSizesIndex++] = numberOfEdges + 1;
|
||
|
for (int i = 0; i < numberOfEdges; i++)
|
||
|
{
|
||
|
|
||
|
Vector3 vec3 = inVerticesPtr[inEdgesPtr[i + currentShapeStart].v0];
|
||
|
tmpVec2.x = vec3.x;
|
||
|
tmpVec2.y = vec3.y;
|
||
|
openPathPtr[openPathArrayIndex++] = tmpVec2;
|
||
|
}
|
||
|
|
||
|
openPathPtr[openPathArrayIndex++] = inVerticesPtr[inEdgesPtr[numberOfEdges + currentShapeStart - 1].v1];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
NativeArray<Vector2> clipperOffsetPath = closedPath;
|
||
|
NativeArray<int> clipperOffsetPathSizes = closedPathSizes;
|
||
|
|
||
|
Clipper2D.Solution clipperSolution = new Clipper2D.Solution();
|
||
|
|
||
|
// Run this to try to merge outlines if there is more than one
|
||
|
if (closedPathSizes.Length > 1)
|
||
|
{
|
||
|
Clipper2D.ExecuteArguments executeArguments = new Clipper2D.ExecuteArguments();
|
||
|
executeArguments.clipType = Clipper2D.ClipType.ctUnion;
|
||
|
executeArguments.clipFillType = Clipper2D.PolyFillType.pftEvenOdd;
|
||
|
executeArguments.subjFillType = Clipper2D.PolyFillType.pftEvenOdd;
|
||
|
executeArguments.strictlySimple = false;
|
||
|
executeArguments.preserveColinear = false;
|
||
|
Clipper2D.Execute(ref clipperSolution, closedPath, closedPathSizes, clipperPathArguments, executeArguments, k_ClippingAllocator, inIntScale: k_Precision, useRounding: true);
|
||
|
|
||
|
clipperOffsetPath = clipperSolution.points;
|
||
|
clipperOffsetPathSizes = clipperSolution.pathSizes;
|
||
|
}
|
||
|
|
||
|
ClipperOffset2D.Solution offsetSolution = new ClipperOffset2D.Solution();
|
||
|
NativeArray<ClipperOffset2D.PathArguments> offsetPathArguments = new NativeArray<ClipperOffset2D.PathArguments>(clipperOffsetPathSizes.Length, k_ClippingAllocator, NativeArrayOptions.ClearMemory);
|
||
|
ClipperOffset2D.Execute(ref offsetSolution, clipperOffsetPath, clipperOffsetPathSizes, offsetPathArguments, k_ClippingAllocator, -contractEdge, inIntScale: k_Precision);
|
||
|
|
||
|
|
||
|
if (offsetSolution.pathSizes.Length > 0 || openPathCount > 0)
|
||
|
{
|
||
|
int vertexPos = 0;
|
||
|
|
||
|
// Combine the solutions from clipper and our open paths
|
||
|
int solutionPathLens = offsetSolution.pathSizes.Length + openPathCount;
|
||
|
outVertices = new NativeArray<Vector3>(offsetSolution.points.Length + openPathArrayCount, k_ClippingAllocator);
|
||
|
outEdges = new NativeArray<ShadowEdge>(offsetSolution.points.Length + openPathArrayCount, k_ClippingAllocator);
|
||
|
outShapeStartingEdge = new NativeArray<int>(solutionPathLens, k_ClippingAllocator);
|
||
|
|
||
|
Vector3* outVerticesPtr = (Vector3*)outVertices.m_Buffer;
|
||
|
ShadowEdge* outEdgesPtr = (ShadowEdge*)outEdges.m_Buffer;
|
||
|
int* outShapeStartingEdgePtr = (int*)outShapeStartingEdge.m_Buffer;
|
||
|
|
||
|
Vector2* offsetSolutionPointsPtr = (Vector2*)offsetSolution.points.m_Buffer;
|
||
|
int offsetSolutionPointsLength = offsetSolution.points.Length;
|
||
|
|
||
|
int* offsetSolutionPathSizesPtr = (int*)offsetSolution.pathSizes.m_Buffer;
|
||
|
int offsetSolutionPathSizesLength = offsetSolution.pathSizes.Length;
|
||
|
|
||
|
|
||
|
|
||
|
// Copy out the solution first..
|
||
|
for (int i = 0; i < offsetSolutionPointsLength; i++)
|
||
|
{
|
||
|
tmpVec3.x = offsetSolutionPointsPtr[i].x;
|
||
|
tmpVec3.y = offsetSolutionPointsPtr[i].y;
|
||
|
outVerticesPtr[vertexPos++] = tmpVec3;
|
||
|
}
|
||
|
|
||
|
int start = 0;
|
||
|
for (int pathSizeIndex = 0; pathSizeIndex < offsetSolutionPathSizesLength; pathSizeIndex++)
|
||
|
{
|
||
|
int pathSize = offsetSolutionPathSizesPtr[pathSizeIndex];
|
||
|
int end = start + pathSize;
|
||
|
outShapeStartingEdgePtr[pathSizeIndex] = start;
|
||
|
|
||
|
for (int shapeIndex = 0; shapeIndex < pathSize; shapeIndex++)
|
||
|
{
|
||
|
ShadowEdge edge = new ShadowEdge(shapeIndex + start, (shapeIndex + 1) % pathSize + start);
|
||
|
outEdgesPtr[shapeIndex + start] = edge;
|
||
|
}
|
||
|
|
||
|
start = end;
|
||
|
}
|
||
|
|
||
|
// Copy out the open vertices
|
||
|
int pathStartIndex = offsetSolutionPathSizesLength;
|
||
|
start = vertexPos; // We need to remap our vertices;
|
||
|
|
||
|
for (int i = 0; i < openPath.Length; i++)
|
||
|
{
|
||
|
tmpVec3.x = openPathPtr[i].x;
|
||
|
tmpVec3.y = openPathPtr[i].y;
|
||
|
outVerticesPtr[vertexPos++] = tmpVec3;
|
||
|
}
|
||
|
|
||
|
for (int openPathIndex = 0; openPathIndex < openPathCount; openPathIndex++)
|
||
|
{
|
||
|
int pathSize = openPathSizesPtr[openPathIndex];
|
||
|
int end = start + pathSize;
|
||
|
outShapeStartingEdgePtr[pathStartIndex + openPathIndex] = start;
|
||
|
|
||
|
for (int shapeIndex = 0; shapeIndex < pathSize - 1; shapeIndex++)
|
||
|
{
|
||
|
ShadowEdge edge = new ShadowEdge(shapeIndex + start, shapeIndex + 1);
|
||
|
outEdgesPtr[shapeIndex + start] = edge;
|
||
|
}
|
||
|
|
||
|
start = end;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
outVertices = new NativeArray<Vector3>(0, k_ClippingAllocator);
|
||
|
outEdges = new NativeArray<ShadowEdge>(0, k_ClippingAllocator);
|
||
|
outShapeStartingEdge = new NativeArray<int>(0, k_ClippingAllocator);
|
||
|
}
|
||
|
|
||
|
closedPathSizes.Dispose();
|
||
|
closedPath.Dispose();
|
||
|
openPathSizes.Dispose();
|
||
|
openPath.Dispose();
|
||
|
|
||
|
clipperPathArguments.Dispose();
|
||
|
offsetPathArguments.Dispose();
|
||
|
clipperSolution.Dispose();
|
||
|
offsetSolution.Dispose();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|