mirror of
https://gitgud.io/AbstractConcept/rimworld-animation-studio.git
synced 2024-08-15 00:43:27 +00:00
2112 lines
85 KiB
C#
2112 lines
85 KiB
C#
|
using System;
|
||
|
using System.Linq;
|
||
|
using Unity.Jobs;
|
||
|
using Unity.Collections;
|
||
|
using Unity.Mathematics;
|
||
|
using Unity.Collections.LowLevel.Unsafe;
|
||
|
using Unity.SpriteShape.External.LibTessDotNet;
|
||
|
|
||
|
// We will enable this once Burst gets a verified final version as this attribute keeps changing.
|
||
|
#if ENABLE_SPRITESHAPE_BURST
|
||
|
using Unity.Burst;
|
||
|
#endif
|
||
|
|
||
|
namespace UnityEngine.U2D
|
||
|
{
|
||
|
|
||
|
#if ENABLE_SPRITESHAPE_BURST
|
||
|
[BurstCompile]
|
||
|
#endif
|
||
|
public struct SpriteShapeGenerator : IJob
|
||
|
{
|
||
|
|
||
|
struct JobParameters
|
||
|
{
|
||
|
public int4 shapeData; // x : ClosedShape (bool) y : AdaptiveUV (bool) z : SpriteBorders (bool) w : Enable Fill Texture.
|
||
|
public int4 splineData; // x : StrtechUV. y : splineDetail z : AngleThreshold w: Collider On/Off
|
||
|
public float4 curveData; // x : ColliderPivot y : BorderPivot z : BevelCutoff w : BevelSize.
|
||
|
public float4 fillData; // x : fillScale y : fillScale.x W z : fillScale.y H w: 0.
|
||
|
}
|
||
|
|
||
|
struct JobSpriteInfo
|
||
|
{
|
||
|
public float4 texRect; // TextureRect.
|
||
|
public float4 texData; // x : GPUWidth y : GPUHeight z : TexelWidth w : TexelHeight
|
||
|
public float4 uvInfo; // x : x, y : y, z : width, w : height
|
||
|
public float4 metaInfo; // x : PPU, y : Pivot Y z : Original Rect Width w : Original Rect Height.
|
||
|
public float4 border; // Sprite Border.
|
||
|
}
|
||
|
|
||
|
struct JobAngleRange
|
||
|
{
|
||
|
public float4 spriteAngles; // x, y | First Angle & z,w | Second Angle.
|
||
|
public int4 spriteVariant1; // First 4 variants here.
|
||
|
public int4 spriteVariant2; // Second 4 variants here. Total 8 max variants.
|
||
|
public int4 spriteData; // Additional Data. x : sorting Order. y : variant Count. z : render Order Max.
|
||
|
};
|
||
|
|
||
|
struct JobControlPoint
|
||
|
{
|
||
|
public int4 cpData; // x : Sprite Index y : Corner Type z : Mode w : Internal Sprite Index.
|
||
|
public int4 exData; // x : Corner Type y: Corner Sprite z : Start/End Corner
|
||
|
public float4 cpInfo; // x : Height y : Bevel Cutoff z : Bevel Size. w : Render Order.
|
||
|
public float2 position;
|
||
|
public float2 tangentLt;
|
||
|
public float2 tangentRt;
|
||
|
};
|
||
|
|
||
|
struct JobContourPoint
|
||
|
{
|
||
|
public float2 position; // Position.
|
||
|
public float2 ptData; // x : height.
|
||
|
}
|
||
|
|
||
|
// Tessellation Structures.
|
||
|
struct JobSegmentInfo
|
||
|
{
|
||
|
public int4 spInfo; // x : Begin y : End. z : Sprite w : First Sprite for that Angle Range.
|
||
|
public float4 spriteInfo; // x : width y : height z : Render Order. w: Distance of the Segment.
|
||
|
};
|
||
|
|
||
|
struct JobCornerInfo
|
||
|
{
|
||
|
public float2 bottom;
|
||
|
public float2 top;
|
||
|
public float2 left;
|
||
|
public float2 right;
|
||
|
public int2 cornerData;
|
||
|
}
|
||
|
|
||
|
struct JobShapeVertex
|
||
|
{
|
||
|
public float2 pos;
|
||
|
public float2 uv;
|
||
|
public float4 tan;
|
||
|
public float2 meta; // x : height y : -
|
||
|
public int2 sprite; // x : sprite y : is main Point.
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Native Arrays : Scope : Initialized before and ReadOnly During Job
|
||
|
/// </summary>
|
||
|
[ReadOnly]
|
||
|
private JobParameters m_ShapeParams;
|
||
|
|
||
|
[ReadOnly]
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<JobSpriteInfo> m_SpriteInfos;
|
||
|
|
||
|
[ReadOnly]
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<JobSpriteInfo> m_CornerSpriteInfos;
|
||
|
|
||
|
[ReadOnly]
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<JobAngleRange> m_AngleRanges;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Native Arrays : Scope : Job
|
||
|
/// </summary>
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<JobSegmentInfo> m_Segments;
|
||
|
private int m_SegmentCount;
|
||
|
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<JobContourPoint> m_ContourPoints;
|
||
|
private int m_ContourPointCount;
|
||
|
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<JobCornerInfo> m_Corners;
|
||
|
private int m_CornerCount;
|
||
|
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<float2> m_TessPoints;
|
||
|
private int m_TessPointCount;
|
||
|
|
||
|
[DeallocateOnJobCompletion]
|
||
|
NativeArray<JobShapeVertex> m_VertexData;
|
||
|
|
||
|
[DeallocateOnJobCompletion]
|
||
|
NativeArray<JobShapeVertex> m_OutputVertexData;
|
||
|
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<JobControlPoint> m_ControlPoints;
|
||
|
private int m_ControlPointCount;
|
||
|
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<float2> m_CornerCoordinates;
|
||
|
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<float2> m_TempPoints;
|
||
|
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<JobControlPoint> m_GeneratedControlPoints;
|
||
|
|
||
|
[DeallocateOnJobCompletion]
|
||
|
private NativeArray<int2> m_SpriteIndices;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Output Native Arrays : Scope : SpriteShapeRenderer / SpriteShapeController.
|
||
|
/// </summary>
|
||
|
|
||
|
private int m_IndexArrayCount;
|
||
|
public NativeArray<ushort> m_IndexArray;
|
||
|
|
||
|
private int m_VertexArrayCount;
|
||
|
public NativeSlice<Vector3> m_PosArray;
|
||
|
public NativeSlice<Vector2> m_Uv0Array;
|
||
|
public NativeSlice<Vector4> m_TanArray;
|
||
|
|
||
|
private int m_GeomArrayCount;
|
||
|
public NativeArray<SpriteShapeSegment> m_GeomArray;
|
||
|
|
||
|
private int m_ColliderPointCount;
|
||
|
public NativeArray<float2> m_ColliderPoints;
|
||
|
public NativeArray<Bounds> m_Bounds;
|
||
|
|
||
|
int m_IndexDataCount;
|
||
|
int m_VertexDataCount;
|
||
|
int m_ColliderDataCount;
|
||
|
int m_ActiveIndexCount;
|
||
|
int m_ActiveVertexCount;
|
||
|
|
||
|
float2 m_FirstLT;
|
||
|
float2 m_FirstLB;
|
||
|
float4x4 m_Transform;
|
||
|
|
||
|
int kModeLinear;
|
||
|
int kModeContinous;
|
||
|
int kModeBroken;
|
||
|
|
||
|
int kCornerTypeOuterTopLeft;
|
||
|
int kCornerTypeOuterTopRight;
|
||
|
int kCornerTypeOuterBottomLeft;
|
||
|
int kCornerTypeOuterBottomRight;
|
||
|
int kCornerTypeInnerTopLeft;
|
||
|
int kCornerTypeInnerTopRight;
|
||
|
int kCornerTypeInnerBottomLeft;
|
||
|
int kCornerTypeInnerBottomRight;
|
||
|
int kControlPointCount;
|
||
|
|
||
|
float kEpsilon;
|
||
|
float kEpsilonOrder;
|
||
|
float kEpsilonRelaxed;
|
||
|
float kExtendSegment;
|
||
|
float kRenderQuality;
|
||
|
float kOptimizeRender;
|
||
|
float kColliderQuality;
|
||
|
float kOptimizeCollider;
|
||
|
float kLowestQualityTolerance;
|
||
|
float kHighestQualityTolerance;
|
||
|
|
||
|
#region Getters.
|
||
|
|
||
|
// Return Vertex Data Count
|
||
|
private int vertexDataCount
|
||
|
{
|
||
|
get { return m_VertexDataCount; }
|
||
|
}
|
||
|
|
||
|
// Return Index Data Count
|
||
|
private int indexDataCount
|
||
|
{
|
||
|
get { return m_IndexDataCount; }
|
||
|
}
|
||
|
|
||
|
// Return Sprite Count
|
||
|
private int spriteCount
|
||
|
{
|
||
|
get { return m_SpriteInfos.Length; }
|
||
|
}
|
||
|
|
||
|
private int cornerSpriteCount
|
||
|
{
|
||
|
get { return m_CornerSpriteInfos.Length; }
|
||
|
}
|
||
|
|
||
|
// Return Angle Range Count
|
||
|
private int angleRangeCount
|
||
|
{
|
||
|
get { return m_AngleRanges.Length; }
|
||
|
}
|
||
|
|
||
|
// Return the Input Control Point Count.
|
||
|
private int controlPointCount
|
||
|
{
|
||
|
get { return m_ControlPointCount; }
|
||
|
}
|
||
|
|
||
|
// Return the Contour Point Count.
|
||
|
private int contourPointCount
|
||
|
{
|
||
|
get { return m_ContourPointCount; }
|
||
|
}
|
||
|
|
||
|
// Return Segment Count
|
||
|
private int segmentCount
|
||
|
{
|
||
|
get { return m_SegmentCount; }
|
||
|
}
|
||
|
|
||
|
// Needs Collider Generaie.
|
||
|
private bool hasCollider
|
||
|
{
|
||
|
get { return m_ShapeParams.splineData.w == 1; }
|
||
|
}
|
||
|
|
||
|
// Collider Pivot
|
||
|
private float colliderPivot
|
||
|
{
|
||
|
get { return m_ShapeParams.curveData.x; }
|
||
|
}
|
||
|
|
||
|
// Border Pivot
|
||
|
private float borderPivot
|
||
|
{
|
||
|
get { return m_ShapeParams.curveData.y; }
|
||
|
}
|
||
|
|
||
|
// Spline Detail
|
||
|
private int splineDetail
|
||
|
{
|
||
|
get { return m_ShapeParams.splineData.y; }
|
||
|
}
|
||
|
|
||
|
// Is this Closed-Loop.
|
||
|
private bool isCarpet
|
||
|
{
|
||
|
get { return m_ShapeParams.shapeData.x == 1; }
|
||
|
}
|
||
|
|
||
|
// Is Adaptive UV
|
||
|
private bool isAdaptive
|
||
|
{
|
||
|
get { return m_ShapeParams.shapeData.y == 1; }
|
||
|
}
|
||
|
|
||
|
// Has Sprite Border.
|
||
|
private bool hasSpriteBorder
|
||
|
{
|
||
|
get { return m_ShapeParams.shapeData.z == 1; }
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Safe Getters.
|
||
|
JobSpriteInfo GetSpriteInfo(int index)
|
||
|
{
|
||
|
if (index >= m_SpriteInfos.Length)
|
||
|
throw new ArgumentException(string.Format("GetSpriteInfo accessed with invalid Index {0} / {1}", index, m_SpriteInfos.Length));
|
||
|
return m_SpriteInfos[index];
|
||
|
}
|
||
|
|
||
|
JobSpriteInfo GetCornerSpriteInfo(int index)
|
||
|
{
|
||
|
int ai = index - 1;
|
||
|
if (ai >= m_CornerSpriteInfos.Length || index == 0)
|
||
|
throw new ArgumentException(string.Format("GetCornerSpriteInfo accessed with invalid Index {0} / {1}", index, m_CornerSpriteInfos.Length));
|
||
|
return m_CornerSpriteInfos[ai];
|
||
|
}
|
||
|
|
||
|
JobAngleRange GetAngleRange(int index)
|
||
|
{
|
||
|
if (index >= m_AngleRanges.Length)
|
||
|
throw new ArgumentException(string.Format("GetAngleRange accessed with invalid Index {0} / {1}", index, m_AngleRanges.Length));
|
||
|
return m_AngleRanges[index];
|
||
|
}
|
||
|
|
||
|
JobControlPoint GetControlPoint(int index)
|
||
|
{
|
||
|
if (index >= m_ControlPoints.Length)
|
||
|
throw new ArgumentException(string.Format("GetControlPoint accessed with invalid Index {0} / {1}", index, m_ControlPoints.Length));
|
||
|
return m_ControlPoints[index];
|
||
|
}
|
||
|
|
||
|
JobContourPoint GetContourPoint(int index)
|
||
|
{
|
||
|
if (index >= m_ContourPointCount)
|
||
|
throw new ArgumentException(string.Format("GetContourPoint accessed with invalid Index {0} / {1}", index, m_ContourPointCount));
|
||
|
return m_ContourPoints[index];
|
||
|
}
|
||
|
|
||
|
JobSegmentInfo GetSegmentInfo(int index)
|
||
|
{
|
||
|
if (index >= m_SegmentCount)
|
||
|
throw new ArgumentException(string.Format("GetSegmentInfo accessed with invalid Index {0} / {1}", index, m_SegmentCount));
|
||
|
return m_Segments[index];
|
||
|
}
|
||
|
|
||
|
int GetContourIndex(int index)
|
||
|
{
|
||
|
if (index >= m_ControlPoints.Length)
|
||
|
throw new ArgumentException(string.Format("GetContourIndex accessed with invalid Index {0} / {1}", index, m_ControlPoints.Length));
|
||
|
return index * m_ShapeParams.splineData.y;
|
||
|
}
|
||
|
|
||
|
int GetEndContourIndexOfSegment(JobSegmentInfo isi)
|
||
|
{
|
||
|
int contourIndex = GetContourIndex(isi.spInfo.y) - 1;
|
||
|
if (isi.spInfo.y >= m_ControlPoints.Length || isi.spInfo.y == 0)
|
||
|
throw new ArgumentException("GetEndContourIndexOfSegment accessed with invalid Index");
|
||
|
return contourIndex;
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Utility
|
||
|
static void CopyToNativeArray<T>(NativeArray<T> from, int length, ref NativeArray<T> to) where T : struct
|
||
|
{
|
||
|
to = new NativeArray<T>(length, Allocator.TempJob);
|
||
|
for (int i = 0; i < length; ++i)
|
||
|
to[i] = from[i];
|
||
|
}
|
||
|
|
||
|
static void SafeDispose<T>(NativeArray<T> na) where T : struct
|
||
|
{
|
||
|
if (na.Length > 0)
|
||
|
na.Dispose();
|
||
|
}
|
||
|
|
||
|
static bool IsPointOnLine(float epsilon, float2 a, float2 b, float2 c)
|
||
|
{
|
||
|
float cp = (c.y - a.y) * (b.x - a.x) - (c.x - a.x) * (b.y - a.y);
|
||
|
if (math.abs(cp) > epsilon)
|
||
|
return false;
|
||
|
|
||
|
float dp = (c.x - a.x) * (b.x - a.x) + (c.y - a.y) * (b.y - a.y);
|
||
|
if (dp < 0)
|
||
|
return false;
|
||
|
|
||
|
float ba = (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y);
|
||
|
if (dp > ba)
|
||
|
return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool IsPointOnLines(float epsilon, float2 p1, float2 p2, float2 p3, float2 p4, float2 r)
|
||
|
{
|
||
|
return IsPointOnLine(epsilon, p1, p2, r) && IsPointOnLine(epsilon, p3, p4, r);
|
||
|
}
|
||
|
|
||
|
static bool LineIntersection(float epsilon, float2 p1, float2 p2, float2 p3, float2 p4, ref float2 result)
|
||
|
{
|
||
|
float bx = p2.x - p1.x;
|
||
|
float by = p2.y - p1.y;
|
||
|
float dx = p4.x - p3.x;
|
||
|
float dy = p4.y - p3.y;
|
||
|
float bDotDPerp = bx * dy - by * dx;
|
||
|
if (math.abs(bDotDPerp) < epsilon)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
float cx = p3.x - p1.x;
|
||
|
float cy = p3.y - p1.y;
|
||
|
float t = (cx * dy - cy * dx) / bDotDPerp;
|
||
|
if ((t >= -epsilon) && (t <= 1.0f + epsilon))
|
||
|
{
|
||
|
result.x = p1.x + t * bx;
|
||
|
result.y = p1.y + t * by;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static float AngleBetweenVector(float2 a, float2 b)
|
||
|
{
|
||
|
float dot = math.dot(a, b);
|
||
|
float det = (a.x * b.y) - (b.x * a.y);
|
||
|
return math.atan2(det, dot) * Mathf.Rad2Deg;
|
||
|
}
|
||
|
|
||
|
static bool GenerateColumnsBi(float2 a, float2 b, float2 whsize, bool flip, ref float2 rt, ref float2 rb, float cph)
|
||
|
{
|
||
|
float2 v1 = flip ? (a - b) : (b - a);
|
||
|
if (math.length(v1) < 1e-30f)
|
||
|
return false;
|
||
|
|
||
|
float2 rvxy = new float2(-1f, 1f);
|
||
|
float2 v2 = v1.yx * rvxy;
|
||
|
float2 whsizey = new float2(whsize.y * cph);
|
||
|
v2 = math.normalize(v2);
|
||
|
|
||
|
float2 v3 = v2 * whsizey;
|
||
|
rt = a - v3;
|
||
|
rb = a + v3;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool GenerateColumnsTri(float2 a, float2 b, float2 c, float2 whsize, bool flip, ref float2 rt, ref float2 rb, float cph)
|
||
|
{
|
||
|
float2 rvxy = new float2(-1f, 1f);
|
||
|
float2 v0 = b - a;
|
||
|
float2 v1 = c - b;
|
||
|
v0 = v0.yx * rvxy;
|
||
|
v1 = v1.yx * rvxy;
|
||
|
|
||
|
float2 v2 = math.normalize(v0) + math.normalize(v1);
|
||
|
if (math.length(v2) < 1e-30f)
|
||
|
return false;
|
||
|
v2 = math.normalize(v2);
|
||
|
float2 whsizey = new float2(whsize.y * cph);
|
||
|
float2 v3 = v2 * whsizey;
|
||
|
|
||
|
rt = b - v3;
|
||
|
rb = b + v3;
|
||
|
return true;
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Input Preparation.
|
||
|
void AppendCornerCoordinates(ref NativeArray<float2> corners, ref int cornerCount, float2 a, float2 b, float2 c, float2 d)
|
||
|
{
|
||
|
corners[cornerCount++] = a;
|
||
|
corners[cornerCount++] = b;
|
||
|
corners[cornerCount++] = c;
|
||
|
corners[cornerCount++] = d;
|
||
|
}
|
||
|
|
||
|
unsafe void PrepareInput(SpriteShapeParameters shapeParams, int maxArrayCount, NativeArray<ShapeControlPoint> shapePoints, bool optimizeGeometry, bool updateCollider, bool optimizeCollider, float colliderPivot, float colliderDetail)
|
||
|
{
|
||
|
kModeLinear = 0;
|
||
|
kModeContinous = 1;
|
||
|
kModeBroken = 2;
|
||
|
|
||
|
kCornerTypeOuterTopLeft = 1;
|
||
|
kCornerTypeOuterTopRight = 2;
|
||
|
kCornerTypeOuterBottomLeft = 3;
|
||
|
kCornerTypeOuterBottomRight = 4;
|
||
|
kCornerTypeInnerTopLeft = 5;
|
||
|
kCornerTypeInnerTopRight = 6;
|
||
|
kCornerTypeInnerBottomLeft = 7;
|
||
|
kCornerTypeInnerBottomRight = 8;
|
||
|
|
||
|
m_IndexDataCount = 0;
|
||
|
m_VertexDataCount = 0;
|
||
|
m_ColliderDataCount = 0;
|
||
|
m_ActiveIndexCount = 0;
|
||
|
m_ActiveVertexCount = 0;
|
||
|
|
||
|
kEpsilon = 0.00001f;
|
||
|
kEpsilonOrder = -0.0001f;
|
||
|
kEpsilonRelaxed = 0.001f;
|
||
|
kExtendSegment = 10000.0f;
|
||
|
|
||
|
kLowestQualityTolerance = 4.0f;
|
||
|
kHighestQualityTolerance = 16.0f;
|
||
|
|
||
|
kColliderQuality = math.clamp(colliderDetail, kLowestQualityTolerance, kHighestQualityTolerance);
|
||
|
kOptimizeCollider = optimizeCollider ? 1 : 0;
|
||
|
kColliderQuality = (kHighestQualityTolerance - kColliderQuality + 2.0f) * 0.002f;
|
||
|
colliderPivot = (colliderPivot == 0) ? 0.001f : colliderPivot;
|
||
|
|
||
|
kOptimizeRender = optimizeGeometry ? 1 : 0;
|
||
|
kRenderQuality = math.clamp(shapeParams.splineDetail, kLowestQualityTolerance, kHighestQualityTolerance);
|
||
|
kRenderQuality = (kHighestQualityTolerance - kRenderQuality + 2.0f) * 0.0002f;
|
||
|
|
||
|
m_ShapeParams.shapeData = new int4(shapeParams.carpet ? 1 : 0, shapeParams.adaptiveUV ? 1 : 0, shapeParams.spriteBorders ? 1 : 0, shapeParams.fillTexture != null ? 1 : 0);
|
||
|
m_ShapeParams.splineData = new int4(shapeParams.stretchUV ? 1 : 0, (shapeParams.splineDetail > 4) ? (int)shapeParams.splineDetail : 4, (int)shapeParams.angleThreshold, updateCollider ? 1 : 0);
|
||
|
m_ShapeParams.curveData = new float4(colliderPivot, shapeParams.borderPivot, shapeParams.bevelCutoff, shapeParams.bevelSize);
|
||
|
float fx = 0, fy = 0;
|
||
|
if (shapeParams.fillTexture != null)
|
||
|
{
|
||
|
fx = (float)shapeParams.fillTexture.width * (1.0f / (float)shapeParams.fillScale);
|
||
|
fy = (float)shapeParams.fillTexture.height * (1.0f / (float)shapeParams.fillScale);
|
||
|
}
|
||
|
m_ShapeParams.fillData = new float4(shapeParams.fillScale, fx, fy, 0);
|
||
|
UnsafeUtility.MemClear(m_GeomArray.GetUnsafePtr(), m_GeomArray.Length * UnsafeUtility.SizeOf<SpriteShapeSegment>());
|
||
|
|
||
|
m_Transform = new float4x4(shapeParams.transform.m00, shapeParams.transform.m01, shapeParams.transform.m02, shapeParams.transform.m03,
|
||
|
shapeParams.transform.m10, shapeParams.transform.m11, shapeParams.transform.m12, shapeParams.transform.m13,
|
||
|
shapeParams.transform.m20, shapeParams.transform.m21, shapeParams.transform.m22, shapeParams.transform.m23,
|
||
|
shapeParams.transform.m30, shapeParams.transform.m31, shapeParams.transform.m32, shapeParams.transform.m33);
|
||
|
|
||
|
kControlPointCount = shapePoints.Length * (int)shapeParams.splineDetail * 32;
|
||
|
m_Segments = new NativeArray<JobSegmentInfo>(shapePoints.Length * 2, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||
|
m_ContourPoints = new NativeArray<JobContourPoint>(kControlPointCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||
|
m_TessPoints = new NativeArray<float2>(shapePoints.Length * (int)shapeParams.splineDetail * 128, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||
|
m_VertexData = new NativeArray<JobShapeVertex>(maxArrayCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||
|
m_OutputVertexData = new NativeArray<JobShapeVertex>(maxArrayCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||
|
m_CornerCoordinates = new NativeArray<float2>(32, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||
|
|
||
|
m_TempPoints = new NativeArray<float2>(kControlPointCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||
|
m_GeneratedControlPoints = new NativeArray<JobControlPoint>(kControlPointCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||
|
m_SpriteIndices = new NativeArray<int2>(kControlPointCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||
|
|
||
|
int cornerCount = 0;
|
||
|
AppendCornerCoordinates(ref m_CornerCoordinates, ref cornerCount, new float2(1f, 1f), new float2(0, 1f), new float2(1f, 0), new float2(0, 0));
|
||
|
AppendCornerCoordinates(ref m_CornerCoordinates, ref cornerCount, new float2(1f, 0), new float2(1f, 1f), new float2(0, 0), new float2(0, 1f));
|
||
|
AppendCornerCoordinates(ref m_CornerCoordinates, ref cornerCount, new float2(0, 1f), new float2(0, 0), new float2(1f, 1f), new float2(1f, 0));
|
||
|
AppendCornerCoordinates(ref m_CornerCoordinates, ref cornerCount, new float2(0, 0), new float2(1f, 0), new float2(0, 1f), new float2(1f, 1f));
|
||
|
AppendCornerCoordinates(ref m_CornerCoordinates, ref cornerCount, new float2(0, 0), new float2(0, 1f), new float2(1f, 0), new float2(1f, 1f));
|
||
|
AppendCornerCoordinates(ref m_CornerCoordinates, ref cornerCount, new float2(0, 1f), new float2(1f, 1f), new float2(0, 0), new float2(1f, 0));
|
||
|
AppendCornerCoordinates(ref m_CornerCoordinates, ref cornerCount, new float2(1f, 0), new float2(0, 0), new float2(1f, 1f), new float2(0, 1f));
|
||
|
AppendCornerCoordinates(ref m_CornerCoordinates, ref cornerCount, new float2(1f, 1f), new float2(1f, 0), new float2(0, 1f), new float2(0, 0));
|
||
|
}
|
||
|
|
||
|
void TransferSprites(ref NativeArray<JobSpriteInfo> spriteInfos, Sprite[] sprites, int maxCount)
|
||
|
{
|
||
|
|
||
|
for (int i = 0; i < sprites.Length && i < maxCount; ++i)
|
||
|
{
|
||
|
JobSpriteInfo spriteInfo = spriteInfos[i];
|
||
|
Sprite spr = sprites[i];
|
||
|
if (spr != null)
|
||
|
{
|
||
|
Texture2D tex = spr.texture;
|
||
|
spriteInfo.texRect = new float4(spr.textureRect.x, spr.textureRect.y, spr.textureRect.width, spr.textureRect.height);
|
||
|
spriteInfo.texData = new float4(tex.width, tex.height, tex.texelSize.x, tex.texelSize.y);
|
||
|
spriteInfo.border = new float4(spr.border.x, spr.border.y, spr.border.z, spr.border.w);
|
||
|
spriteInfo.uvInfo = new float4(spriteInfo.texRect.x / spriteInfo.texData.x, spriteInfo.texRect.y / spriteInfo.texData.y, spriteInfo.texRect.z / spriteInfo.texData.x, spriteInfo.texRect.w / spriteInfo.texData.y);
|
||
|
spriteInfo.metaInfo = new float4(spr.pixelsPerUnit, spr.pivot.y / spr.textureRect.height, spr.rect.width, spr.rect.height);
|
||
|
|
||
|
if (!math.any(spriteInfo.texRect))
|
||
|
{
|
||
|
Cleanup();
|
||
|
throw new ArgumentException(string.Format("{0} is packed with Tight packing or mesh type set to Tight. Please check input sprites", spr.name));
|
||
|
}
|
||
|
}
|
||
|
spriteInfos[i] = spriteInfo;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void PrepareSprites(Sprite[] edgeSprites, Sprite[] cornerSprites)
|
||
|
{
|
||
|
m_SpriteInfos = new NativeArray<JobSpriteInfo>(edgeSprites.Length, Allocator.TempJob);
|
||
|
TransferSprites(ref m_SpriteInfos, edgeSprites, edgeSprites.Length);
|
||
|
|
||
|
m_CornerSpriteInfos = new NativeArray<JobSpriteInfo>(kCornerTypeInnerBottomRight, Allocator.TempJob);
|
||
|
TransferSprites(ref m_CornerSpriteInfos, cornerSprites, cornerSprites.Length);
|
||
|
}
|
||
|
|
||
|
void PrepareAngleRanges(AngleRangeInfo[] angleRanges)
|
||
|
{
|
||
|
m_AngleRanges = new NativeArray<JobAngleRange>(angleRanges.Length, Allocator.TempJob);
|
||
|
|
||
|
for (int i = 0; i < angleRanges.Length; ++i)
|
||
|
{
|
||
|
JobAngleRange angleRange = m_AngleRanges[i];
|
||
|
AngleRangeInfo ari = angleRanges[i];
|
||
|
int[] spr = ari.sprites;
|
||
|
if (ari.start > ari.end)
|
||
|
{
|
||
|
var sw = ari.start;
|
||
|
ari.start = ari.end;
|
||
|
ari.end = sw;
|
||
|
}
|
||
|
angleRange.spriteAngles = new float4(ari.start + 90f, ari.end + 90f, 0, 0);
|
||
|
angleRange.spriteVariant1 = new int4(spr.Length > 0 ? spr[0] : -1, spr.Length > 1 ? spr[1] : -1, spr.Length > 2 ? spr[2] : -1, spr.Length > 3 ? spr[3] : -1);
|
||
|
angleRange.spriteVariant2 = new int4(spr.Length > 4 ? spr[4] : -1, spr.Length > 5 ? spr[5] : -1, spr.Length > 6 ? spr[6] : -1, spr.Length > 7 ? spr[7] : -1);
|
||
|
angleRange.spriteData = new int4((int)ari.order, spr.Length, 32, 0);
|
||
|
m_AngleRanges[i] = angleRange;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PrepareControlPoints(NativeArray<ShapeControlPoint> shapePoints, NativeArray<SpriteShapeMetaData> metaData)
|
||
|
{
|
||
|
float2 zero = new float2(0, 0);
|
||
|
m_ControlPoints = new NativeArray<JobControlPoint>(kControlPointCount, Allocator.TempJob);
|
||
|
|
||
|
for (int i = 0; i < shapePoints.Length; ++i)
|
||
|
{
|
||
|
JobControlPoint shapePoint = m_ControlPoints[i];
|
||
|
ShapeControlPoint sp = shapePoints[i];
|
||
|
SpriteShapeMetaData md = metaData[i];
|
||
|
shapePoint.position = new float2(sp.position.x, sp.position.y);
|
||
|
shapePoint.tangentLt = (sp.mode == kModeLinear) ? zero : new float2(sp.leftTangent.x, sp.leftTangent.y);
|
||
|
shapePoint.tangentRt = (sp.mode == kModeLinear) ? zero : new float2(sp.rightTangent.x, sp.rightTangent.y);
|
||
|
shapePoint.cpInfo = new float4(md.height, md.bevelCutoff, md.bevelSize, 0);
|
||
|
shapePoint.cpData = new int4((int)md.spriteIndex, md.corner ? 1 : 0, sp.mode, 0);
|
||
|
shapePoint.exData = new int4(-1, 0, 0, 0);
|
||
|
m_ControlPoints[i] = shapePoint;
|
||
|
}
|
||
|
m_ControlPointCount = shapePoints.Length;
|
||
|
m_Corners = new NativeArray<JobCornerInfo>(shapePoints.Length, Allocator.TempJob);
|
||
|
GenerateControlPoints();
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Resolve Angles for Points.
|
||
|
bool WithinRange(JobAngleRange angleRange, float inputAngle)
|
||
|
{
|
||
|
float range = angleRange.spriteAngles.y - angleRange.spriteAngles.x;
|
||
|
float angle = Mathf.Repeat(inputAngle - angleRange.spriteAngles.x, 360f);
|
||
|
return (angle >= 0f && angle <= range);
|
||
|
}
|
||
|
|
||
|
bool AngleWithinRange(float t, float a, float b)
|
||
|
{
|
||
|
return (a != 0 && b != 0) && (t >= a && t <= b);
|
||
|
}
|
||
|
|
||
|
static float2 BezierPoint(float2 st, float2 sp, float2 ep, float2 et, float t)
|
||
|
{
|
||
|
float2 xt = new float2(t);
|
||
|
float2 nt = new float2(1.0f - t);
|
||
|
float2 x3 = new float2(3.0f);
|
||
|
return (sp * nt * nt * nt) + (st * nt * nt * xt * x3) + (et * nt * xt * xt * x3) + (ep * xt * xt * xt);
|
||
|
}
|
||
|
|
||
|
static float SlopeAngle(float2 dirNormalized)
|
||
|
{
|
||
|
float2 dvup = new float2(0, 1f);
|
||
|
float2 dvrt = new float2(1f, 0);
|
||
|
|
||
|
float dr = math.dot(dirNormalized, dvrt);
|
||
|
float du = math.dot(dirNormalized, dvup);
|
||
|
float cu = math.acos(du);
|
||
|
float sn = dr >= 0 ? 1.0f : -1.0f;
|
||
|
float an = cu * Mathf.Rad2Deg * sn;
|
||
|
|
||
|
// Adjust angles when direction is parallel to Up Axis.
|
||
|
an = (du != 1f) ? an : 0;
|
||
|
an = (du != -1f) ? an : -180f;
|
||
|
return an;
|
||
|
}
|
||
|
|
||
|
static float SlopeAngle(float2 start, float2 end)
|
||
|
{
|
||
|
float2 dir = math.normalize(start - end);
|
||
|
return SlopeAngle(dir);
|
||
|
}
|
||
|
|
||
|
bool ResolveAngle(float angle, int activeIndex, ref float renderOrder, ref int spriteIndex, ref int firstSpriteIndex)
|
||
|
{
|
||
|
int localRenderOrder = 0;
|
||
|
int localSpriteIndex = 0;
|
||
|
for (int i = 0; i < m_AngleRanges.Length; ++i)
|
||
|
{
|
||
|
bool withinRange = WithinRange(m_AngleRanges[i], angle);
|
||
|
if (withinRange)
|
||
|
{
|
||
|
int validIndex = (activeIndex < m_AngleRanges[i].spriteData.y) ? activeIndex : 0;
|
||
|
renderOrder = localRenderOrder + validIndex;
|
||
|
spriteIndex = localSpriteIndex + validIndex;
|
||
|
firstSpriteIndex = localSpriteIndex;
|
||
|
return true;
|
||
|
}
|
||
|
localRenderOrder += m_AngleRanges[i].spriteData.z;
|
||
|
localSpriteIndex += m_AngleRanges[i].spriteData.y;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int GetSpriteIndex(int index, int previousIndex, ref int resolved)
|
||
|
{
|
||
|
int next = (index + 1) % controlPointCount, spriteIndex = -1, firstSpriteIndex = -1;
|
||
|
float order = 0;
|
||
|
var cp = GetControlPoint(index);
|
||
|
float angle = SlopeAngle(GetControlPoint(next).position, cp.position);
|
||
|
bool resolve = ResolveAngle(angle, cp.cpData.x, ref order, ref spriteIndex, ref firstSpriteIndex);
|
||
|
resolved = resolve ? 1 : 0;
|
||
|
return resolve ? spriteIndex : previousIndex;
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Segments.
|
||
|
void GenerateSegments()
|
||
|
{
|
||
|
int activeSpriteIndex = 0, activeSegmentIndex = 0, firstSpriteIndex = -1;
|
||
|
JobSegmentInfo activeSegment = m_Segments[0];
|
||
|
activeSegment.spInfo = int4.zero;
|
||
|
activeSegment.spriteInfo = int4.zero;
|
||
|
float angle = 0;
|
||
|
|
||
|
// Generate Segments.
|
||
|
for (int i = 0; i < controlPointCount; ++i)
|
||
|
{
|
||
|
int next = (i + 1) % controlPointCount;
|
||
|
|
||
|
// Check for Last Point and see if we need loop-back.
|
||
|
bool skipSegmenting = false;
|
||
|
if (next == 0)
|
||
|
{
|
||
|
if (!isCarpet)
|
||
|
continue;
|
||
|
next = 1;
|
||
|
skipSegmenting = true;
|
||
|
}
|
||
|
|
||
|
JobControlPoint iscp = GetControlPoint(i);
|
||
|
JobControlPoint iscpNext = GetControlPoint(next);
|
||
|
|
||
|
// If this segment is corner, continue.
|
||
|
if (iscp.exData.x > 0 && iscp.exData.x == iscpNext.exData.x && iscp.exData.z == 1)
|
||
|
continue;
|
||
|
|
||
|
// Resolve Angle and Order.
|
||
|
int4 pointData = iscp.cpData;
|
||
|
float4 pointInfo = iscp.cpInfo;
|
||
|
|
||
|
// Get Min Max Segment.
|
||
|
int mn = (i < next) ? i : next;
|
||
|
int mx = (i > next) ? i : next;
|
||
|
bool continueStrip = (iscp.cpData.z == kModeContinous), edgeUpdated = false;
|
||
|
|
||
|
if (false == continueStrip || 0 == activeSegmentIndex)
|
||
|
angle = SlopeAngle(iscpNext.position, iscp.position);
|
||
|
bool resolved = ResolveAngle(angle, pointData.x, ref pointInfo.w, ref pointData.w, ref firstSpriteIndex);
|
||
|
if (!resolved && !skipSegmenting)
|
||
|
{
|
||
|
// If we do not resolve SpriteIndex (AngleRange) just continue existing segment.
|
||
|
pointData.w = activeSpriteIndex;
|
||
|
iscp.cpData = pointData;
|
||
|
m_ControlPoints[i] = iscp;
|
||
|
|
||
|
// Insert Dummy Segment.
|
||
|
activeSegment = m_Segments[activeSegmentIndex];
|
||
|
activeSegment.spInfo.x = mn;
|
||
|
activeSegment.spInfo.y = mx;
|
||
|
activeSegment.spInfo.z = -1;
|
||
|
m_Segments[activeSegmentIndex] = activeSegment;
|
||
|
activeSegmentIndex++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Update current Point.
|
||
|
activeSpriteIndex = pointData.w;
|
||
|
iscp.cpData = pointData;
|
||
|
m_ControlPoints[i] = iscp;
|
||
|
if (skipSegmenting)
|
||
|
continue;
|
||
|
|
||
|
// Check for Segments. Also check if the Segment Start has been resolved. Otherwise simply start with the next one.
|
||
|
if (activeSegmentIndex != 0)
|
||
|
continueStrip = continueStrip && (m_SpriteIndices[activeSegment.spInfo.x].y != 0 && activeSpriteIndex == activeSegment.spInfo.z);
|
||
|
|
||
|
if (continueStrip && i != (controlPointCount - 1))
|
||
|
{
|
||
|
for (int s = 0; s < activeSegmentIndex; ++s)
|
||
|
{
|
||
|
activeSegment = m_Segments[s];
|
||
|
if (activeSegment.spInfo.x - mn == 1)
|
||
|
{
|
||
|
edgeUpdated = true;
|
||
|
activeSegment.spInfo.x = mn;
|
||
|
m_Segments[s] = activeSegment;
|
||
|
break;
|
||
|
}
|
||
|
if (mx - activeSegment.spInfo.y == 1)
|
||
|
{
|
||
|
edgeUpdated = true;
|
||
|
activeSegment.spInfo.y = mx;
|
||
|
m_Segments[s] = activeSegment;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!edgeUpdated)
|
||
|
{
|
||
|
activeSegment = m_Segments[activeSegmentIndex];
|
||
|
JobSpriteInfo sprLt = GetSpriteInfo(iscp.cpData.w);
|
||
|
activeSegment.spInfo.x = mn;
|
||
|
activeSegment.spInfo.y = mx;
|
||
|
activeSegment.spInfo.z = activeSpriteIndex;
|
||
|
activeSegment.spInfo.w = firstSpriteIndex;
|
||
|
activeSegment.spriteInfo.x = sprLt.texRect.z;
|
||
|
activeSegment.spriteInfo.y = sprLt.texRect.w;
|
||
|
activeSegment.spriteInfo.z = pointInfo.w;
|
||
|
m_Segments[activeSegmentIndex] = activeSegment;
|
||
|
activeSegmentIndex++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_SegmentCount = activeSegmentIndex;
|
||
|
|
||
|
}
|
||
|
|
||
|
bool GenerateControlPoints()
|
||
|
{
|
||
|
// Globals.
|
||
|
int activePoint = 0, activeIndex = 0;
|
||
|
int startPoint = 0, endPoint = controlPointCount, lastPoint = (controlPointCount - 1);
|
||
|
|
||
|
float2 rvxy = new float2(-1f, 1f);
|
||
|
int2 sprData = new int2(0, 0);
|
||
|
bool useSlice = true;
|
||
|
int spriteCount = m_SpriteInfos.Length;
|
||
|
|
||
|
// Calc and calculate Indices.
|
||
|
for (int i = 0; i < controlPointCount; ++i)
|
||
|
{
|
||
|
var resolved = 0;
|
||
|
int spriteIndex = GetSpriteIndex(i, activeIndex, ref resolved);
|
||
|
sprData.x = activeIndex = spriteIndex;
|
||
|
sprData.y = resolved;
|
||
|
m_SpriteIndices[i] = sprData;
|
||
|
}
|
||
|
|
||
|
// Open-Ended. We simply dont allow Continous mode in End-points.
|
||
|
if (!isCarpet)
|
||
|
{
|
||
|
JobControlPoint cp = GetControlPoint(0);
|
||
|
cp.cpData.z = (cp.cpData.z == kModeContinous) ? kModeBroken : cp.cpData.z;
|
||
|
m_GeneratedControlPoints[activePoint++] = cp;
|
||
|
// If its not carpet, we already pre-insert start and endpoint.
|
||
|
startPoint = 1;
|
||
|
endPoint = controlPointCount - 1;
|
||
|
}
|
||
|
|
||
|
// Generate Intermediates.
|
||
|
for (int i = startPoint; i < endPoint; ++i)
|
||
|
{
|
||
|
|
||
|
// Check if the Neighbor Points are all in Linear Mode,
|
||
|
bool vc = InsertCorner(i, ref m_SpriteIndices, ref m_GeneratedControlPoints, ref activePoint);
|
||
|
if (vc)
|
||
|
continue;
|
||
|
|
||
|
// NO Corners.
|
||
|
m_GeneratedControlPoints[activePoint++] = GetControlPoint(i);
|
||
|
}
|
||
|
|
||
|
// Open-Ended.
|
||
|
if (!isCarpet)
|
||
|
{
|
||
|
JobControlPoint cp = GetControlPoint(endPoint);
|
||
|
cp.cpData.z = (cp.cpData.z == kModeContinous) ? kModeBroken : cp.cpData.z;
|
||
|
m_GeneratedControlPoints[activePoint++] = cp;
|
||
|
}
|
||
|
// If Closed Shape
|
||
|
else
|
||
|
{
|
||
|
JobControlPoint cp = m_GeneratedControlPoints[0];
|
||
|
m_GeneratedControlPoints[activePoint++] = cp;
|
||
|
}
|
||
|
|
||
|
// Copy from these intermediate Points to main Control Points.
|
||
|
for (int i = 0; i < activePoint; ++i)
|
||
|
m_ControlPoints[i] = m_GeneratedControlPoints[i];
|
||
|
m_ControlPointCount = activePoint;
|
||
|
|
||
|
// Calc and calculate Indices.
|
||
|
for (int i = 0; i < controlPointCount; ++i)
|
||
|
{
|
||
|
var resolved = 0;
|
||
|
int spriteIndex = GetSpriteIndex(i, activeIndex, ref resolved);
|
||
|
sprData.x = activeIndex = spriteIndex;
|
||
|
sprData.y = resolved;
|
||
|
m_SpriteIndices[i] = sprData;
|
||
|
}
|
||
|
|
||
|
// End
|
||
|
return useSlice;
|
||
|
}
|
||
|
|
||
|
float SegmentDistance(JobSegmentInfo isi)
|
||
|
{
|
||
|
float distance = 0;
|
||
|
int stIx = GetContourIndex(isi.spInfo.x);
|
||
|
int enIx = GetEndContourIndexOfSegment(isi);
|
||
|
|
||
|
for (int i = stIx; i < enIx; ++i)
|
||
|
{
|
||
|
int j = i + 1;
|
||
|
JobContourPoint lt = GetContourPoint(i);
|
||
|
JobContourPoint rt = GetContourPoint(j);
|
||
|
distance = distance + math.distance(lt.position, rt.position);
|
||
|
}
|
||
|
|
||
|
return distance;
|
||
|
}
|
||
|
|
||
|
void GenerateContour()
|
||
|
{
|
||
|
int controlPointContour = controlPointCount - 1;
|
||
|
|
||
|
// Expand the Bezier.
|
||
|
int ap = 0;
|
||
|
float fmax = (float)(splineDetail - 1);
|
||
|
for (int i = 0; i < controlPointContour; ++i)
|
||
|
{
|
||
|
int j = i + 1;
|
||
|
JobControlPoint cp = GetControlPoint(i);
|
||
|
JobControlPoint pp = GetControlPoint(j);
|
||
|
|
||
|
float2 p0 = cp.position;
|
||
|
float2 p1 = pp.position;
|
||
|
float2 sp = p0;
|
||
|
float2 rt = p0 + cp.tangentRt;
|
||
|
float2 lt = p1 + pp.tangentLt;
|
||
|
int cap = ap;
|
||
|
float spd = 0, cpd = 0;
|
||
|
|
||
|
for (int n = 0; n < splineDetail; ++n)
|
||
|
{
|
||
|
JobContourPoint xp = m_ContourPoints[ap];
|
||
|
float t = (float)n / fmax;
|
||
|
float2 bp = BezierPoint(rt, p0, p1, lt, t);
|
||
|
xp.position = bp;
|
||
|
spd += math.distance(bp, sp);
|
||
|
m_ContourPoints[ap++] = xp;
|
||
|
sp = bp;
|
||
|
}
|
||
|
|
||
|
sp = p0;
|
||
|
for (int n = 0; n < splineDetail; ++n)
|
||
|
{
|
||
|
JobContourPoint xp = m_ContourPoints[cap];
|
||
|
cpd += math.distance(xp.position, sp);
|
||
|
xp.ptData.x = math.lerp(cp.cpInfo.x, pp.cpInfo.x, cpd / spd);
|
||
|
m_ContourPoints[cap++] = xp;
|
||
|
sp = xp.position;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// End
|
||
|
m_ContourPointCount = ap;
|
||
|
}
|
||
|
|
||
|
void TessellateContour()
|
||
|
{
|
||
|
|
||
|
int tessPoints = 0;
|
||
|
|
||
|
// Create Tessallator if required.
|
||
|
for (int i = 0; i < contourPointCount; ++i)
|
||
|
{
|
||
|
if ((i + 1) % splineDetail == 0)
|
||
|
continue;
|
||
|
int h = (i == 0) ? (contourPointCount - 1) : (i - 1);
|
||
|
int j = (i + 1) % contourPointCount;
|
||
|
h = (i % splineDetail == 0) ? (h - 1) : h;
|
||
|
|
||
|
JobContourPoint pp = GetContourPoint(h);
|
||
|
JobContourPoint cp = GetContourPoint(i);
|
||
|
JobContourPoint np = GetContourPoint(j);
|
||
|
|
||
|
float2 cpd = cp.position - pp.position;
|
||
|
float2 npd = np.position - cp.position;
|
||
|
if (math.length(cpd) < kEpsilon || math.length(npd) < kEpsilon)
|
||
|
continue;
|
||
|
|
||
|
float2 vl = math.normalize(cpd);
|
||
|
float2 vr = math.normalize(npd);
|
||
|
|
||
|
vl = new float2(-vl.y, vl.x);
|
||
|
vr = new float2(-vr.y, vr.x);
|
||
|
|
||
|
float2 va = math.normalize(vl) + math.normalize(vr);
|
||
|
float2 vn = math.normalize(va);
|
||
|
|
||
|
if (math.any(va) && math.any(vn))
|
||
|
m_TessPoints[tessPoints++] = cp.position + (vn * borderPivot);
|
||
|
}
|
||
|
|
||
|
m_TessPointCount = tessPoints;
|
||
|
|
||
|
// Fill Geom. Generate in Native code until we have a reasonably fast enough Tessellation in NativeArray based Jobs.
|
||
|
SpriteShapeSegment geom = m_GeomArray[0];
|
||
|
Vector3 pos = m_PosArray[0];
|
||
|
|
||
|
geom.vertexCount = 0;
|
||
|
geom.geomIndex = 0;
|
||
|
geom.indexCount = 0;
|
||
|
geom.spriteIndex = -1;
|
||
|
|
||
|
// Fill Geometry. Check if Fill Texture and Fill Scale is Valid.
|
||
|
if (math.all(m_ShapeParams.shapeData.xw))
|
||
|
{
|
||
|
// Fill Geometry. Check if Fill Texture and Fill Scale is Valid.
|
||
|
if (m_TessPointCount > 0)
|
||
|
{
|
||
|
if (kOptimizeRender > 0)
|
||
|
OptimizePoints(kRenderQuality, ref m_TessPoints, ref m_TessPointCount);
|
||
|
|
||
|
var inputs = new ContourVertex[m_TessPointCount];
|
||
|
for (int i = 0; i < m_TessPointCount; ++i)
|
||
|
inputs[i] = new ContourVertex() { Position = new Vec3() { X = m_TessPoints[i].x, Y = m_TessPoints[i].y } };
|
||
|
|
||
|
Tess tess = new Tess();
|
||
|
tess.AddContour(inputs, ContourOrientation.Original);
|
||
|
tess.Tessellate(WindingRule.NonZero, ElementType.Polygons, 3);
|
||
|
|
||
|
var indices = tess.Elements.Select(i => (UInt16)i).ToArray();
|
||
|
var vertices = tess.Vertices.Select(v => new Vector2(v.Position.X, v.Position.Y)).ToArray();
|
||
|
m_IndexDataCount = indices.Length;
|
||
|
m_VertexDataCount = vertices.Length;
|
||
|
|
||
|
if (vertices.Length > 0)
|
||
|
{
|
||
|
for (m_ActiveIndexCount = 0; m_ActiveIndexCount < m_IndexDataCount; ++m_ActiveIndexCount)
|
||
|
{
|
||
|
m_IndexArray[m_ActiveIndexCount] = indices[m_ActiveIndexCount];
|
||
|
}
|
||
|
for (m_ActiveVertexCount = 0; m_ActiveVertexCount < m_VertexDataCount; ++m_ActiveVertexCount)
|
||
|
{
|
||
|
pos = new Vector3(vertices[m_ActiveVertexCount].x, vertices[m_ActiveVertexCount].y, 0);
|
||
|
m_PosArray[m_ActiveVertexCount] = pos;
|
||
|
}
|
||
|
|
||
|
geom.indexCount = m_ActiveIndexCount;
|
||
|
geom.vertexCount = m_ActiveVertexCount;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (m_TanArray.Length > 1)
|
||
|
{
|
||
|
for (int i = 0; i < m_ActiveVertexCount; ++i)
|
||
|
m_TanArray[i] = new Vector4(1.0f, 0, 0, -1.0f);
|
||
|
}
|
||
|
|
||
|
m_GeomArray[0] = geom;
|
||
|
|
||
|
}
|
||
|
|
||
|
void CalculateBoundingBox()
|
||
|
{
|
||
|
Bounds bounds = new Bounds();
|
||
|
|
||
|
if (vertexDataCount > 0)
|
||
|
{
|
||
|
for (int i = 0; i < vertexDataCount; ++i)
|
||
|
{
|
||
|
Vector3 pos = m_PosArray[i];
|
||
|
bounds.Encapsulate(pos);
|
||
|
}
|
||
|
}
|
||
|
{
|
||
|
for (int i = 0; i < contourPointCount; ++i)
|
||
|
{
|
||
|
Vector3 pos = new Vector3(m_ContourPoints[i].position.x, m_ContourPoints[i].position.y, 0);
|
||
|
bounds.Encapsulate(pos);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_Bounds[0] = bounds;
|
||
|
}
|
||
|
|
||
|
void CalculateTexCoords()
|
||
|
{
|
||
|
|
||
|
SpriteShapeSegment geom = m_GeomArray[0];
|
||
|
if (m_ShapeParams.splineData.x > 0)
|
||
|
{
|
||
|
float3 ext = m_Bounds[0].extents * 2;
|
||
|
float3 min = m_Bounds[0].center - m_Bounds[0].extents;
|
||
|
for (int i = 0; i < geom.vertexCount; ++i)
|
||
|
{
|
||
|
Vector3 pos = m_PosArray[i];
|
||
|
Vector2 uv0 = m_Uv0Array[i];
|
||
|
float3 uv = ((new float3(pos.x, pos.y, pos.z) - min) / ext) * m_ShapeParams.fillData.x;
|
||
|
uv0.x = uv.x;
|
||
|
uv0.y = uv.y;
|
||
|
m_Uv0Array[i] = uv0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (int i = 0; i < geom.vertexCount; ++i)
|
||
|
{
|
||
|
Vector3 pos = m_PosArray[i];
|
||
|
Vector2 uv0 = m_Uv0Array[i];
|
||
|
float3 uv = math.transform(m_Transform, new float3(pos.x, pos.y, pos.z));
|
||
|
uv0.x = uv.x / m_ShapeParams.fillData.y;
|
||
|
uv0.y = uv.y / m_ShapeParams.fillData.z;
|
||
|
m_Uv0Array[i] = uv0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void CopyVertexData(ref NativeSlice<Vector3> outPos, ref NativeSlice<Vector2> outUV0, ref NativeSlice<Vector4> outTan, int outIndex, NativeArray<JobShapeVertex> inVertices, int inIndex, float pivot, float sOrder)
|
||
|
{
|
||
|
Vector3 iscp = outPos[outIndex];
|
||
|
Vector2 iscu = outUV0[outIndex];
|
||
|
|
||
|
float3 v0 = new float3(inVertices[inIndex].pos.x, inVertices[inIndex].pos.y, sOrder);
|
||
|
float3 v1 = new float3(inVertices[inIndex + 1].pos.x, inVertices[inIndex + 1].pos.y, sOrder);
|
||
|
float3 v2 = new float3(inVertices[inIndex + 2].pos.x, inVertices[inIndex + 2].pos.y, sOrder);
|
||
|
float3 v3 = new float3(inVertices[inIndex + 3].pos.x, inVertices[inIndex + 3].pos.y, sOrder);
|
||
|
|
||
|
float3 lt = (v2 - v0) * pivot;
|
||
|
float3 rt = (v3 - v1) * pivot;
|
||
|
v0 = v0 + lt;
|
||
|
v2 = v2 + lt;
|
||
|
v1 = v1 + rt;
|
||
|
v3 = v3 + rt;
|
||
|
|
||
|
outPos[outIndex] = v0;
|
||
|
outUV0[outIndex] = inVertices[inIndex].uv;
|
||
|
outPos[outIndex + 1] = v1;
|
||
|
outUV0[outIndex + 1] = inVertices[inIndex + 1].uv;
|
||
|
outPos[outIndex + 2] = v2;
|
||
|
outUV0[outIndex + 2] = inVertices[inIndex + 2].uv;
|
||
|
outPos[outIndex + 3] = v3;
|
||
|
outUV0[outIndex + 3] = inVertices[inIndex + 3].uv;
|
||
|
|
||
|
if (outTan.Length > 1)
|
||
|
{
|
||
|
outTan[outIndex] = inVertices[inIndex].tan;
|
||
|
outTan[outIndex + 1] = inVertices[inIndex + 1].tan;
|
||
|
outTan[outIndex + 2] = inVertices[inIndex + 2].tan;
|
||
|
outTan[outIndex + 3] = inVertices[inIndex + 3].tan;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int CopySegmentRenderData(JobSpriteInfo ispr, ref NativeSlice<Vector3> outPos, ref NativeSlice<Vector2> outUV0, ref NativeSlice<Vector4> outTan, ref int outCount, ref NativeArray<ushort> indexData, ref int indexCount, NativeArray<JobShapeVertex> inVertices, int inCount, float sOrder)
|
||
|
{
|
||
|
if (inCount < 4)
|
||
|
return -1;
|
||
|
|
||
|
int localVertex = 0;
|
||
|
float pivot = 0.5f - ispr.metaInfo.y;
|
||
|
int finalCount = indexCount + inCount;
|
||
|
if (finalCount >= indexData.Length)
|
||
|
throw new InvalidOperationException("Mesh data has reached Limits. Please try dividing shape into smaller blocks.");
|
||
|
|
||
|
for (int i = 0; i < inCount; i = i + 4, outCount = outCount + 4, localVertex = localVertex + 4)
|
||
|
{
|
||
|
CopyVertexData(ref outPos, ref outUV0, ref outTan, outCount, inVertices, i, pivot, sOrder);
|
||
|
indexData[indexCount++] = (ushort)(localVertex);
|
||
|
indexData[indexCount++] = (ushort)(3 + localVertex);
|
||
|
indexData[indexCount++] = (ushort)(1 + localVertex);
|
||
|
indexData[indexCount++] = (ushort)(localVertex);
|
||
|
indexData[indexCount++] = (ushort)(2 + localVertex);
|
||
|
indexData[indexCount++] = (ushort)(3 + localVertex);
|
||
|
}
|
||
|
return outCount;
|
||
|
}
|
||
|
|
||
|
void TessellateSegment(JobSpriteInfo sprInfo, JobSegmentInfo segment, float2 whsize, float4 border, float pxlWidth, bool useClosure, bool validHead, bool validTail, NativeArray<JobShapeVertex> vertices, int vertexCount, ref NativeArray<JobShapeVertex> outputVertices, ref int outputCount)
|
||
|
{
|
||
|
int outputVertexCount = 0;
|
||
|
float2 zero = new float2(0, 0);
|
||
|
float2 lt = zero, lb = zero, rt = zero, rb = zero;
|
||
|
var column0 = new JobShapeVertex();
|
||
|
var column1 = new JobShapeVertex();
|
||
|
var column2 = new JobShapeVertex();
|
||
|
var column3 = new JobShapeVertex();
|
||
|
|
||
|
|
||
|
int cms = vertexCount - 1;
|
||
|
int lcm = cms - 1;
|
||
|
int expectedCount = outputCount + (cms * 4);
|
||
|
var sprite = vertices[0].sprite;
|
||
|
|
||
|
float uvDist = 0;
|
||
|
float uvStart = border.x;
|
||
|
float uvEnd = whsize.x - border.z;
|
||
|
float uvTotal = whsize.x;
|
||
|
float uvInter = uvEnd - uvStart;
|
||
|
float uvNow = uvStart / uvTotal;
|
||
|
float dt = uvInter / pxlWidth;
|
||
|
|
||
|
if (expectedCount >= outputVertices.Length)
|
||
|
throw new InvalidOperationException("Mesh data has reached Limits. Please try dividing shape into smaller blocks.");
|
||
|
|
||
|
// Generate Render Inputs.
|
||
|
for (int i = 0; i < cms; ++i)
|
||
|
{
|
||
|
bool lc = (cms > 1) && (i == lcm);
|
||
|
bool im = (i != 0 && !lc);
|
||
|
bool sa = false, sb = false;
|
||
|
|
||
|
JobShapeVertex cs = vertices[i];
|
||
|
JobShapeVertex ns = vertices[i + 1];
|
||
|
|
||
|
float2 es = lc ? cs.pos : vertices[i + 2].pos;
|
||
|
lt = column1.pos;
|
||
|
lb = column3.pos;
|
||
|
sa = true;
|
||
|
|
||
|
if (im)
|
||
|
{
|
||
|
// Left from Previous.
|
||
|
sb = GenerateColumnsTri(cs.pos, ns.pos, es, whsize, lc, ref rt, ref rb, ns.meta.x * 0.5f);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!lc)
|
||
|
{
|
||
|
JobControlPoint icp = GetControlPoint(segment.spInfo.x);
|
||
|
var nsPos = ns.pos;
|
||
|
if (math.any(icp.tangentRt))
|
||
|
nsPos = icp.tangentRt + cs.pos;
|
||
|
sa = GenerateColumnsBi(cs.pos, nsPos, whsize, false, ref lt, ref lb, cs.meta.x * 0.5f);
|
||
|
}
|
||
|
if (lc && useClosure)
|
||
|
{
|
||
|
rb = m_FirstLB;
|
||
|
rt = m_FirstLT;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var esPos = es;
|
||
|
if (i == lcm)
|
||
|
{
|
||
|
JobControlPoint jcp = GetControlPoint(segment.spInfo.y);
|
||
|
if (math.any(jcp.tangentLt))
|
||
|
esPos = jcp.tangentLt + ns.pos;
|
||
|
}
|
||
|
sb = GenerateColumnsBi(ns.pos, esPos, whsize, lc, ref rt, ref rb, ns.meta.x * 0.5f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (i == 0 && segment.spInfo.x == 0)
|
||
|
{
|
||
|
m_FirstLB = lb;
|
||
|
m_FirstLT = lt;
|
||
|
}
|
||
|
|
||
|
if (!((math.any(lt) || math.any(lb)) && (math.any(rt) || math.any(rb))))
|
||
|
continue;
|
||
|
|
||
|
// default tan (1, 0, 0, -1) which is along uv. same here.
|
||
|
float2 nlt = math.normalize(rt - lt);
|
||
|
float4 tan = new float4(nlt.x, nlt.y, 0, -1.0f);
|
||
|
column0.pos = lt;
|
||
|
column0.meta = cs.meta;
|
||
|
column0.sprite = sprite;
|
||
|
column0.tan = tan;
|
||
|
column1.pos = rt;
|
||
|
column1.meta = ns.meta;
|
||
|
column1.sprite = sprite;
|
||
|
column1.tan = tan;
|
||
|
column2.pos = lb;
|
||
|
column2.meta = cs.meta;
|
||
|
column2.sprite = sprite;
|
||
|
column2.tan = tan;
|
||
|
column3.pos = rb;
|
||
|
column3.meta = ns.meta;
|
||
|
column3.sprite = sprite;
|
||
|
column3.tan = tan;
|
||
|
|
||
|
// Calculate UV.
|
||
|
if (validHead && i == 0)
|
||
|
{
|
||
|
column0.uv.x = column0.uv.y = column1.uv.y = column2.uv.x = 0;
|
||
|
column1.uv.x = column3.uv.x = border.x / whsize.x;
|
||
|
column2.uv.y = column3.uv.y = 1.0f;
|
||
|
}
|
||
|
else if (validTail && i == lcm)
|
||
|
{
|
||
|
column0.uv.y = column1.uv.y = 0;
|
||
|
column0.uv.x = column2.uv.x = (whsize.x - border.z) / whsize.x;
|
||
|
column1.uv.x = column2.uv.y = column3.uv.x = column3.uv.y = 1.0f;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((uvInter - uvDist) < kEpsilonRelaxed)
|
||
|
{
|
||
|
uvNow = uvStart / uvTotal;
|
||
|
uvDist = 0;
|
||
|
}
|
||
|
|
||
|
uvDist = uvDist + (math.distance(ns.pos, cs.pos) * dt);
|
||
|
float uvNext = (uvDist + uvStart) / uvTotal;
|
||
|
|
||
|
if ((uvDist - uvInter) > kEpsilonRelaxed)
|
||
|
{
|
||
|
uvNext = uvEnd / uvTotal;
|
||
|
uvDist = uvEnd;
|
||
|
}
|
||
|
|
||
|
column0.uv.y = column1.uv.y = 0;
|
||
|
column0.uv.x = column2.uv.x = uvNow;
|
||
|
column1.uv.x = column3.uv.x = uvNext;
|
||
|
column2.uv.y = column3.uv.y = 1.0f;
|
||
|
uvNow = uvNext;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
// Fix UV and Copy.
|
||
|
column0.uv.x = (column0.uv.x * sprInfo.uvInfo.z) + sprInfo.uvInfo.x;
|
||
|
column0.uv.y = (column0.uv.y * sprInfo.uvInfo.w) + sprInfo.uvInfo.y;
|
||
|
outputVertices[outputVertexCount++] = column0;
|
||
|
|
||
|
column1.uv.x = (column1.uv.x * sprInfo.uvInfo.z) + sprInfo.uvInfo.x;
|
||
|
column1.uv.y = (column1.uv.y * sprInfo.uvInfo.w) + sprInfo.uvInfo.y;
|
||
|
outputVertices[outputVertexCount++] = column1;
|
||
|
|
||
|
column2.uv.x = (column2.uv.x * sprInfo.uvInfo.z) + sprInfo.uvInfo.x;
|
||
|
column2.uv.y = (column2.uv.y * sprInfo.uvInfo.w) + sprInfo.uvInfo.y;
|
||
|
outputVertices[outputVertexCount++] = column2;
|
||
|
|
||
|
column3.uv.x = (column3.uv.x * sprInfo.uvInfo.z) + sprInfo.uvInfo.x;
|
||
|
column3.uv.y = (column3.uv.y * sprInfo.uvInfo.w) + sprInfo.uvInfo.y;
|
||
|
outputVertices[outputVertexCount++] = column3;
|
||
|
}
|
||
|
}
|
||
|
outputCount = outputVertexCount;
|
||
|
}
|
||
|
|
||
|
bool SkipSegment(JobSegmentInfo isi)
|
||
|
{
|
||
|
// Start the Generation.
|
||
|
bool skip = (isi.spInfo.z < 0);
|
||
|
if (!skip)
|
||
|
{
|
||
|
JobSpriteInfo ispr = GetSpriteInfo(isi.spInfo.z);
|
||
|
skip = (math.any(ispr.uvInfo) == false);
|
||
|
}
|
||
|
if (skip)
|
||
|
{
|
||
|
int cis = GetContourIndex(isi.spInfo.x);
|
||
|
int cie = GetEndContourIndexOfSegment(isi);
|
||
|
while (cis < cie)
|
||
|
{
|
||
|
JobContourPoint icp = GetContourPoint(cis);
|
||
|
m_ColliderPoints[m_ColliderDataCount++] = icp.position;
|
||
|
cis++;
|
||
|
}
|
||
|
}
|
||
|
return skip;
|
||
|
}
|
||
|
|
||
|
void TessellateSegments()
|
||
|
{
|
||
|
|
||
|
JobControlPoint iscp = GetControlPoint(0);
|
||
|
bool disableHead = (iscp.cpData.z == kModeContinous && isCarpet);
|
||
|
|
||
|
// Determine Distance of Segment.
|
||
|
for (int i = 0; i < segmentCount; ++i)
|
||
|
{
|
||
|
// Calculate Segment Distances.
|
||
|
JobSegmentInfo isi = GetSegmentInfo(i);
|
||
|
if (isi.spriteInfo.z >= 0)
|
||
|
{
|
||
|
isi.spriteInfo.w = SegmentDistance(isi);
|
||
|
m_Segments[i] = isi;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float2 zero = new float2(0, 0);
|
||
|
float2 firstLT = zero;
|
||
|
float2 firstLB = zero;
|
||
|
float2 ec = zero;
|
||
|
|
||
|
for (int i = 0; i < segmentCount; ++i)
|
||
|
{
|
||
|
// Tessellate the Segment.
|
||
|
JobSegmentInfo isi = GetSegmentInfo(i);
|
||
|
bool skip = SkipSegment(isi);
|
||
|
if (skip)
|
||
|
continue;
|
||
|
|
||
|
// Internal Data : x, y : pos z : height w : renderIndex
|
||
|
JobShapeVertex isv = m_VertexData[0];
|
||
|
JobSpriteInfo ispr = GetSpriteInfo(isi.spInfo.z);
|
||
|
|
||
|
int vertexCount = 0;
|
||
|
int sprIx = isi.spInfo.z;
|
||
|
float rpunits = 1.0f / ispr.metaInfo.x;
|
||
|
float2 whsize = new float2(ispr.metaInfo.z, ispr.metaInfo.w) * rpunits;
|
||
|
float4 border = ispr.border * rpunits;
|
||
|
|
||
|
bool validHead = hasSpriteBorder && (border.x > 0);
|
||
|
bool validTail = hasSpriteBorder && (border.z > 0);
|
||
|
|
||
|
// Generate the UV Increments.
|
||
|
float extendUV = 0;
|
||
|
float stPixelU = border.x;
|
||
|
float enPixelU = whsize.x - border.z;
|
||
|
float pxlWidth = enPixelU - stPixelU;
|
||
|
float segmentD = isi.spriteInfo.w;
|
||
|
float uIncStep = math.floor(segmentD / pxlWidth);
|
||
|
uIncStep = uIncStep == 0 ? 1f : uIncStep;
|
||
|
pxlWidth = isAdaptive ? (segmentD / uIncStep) : pxlWidth;
|
||
|
|
||
|
// Check for any invalid Sizes.
|
||
|
if (pxlWidth < kEpsilon)
|
||
|
{
|
||
|
Cleanup();
|
||
|
throw new ArgumentException("One of the sprites seem to have Invalid Borders. Please check Input Sprites.");
|
||
|
}
|
||
|
|
||
|
// Start the Generation.
|
||
|
int stIx = GetContourIndex(isi.spInfo.x);
|
||
|
int enIx = GetEndContourIndexOfSegment(isi);
|
||
|
|
||
|
// Single Segment Loop.
|
||
|
if (stIx == 0)
|
||
|
validHead = (validHead && !disableHead);
|
||
|
|
||
|
// Do we have a Sprite Head Slice
|
||
|
if (validHead)
|
||
|
{
|
||
|
JobContourPoint icp = GetContourPoint(stIx);
|
||
|
float2 v1 = icp.position;
|
||
|
float2 v2 = GetContourPoint(stIx + 1).position;
|
||
|
isv.pos = v1 + (math.normalize(v1 - v2) * border.x);
|
||
|
isv.meta.x = icp.ptData.x;
|
||
|
isv.sprite.x = sprIx;
|
||
|
m_VertexData[vertexCount++] = isv;
|
||
|
}
|
||
|
|
||
|
// Generate the Strip.
|
||
|
float sl = 0;
|
||
|
int it = stIx, nt = 0;
|
||
|
while (it < enIx)
|
||
|
{
|
||
|
nt = it + 1;
|
||
|
JobContourPoint icp = GetContourPoint(it);
|
||
|
JobContourPoint ncp = GetContourPoint(nt);
|
||
|
|
||
|
float2 sp = icp.position;
|
||
|
float2 ip = sp;
|
||
|
float2 ep = ncp.position;
|
||
|
float2 df = ep - sp;
|
||
|
float al = math.length(df);
|
||
|
if (al > kEpsilon)
|
||
|
{
|
||
|
float sh = icp.ptData.x, eh = ncp.ptData.x, hl = 0;
|
||
|
sl = sl + al;
|
||
|
|
||
|
// Connect previously left out space when sl < pxlWidth
|
||
|
var addtail = (0 == vertexCount);
|
||
|
float2 step = math.normalize(df);
|
||
|
isv.pos = icp.position;
|
||
|
isv.meta.x = icp.ptData.x;
|
||
|
isv.sprite.x = sprIx;
|
||
|
|
||
|
if (vertexCount > 0)
|
||
|
{
|
||
|
var dt = math.length(m_VertexData[vertexCount-1].pos - isv.pos);
|
||
|
addtail = dt > kEpsilonRelaxed;
|
||
|
}
|
||
|
if (addtail)
|
||
|
m_VertexData[vertexCount++] = isv;
|
||
|
|
||
|
while (sl > pxlWidth)
|
||
|
{
|
||
|
float _uv = pxlWidth - extendUV;
|
||
|
float2 uv = new float2(_uv);
|
||
|
ip = sp + (step * uv);
|
||
|
hl = hl + math.length(ip - sp);
|
||
|
|
||
|
isv.pos = ip;
|
||
|
isv.meta.x = math.lerp(sh, eh, hl / al);
|
||
|
isv.sprite.x = sprIx;
|
||
|
if (math.any(m_VertexData[vertexCount-1].pos - isv.pos))
|
||
|
m_VertexData[vertexCount++] = isv;
|
||
|
|
||
|
sl = sl - pxlWidth;
|
||
|
sp = ip;
|
||
|
extendUV = 0;
|
||
|
}
|
||
|
extendUV = sl;
|
||
|
}
|
||
|
it++;
|
||
|
}
|
||
|
|
||
|
// The Remains from the above Loop. Finish the Curve.
|
||
|
if (sl > kEpsilon)
|
||
|
{
|
||
|
JobContourPoint ecp = GetContourPoint(enIx);
|
||
|
isv.pos = ecp.position;
|
||
|
isv.meta.x = ecp.ptData.x;
|
||
|
isv.sprite.x = sprIx;
|
||
|
m_VertexData[vertexCount++] = isv;
|
||
|
}
|
||
|
|
||
|
// Generate Tail
|
||
|
if (validTail)
|
||
|
{
|
||
|
JobContourPoint icp = GetContourPoint(enIx);
|
||
|
float2 v1 = icp.position;
|
||
|
float2 v2 = GetContourPoint(enIx - 1).position;
|
||
|
isv.pos = v1 + (math.normalize(v1 - v2) * border.z);
|
||
|
isv.meta.x = icp.ptData.x;
|
||
|
isv.sprite.x = sprIx;
|
||
|
m_VertexData[vertexCount++] = isv;
|
||
|
}
|
||
|
|
||
|
// Generate the Renderer Data.
|
||
|
int outputCount = 0;
|
||
|
bool useClosure = (m_ControlPoints[0].cpData.z == kModeContinous) && (isi.spInfo.y == controlPointCount - 1);
|
||
|
TessellateSegment(ispr, isi, whsize, border, pxlWidth, useClosure, validHead, validTail, m_VertexData, vertexCount, ref m_OutputVertexData, ref outputCount);
|
||
|
if (outputCount == 0)
|
||
|
continue;
|
||
|
var z = ((float)(i + 1) * kEpsilonOrder) + ((float)isi.spInfo.z * kEpsilonOrder * 0.001f);
|
||
|
CopySegmentRenderData(ispr, ref m_PosArray, ref m_Uv0Array, ref m_TanArray, ref m_VertexDataCount, ref m_IndexArray, ref m_IndexDataCount, m_OutputVertexData, outputCount, z);
|
||
|
|
||
|
if (hasCollider)
|
||
|
{
|
||
|
JobSpriteInfo isprc = (ispr.metaInfo.x == 0) ? GetSpriteInfo(isi.spInfo.w) : ispr;
|
||
|
outputCount = 0;
|
||
|
rpunits = 1.0f / isprc.metaInfo.x;
|
||
|
whsize = new float2(isprc.metaInfo.z, isprc.metaInfo.w) * rpunits;
|
||
|
border = isprc.border * rpunits;
|
||
|
stPixelU = border.x;
|
||
|
enPixelU = whsize.x - border.z;
|
||
|
pxlWidth = enPixelU - stPixelU;
|
||
|
TessellateSegment(isprc, isi, whsize, border, pxlWidth, useClosure, validHead, validTail, m_VertexData, vertexCount, ref m_OutputVertexData, ref outputCount);
|
||
|
ec = UpdateCollider(isi, isprc, m_OutputVertexData, outputCount, ref m_ColliderPoints, ref m_ColliderDataCount);
|
||
|
}
|
||
|
|
||
|
// Geom Data
|
||
|
var geom = m_GeomArray[i + 1];
|
||
|
geom.geomIndex = i + 1;
|
||
|
geom.indexCount = m_IndexDataCount - m_ActiveIndexCount;
|
||
|
geom.vertexCount = m_VertexDataCount - m_ActiveVertexCount;
|
||
|
geom.spriteIndex = isi.spInfo.z;
|
||
|
m_GeomArray[i + 1] = geom;
|
||
|
|
||
|
// Exit
|
||
|
m_ActiveIndexCount = m_IndexDataCount;
|
||
|
m_ActiveVertexCount = m_VertexDataCount;
|
||
|
}
|
||
|
|
||
|
// Copy Collider, Copy Render Data.
|
||
|
m_GeomArrayCount = segmentCount + 1;
|
||
|
m_IndexArrayCount = m_IndexDataCount;
|
||
|
m_VertexArrayCount = m_VertexDataCount;
|
||
|
m_ColliderPointCount = m_ColliderDataCount;
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Corners
|
||
|
|
||
|
bool AttachCorner(int cp, int ct, JobSpriteInfo ispr, ref NativeArray<JobControlPoint> newPoints, ref int activePoint)
|
||
|
{
|
||
|
// Correct Left.
|
||
|
float2 zero = new float2(0, 0);
|
||
|
int pp = (cp == 0) ? (controlPointCount - 1) : (cp - 1);
|
||
|
int np = (cp + 1) % controlPointCount;
|
||
|
|
||
|
JobControlPoint lcp = GetControlPoint(pp);
|
||
|
JobControlPoint ccp = GetControlPoint(cp);
|
||
|
JobControlPoint rcp = GetControlPoint(np);
|
||
|
|
||
|
float rpunits = 1.0f / ispr.metaInfo.x;
|
||
|
float2 whsize = new float2(ispr.texRect.z, ispr.texRect.w) * rpunits;
|
||
|
float4 border = ispr.border * rpunits;
|
||
|
|
||
|
// Generate the UV Increments.
|
||
|
float stPixelV = border.y;
|
||
|
float enPixelV = whsize.y - border.y;
|
||
|
float pxlWidth = enPixelV - stPixelV; // pxlWidth is the square size of the corner sprite.
|
||
|
|
||
|
// Generate the LeftTop, LeftBottom, RightTop & RightBottom for both sides.
|
||
|
float2 lt0 = zero;
|
||
|
float2 lb0 = zero;
|
||
|
float2 rt0 = zero;
|
||
|
float2 rb0 = zero;
|
||
|
GenerateColumnsBi(lcp.position, ccp.position, whsize, false, ref lb0, ref lt0, 0.5f);
|
||
|
GenerateColumnsBi(ccp.position, lcp.position, whsize, false, ref rt0, ref rb0, 0.5f);
|
||
|
|
||
|
float2 lt1 = zero;
|
||
|
float2 lb1 = zero;
|
||
|
float2 rt1 = zero;
|
||
|
float2 rb1 = zero;
|
||
|
GenerateColumnsBi(ccp.position, rcp.position, whsize, false, ref lb1, ref lt1, 0.5f);
|
||
|
GenerateColumnsBi(rcp.position, ccp.position, whsize, false, ref rt1, ref rb1, 0.5f);
|
||
|
|
||
|
rt0 = rt0 + (math.normalize(rt0 - lt0) * kExtendSegment);
|
||
|
rb0 = rb0 + (math.normalize(rb0 - lb0) * kExtendSegment);
|
||
|
lt1 = lt1 + (math.normalize(lt1 - rt1) * kExtendSegment);
|
||
|
lb1 = lb1 + (math.normalize(lb1 - rb1) * kExtendSegment);
|
||
|
|
||
|
float2 tp = zero;
|
||
|
float2 bt = zero;
|
||
|
|
||
|
// Generate Intersection of the Bottom Line Segments.
|
||
|
bool t = LineIntersection(kEpsilon, lt0, rt0, lt1, rt1, ref tp);
|
||
|
bool b = LineIntersection(kEpsilon, lb0, rb0, lb1, rb1, ref bt);
|
||
|
if (!b && !t)
|
||
|
return false;
|
||
|
|
||
|
float2 pt = ccp.position;
|
||
|
float2 lt = lcp.position - pt;
|
||
|
float2 rt = rcp.position - pt;
|
||
|
|
||
|
float ld = math.length(lt);
|
||
|
float rd = math.length(rt);
|
||
|
|
||
|
if (ld < pxlWidth || rd < pxlWidth)
|
||
|
return false;
|
||
|
|
||
|
float lrd = 0, rrd = 0;
|
||
|
float a = AngleBetweenVector(math.normalize(lcp.position - ccp.position), math.normalize(rcp.position - ccp.position));
|
||
|
if (a > 0)
|
||
|
{
|
||
|
lrd = ld - math.distance(lb0, bt);
|
||
|
rrd = rd - math.distance(bt, rb1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lrd = ld - math.distance(lt0, tp);
|
||
|
rrd = rd - math.distance(tp, rt1);
|
||
|
}
|
||
|
|
||
|
float2 la = pt + (math.normalize(lt) * lrd);
|
||
|
float2 ra = pt + (math.normalize(rt) * rrd);
|
||
|
|
||
|
ccp.exData.x = ct;
|
||
|
ccp.exData.z = 1;
|
||
|
ccp.position = la;
|
||
|
newPoints[activePoint++] = ccp;
|
||
|
|
||
|
ccp.exData.x = ct;
|
||
|
ccp.exData.z = 0;
|
||
|
ccp.position = ra;
|
||
|
newPoints[activePoint++] = ccp;
|
||
|
|
||
|
JobCornerInfo iscp = m_Corners[m_CornerCount];
|
||
|
if (a > 0)
|
||
|
{
|
||
|
iscp.bottom = bt;
|
||
|
iscp.top = tp;
|
||
|
GenerateColumnsBi(la, lcp.position, whsize, false, ref lt0, ref lb0, ispr.metaInfo.y);
|
||
|
GenerateColumnsBi(ra, rcp.position, whsize, false, ref lt1, ref lb1, ispr.metaInfo.y);
|
||
|
iscp.left = lt0;
|
||
|
iscp.right = lb1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iscp.bottom = tp;
|
||
|
iscp.top = bt;
|
||
|
GenerateColumnsBi(la, lcp.position, whsize, false, ref lt0, ref lb0, ispr.metaInfo.y);
|
||
|
GenerateColumnsBi(ra, rcp.position, whsize, false, ref lt1, ref lb1, ispr.metaInfo.y);
|
||
|
iscp.left = lb0;
|
||
|
iscp.right = lt1;
|
||
|
}
|
||
|
iscp.cornerData.x = ct;
|
||
|
iscp.cornerData.y = activePoint;
|
||
|
m_Corners[m_CornerCount] = iscp;
|
||
|
|
||
|
m_CornerCount++;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
float2 CornerTextureCoordinate(int cornerType, int index)
|
||
|
{
|
||
|
int cornerArrayIndex = (cornerType - 1) * 4;
|
||
|
return m_CornerCoordinates[cornerArrayIndex + index];
|
||
|
}
|
||
|
|
||
|
int CalculateCorner(int index, float angle, float2 lt, float2 rt)
|
||
|
{
|
||
|
var ct = 0;
|
||
|
float slope = SlopeAngle(lt);
|
||
|
var slopePairs = new float2[]
|
||
|
{
|
||
|
new float2(-135.0f, -35.0f),
|
||
|
new float2(35.0f, 135.0f),
|
||
|
new float2(-35.0f, 35.0f),
|
||
|
new float2(-135.0f, 135.0f)
|
||
|
};
|
||
|
var cornerPairs = new int2[]
|
||
|
{
|
||
|
new int2(kCornerTypeInnerTopLeft, kCornerTypeOuterBottomLeft),
|
||
|
new int2(kCornerTypeInnerBottomRight, kCornerTypeOuterTopRight),
|
||
|
new int2(kCornerTypeInnerTopRight, kCornerTypeOuterTopLeft),
|
||
|
new int2(kCornerTypeInnerBottomLeft, kCornerTypeOuterBottomRight)
|
||
|
};
|
||
|
for (int i = 0; i < 3; ++i)
|
||
|
{
|
||
|
if ( slope > slopePairs[i].x && slope < slopePairs[i].y )
|
||
|
{
|
||
|
ct = (angle > 0) ? cornerPairs[i].x : cornerPairs[i].y;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (ct == 0)
|
||
|
{
|
||
|
ct = (angle > 0) ? kCornerTypeInnerBottomLeft : kCornerTypeOuterBottomRight;
|
||
|
}
|
||
|
return ct;
|
||
|
|
||
|
}
|
||
|
|
||
|
bool InsertCorner(int index, ref NativeArray<int2> cpSpriteIndices, ref NativeArray<JobControlPoint> newPoints, ref int activePoint)
|
||
|
{
|
||
|
int i = (index == 0) ? (controlPointCount - 1) : (index - 1);
|
||
|
int k = (index + 1) % controlPointCount;
|
||
|
|
||
|
// Check if we have valid Sprites.
|
||
|
if (cpSpriteIndices[i].x >= spriteCount || cpSpriteIndices[index].x >= spriteCount)
|
||
|
return false;
|
||
|
|
||
|
// Check if they have been resolved.
|
||
|
if (cpSpriteIndices[i].y == 0 || cpSpriteIndices[index].y == 0)
|
||
|
return false;
|
||
|
|
||
|
JobControlPoint pcp = GetControlPoint(i);
|
||
|
JobControlPoint icp = GetControlPoint(index);
|
||
|
JobControlPoint ncp = GetControlPoint(k);
|
||
|
|
||
|
// Check if the Mode of control Point and previous neighbor is same. Also check if Corner Toggle is enabled.
|
||
|
if (icp.cpData.y == 0 || pcp.cpData.z != kModeLinear || icp.cpData.z != kModeLinear || ncp.cpData.z != kModeLinear)
|
||
|
return false;
|
||
|
|
||
|
// Check if the Height of the Control Points match
|
||
|
if (pcp.cpInfo.x != icp.cpInfo.x || icp.cpInfo.x != ncp.cpInfo.x)
|
||
|
return false;
|
||
|
|
||
|
JobSpriteInfo psi = GetSpriteInfo(cpSpriteIndices[i].x);
|
||
|
JobSpriteInfo isi = GetSpriteInfo(cpSpriteIndices[index].x);
|
||
|
|
||
|
// Check if the Sprites Pivot matches. Otherwise not allowed. // psi.uvInfo.w != isi.uvInfo.w (no more height checks)
|
||
|
if (psi.metaInfo.y != 0.5f || psi.metaInfo.y != isi.metaInfo.y)
|
||
|
return false;
|
||
|
|
||
|
// Now perform expensive stuff like angles etc..
|
||
|
float2 idir = math.normalize(ncp.position - icp.position);
|
||
|
float2 ndir = math.normalize(pcp.position - icp.position);
|
||
|
float angle = AngleBetweenVector(idir, ndir);
|
||
|
float angleAbs = math.abs(angle);
|
||
|
bool corner = AngleWithinRange(angleAbs, (90f - m_ShapeParams.splineData.z), (90f + m_ShapeParams.splineData.z));
|
||
|
if (corner)
|
||
|
{
|
||
|
float2 rdir = math.normalize(icp.position - pcp.position);
|
||
|
int ct = CalculateCorner(index, angle, rdir, idir);
|
||
|
// Check if we have a valid Sprite.
|
||
|
if (ct > 0)
|
||
|
{
|
||
|
JobSpriteInfo cspr = GetCornerSpriteInfo(ct);
|
||
|
return AttachCorner(index, ct, cspr, ref newPoints, ref activePoint);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void TessellateCorners()
|
||
|
{
|
||
|
|
||
|
for (int corner = 1; corner <= kCornerTypeInnerBottomRight; ++corner)
|
||
|
{
|
||
|
JobSpriteInfo isi = GetCornerSpriteInfo(corner);
|
||
|
if (isi.metaInfo.x == 0)
|
||
|
continue;
|
||
|
|
||
|
int ic = 0;
|
||
|
int vc = 0;
|
||
|
Vector3 pos = m_PosArray[ic];
|
||
|
Vector2 uv0 = m_Uv0Array[ic];
|
||
|
bool ccw = (corner <= kCornerTypeOuterBottomRight);
|
||
|
int vertexArrayCount = m_VertexArrayCount;
|
||
|
|
||
|
for (int i = 0; i < m_CornerCount; ++i)
|
||
|
{
|
||
|
JobCornerInfo isc = m_Corners[i];
|
||
|
if (isc.cornerData.x == corner)
|
||
|
{
|
||
|
// Vertices.
|
||
|
pos.x = isc.top.x;
|
||
|
pos.y = isc.top.y;
|
||
|
uv0.x = (CornerTextureCoordinate(corner, 1).x * isi.uvInfo.z) + isi.uvInfo.x;
|
||
|
uv0.y = (CornerTextureCoordinate(corner, 1).y * isi.uvInfo.w) + isi.uvInfo.y;
|
||
|
m_PosArray[m_VertexArrayCount] = pos;
|
||
|
m_Uv0Array[m_VertexArrayCount++] = uv0;
|
||
|
|
||
|
|
||
|
pos.x = isc.right.x;
|
||
|
pos.y = isc.right.y;
|
||
|
uv0.x = (CornerTextureCoordinate(corner, 0).x * isi.uvInfo.z) + isi.uvInfo.x;
|
||
|
uv0.y = (CornerTextureCoordinate(corner, 0).y * isi.uvInfo.w) + isi.uvInfo.y;
|
||
|
m_PosArray[m_VertexArrayCount] = pos;
|
||
|
m_Uv0Array[m_VertexArrayCount++] = uv0;
|
||
|
|
||
|
pos.x = isc.left.x;
|
||
|
pos.y = isc.left.y;
|
||
|
uv0.x = (CornerTextureCoordinate(corner, 3).x * isi.uvInfo.z) + isi.uvInfo.x;
|
||
|
uv0.y = (CornerTextureCoordinate(corner, 3).y * isi.uvInfo.w) + isi.uvInfo.y;
|
||
|
m_PosArray[m_VertexArrayCount] = pos;
|
||
|
m_Uv0Array[m_VertexArrayCount++] = uv0;
|
||
|
|
||
|
pos.x = isc.bottom.x;
|
||
|
pos.y = isc.bottom.y;
|
||
|
uv0.x = (CornerTextureCoordinate(corner, 2).x * isi.uvInfo.z) + isi.uvInfo.x;
|
||
|
uv0.y = (CornerTextureCoordinate(corner, 2).y * isi.uvInfo.w) + isi.uvInfo.y;
|
||
|
m_PosArray[m_VertexArrayCount] = pos;
|
||
|
m_Uv0Array[m_VertexArrayCount++] = uv0;
|
||
|
|
||
|
// Indices.
|
||
|
m_IndexArray[m_IndexArrayCount++] = (ushort)(vc + 0);
|
||
|
m_IndexArray[m_IndexArrayCount++] = (ushort)(vc + (ccw ? 1 : 3));
|
||
|
m_IndexArray[m_IndexArrayCount++] = (ushort)(vc + (ccw ? 3 : 1));
|
||
|
|
||
|
m_IndexArray[m_IndexArrayCount++] = (ushort)(vc + 0);
|
||
|
m_IndexArray[m_IndexArrayCount++] = (ushort)(vc + (ccw ? 3 : 2));
|
||
|
m_IndexArray[m_IndexArrayCount++] = (ushort)(vc + (ccw ? 2 : 3));
|
||
|
|
||
|
vc = vc + 4;
|
||
|
ic = ic + 6;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (m_TanArray.Length > 1)
|
||
|
{
|
||
|
for (int i = vertexArrayCount; i < m_VertexArrayCount; ++i)
|
||
|
m_TanArray[i] = new Vector4(1.0f, 0, 0, -1.0f);
|
||
|
}
|
||
|
|
||
|
// Geom Data
|
||
|
if (ic > 0 && vc > 0)
|
||
|
{
|
||
|
var geom = m_GeomArray[m_GeomArrayCount];
|
||
|
geom.geomIndex = m_GeomArrayCount;
|
||
|
geom.indexCount = ic;
|
||
|
geom.vertexCount = vc;
|
||
|
geom.spriteIndex = m_SpriteInfos.Length + (corner - 1);
|
||
|
m_GeomArray[m_GeomArrayCount++] = geom;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Fast Optimizations
|
||
|
|
||
|
bool AreCollinear(float2 a, float2 b, float2 c, float t)
|
||
|
{
|
||
|
float ax = (a.y - b.y) * (a.x - c.x);
|
||
|
float bx = (a.y - c.y) * (a.x - b.x);
|
||
|
float aa = math.abs(ax - bx);
|
||
|
return aa < t;
|
||
|
}
|
||
|
|
||
|
// Check if points are co linear and reduce.
|
||
|
void OptimizePoints(float tolerance, ref NativeArray<float2> pointSet, ref int pointCount)
|
||
|
{
|
||
|
int kMinimumPointsRequired = 8;
|
||
|
if (pointCount < kMinimumPointsRequired)
|
||
|
return;
|
||
|
|
||
|
int optimizedColliderPointCount = 0;
|
||
|
int endColliderPointCount = pointCount - 2;
|
||
|
bool val = true;
|
||
|
m_TempPoints[0] = pointSet[0];
|
||
|
for (int i = 0; i < endColliderPointCount; ++i)
|
||
|
{
|
||
|
int j = i;
|
||
|
float2 v0 = pointSet[i];
|
||
|
float2 v1 = pointSet[i + 1];
|
||
|
float2 v2 = pointSet[i + 2];
|
||
|
val = true;
|
||
|
while (val && j < endColliderPointCount)
|
||
|
{
|
||
|
val = AreCollinear(v0, v1, v2, tolerance);
|
||
|
if (false == val)
|
||
|
{
|
||
|
m_TempPoints[++optimizedColliderPointCount] = v1;
|
||
|
i = j;
|
||
|
break;
|
||
|
}
|
||
|
j++;
|
||
|
v1 = pointSet[j + 1];
|
||
|
v2 = pointSet[j + 2];
|
||
|
}
|
||
|
}
|
||
|
m_TempPoints[++optimizedColliderPointCount] = pointSet[endColliderPointCount];
|
||
|
m_TempPoints[++optimizedColliderPointCount] = pointSet[endColliderPointCount + 1];
|
||
|
if (isCarpet)
|
||
|
m_TempPoints[++optimizedColliderPointCount] = pointSet[0];
|
||
|
pointCount = optimizedColliderPointCount + 1;
|
||
|
for (int i = 0; i < pointCount; ++i)
|
||
|
pointSet[i] = m_TempPoints[i];
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Collider Specific.
|
||
|
void AttachCornerToCollider(JobSegmentInfo isi, float pivot, ref NativeArray<float2> colliderPoints, ref int colliderPointCount)
|
||
|
{
|
||
|
float2 zero = new float2(0, 0);
|
||
|
int cornerIndex = isi.spInfo.x + 1;
|
||
|
for (int i = 0; i < m_CornerCount; ++i)
|
||
|
{
|
||
|
JobCornerInfo isc = m_Corners[i];
|
||
|
if (cornerIndex == isc.cornerData.y)
|
||
|
{
|
||
|
float2 cp = zero;
|
||
|
float2 v0 = zero;
|
||
|
if (isc.cornerData.x > kCornerTypeOuterBottomRight)
|
||
|
v0 = isc.top;
|
||
|
else
|
||
|
v0 = isc.bottom;
|
||
|
|
||
|
float2 v2 = zero;
|
||
|
if (isc.cornerData.x > kCornerTypeOuterBottomRight)
|
||
|
v2 = isc.bottom;
|
||
|
else
|
||
|
v2 = isc.top;
|
||
|
cp = (v2 - v0) * pivot;
|
||
|
cp = (v2 + cp + v0 + cp) * 0.5f;
|
||
|
colliderPoints[colliderPointCount++] = cp;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float2 UpdateCollider(JobSegmentInfo isi, JobSpriteInfo ispr, NativeArray<JobShapeVertex> vertices, int count, ref NativeArray<float2> colliderPoints, ref int colliderPointCount)
|
||
|
{
|
||
|
float2 zero = new float2(0, 0);
|
||
|
float pivot = 0.5f - ispr.metaInfo.y;
|
||
|
pivot = pivot + colliderPivot;
|
||
|
AttachCornerToCollider(isi, pivot, ref colliderPoints, ref colliderPointCount);
|
||
|
|
||
|
float2 cp = zero;
|
||
|
float2 v0 = zero;
|
||
|
float2 v2 = zero;
|
||
|
|
||
|
for (int k = 0; k < count; k = k + 4)
|
||
|
{
|
||
|
v0 = vertices[k].pos;
|
||
|
v2 = vertices[k + 2].pos;
|
||
|
cp = (v2 - v0) * pivot;
|
||
|
colliderPoints[colliderPointCount++] = (v2 + cp + v0 + cp) * 0.5f;
|
||
|
}
|
||
|
|
||
|
float2 v1 = vertices[count - 1].pos;
|
||
|
float2 v3 = vertices[count - 3].pos;
|
||
|
cp = (v1 - v3) * pivot;
|
||
|
colliderPoints[colliderPointCount++] = (v1 + cp + v3 + cp) * 0.5f;
|
||
|
return cp;
|
||
|
}
|
||
|
|
||
|
void TrimOverlaps()
|
||
|
{
|
||
|
int kMinimumPointTolerance = 4;
|
||
|
if (m_ColliderPointCount < kMinimumPointTolerance)
|
||
|
return;
|
||
|
int trimmedPointCount = 0;
|
||
|
int i = 0;
|
||
|
int kColliderPointCountClamped = m_ColliderPointCount / 2;
|
||
|
int kSplineDetailClamped = math.clamp(splineDetail * 3, 0, 8);
|
||
|
int kNeighbors = kSplineDetailClamped > kColliderPointCountClamped ? kColliderPointCountClamped : kSplineDetailClamped;
|
||
|
// Debug.Log(kSplineDetailClamped + " : " + kNeighbors + " = " + m_ColliderPointCount);
|
||
|
|
||
|
if (!isCarpet)
|
||
|
m_TempPoints[trimmedPointCount++] = m_ColliderPoints[0];
|
||
|
|
||
|
while (i < m_ColliderPointCount)
|
||
|
{
|
||
|
int h = (i > 0) ? (i - 1) : (m_ColliderPointCount - 1);
|
||
|
bool noIntersection = true;
|
||
|
float2 v0 = m_ColliderPoints[h];
|
||
|
float2 v1 = m_ColliderPoints[i];
|
||
|
|
||
|
for (int n = kNeighbors; n > 1; --n)
|
||
|
{
|
||
|
int j = (i + n - 1) % m_ColliderPointCount;
|
||
|
int k = (i + n) % m_ColliderPointCount;
|
||
|
if (k == 0 || i == 0)
|
||
|
continue;
|
||
|
|
||
|
float2 v2 = m_ColliderPoints[j];
|
||
|
float2 v3 = m_ColliderPoints[k];
|
||
|
float2 vx = v0 - v3;
|
||
|
|
||
|
if (math.abs(math.length(vx)) < kEpsilon)
|
||
|
break;
|
||
|
|
||
|
float2 vi = v0;
|
||
|
|
||
|
bool overLaps = LineIntersection(kEpsilonRelaxed, v0, v1, v2, v3, ref vi);
|
||
|
if (overLaps && IsPointOnLines(kEpsilonRelaxed, v0, v1, v2, v3, vi))
|
||
|
{
|
||
|
// Debug.Log(v0 + " = " + v1 + " : " + v2 + " = " + v3 + " & " + h + " = " + i + " : " + j + " = " + k + " => " + vi + " : " + n);
|
||
|
noIntersection = false;
|
||
|
m_TempPoints[trimmedPointCount++] = vi;
|
||
|
i = i + n;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (noIntersection)
|
||
|
{
|
||
|
m_TempPoints[trimmedPointCount++] = v1;
|
||
|
i = i + 1;
|
||
|
}
|
||
|
}
|
||
|
for (; i < m_ColliderPointCount; ++i)
|
||
|
m_TempPoints[trimmedPointCount++] = m_ColliderPoints[i];
|
||
|
|
||
|
for (int j = 0; j < trimmedPointCount; ++j)
|
||
|
m_ColliderPoints[j] = m_TempPoints[j];
|
||
|
|
||
|
// Check intersection of first line Segment and last.
|
||
|
float2 vin = m_ColliderPoints[0];
|
||
|
LineIntersection(kEpsilonRelaxed, m_ColliderPoints[0], m_ColliderPoints[1], m_ColliderPoints[trimmedPointCount - 1], m_ColliderPoints[trimmedPointCount - 2], ref vin);
|
||
|
m_ColliderPoints[0] = vin;
|
||
|
|
||
|
m_ColliderPointCount = trimmedPointCount;
|
||
|
}
|
||
|
|
||
|
void OptimizeCollider()
|
||
|
{
|
||
|
if (hasCollider)
|
||
|
{
|
||
|
if (kColliderQuality > 0)
|
||
|
{
|
||
|
if (kOptimizeCollider > 0)
|
||
|
{
|
||
|
OptimizePoints(kColliderQuality, ref m_ColliderPoints, ref m_ColliderPointCount);
|
||
|
TrimOverlaps();
|
||
|
}
|
||
|
m_ColliderPoints[m_ColliderPointCount++] = new float2(0, 0);
|
||
|
m_ColliderPoints[m_ColliderPointCount++] = new float2(0, 0);
|
||
|
}
|
||
|
if (m_ColliderPointCount <= 2)
|
||
|
{
|
||
|
for (int i = 0; i < m_TessPointCount; ++i)
|
||
|
m_ColliderPoints[i] = m_TessPoints[i];
|
||
|
m_ColliderPoints[m_TessPointCount] = new float2(0, 0);
|
||
|
m_ColliderPoints[m_TessPointCount + 1] = new float2(0, 0);
|
||
|
m_ColliderPointCount = m_TessPointCount + 2;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Entry, Exit Points.
|
||
|
|
||
|
public void Prepare(UnityEngine.U2D.SpriteShapeController controller, SpriteShapeParameters shapeParams, int maxArrayCount, NativeArray<ShapeControlPoint> shapePoints, NativeArray<SpriteShapeMetaData> metaData, AngleRangeInfo[] angleRanges, Sprite[] segmentSprites, Sprite[] cornerSprites)
|
||
|
{
|
||
|
// Prepare Inputs.
|
||
|
PrepareInput(shapeParams, maxArrayCount, shapePoints, controller.optimizeGeometry, controller.autoUpdateCollider, controller.optimizeCollider, controller.colliderOffset, controller.colliderDetail);
|
||
|
PrepareSprites(segmentSprites, cornerSprites);
|
||
|
PrepareAngleRanges(angleRanges);
|
||
|
PrepareControlPoints(shapePoints, metaData);
|
||
|
|
||
|
// Generate Intermediates.
|
||
|
GenerateContour();
|
||
|
TessellateContour();
|
||
|
}
|
||
|
|
||
|
public void Execute()
|
||
|
{
|
||
|
// BURST
|
||
|
GenerateSegments();
|
||
|
TessellateSegments();
|
||
|
TessellateCorners();
|
||
|
CalculateBoundingBox();
|
||
|
CalculateTexCoords();
|
||
|
OptimizeCollider();
|
||
|
}
|
||
|
|
||
|
// Only needed if Burst is disabled.
|
||
|
// [BurstDiscard]
|
||
|
public void Cleanup()
|
||
|
{
|
||
|
SafeDispose(m_Corners);
|
||
|
SafeDispose(m_CornerSpriteInfos);
|
||
|
SafeDispose(m_SpriteInfos);
|
||
|
SafeDispose(m_AngleRanges);
|
||
|
SafeDispose(m_Segments);
|
||
|
SafeDispose(m_ControlPoints);
|
||
|
SafeDispose(m_ContourPoints);
|
||
|
SafeDispose(m_TempPoints);
|
||
|
SafeDispose(m_GeneratedControlPoints);
|
||
|
SafeDispose(m_SpriteIndices);
|
||
|
|
||
|
SafeDispose(m_TessPoints);
|
||
|
SafeDispose(m_VertexData);
|
||
|
SafeDispose(m_OutputVertexData);
|
||
|
SafeDispose(m_CornerCoordinates);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
}
|
||
|
};
|