Нема описа
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ShadowUtility.cs 44KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using System.Runtime.InteropServices;
  4. using Unity.Collections;
  5. using System.Collections.Generic;
  6. using UnityEngine.U2D;
  7. using UnityEngine.Rendering.Universal.UTess;
  8. using Unity.Collections.LowLevel.Unsafe;
  9. using Unity.Mathematics;
  10. using Unity.Burst;
  11. namespace UnityEngine.Rendering.Universal
  12. {
  13. [BurstCompile]
  14. internal class ShadowUtility
  15. {
  16. internal const int k_AdditionalVerticesPerEdge = 4;
  17. internal const int k_VerticesPerTriangle = 3;
  18. internal const int k_TrianglesPerEdge = 3;
  19. internal const int k_MinimumEdges = 3;
  20. internal const int k_SafeSize = 40;
  21. public enum ProjectionType
  22. {
  23. ProjectionNone = -1,
  24. ProjectionHard = 0,
  25. ProjectionSoftLeft = 1,
  26. ProjectionSoftRight = 3,
  27. }
  28. [StructLayout(LayoutKind.Sequential)]
  29. internal struct ShadowMeshVertex
  30. {
  31. internal Vector3 position; // stores: xy: position z: projection type w: soft shadow value (0 is fully shadowed)
  32. internal Vector4 tangent; // stores: xy: contraction dir zw: other edge position
  33. internal ShadowMeshVertex(ProjectionType inProjectionType, Vector2 inEdgePosition0, Vector2 inEdgePosition1)
  34. {
  35. position.x = inEdgePosition0.x;
  36. position.y = inEdgePosition0.y;
  37. position.z = 0;
  38. tangent.x = (int)inProjectionType;
  39. tangent.y = 0;
  40. tangent.z = inEdgePosition1.x;
  41. tangent.w = inEdgePosition1.y;
  42. }
  43. }
  44. [StructLayout(LayoutKind.Sequential)]
  45. internal struct RemappingInfo
  46. {
  47. public int count;
  48. public int index;
  49. public int v0Offset;
  50. public int v1Offset;
  51. public void Initialize()
  52. {
  53. count = 0;
  54. index = -1;
  55. v0Offset = 0;
  56. v1Offset = 0;
  57. }
  58. }
  59. static VertexAttributeDescriptor[] m_VertexLayout = new VertexAttributeDescriptor[]
  60. {
  61. new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
  62. new VertexAttributeDescriptor(VertexAttribute.Tangent, VertexAttributeFormat.Float32, 4),
  63. };
  64. unsafe static int GetNextShapeStart(int currentShape, int* inShapeStartingEdgePtr, int inShapeStartingEdgeLength, int maxValue)
  65. {
  66. // Make sure we are in the bounds of the shapes we have. Also make sure our starting edge isn't negative
  67. return ((currentShape + 1 < inShapeStartingEdgeLength) && (inShapeStartingEdgePtr[currentShape + 1] >= 0)) ? inShapeStartingEdgePtr[currentShape + 1] : maxValue;
  68. }
  69. [BurstCompile]
  70. static internal void CalculateProjectionInfo(ref NativeArray<Vector3> inVertices, ref NativeArray<ShadowEdge> inEdges, ref NativeArray<int> inShapeStartingEdge, ref NativeArray<bool> inShapeIsClosedArray, ref NativeArray<Vector2> outProjectionInfo)
  71. {
  72. unsafe
  73. {
  74. Vector3* inVerticesPtr = (Vector3*)inVertices.m_Buffer;
  75. ShadowEdge* inEdgesPtr = (ShadowEdge*)inEdges.m_Buffer;
  76. int* inShapeStartingEdgePtr = (int *)inShapeStartingEdge.m_Buffer;
  77. bool* inShapeIsClosedArrayPtr = (bool*)inShapeIsClosedArray.m_Buffer;
  78. Vector2* outProjectionInfoPtr = (Vector2*)outProjectionInfo.m_Buffer;
  79. Vector2 tmpVec2 = new Vector2(); // So we don't call the constructor
  80. int inEdgesLength = inEdges.Length;
  81. int inShapeStartingEdgeLength = inShapeStartingEdge.Length;
  82. int inVerticesLength = inVertices.Length;
  83. int currentShape = 0;
  84. int shapeStart = 0;
  85. int nextShapeStart = GetNextShapeStart(currentShape, inShapeStartingEdgePtr, inShapeStartingEdgeLength, inEdgesLength);
  86. int shapeSize = nextShapeStart;
  87. for (int i = 0; i < inEdgesLength; i++)
  88. {
  89. if (i == nextShapeStart)
  90. {
  91. currentShape++;
  92. shapeStart = nextShapeStart;
  93. nextShapeStart = GetNextShapeStart(currentShape, inShapeStartingEdgePtr, inShapeStartingEdgeLength, inEdgesLength);
  94. shapeSize = nextShapeStart - shapeStart;
  95. }
  96. int nextEdgeIndex = (i - shapeStart + 1) % shapeSize + shapeStart;
  97. int prevEdgeIndex = (i - shapeStart + shapeSize - 1) % shapeSize + shapeStart;
  98. int v0 = inEdgesPtr[i].v0;
  99. int v1 = inEdgesPtr[i].v1;
  100. int prev1 = inEdgesPtr[prevEdgeIndex].v0;
  101. int next0 = inEdgesPtr[nextEdgeIndex].v1;
  102. tmpVec2.x = inVerticesPtr[v0].x;
  103. tmpVec2.y = inVerticesPtr[v0].y;
  104. Vector2 startPt = tmpVec2;
  105. tmpVec2.x = inVerticesPtr[v1].x;
  106. tmpVec2.y = inVerticesPtr[v1].y;
  107. Vector2 endPt = tmpVec2;
  108. tmpVec2.x = inVerticesPtr[prev1].x;
  109. tmpVec2.y = inVerticesPtr[prev1].y;
  110. Vector2 prevPt = tmpVec2;
  111. tmpVec2.x = inVerticesPtr[next0].x;
  112. tmpVec2.y = inVerticesPtr[next0].y;
  113. Vector2 nextPt = tmpVec2;
  114. // Original Vertex
  115. outProjectionInfoPtr[v0] = endPt;
  116. // Hard Shadows
  117. int additionalVerticesStart = k_AdditionalVerticesPerEdge * i + inVerticesLength;
  118. outProjectionInfoPtr[additionalVerticesStart] = endPt;
  119. outProjectionInfoPtr[additionalVerticesStart + 1] = startPt;
  120. // Soft Triangles
  121. outProjectionInfoPtr[additionalVerticesStart + 2] = endPt;
  122. outProjectionInfoPtr[additionalVerticesStart + 3] = endPt;
  123. }
  124. }
  125. }
  126. [BurstCompile]
  127. static internal void CalculateVertices(ref NativeArray<Vector3> inVertices, ref NativeArray<ShadowEdge> inEdges, ref NativeArray<Vector2> inEdgeOtherPoints, ref NativeArray<ShadowMeshVertex> outMeshVertices)
  128. {
  129. unsafe
  130. {
  131. Vector3* inVerticesPtr = (Vector3*)inVertices.m_Buffer;
  132. ShadowEdge* inEdgesPtr = (ShadowEdge*)inEdges.m_Buffer;
  133. Vector2* inEdgeOtherPointsPtr = (Vector2*)inEdgeOtherPoints.m_Buffer;
  134. ShadowMeshVertex* outMeshVerticesPtr = (ShadowMeshVertex*)outMeshVertices.m_Buffer;
  135. Vector2 tmpVec2 = new Vector2(); // So we don't call the constructor
  136. int inEdgesLength = inEdges.Length;
  137. int inVerticesLength = inVertices.Length;
  138. for (int i = 0; i < inVerticesLength; i++)
  139. {
  140. tmpVec2.x = inVerticesPtr[i].x;
  141. tmpVec2.y = inVerticesPtr[i].y;
  142. ShadowMeshVertex originalShadowMesh = new ShadowMeshVertex(ProjectionType.ProjectionNone, tmpVec2, inEdgeOtherPointsPtr[i]);
  143. outMeshVerticesPtr[i] = originalShadowMesh;
  144. }
  145. for (int i = 0; i < inEdgesLength; i++)
  146. {
  147. int v0 = inEdgesPtr[i].v0;
  148. int v1 = inEdgesPtr[i].v1;
  149. tmpVec2.x = inVerticesPtr[v0].x;
  150. tmpVec2.y = inVerticesPtr[v0].y;
  151. Vector2 pt0 = tmpVec2;
  152. tmpVec2.x = inVerticesPtr[v1].x;
  153. tmpVec2.y = inVerticesPtr[v1].y;
  154. Vector2 pt1 = tmpVec2;
  155. int additionalVerticesStart = k_AdditionalVerticesPerEdge * i + inVerticesLength;
  156. ShadowMeshVertex additionalVertex0 = new ShadowMeshVertex(ProjectionType.ProjectionHard, pt0, inEdgeOtherPointsPtr[additionalVerticesStart]);
  157. ShadowMeshVertex additionalVertex1 = new ShadowMeshVertex(ProjectionType.ProjectionHard, pt1, inEdgeOtherPointsPtr[additionalVerticesStart + 1]);
  158. ShadowMeshVertex additionalVertex2 = new ShadowMeshVertex(ProjectionType.ProjectionSoftLeft, pt0, inEdgeOtherPointsPtr[additionalVerticesStart + 2]);
  159. ShadowMeshVertex additionalVertex3 = new ShadowMeshVertex(ProjectionType.ProjectionSoftRight, pt0, inEdgeOtherPointsPtr[additionalVerticesStart + 3]);
  160. outMeshVerticesPtr[additionalVerticesStart] = additionalVertex0;
  161. outMeshVerticesPtr[additionalVerticesStart + 1] = additionalVertex1;
  162. outMeshVerticesPtr[additionalVerticesStart + 2] = additionalVertex2;
  163. outMeshVerticesPtr[additionalVerticesStart + 3] = additionalVertex3;
  164. }
  165. }
  166. }
  167. [BurstCompile]
  168. static internal void CalculateTriangles(ref NativeArray<Vector3> inVertices, ref NativeArray<ShadowEdge> inEdges, ref NativeArray<int> inShapeStartingEdge, ref NativeArray<bool> inShapeIsClosedArray, ref NativeArray<int> outMeshIndices)
  169. {
  170. unsafe
  171. {
  172. ShadowEdge* inEdgesPtr = (ShadowEdge*)inEdges.m_Buffer;
  173. int* inShapeStartingEdgePtr = (int*)inShapeStartingEdge.m_Buffer;
  174. int* outMeshIndicesPtr = (int*)outMeshIndices.m_Buffer;
  175. int inEdgesLength = inEdges.Length;
  176. int inShapeStartingEdgeLength = inShapeStartingEdge.Length;
  177. int inVerticesLength = inVertices.Length;
  178. int meshIndex = 0;
  179. for (int shapeIndex = 0; shapeIndex < inShapeStartingEdgeLength; shapeIndex++)
  180. {
  181. int startingIndex = inShapeStartingEdgePtr[shapeIndex];
  182. if (startingIndex < 0)
  183. return;
  184. int endIndex = inEdgesLength;
  185. if ((shapeIndex + 1) < inShapeStartingEdgeLength && inShapeStartingEdgePtr[shapeIndex + 1] > -1)
  186. endIndex = inShapeStartingEdgePtr[shapeIndex + 1];
  187. //// Hard Shadow Geometry
  188. int prevEdge = endIndex - 1;
  189. for (int i = startingIndex; i < endIndex; i++)
  190. {
  191. int v0 = inEdgesPtr[i].v0;
  192. int v1 = inEdgesPtr[i].v1;
  193. int additionalVerticesStart = k_AdditionalVerticesPerEdge * i + inVerticesLength;
  194. // Add a degenerate rectangle
  195. outMeshIndicesPtr[meshIndex++] = (ushort)v0;
  196. outMeshIndicesPtr[meshIndex++] = (ushort)additionalVerticesStart;
  197. outMeshIndicesPtr[meshIndex++] = (ushort)(additionalVerticesStart + 1);
  198. outMeshIndicesPtr[meshIndex++] = (ushort)(additionalVerticesStart + 1);
  199. outMeshIndicesPtr[meshIndex++] = (ushort)v1;
  200. outMeshIndicesPtr[meshIndex++] = (ushort)v0;
  201. prevEdge = i;
  202. }
  203. // Soft Shadow Geometry
  204. for (int i = startingIndex; i < endIndex; i++)
  205. //int i = 0;
  206. {
  207. int v0 = inEdgesPtr[i].v0;
  208. int v1 = inEdgesPtr[i].v1;
  209. int additionalVerticesStart = k_AdditionalVerticesPerEdge * i + inVerticesLength;
  210. // We also need 1 more triangles for soft shadows (3 indices)
  211. outMeshIndicesPtr[meshIndex++] = (ushort)v0;
  212. outMeshIndicesPtr[meshIndex++] = (ushort)additionalVerticesStart + 2;
  213. outMeshIndicesPtr[meshIndex++] = (ushort)additionalVerticesStart + 3;
  214. }
  215. }
  216. }
  217. }
  218. [BurstCompile]
  219. static internal void CalculateLocalBounds(ref NativeArray<Vector3> inVertices, out Bounds retBounds)
  220. {
  221. if (inVertices.Length <= 0)
  222. {
  223. retBounds = new Bounds(Vector3.zero, Vector3.zero);
  224. }
  225. else
  226. {
  227. Vector2 minVec = Vector2.positiveInfinity;
  228. Vector2 maxVec = Vector2.negativeInfinity;
  229. unsafe
  230. {
  231. Vector3* inVerticesPtr = (Vector3*)inVertices.m_Buffer;
  232. int inVerticesLength = inVertices.Length;
  233. // Add outline vertices
  234. for (int i = 0; i < inVerticesLength; i++)
  235. {
  236. Vector2 vertex = new Vector2(inVerticesPtr[i].x, inVerticesPtr[i].y);
  237. minVec = Vector2.Min(minVec, vertex);
  238. maxVec = Vector2.Max(maxVec, vertex);
  239. }
  240. }
  241. retBounds = new Bounds { max = maxVec, min = minVec };
  242. }
  243. }
  244. [BurstCompile]
  245. 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)
  246. {
  247. int inEdgeCount = inEdges.Length;
  248. // Do tessellation
  249. NativeArray<int2> tessInEdges = new NativeArray<int2>(inEdgeCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  250. NativeArray<float2> tessInVertices = new NativeArray<float2>(inEdgeCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  251. for (int i = 0; i < inEdgeCount; i++)
  252. {
  253. int2 edge = new int2(inEdges[i].v0, inEdges[i].v1);
  254. tessInEdges[i] = edge;
  255. int index = edge.x;
  256. tessInVertices[index] = new float2(inVertices[index].position.x, inVertices[index].position.y);
  257. }
  258. NativeArray<int> tessOutIndices = new NativeArray<int>(tessInVertices.Length * 8, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  259. NativeArray<float2> tessOutVertices = new NativeArray<float2>(tessInVertices.Length * 4, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  260. NativeArray<int2> tessOutEdges = new NativeArray<int2>(tessInEdges.Length * 4, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  261. int tessOutVertexCount = 0;
  262. int tessOutIndexCount = 0;
  263. int tessOutEdgeCount = 0;
  264. UTess.ModuleHandle.Tessellate(Allocator.Persistent, tessInVertices, tessInEdges, ref tessOutVertices, ref tessOutVertexCount, ref tessOutIndices, ref tessOutIndexCount, ref tessOutEdges, ref tessOutEdgeCount);
  265. int indexOffset = inIndices.Length;
  266. int vertexOffset = inVertices.Length;
  267. int totalOutVertices = tessOutVertexCount + inVertices.Length;
  268. int totalOutIndices = tessOutIndexCount + inIndices.Length;
  269. outVertices = new NativeArray<ShadowMeshVertex>(totalOutVertices, Allocator.Persistent, NativeArrayOptions.ClearMemory);
  270. outIndices = new NativeArray<int>(totalOutIndices, Allocator.Persistent, NativeArrayOptions.ClearMemory);
  271. // Copy vertices
  272. for (int i = 0; i < inVertices.Length; i++)
  273. outVertices[i] = inVertices[i];
  274. for (int i = 0; i < tessOutVertexCount; i++)
  275. {
  276. float2 tessVertex = tessOutVertices[i];
  277. ShadowMeshVertex vertex = new ShadowMeshVertex(ProjectionType.ProjectionNone, tessVertex, Vector2.zero);
  278. outVertices[i + vertexOffset] = vertex;
  279. }
  280. // Copy indices
  281. for (int i = 0; i < inIndices.Length; i++)
  282. outIndices[i] = inIndices[i];
  283. // Copy and remap indices
  284. for (int i = 0; i < tessOutIndexCount; i++)
  285. {
  286. outIndices[i + indexOffset] = tessOutIndices[i] + vertexOffset;
  287. }
  288. outStartIndex = indexOffset;
  289. outIndexCount = tessOutIndexCount;
  290. tessInEdges.Dispose();
  291. tessInVertices.Dispose();
  292. tessOutIndices.Dispose();
  293. tessOutVertices.Dispose();
  294. tessOutEdges.Dispose();
  295. }
  296. //inEdges is expected to be contiguous
  297. 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)
  298. {
  299. // Setup our buffers
  300. int meshVertexCount = inVertices.Length + k_AdditionalVerticesPerEdge * inEdges.Length; // Each vertex will have a duplicate that can be extruded.
  301. int meshIndexCount = inEdges.Length * k_VerticesPerTriangle * k_TrianglesPerEdge; // There are two triangles per edge making a degenerate rectangle (0 area)
  302. NativeArray<Vector2> meshProjectionInfo = new NativeArray<Vector2>(meshVertexCount, Allocator.Persistent);
  303. NativeArray<int> meshIndices = new NativeArray<int>(meshIndexCount, Allocator.Persistent);
  304. NativeArray<ShadowMeshVertex> meshVertices = new NativeArray<ShadowMeshVertex>(meshVertexCount, Allocator.Persistent);
  305. CalculateProjectionInfo(ref inVertices, ref inEdges, ref inShapeStartingEdge, ref inShapeIsClosedArray, ref meshProjectionInfo);
  306. CalculateVertices(ref inVertices, ref inEdges, ref meshProjectionInfo, ref meshVertices);
  307. CalculateTriangles(ref inVertices, ref inEdges, ref inShapeStartingEdge, ref inShapeIsClosedArray, ref meshIndices);
  308. NativeArray<ShadowMeshVertex> finalVertices;
  309. NativeArray<int> finalIndices;
  310. int fillSubmeshStartIndex = 0;
  311. int fillSubmeshIndexCount = 0;
  312. if (fill) // This has limited utility at the moment as contraction is not calculated. More work will need to be done to generalize this
  313. {
  314. GenerateInteriorMesh(ref meshVertices, ref meshIndices, ref inEdges, out finalVertices, out finalIndices, out fillSubmeshStartIndex, out fillSubmeshIndexCount);
  315. meshVertices.Dispose();
  316. meshIndices.Dispose();
  317. }
  318. else
  319. {
  320. finalVertices = meshVertices;
  321. finalIndices = meshIndices;
  322. }
  323. // Set the mesh data
  324. mesh.SetVertexBufferParams(finalVertices.Length, m_VertexLayout);
  325. mesh.SetVertexBufferData<ShadowMeshVertex>(finalVertices, 0, 0, finalVertices.Length);
  326. mesh.SetIndexBufferParams(finalIndices.Length, IndexFormat.UInt32);
  327. mesh.SetIndexBufferData<int>(finalIndices, 0, 0, finalIndices.Length);
  328. mesh.SetSubMesh(0, new SubMeshDescriptor(0, finalIndices.Length));
  329. mesh.subMeshCount = 1;
  330. meshProjectionInfo.Dispose();
  331. finalVertices.Dispose();
  332. finalIndices.Dispose();
  333. CalculateLocalBounds(ref inVertices, out Bounds retLocalBound);
  334. return retLocalBound;
  335. }
  336. [BurstCompile]
  337. static public void CalculateEdgesFromLines(ref NativeArray<int> indices, out NativeArray<ShadowEdge> outEdges, out NativeArray<int> outShapeStartingEdge, out NativeArray<bool> outShapeIsClosedArray)
  338. {
  339. unsafe
  340. {
  341. int numOfEdges = indices.Length >> 1;
  342. NativeArray<int> tempShapeStartIndices = new NativeArray<int>(numOfEdges, Allocator.Persistent);
  343. NativeArray<bool> tempShapeIsClosedArray = new NativeArray<bool>(numOfEdges, Allocator.Persistent);
  344. int* indicesPtr = (int*)indices.m_Buffer;
  345. int* tempShapeStartIndicesPtr = (int*)tempShapeStartIndices.m_Buffer;
  346. bool* tempShapeIsClosedArrayPtr = (bool*)tempShapeIsClosedArray.m_Buffer;
  347. int indicesLength = indices.Length;
  348. // Find the shape starting indices and allow contraction
  349. int shapeCount = 0;
  350. int shapeStart = indicesPtr[0];
  351. int lastIndex = indicesPtr[0];
  352. bool closedShapeFound = false;
  353. tempShapeStartIndicesPtr[0] = 0;
  354. for (int i = 0; i < indicesLength; i += 2)
  355. {
  356. if (closedShapeFound)
  357. {
  358. shapeStart = indicesPtr[i];
  359. tempShapeIsClosedArrayPtr[shapeCount] = true;
  360. tempShapeStartIndicesPtr[++shapeCount] = i >> 1;
  361. closedShapeFound = false;
  362. }
  363. else if (indicesPtr[i] != lastIndex)
  364. {
  365. tempShapeIsClosedArrayPtr[shapeCount] = false;
  366. tempShapeStartIndicesPtr[++shapeCount] = i >> 1;
  367. shapeStart = indicesPtr[i];
  368. }
  369. if (shapeStart == indicesPtr[i + 1])
  370. closedShapeFound = true;
  371. lastIndex = indicesPtr[i + 1];
  372. }
  373. tempShapeIsClosedArrayPtr[shapeCount++] = closedShapeFound;
  374. // Copy the our data to a smaller array
  375. outShapeStartingEdge = new NativeArray<int>(shapeCount, Allocator.Persistent);
  376. outShapeIsClosedArray = new NativeArray<bool>(shapeCount, Allocator.Persistent);
  377. int* outShapeStartingEdgePtr = (int*)outShapeStartingEdge.m_Buffer;
  378. bool* outShapeIsClosedArrayPtr = (bool*)outShapeIsClosedArray.m_Buffer;
  379. for (int i = 0; i < shapeCount; i++)
  380. {
  381. outShapeStartingEdgePtr[i] = tempShapeStartIndicesPtr[i];
  382. outShapeIsClosedArrayPtr[i] = tempShapeIsClosedArrayPtr[i];
  383. }
  384. tempShapeStartIndices.Dispose();
  385. tempShapeIsClosedArray.Dispose();
  386. // Add edges
  387. outEdges = new NativeArray<ShadowEdge>(numOfEdges, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  388. ShadowEdge* outEdgesPtr = (ShadowEdge*)outEdges.m_Buffer;
  389. for (int i = 0; i < numOfEdges; i++)
  390. {
  391. int indicesIndex = i << 1;
  392. int v0Index = indicesPtr[indicesIndex];
  393. int v1Index = indicesPtr[indicesIndex + 1];
  394. outEdgesPtr[i] = new ShadowEdge(v0Index, v1Index);
  395. }
  396. }
  397. }
  398. [BurstCompile]
  399. static internal void GetVertexReferenceStats(ref NativeArray<Vector3> vertices, ref NativeArray<ShadowEdge> edges, int vertexCount, out bool hasReusedVertices, out int newVertexCount, out NativeArray<RemappingInfo> remappingInfo)
  400. {
  401. unsafe
  402. {
  403. int edgeCount = edges.Length;
  404. newVertexCount = 0;
  405. hasReusedVertices = false;
  406. remappingInfo = new NativeArray<RemappingInfo>(vertexCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  407. RemappingInfo* remappingInfoPtr = (RemappingInfo*)remappingInfo.GetUnsafePtr();
  408. ShadowEdge* edgesPtr = (ShadowEdge*)edges.GetUnsafePtr();
  409. // Clear the remapping info
  410. for (int i = 0; i < vertexCount; i++)
  411. remappingInfoPtr[i].Initialize();
  412. // Process v0
  413. for (int i = 0; i < edgeCount; i++)
  414. {
  415. int v0 = edgesPtr[i].v0;
  416. remappingInfoPtr[v0].count = remappingInfoPtr[v0].count + 1;
  417. if (remappingInfoPtr[v0].count > 1)
  418. hasReusedVertices = true;
  419. newVertexCount++;
  420. }
  421. // Process v1
  422. for (int i = 0; i < edgeCount; i++)
  423. {
  424. int v1 = edgesPtr[i].v1;
  425. if (remappingInfoPtr[v1].count == 0) // This is an open shape
  426. {
  427. remappingInfoPtr[v1].count = 1;
  428. newVertexCount++;
  429. }
  430. }
  431. // Find the starts of the new indices..
  432. int startPos = 0;
  433. for (int i=0;i<vertexCount;i++)
  434. {
  435. // Leave the other indices -1 for easier validation testing
  436. if (remappingInfoPtr[i].count > 0)
  437. {
  438. remappingInfoPtr[i].index = startPos;
  439. startPos += remappingInfoPtr[i].count;
  440. }
  441. }
  442. }
  443. }
  444. static public bool IsTriangleReversed(NativeArray<Vector3> vertices, int idx0, int idx1, int idx2)
  445. {
  446. Vector3 v0 = vertices[idx0];
  447. Vector3 v1 = vertices[idx1];
  448. Vector3 v2 = vertices[idx2];
  449. 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);
  450. return Mathf.Sign(twiceArea) >= 0;
  451. }
  452. [BurstCompile]
  453. 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)
  454. {
  455. unsafe
  456. {
  457. // Run clipper to calculate edges..
  458. Clipper2D.Solution solution = new Clipper2D.Solution();
  459. Clipper2D.ExecuteArguments executeArguments = new Clipper2D.ExecuteArguments(Clipper2D.InitOptions.ioDefault, Clipper2D.ClipType.ctUnion);
  460. int triangleCount = indices.Length / 3;
  461. NativeArray<Vector2> points = new NativeArray<Vector2>(indices.Length, Allocator.Persistent);
  462. NativeArray<int> pathSizes = new NativeArray<int>(triangleCount, Allocator.Persistent);
  463. NativeArray<Clipper2D.PathArguments> pathArguments = new NativeArray<Clipper2D.PathArguments>(triangleCount, Allocator.Persistent);
  464. // Pointers to our native arrays for performance in editor
  465. Vector2* pointsPtr = (Vector2*)points.GetUnsafePtr<Vector2>();
  466. int* pathSizesPtr = (int*)pathSizes.GetUnsafePtr<int>();
  467. Clipper2D.PathArguments* pathArgumentsPtr = (Clipper2D.PathArguments*)pathArguments.GetUnsafePtr<Clipper2D.PathArguments>();
  468. Vector3* verticesPtr = (Vector3*)vertices.GetUnsafePtr<Vector3>();
  469. // Copy input data for Clipper2D.Execute
  470. Clipper2D.PathArguments sharedPathArg = new Clipper2D.PathArguments(Clipper2D.PolyType.ptSubject, true);
  471. for (int i = 0; i < triangleCount; i++)
  472. {
  473. pathSizesPtr[i] = 3;
  474. pathArgumentsPtr[i] = sharedPathArg;
  475. int pointOffset = 3 * i;
  476. pointsPtr[pointOffset] = verticesPtr[indices[pointOffset]];
  477. pointsPtr[pointOffset + 1] = verticesPtr[indices[pointOffset + 1]];
  478. pointsPtr[pointOffset + 2] = verticesPtr[indices[pointOffset + 2]];
  479. }
  480. Clipper2D.Execute(ref solution, points, pathSizes, pathArguments, executeArguments, Allocator.Persistent);
  481. // Cleanup execute inputs because we have necessary data in our solution
  482. points.Dispose();
  483. pathSizes.Dispose();
  484. pathArguments.Dispose();
  485. // Copy solution to outputs
  486. int pointLen = solution.points.Length;
  487. int shapeCount = solution.pathSizes.Length;
  488. newVertices = new NativeArray<Vector3>(pointLen, Allocator.Persistent);
  489. outEdges = new NativeArray<ShadowEdge>(pointLen, Allocator.Persistent);
  490. outShapeStartingEdge = new NativeArray<int>(shapeCount, Allocator.Persistent);
  491. outShapeIsClosedArray = new NativeArray<bool>(shapeCount, Allocator.Persistent);
  492. // More pointers for edtor time perfomance
  493. int* solutionPathSizesPtr = (int*)solution.pathSizes.GetUnsafePtr<int>();
  494. Vector2* solutionPointsPtr = (Vector2*)solution.points.GetUnsafePtr<Vector2>();
  495. Vector3* newVerticesPtr = (Vector3*)newVertices.GetUnsafePtr<Vector3>();
  496. ShadowEdge* outEdgesPtr = (ShadowEdge*)outEdges.GetUnsafePtr<ShadowEdge>();
  497. int* outShapeStartingEdgePtr = (int*)outShapeStartingEdge.GetUnsafePtr<int>();
  498. bool* outShapeIsClosedArrayPtr = (bool*)outShapeIsClosedArray.GetUnsafePtr<bool>();
  499. // Copy output data from the solution
  500. int nextStart = 0;
  501. for (int shapeIndex = 0; shapeIndex < shapeCount; shapeIndex++)
  502. {
  503. // Copy shape info to outputs
  504. int curStart = nextStart;
  505. int curPathSize = solutionPathSizesPtr[shapeIndex];
  506. outShapeStartingEdgePtr[shapeIndex] = nextStart;
  507. nextStart += curPathSize;
  508. // Copy vertices and edges to outputs;
  509. int previousVertex = nextStart - 1;
  510. for (int pointIndex = curStart; pointIndex < nextStart; pointIndex++)
  511. {
  512. newVerticesPtr[pointIndex] = solutionPointsPtr[pointIndex];
  513. outEdgesPtr[pointIndex] = new ShadowEdge(previousVertex, pointIndex);
  514. previousVertex = pointIndex;
  515. }
  516. // All shapes are closed since they are created from triangles
  517. outShapeIsClosedArrayPtr[shapeIndex] = true;
  518. }
  519. // Cleanup solution
  520. solution.Dispose();
  521. }
  522. }
  523. [BurstCompile]
  524. static public void ReverseWindingOrder(ref NativeArray<int> inShapeStartingEdge, ref NativeArray<ShadowEdge> inOutSortedEdges)
  525. {
  526. for (int shapeIndex = 0; shapeIndex < inShapeStartingEdge.Length; shapeIndex++)
  527. {
  528. int startingIndex = inShapeStartingEdge[shapeIndex];
  529. if (startingIndex < 0)
  530. return;
  531. int endIndex = inOutSortedEdges.Length;
  532. if ((shapeIndex + 1) < inShapeStartingEdge.Length && inShapeStartingEdge[shapeIndex + 1] > -1)
  533. endIndex = inShapeStartingEdge[shapeIndex + 1];
  534. // Reverse the winding order
  535. int count = (endIndex - startingIndex);
  536. for (int i = 0; i < (count >> 1); i++)
  537. {
  538. int edgeAIndex = startingIndex + i;
  539. int edgeBIndex = startingIndex + count - 1 - i;
  540. ShadowEdge edgeA = inOutSortedEdges[edgeAIndex];
  541. ShadowEdge edgeB = inOutSortedEdges[edgeBIndex];
  542. edgeA.Reverse();
  543. edgeB.Reverse();
  544. inOutSortedEdges[edgeAIndex] = edgeB;
  545. inOutSortedEdges[edgeBIndex] = edgeA;
  546. }
  547. bool isOdd = (count & 1) == 1;
  548. if (isOdd)
  549. {
  550. int edgeAIndex = startingIndex + (count >> 1);
  551. ShadowEdge edgeA = inOutSortedEdges[edgeAIndex];
  552. edgeA.Reverse();
  553. inOutSortedEdges[edgeAIndex] = edgeA;
  554. }
  555. }
  556. }
  557. static int GetClosedPathCount(ref NativeArray<int> inShapeStartingEdge, ref NativeArray<bool> inShapeIsClosedArray)
  558. {
  559. int count = 0;
  560. for(int i=0;i<inShapeStartingEdge.Length;i++)
  561. {
  562. if (inShapeStartingEdge[i] < 0)
  563. break;
  564. count++;
  565. }
  566. return count;
  567. }
  568. static void GetPathInfo(NativeArray<ShadowEdge> inEdges, NativeArray<int> inShapeStartingEdge, NativeArray<bool> inShapeIsClosedArray, out int closedPathArrayCount, out int closedPathsCount, out int openPathArrayCount, out int openPathsCount)
  569. {
  570. closedPathArrayCount = 0;
  571. openPathArrayCount = 0;
  572. closedPathsCount = 0;
  573. openPathsCount = 0;
  574. for (int i = 0; i < inShapeStartingEdge.Length; i++)
  575. {
  576. // If this shape starting edge is invalid stop..
  577. if (inShapeStartingEdge[i] < 0)
  578. break;
  579. int start = inShapeStartingEdge[i];
  580. int end = (i < (inShapeStartingEdge.Length - 1 )) && (inShapeStartingEdge[i + 1] != -1) ? inShapeStartingEdge[i + 1] : inEdges.Length;
  581. int edges = end - start;
  582. if (inShapeIsClosedArray[i])
  583. {
  584. closedPathArrayCount += edges + 1;
  585. closedPathsCount++;
  586. }
  587. else
  588. {
  589. openPathArrayCount += edges + 1;
  590. openPathsCount++;
  591. }
  592. }
  593. }
  594. [BurstCompile]
  595. 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)
  596. {
  597. unsafe
  598. {
  599. Allocator k_ClippingAllocator = Allocator.Persistent;
  600. int k_Precision = 65536;
  601. int closedPathCount;
  602. int closedPathArrayCount;
  603. int openPathCount;
  604. int openPathArrayCount;
  605. GetPathInfo(inEdges, inShapeStartingEdge, inShapeIsClosedArray, out closedPathArrayCount, out closedPathCount, out openPathArrayCount, out openPathCount);
  606. NativeArray<Clipper2D.PathArguments> clipperPathArguments = new NativeArray<Clipper2D.PathArguments>(closedPathCount, k_ClippingAllocator, NativeArrayOptions.ClearMemory);
  607. NativeArray<int> closedPathSizes = new NativeArray<int>(closedPathCount, k_ClippingAllocator);
  608. NativeArray<Vector2> closedPath = new NativeArray<Vector2>(closedPathArrayCount, k_ClippingAllocator);
  609. NativeArray<int> openPathSizes = new NativeArray<int>(openPathCount, k_ClippingAllocator);
  610. NativeArray<Vector2> openPath = new NativeArray<Vector2>(openPathArrayCount, k_ClippingAllocator);
  611. Clipper2D.PathArguments* clipperPathArgumentsPtr = (Clipper2D.PathArguments*)clipperPathArguments.m_Buffer;
  612. int* closedPathSizesPtr = (int*)closedPathSizes.m_Buffer;
  613. Vector2* closedPathPtr = (Vector2*)closedPath.m_Buffer;
  614. int* openPathSizesPtr = (int*)openPathSizes.m_Buffer;
  615. Vector2* openPathPtr = (Vector2*)openPath.m_Buffer;
  616. int* inShapeStartingEdgePtr = (int*)inShapeStartingEdge.m_Buffer;
  617. bool* inShapeIsClosedArrayPtr = (bool*)inShapeIsClosedArray.m_Buffer;
  618. Vector3* inVerticesPtr = (Vector3*)inVertices.m_Buffer;
  619. ShadowEdge* inEdgesPtr = (ShadowEdge*)inEdges.m_Buffer;
  620. int inEdgesLength = inEdges.Length;
  621. Vector2 tmpVec2 = new Vector2(); // So we don't call the constructor
  622. Vector3 tmpVec3 = Vector3.zero;
  623. // Seperate out our closed and open shapes. Closed shapes will go through clipper. Open shapes will just be copied.
  624. int closedPathArrayIndex = 0;
  625. int closedPathSizesIndex = 0;
  626. int openPathArrayIndex = 0;
  627. int openPathSizesIndex = 0;
  628. int totalPathCount = closedPathCount + openPathCount;
  629. for (int shapeStartIndex = 0; (shapeStartIndex < totalPathCount); shapeStartIndex++)
  630. {
  631. int currentShapeStart = inShapeStartingEdgePtr[shapeStartIndex];
  632. int nextShapeStart = (shapeStartIndex + 1) < (totalPathCount) ? inShapeStartingEdgePtr[shapeStartIndex + 1] : inEdgesLength;
  633. int numberOfEdges = nextShapeStart - currentShapeStart;
  634. // If we have a closed shape then add it to our path and path sizes.
  635. if (inShapeIsClosedArrayPtr[shapeStartIndex])
  636. {
  637. closedPathSizesPtr[closedPathSizesIndex] = numberOfEdges + 1;
  638. clipperPathArgumentsPtr[closedPathSizesIndex] = new Clipper2D.PathArguments(Clipper2D.PolyType.ptSubject, true);
  639. closedPathSizesIndex++;
  640. for (int i = 0; i < numberOfEdges; i++)
  641. {
  642. Vector3 vec3 = inVerticesPtr[inEdgesPtr[i + currentShapeStart].v0];
  643. tmpVec2.x = vec3.x;
  644. tmpVec2.y = vec3.y;
  645. closedPathPtr[closedPathArrayIndex++] = tmpVec2;
  646. }
  647. closedPathPtr[closedPathArrayIndex++] = inVerticesPtr[inEdgesPtr[numberOfEdges + currentShapeStart - 1].v1];
  648. }
  649. else
  650. {
  651. openPathSizesPtr[openPathSizesIndex++] = numberOfEdges + 1;
  652. for (int i = 0; i < numberOfEdges; i++)
  653. {
  654. Vector3 vec3 = inVerticesPtr[inEdgesPtr[i + currentShapeStart].v0];
  655. tmpVec2.x = vec3.x;
  656. tmpVec2.y = vec3.y;
  657. openPathPtr[openPathArrayIndex++] = tmpVec2;
  658. }
  659. openPathPtr[openPathArrayIndex++] = inVerticesPtr[inEdgesPtr[numberOfEdges + currentShapeStart - 1].v1];
  660. }
  661. }
  662. NativeArray<Vector2> clipperOffsetPath = closedPath;
  663. NativeArray<int> clipperOffsetPathSizes = closedPathSizes;
  664. Clipper2D.Solution clipperSolution = new Clipper2D.Solution();
  665. // Run this to try to merge outlines if there is more than one
  666. if (closedPathSizes.Length > 1)
  667. {
  668. Clipper2D.ExecuteArguments executeArguments = new Clipper2D.ExecuteArguments();
  669. executeArguments.clipType = Clipper2D.ClipType.ctUnion;
  670. executeArguments.clipFillType = Clipper2D.PolyFillType.pftEvenOdd;
  671. executeArguments.subjFillType = Clipper2D.PolyFillType.pftEvenOdd;
  672. executeArguments.strictlySimple = false;
  673. executeArguments.preserveColinear = false;
  674. Clipper2D.Execute(ref clipperSolution, closedPath, closedPathSizes, clipperPathArguments, executeArguments, k_ClippingAllocator, inIntScale: k_Precision, useRounding: true);
  675. clipperOffsetPath = clipperSolution.points;
  676. clipperOffsetPathSizes = clipperSolution.pathSizes;
  677. }
  678. ClipperOffset2D.Solution offsetSolution = new ClipperOffset2D.Solution();
  679. NativeArray<ClipperOffset2D.PathArguments> offsetPathArguments = new NativeArray<ClipperOffset2D.PathArguments>(clipperOffsetPathSizes.Length, k_ClippingAllocator, NativeArrayOptions.ClearMemory);
  680. ClipperOffset2D.Execute(ref offsetSolution, clipperOffsetPath, clipperOffsetPathSizes, offsetPathArguments, k_ClippingAllocator, -contractEdge, inIntScale: k_Precision);
  681. if (offsetSolution.pathSizes.Length > 0 || openPathCount > 0)
  682. {
  683. int vertexPos = 0;
  684. // Combine the solutions from clipper and our open paths
  685. int solutionPathLens = offsetSolution.pathSizes.Length + openPathCount;
  686. outVertices = new NativeArray<Vector3>(offsetSolution.points.Length + openPathArrayCount, k_ClippingAllocator);
  687. outEdges = new NativeArray<ShadowEdge>(offsetSolution.points.Length + openPathArrayCount, k_ClippingAllocator);
  688. outShapeStartingEdge = new NativeArray<int>(solutionPathLens, k_ClippingAllocator);
  689. Vector3* outVerticesPtr = (Vector3*)outVertices.m_Buffer;
  690. ShadowEdge* outEdgesPtr = (ShadowEdge*)outEdges.m_Buffer;
  691. int* outShapeStartingEdgePtr = (int*)outShapeStartingEdge.m_Buffer;
  692. Vector2* offsetSolutionPointsPtr = (Vector2*)offsetSolution.points.m_Buffer;
  693. int offsetSolutionPointsLength = offsetSolution.points.Length;
  694. int* offsetSolutionPathSizesPtr = (int*)offsetSolution.pathSizes.m_Buffer;
  695. int offsetSolutionPathSizesLength = offsetSolution.pathSizes.Length;
  696. // Copy out the solution first..
  697. for (int i = 0; i < offsetSolutionPointsLength; i++)
  698. {
  699. tmpVec3.x = offsetSolutionPointsPtr[i].x;
  700. tmpVec3.y = offsetSolutionPointsPtr[i].y;
  701. outVerticesPtr[vertexPos++] = tmpVec3;
  702. }
  703. int start = 0;
  704. for (int pathSizeIndex = 0; pathSizeIndex < offsetSolutionPathSizesLength; pathSizeIndex++)
  705. {
  706. int pathSize = offsetSolutionPathSizesPtr[pathSizeIndex];
  707. int end = start + pathSize;
  708. outShapeStartingEdgePtr[pathSizeIndex] = start;
  709. for (int shapeIndex = 0; shapeIndex < pathSize; shapeIndex++)
  710. {
  711. ShadowEdge edge = new ShadowEdge(shapeIndex + start, (shapeIndex + 1) % pathSize + start);
  712. outEdgesPtr[shapeIndex + start] = edge;
  713. }
  714. start = end;
  715. }
  716. // Copy out the open vertices
  717. int pathStartIndex = offsetSolutionPathSizesLength;
  718. start = vertexPos; // We need to remap our vertices;
  719. for (int i = 0; i < openPath.Length; i++)
  720. {
  721. tmpVec3.x = openPathPtr[i].x;
  722. tmpVec3.y = openPathPtr[i].y;
  723. outVerticesPtr[vertexPos++] = tmpVec3;
  724. }
  725. for (int openPathIndex = 0; openPathIndex < openPathCount; openPathIndex++)
  726. {
  727. int pathSize = openPathSizesPtr[openPathIndex];
  728. int end = start + pathSize;
  729. outShapeStartingEdgePtr[pathStartIndex + openPathIndex] = start;
  730. for (int shapeIndex = 0; shapeIndex < pathSize - 1; shapeIndex++)
  731. {
  732. ShadowEdge edge = new ShadowEdge(shapeIndex + start, shapeIndex + 1);
  733. outEdgesPtr[shapeIndex + start] = edge;
  734. }
  735. start = end;
  736. }
  737. }
  738. else
  739. {
  740. outVertices = new NativeArray<Vector3>(0, k_ClippingAllocator);
  741. outEdges = new NativeArray<ShadowEdge>(0, k_ClippingAllocator);
  742. outShapeStartingEdge = new NativeArray<int>(0, k_ClippingAllocator);
  743. }
  744. closedPathSizes.Dispose();
  745. closedPath.Dispose();
  746. openPathSizes.Dispose();
  747. openPath.Dispose();
  748. clipperPathArguments.Dispose();
  749. offsetPathArguments.Dispose();
  750. clipperSolution.Dispose();
  751. offsetSolution.Dispose();
  752. }
  753. }
  754. }
  755. }