PolygonMesh.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. namespace FairyGUI
  4. {
  5. /// <summary>
  6. ///
  7. /// </summary>
  8. public class PolygonMesh : IMeshFactory, IHitTest
  9. {
  10. /// <summary>
  11. /// points must be in clockwise order, and must start from bottom-left if stretchUV is set.
  12. /// </summary>
  13. public readonly List<Vector2> points;
  14. /// <summary>
  15. /// if you dont want to provide uv, leave it empty.
  16. /// </summary>
  17. public readonly List<Vector2> texcoords;
  18. /// <summary>
  19. ///
  20. /// </summary>
  21. public float lineWidth;
  22. /// <summary>
  23. ///
  24. /// </summary>
  25. public Color32 lineColor;
  26. /// <summary>
  27. ///
  28. /// </summary>
  29. public Color32? fillColor;
  30. /// <summary>
  31. ///
  32. /// </summary>
  33. public Color32[] colors;
  34. /// <summary>
  35. ///
  36. /// </summary>
  37. public bool usePercentPositions;
  38. static List<int> sRestIndices = new List<int>();
  39. public PolygonMesh()
  40. {
  41. points = new List<Vector2>();
  42. texcoords = new List<Vector2>();
  43. }
  44. /// <summary>
  45. ///
  46. /// </summary>
  47. /// <param name="point"></param>
  48. public void Add(Vector2 point)
  49. {
  50. points.Add(point);
  51. }
  52. /// <summary>
  53. ///
  54. /// </summary>
  55. /// <param name="point"></param>
  56. /// <param name="texcoord"></param>
  57. public void Add(Vector2 point, Vector2 texcoord)
  58. {
  59. points.Add(point);
  60. texcoords.Add(texcoord);
  61. }
  62. public void OnPopulateMesh(VertexBuffer vb)
  63. {
  64. int numVertices = points.Count;
  65. if (numVertices < 3)
  66. return;
  67. int restIndexPos, numRestIndices;
  68. Color32 color = fillColor != null ? (Color32)fillColor : vb.vertexColor;
  69. float w = vb.contentRect.width;
  70. float h = vb.contentRect.height;
  71. bool useTexcoords = texcoords.Count >= numVertices;
  72. bool fullUV = true;
  73. for (int i = 0; i < numVertices; i++)
  74. {
  75. Vector3 vec = new Vector3(points[i].x, points[i].y, 0);
  76. if (usePercentPositions)
  77. {
  78. vec.x *= w;
  79. vec.y *= h;
  80. }
  81. if (useTexcoords)
  82. {
  83. Vector2 uv = texcoords[i];
  84. if (uv.x != 0 && uv.x != 1 || uv.y != 0 && uv.y != 1)
  85. fullUV = false;
  86. uv.x = Mathf.Lerp(vb.uvRect.x, vb.uvRect.xMax, uv.x);
  87. uv.y = Mathf.Lerp(vb.uvRect.y, vb.uvRect.yMax, uv.y);
  88. vb.AddVert(vec, color, uv);
  89. }
  90. else
  91. vb.AddVert(vec, color);
  92. }
  93. if (useTexcoords && fullUV && numVertices == 4)
  94. vb._isArbitraryQuad = true;
  95. // Algorithm "Ear clipping method" described here:
  96. // -> https://en.wikipedia.org/wiki/Polygon_triangulation
  97. //
  98. // Implementation inspired by:
  99. // -> http://polyk.ivank.net
  100. // -> Starling
  101. sRestIndices.Clear();
  102. for (int i = 0; i < numVertices; ++i)
  103. sRestIndices.Add(i);
  104. restIndexPos = 0;
  105. numRestIndices = numVertices;
  106. Vector2 a, b, c, p;
  107. int otherIndex;
  108. bool earFound;
  109. int i0, i1, i2;
  110. while (numRestIndices > 3)
  111. {
  112. earFound = false;
  113. i0 = sRestIndices[restIndexPos % numRestIndices];
  114. i1 = sRestIndices[(restIndexPos + 1) % numRestIndices];
  115. i2 = sRestIndices[(restIndexPos + 2) % numRestIndices];
  116. a = points[i0];
  117. b = points[i1];
  118. c = points[i2];
  119. if ((a.y - b.y) * (c.x - b.x) + (b.x - a.x) * (c.y - b.y) >= 0)
  120. {
  121. earFound = true;
  122. for (int i = 3; i < numRestIndices; ++i)
  123. {
  124. otherIndex = sRestIndices[(restIndexPos + i) % numRestIndices];
  125. p = points[otherIndex];
  126. if (IsPointInTriangle(ref p, ref a, ref b, ref c))
  127. {
  128. earFound = false;
  129. break;
  130. }
  131. }
  132. }
  133. if (earFound)
  134. {
  135. vb.AddTriangle(i0, i1, i2);
  136. sRestIndices.RemoveAt((restIndexPos + 1) % numRestIndices);
  137. numRestIndices--;
  138. restIndexPos = 0;
  139. }
  140. else
  141. {
  142. restIndexPos++;
  143. if (restIndexPos == numRestIndices) break; // no more ears
  144. }
  145. }
  146. vb.AddTriangle(sRestIndices[0], sRestIndices[1], sRestIndices[2]);
  147. if (colors != null)
  148. vb.RepeatColors(colors, 0, vb.currentVertCount);
  149. if (lineWidth > 0)
  150. DrawOutline(vb);
  151. }
  152. void DrawOutline(VertexBuffer vb)
  153. {
  154. int numVertices = points.Count;
  155. int start = vb.currentVertCount - numVertices;
  156. int k = vb.currentVertCount;
  157. for (int i = 0; i < numVertices; i++)
  158. {
  159. Vector3 p0 = vb.vertices[start + i];
  160. p0.y = -p0.y;
  161. Vector3 p1;
  162. if (i < numVertices - 1)
  163. p1 = vb.vertices[start + i + 1];
  164. else
  165. p1 = vb.vertices[start];
  166. p1.y = -p1.y;
  167. Vector3 lineVector = p1 - p0;
  168. Vector3 widthVector = Vector3.Cross(lineVector, new Vector3(0, 0, 1));
  169. widthVector.Normalize();
  170. vb.AddVert(p0 - widthVector * lineWidth * 0.5f, lineColor);
  171. vb.AddVert(p0 + widthVector * lineWidth * 0.5f, lineColor);
  172. vb.AddVert(p1 - widthVector * lineWidth * 0.5f, lineColor);
  173. vb.AddVert(p1 + widthVector * lineWidth * 0.5f, lineColor);
  174. k += 4;
  175. vb.AddTriangle(k - 4, k - 3, k - 1);
  176. vb.AddTriangle(k - 4, k - 1, k - 2);
  177. //joint
  178. if (i != 0)
  179. {
  180. vb.AddTriangle(k - 6, k - 5, k - 3);
  181. vb.AddTriangle(k - 6, k - 3, k - 4);
  182. }
  183. if (i == numVertices - 1)
  184. {
  185. start += numVertices;
  186. vb.AddTriangle(k - 2, k - 1, start + 1);
  187. vb.AddTriangle(k - 2, start + 1, start);
  188. }
  189. }
  190. }
  191. bool IsPointInTriangle(ref Vector2 p, ref Vector2 a, ref Vector2 b, ref Vector2 c)
  192. {
  193. // From Starling
  194. // This algorithm is described well in this article:
  195. // http://www.blackpawn.com/texts/pointinpoly/default.html
  196. float v0x = c.x - a.x;
  197. float v0y = c.y - a.y;
  198. float v1x = b.x - a.x;
  199. float v1y = b.y - a.y;
  200. float v2x = p.x - a.x;
  201. float v2y = p.y - a.y;
  202. float dot00 = v0x * v0x + v0y * v0y;
  203. float dot01 = v0x * v1x + v0y * v1y;
  204. float dot02 = v0x * v2x + v0y * v2y;
  205. float dot11 = v1x * v1x + v1y * v1y;
  206. float dot12 = v1x * v2x + v1y * v2y;
  207. float invDen = 1.0f / (dot00 * dot11 - dot01 * dot01);
  208. float u = (dot11 * dot02 - dot01 * dot12) * invDen;
  209. float v = (dot00 * dot12 - dot01 * dot02) * invDen;
  210. return (u >= 0) && (v >= 0) && (u + v < 1);
  211. }
  212. public bool HitTest(Rect contentRect, Vector2 point)
  213. {
  214. if (!contentRect.Contains(point))
  215. return false;
  216. // Algorithm & implementation thankfully taken from:
  217. // -> http://alienryderflex.com/polygon/
  218. // inspired by Starling
  219. int len = points.Count;
  220. int i;
  221. int j = len - 1;
  222. bool oddNodes = false;
  223. float w = contentRect.width;
  224. float h = contentRect.height;
  225. for (i = 0; i < len; ++i)
  226. {
  227. float ix = points[i].x;
  228. float iy = points[i].y;
  229. float jx = points[j].x;
  230. float jy = points[j].y;
  231. if (usePercentPositions)
  232. {
  233. ix *= w;
  234. iy *= h;
  235. ix *= w;
  236. iy *= h;
  237. }
  238. if ((iy < point.y && jy >= point.y || jy < point.y && iy >= point.y) && (ix <= point.x || jx <= point.x))
  239. {
  240. if (ix + (point.y - iy) / (jy - iy) * (jx - ix) < point.x)
  241. oddNodes = !oddNodes;
  242. }
  243. j = i;
  244. }
  245. return oddNodes;
  246. }
  247. }
  248. }