Sin descripción
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.

ShadowMesh2D.cs 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. using System.Collections.Generic;
  2. using System.Collections.Specialized;
  3. using Unity.Collections;
  4. using UnityEngine;
  5. using UnityEngine.U2D;
  6. using System;
  7. namespace UnityEngine.Rendering.Universal
  8. {
  9. [Serializable]
  10. internal class ShadowMesh2D : ShadowShape2D
  11. {
  12. internal const int k_CapsuleCapSegments = 8;
  13. internal const float k_TrimEdgeUninitialized = -1;
  14. public enum EdgeProcessing
  15. {
  16. None,
  17. Clipping,
  18. }
  19. [SerializeField] Mesh m_Mesh;
  20. [SerializeField] Bounds m_LocalBounds;
  21. [SerializeField] EdgeProcessing m_EdgeProcessing = EdgeProcessing.Clipping;
  22. [SerializeField] float m_TrimEdge = k_TrimEdgeUninitialized;
  23. [SerializeField] bool m_FlipX;
  24. [SerializeField] bool m_FlipY;
  25. [SerializeField] float m_InitialTrim = 0;
  26. public Mesh mesh { get => m_Mesh; }
  27. public BoundingSphere boundingSphere { get => m_BoundingSphere; }
  28. internal BoundingSphere m_BoundingSphere; // update to world space
  29. public EdgeProcessing edgeProcessing { get { return m_EdgeProcessing; } set { m_EdgeProcessing = value; } }
  30. public float trimEdge { get { return m_TrimEdge; } set { m_TrimEdge = value; } }
  31. static internal void DuplicateShadowMesh(Mesh source, out Mesh dest)
  32. {
  33. // This is not gc tested as this generates garbage
  34. dest = new Mesh();
  35. dest.Clear();
  36. if (source != null)
  37. {
  38. dest.vertices = source.vertices;
  39. dest.tangents = source.tangents;
  40. dest.triangles = source.triangles;
  41. dest.bounds = source.bounds;
  42. }
  43. }
  44. internal void CopyFrom(ShadowMesh2D source)
  45. {
  46. // This is not gc tested as this generates garbage (calls DuplicateShadowMesh)
  47. DuplicateShadowMesh(source.m_Mesh, out m_Mesh);
  48. m_TrimEdge = source.trimEdge;
  49. m_LocalBounds = source.m_LocalBounds;
  50. m_EdgeProcessing = source.edgeProcessing;
  51. }
  52. internal void AddCircle(Vector3 center, float r, NativeArray<Vector3> generatedVertices, NativeArray<int> generatedIndices, bool reverseWindingOrder, ref int vertexWritePos, ref int indexWritePos)
  53. {
  54. float direction = reverseWindingOrder ? 1 : -1;
  55. // Special case a full circle
  56. float segments = 2 * k_CapsuleCapSegments;
  57. float angle;
  58. int startWritePos = vertexWritePos;
  59. for (int i = 0; i < segments; i++)
  60. {
  61. angle = direction * (2 * Mathf.PI * (float)i / (float)segments);
  62. float x = r * Mathf.Cos(angle) + center.x;
  63. float y = r * Mathf.Sin(angle) + center.y;
  64. generatedIndices[indexWritePos++] = vertexWritePos;
  65. generatedIndices[indexWritePos++] = i + 1 < segments ? vertexWritePos + 1 : startWritePos;
  66. generatedVertices[vertexWritePos++] = new Vector3(x, y, 0);
  67. }
  68. }
  69. internal void AddCapsuleCap(Vector3 center, float r, Vector3 otherCenter, NativeArray<Vector3> generatedVertices, NativeArray<int> generatedIndices, bool reverseWindingOrder, ref int vertexWritePos, ref int indexWritePos)
  70. {
  71. float startAngle;
  72. float endAngle;
  73. // Special case a full circle
  74. float segments = k_CapsuleCapSegments;
  75. Vector3 otherCenterDir = (otherCenter - center).normalized;
  76. float absCenterAngle = Mathf.Acos(Vector3.Dot(otherCenterDir, new Vector3(1, 0, 0)));
  77. float angleSign = Vector3.Dot(otherCenterDir, new Vector3(0, 1, 0)) < 0 ? -1f : 1f;
  78. float centerAngle = absCenterAngle * angleSign;
  79. // This is hard coded for a half circle
  80. if (reverseWindingOrder)
  81. {
  82. float HalfPI = 0.5f * Mathf.PI;
  83. startAngle = centerAngle + HalfPI;
  84. endAngle = startAngle + Mathf.PI;
  85. }
  86. else
  87. {
  88. float ThreeHalfsPI = 1.5f * Mathf.PI;
  89. startAngle = centerAngle + ThreeHalfsPI;
  90. endAngle = startAngle - Mathf.PI;
  91. }
  92. float deltaAngle = endAngle - startAngle;
  93. float angle;
  94. for (int i = 0; i < segments; i++)
  95. {
  96. angle = (deltaAngle * (float)i / (float)segments) + startAngle;
  97. float x = r * Mathf.Cos(angle) + center.x;
  98. float y = r * Mathf.Sin(angle) + center.y;
  99. generatedIndices[indexWritePos++] = vertexWritePos;
  100. generatedIndices[indexWritePos++] = vertexWritePos + 1;
  101. generatedVertices[vertexWritePos++] = new Vector3(x, y, 0);
  102. }
  103. angle = deltaAngle + startAngle;
  104. generatedVertices[vertexWritePos++] = new Vector3(r * Mathf.Cos(angle) + center.x, r * Mathf.Sin(angle) + center.y, 0);
  105. }
  106. internal void AddCapsule(Vector3 pt0, Vector3 pt1, float r0, float r1, NativeArray<Vector3> generatedVertices, NativeArray<int> generatedIndices, bool reverseWindingOrder, ref int vertexWritePos, ref int indexWritePos)
  107. {
  108. // Add Straight Segments
  109. Vector3 delta = (pt1 - pt0).normalized;
  110. Vector3 relOffset0 = new Vector3(delta.y, -delta.x, 0);
  111. Vector3 relOffset1 = new Vector3(-delta.y, delta.x, 0);
  112. if (pt1.x < pt0.x)
  113. {
  114. Vector3 temp = pt0;
  115. pt0 = pt1;
  116. pt1 = temp;
  117. }
  118. int circle0Start = vertexWritePos;
  119. // Add circles
  120. AddCapsuleCap(pt0, r0, pt1, generatedVertices, generatedIndices, reverseWindingOrder, ref vertexWritePos, ref indexWritePos);
  121. generatedIndices[indexWritePos++] = vertexWritePos - 1;
  122. generatedIndices[indexWritePos++] = vertexWritePos;
  123. AddCapsuleCap(pt1, r1, pt0, generatedVertices, generatedIndices, reverseWindingOrder, ref vertexWritePos, ref indexWritePos);
  124. generatedIndices[indexWritePos++] = vertexWritePos - 1;
  125. generatedIndices[indexWritePos++] = circle0Start;
  126. }
  127. internal int AddShape(NativeArray<Vector3> vertices, NativeArray<int> indices, int indicesProcessed, NativeArray<Vector3> generatedVertices, NativeArray<int> generatedIndices, ref int vertexWritePos, ref int indexWritePos)
  128. {
  129. int indexToProcess = indicesProcessed;
  130. int prevIndex = indices[indexToProcess];
  131. int startIndex = indices[indexToProcess];
  132. int startWriteIndex = vertexWritePos;
  133. generatedVertices[vertexWritePos++] = vertices[prevIndex];
  134. bool continueProcessing = true;
  135. while (indexToProcess < indices.Length && continueProcessing)
  136. {
  137. int index0 = indices[indexToProcess++];
  138. int index1 = indices[indexToProcess++];
  139. generatedIndices[indexWritePos++] = vertexWritePos - 1;
  140. if (index1 != startIndex)
  141. {
  142. generatedIndices[indexWritePos++] = vertexWritePos;
  143. generatedVertices[vertexWritePos++] = vertices[index1];
  144. continueProcessing = index0 == prevIndex;
  145. }
  146. else
  147. {
  148. generatedIndices[indexWritePos++] = startWriteIndex;
  149. continueProcessing = false;
  150. }
  151. prevIndex = index1;
  152. }
  153. return indexToProcess;
  154. }
  155. public override void SetShape(NativeArray<Vector3> vertices, NativeArray<int> indices, NativeArray<float> radii, Matrix4x4 transform, ShadowShape2D.WindingOrder windingOrder = ShadowShape2D.WindingOrder.Clockwise, bool allowTriming = true, bool createInteriorGeometry = false)
  156. {
  157. if (m_TrimEdge == k_TrimEdgeUninitialized)
  158. m_TrimEdge = m_InitialTrim;
  159. if (m_Mesh == null)
  160. m_Mesh = new Mesh();
  161. if (indices.Length == 0)
  162. {
  163. m_Mesh.Clear();
  164. return;
  165. }
  166. bool reverseWindingOrder = windingOrder == ShadowShape2D.WindingOrder.CounterClockwise;
  167. int circleCount = 0;
  168. int capsuleCount = 0;
  169. for (int i = 0; i < indices.Length; i += 2)
  170. {
  171. int index0 = indices[i];
  172. int index1 = indices[i + 1];
  173. if (radii[index0] > 0 || radii[index1] > 0)
  174. {
  175. if (index0 == index1)
  176. circleCount++;
  177. else
  178. capsuleCount++;
  179. }
  180. }
  181. int capsuleStraightSegments = capsuleCount * 2;
  182. int capsuleCapSegments = capsuleCount * k_CapsuleCapSegments; // This can be refined later
  183. int circleSegments = circleCount * 2 * k_CapsuleCapSegments;
  184. int lineCount = (indices.Length >> 1) - (capsuleCount + circleCount);
  185. int indexCount = 2 * (lineCount + capsuleStraightSegments + (2 * capsuleCapSegments) + circleSegments);
  186. int vertexCount = indexCount; // Keep this simple for now
  187. NativeArray<Vector3> generatedVertices = new NativeArray<Vector3>(vertexCount, Allocator.Temp);
  188. NativeArray<int> generatedIndices = new NativeArray<int>(indexCount, Allocator.Temp);
  189. int vertexWritePos = 0;
  190. int indexWritePos = 0;
  191. int indicesProcessed = 0;
  192. while (indicesProcessed < indices.Length)
  193. {
  194. int v0 = indices[indicesProcessed];
  195. int v1 = indices[indicesProcessed + 1];
  196. float r0 = radii[v0];
  197. float r1 = radii[v1];
  198. if (radii[v0] > 0 || radii[v1] > 0)
  199. {
  200. Vector3 pt0 = vertices[v0];
  201. Vector3 pt1 = vertices[v1];
  202. if (vertices[v0].x == vertices[v1].x && vertices[v0].y == vertices[v1].y)
  203. AddCircle(pt0, r0, generatedVertices, generatedIndices, reverseWindingOrder, ref vertexWritePos, ref indexWritePos);
  204. else
  205. AddCapsule(pt0, pt1, r0, r1, generatedVertices, generatedIndices, reverseWindingOrder, ref vertexWritePos, ref indexWritePos);
  206. indicesProcessed += 2;
  207. }
  208. else
  209. {
  210. // Will add edges or polygons
  211. indicesProcessed = AddShape(vertices, indices, indicesProcessed, generatedVertices, generatedIndices, ref vertexWritePos, ref indexWritePos);
  212. }
  213. }
  214. for (int i = 0; i < generatedVertices.Length; i++)
  215. generatedVertices[i] = transform.MultiplyPoint(generatedVertices[i]);
  216. NativeArray<ShadowEdge> calculatedEdges;
  217. NativeArray<int> calculatedStartingEdges;
  218. NativeArray<bool> calculatedIsClosedArray;
  219. ShadowUtility.CalculateEdgesFromLines(ref generatedIndices, out calculatedEdges, out calculatedStartingEdges, out calculatedIsClosedArray);
  220. if (reverseWindingOrder)
  221. ShadowUtility.ReverseWindingOrder(ref calculatedStartingEdges, ref calculatedEdges);
  222. if (m_EdgeProcessing == EdgeProcessing.Clipping)
  223. {
  224. NativeArray<Vector3> clippedVertices;
  225. NativeArray<ShadowEdge> clippedEdges;
  226. NativeArray<int> clippedStartingIndices;
  227. ShadowUtility.ClipEdges(ref generatedVertices, ref calculatedEdges, ref calculatedStartingEdges, ref calculatedIsClosedArray, trimEdge, out clippedVertices, out clippedEdges, out clippedStartingIndices);
  228. if (clippedStartingIndices.Length > 0)
  229. m_LocalBounds = ShadowUtility.GenerateShadowMesh(m_Mesh, clippedVertices, clippedEdges, clippedStartingIndices, calculatedIsClosedArray, true, createInteriorGeometry, ShadowShape2D.OutlineTopology.Lines);
  230. else
  231. {
  232. m_LocalBounds = new Bounds();
  233. m_Mesh.Clear();
  234. }
  235. clippedVertices.Dispose();
  236. clippedEdges.Dispose();
  237. clippedStartingIndices.Dispose();
  238. }
  239. else
  240. {
  241. m_LocalBounds = ShadowUtility.GenerateShadowMesh(m_Mesh, generatedVertices, calculatedEdges, calculatedStartingEdges, calculatedIsClosedArray, true, createInteriorGeometry, ShadowShape2D.OutlineTopology.Lines);
  242. }
  243. generatedVertices.Dispose();
  244. generatedIndices.Dispose();
  245. calculatedEdges.Dispose();
  246. calculatedIsClosedArray.Dispose();
  247. calculatedStartingEdges.Dispose();
  248. }
  249. bool AreDegenerateVertices(NativeArray<Vector3> vertices)
  250. {
  251. if (vertices == null || vertices.Length == 0)
  252. return true;
  253. // This should is a trade off between perfomance and accuracy. This may need to be refined later if we find cases where this is not good enough.
  254. int prevIndex = vertices.Length - 1;
  255. for (int i=0;i< vertices.Length; i++)
  256. {
  257. if (vertices[prevIndex].x != vertices[i].x || vertices[prevIndex].y != vertices[i].y)
  258. return false;
  259. prevIndex = i;
  260. }
  261. return true;
  262. }
  263. public override void SetShape(NativeArray<Vector3> vertices, NativeArray<int> indices, ShadowShape2D.OutlineTopology outlineTopology, ShadowShape2D.WindingOrder windingOrder = ShadowShape2D.WindingOrder.Clockwise, bool allowTrimming = true, bool createInteriorGeometry = false)
  264. {
  265. if (AreDegenerateVertices(vertices))
  266. return;
  267. if (m_TrimEdge == k_TrimEdgeUninitialized)
  268. m_TrimEdge = m_InitialTrim;
  269. bool disposeVertices = false;
  270. NativeArray<ShadowEdge> edges;
  271. NativeArray<int> shapeStartingIndices;
  272. NativeArray<bool> shapeIsClosedArray;
  273. if (m_Mesh == null)
  274. m_Mesh = new Mesh();
  275. if (indices.Length == 0)
  276. {
  277. m_Mesh.Clear();
  278. return;
  279. }
  280. if (outlineTopology == ShadowShape2D.OutlineTopology.Triangles)
  281. {
  282. NativeArray<Vector3> newVertices;
  283. ShadowUtility.CalculateEdgesFromTriangles(ref vertices, ref indices, true, out newVertices, out edges, out shapeStartingIndices, out shapeIsClosedArray);
  284. disposeVertices = true;
  285. vertices = newVertices;
  286. }
  287. else // if (outlineTopology == ShadowShape2D.OutlineTopology.Lines)
  288. {
  289. ShadowUtility.CalculateEdgesFromLines(ref indices, out edges, out shapeStartingIndices, out shapeIsClosedArray);
  290. }
  291. if (windingOrder == ShadowShape2D.WindingOrder.CounterClockwise)
  292. ShadowUtility.ReverseWindingOrder(ref shapeStartingIndices, ref edges);
  293. // It would be better if we don't have to rerun SetShape after a trimEdge change.
  294. if (m_EdgeProcessing == EdgeProcessing.Clipping && allowTrimming)
  295. {
  296. NativeArray<Vector3> clippedVertices;
  297. NativeArray<ShadowEdge> clippedEdges;
  298. NativeArray<int> clippedStartingIndices;
  299. ShadowUtility.ClipEdges(ref vertices, ref edges, ref shapeStartingIndices, ref shapeIsClosedArray, trimEdge, out clippedVertices, out clippedEdges, out clippedStartingIndices);
  300. m_LocalBounds = ShadowUtility.GenerateShadowMesh(m_Mesh, clippedVertices, clippedEdges, clippedStartingIndices, shapeIsClosedArray, allowTrimming, createInteriorGeometry, outlineTopology);
  301. clippedVertices.Dispose();
  302. clippedEdges.Dispose();
  303. clippedStartingIndices.Dispose();
  304. }
  305. else
  306. {
  307. m_LocalBounds = ShadowUtility.GenerateShadowMesh(m_Mesh, vertices, edges, shapeStartingIndices, shapeIsClosedArray, allowTrimming, createInteriorGeometry, outlineTopology);
  308. }
  309. if(disposeVertices)
  310. vertices.Dispose();
  311. edges.Dispose();
  312. shapeStartingIndices.Dispose();
  313. shapeIsClosedArray.Dispose();
  314. }
  315. public void SetShapeWithLines(NativeArray<Vector3> vertices, NativeArray<int> indices, bool allowTrimming)
  316. {
  317. SetShape(vertices, indices, ShadowShape2D.OutlineTopology.Lines, allowTrimming: allowTrimming);
  318. }
  319. public override void SetFlip(bool flipX, bool flipY)
  320. {
  321. m_FlipX = flipX;
  322. m_FlipY = flipY;
  323. }
  324. public override void GetFlip(out bool flipX, out bool flipY)
  325. {
  326. flipX = m_FlipX;
  327. flipY = m_FlipY;
  328. }
  329. public override void SetDefaultTrim(float trim)
  330. {
  331. m_InitialTrim = trim;
  332. }
  333. public void UpdateBoundingSphere(Transform transform)
  334. {
  335. var maxBound = transform.TransformPoint(m_LocalBounds.max);
  336. var minBound = transform.TransformPoint(m_LocalBounds.min);
  337. var center = 0.5f * (maxBound + minBound);
  338. var radius = Vector3.Magnitude(maxBound - center);
  339. m_BoundingSphere = new BoundingSphere(center, radius);
  340. }
  341. }
  342. }