PolyImage.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. using System;
  2. using System.Collections.Generic;
  3. namespace UnityEngine.UI
  4. {
  5. [AddComponentMenu("UI/PolyImage", 51)]
  6. [DisallowMultipleComponent]
  7. public class PolyImage : Image
  8. {
  9. #region Image.cs
  10. /// Image's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top.
  11. private Vector4 _GetDrawingDimensions(bool shouldPreserveAspect)
  12. {
  13. var padding = overrideSprite == null ? Vector4.zero : Sprites.DataUtility.GetPadding(overrideSprite);
  14. var size = overrideSprite == null ? Vector2.zero : new Vector2(overrideSprite.rect.width, overrideSprite.rect.height);
  15. Rect r = GetPixelAdjustedRect();
  16. // Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r));
  17. int spriteW = Mathf.RoundToInt(size.x);
  18. int spriteH = Mathf.RoundToInt(size.y);
  19. var v = new Vector4(
  20. padding.x / spriteW,
  21. padding.y / spriteH,
  22. (spriteW - padding.z) / spriteW,
  23. (spriteH - padding.w) / spriteH);
  24. if (shouldPreserveAspect && size.sqrMagnitude > 0.0f)
  25. {
  26. var spriteRatio = size.x / size.y;
  27. var rectRatio = r.width / r.height;
  28. if (spriteRatio > rectRatio)
  29. {
  30. var oldHeight = r.height;
  31. r.height = r.width * (1.0f / spriteRatio);
  32. r.y += (oldHeight - r.height) * rectTransform.pivot.y;
  33. }
  34. else
  35. {
  36. var oldWidth = r.width;
  37. r.width = r.height * spriteRatio;
  38. r.x += (oldWidth - r.width) * rectTransform.pivot.x;
  39. }
  40. }
  41. v = new Vector4(
  42. r.x + r.width * v.x,
  43. r.y + r.height * v.y,
  44. r.x + r.width * v.z,
  45. r.y + r.height * v.w
  46. );
  47. return v;
  48. }
  49. Vector4 _GetAdjustedBorders(Vector4 border, Rect rect)
  50. {
  51. for (int axis = 0; axis <= 1; axis++)
  52. {
  53. // If the rect is smaller than the combined borders, then there's not room for the borders at their normal size.
  54. // In order to avoid artefacts with overlapping borders, we scale the borders down to fit.
  55. float combinedBorders = border[axis] + border[axis + 2];
  56. if (rect.size[axis] < combinedBorders && combinedBorders != 0)
  57. {
  58. float borderScaleRatio = rect.size[axis] / combinedBorders;
  59. border[axis] *= borderScaleRatio;
  60. border[axis + 2] *= borderScaleRatio;
  61. }
  62. }
  63. return border;
  64. }
  65. #endregion
  66. #if UNITY_5_0 || UNITY_5_1
  67. static VertexHelper toFill = new VertexHelper();
  68. protected override void OnFillVBO(List<UIVertex> vbo)
  69. #elif UNITY_5_2
  70. protected override void OnPopulateMesh(Mesh mesh)
  71. #else
  72. protected override void OnPopulateMesh(VertexHelper toFill)
  73. #endif
  74. {
  75. // Tiled & Filled not supported yet
  76. if (overrideSprite == null || type == Type.Tiled || type == Type.Filled)
  77. {
  78. #if UNITY_5_0 || UNITY_5_1
  79. base.OnFillVBO(vbo);
  80. #elif UNITY_5_2
  81. base.OnPopulateMesh(mesh);
  82. #else
  83. base.OnPopulateMesh(toFill);
  84. #endif
  85. return;
  86. }
  87. toFill.Clear();
  88. switch (type)
  89. {
  90. case Type.Simple:
  91. GenerateSimplePolySprite(toFill, preserveAspect);
  92. break;
  93. case Type.Sliced:
  94. GenerateSlicedPolySprite(toFill);
  95. break;
  96. }
  97. #if UNITY_5_0 || UNITY_5_1
  98. toFill.FillVBO(vbo);
  99. #elif UNITY_5_2
  100. toFill.FillMesh(mesh);
  101. #endif
  102. }
  103. void GenerateSimplePolySprite(VertexHelper vh, bool lPreserveAspect)
  104. {
  105. Vector4 v = _GetDrawingDimensions(lPreserveAspect);
  106. Vector4 uv = Sprites.DataUtility.GetOuterUV(overrideSprite);
  107. Color color32 = color;
  108. Vector2[] uvs = overrideSprite.uv;
  109. float invU = 1 / (uv.z - uv.x);
  110. float invV = 1 / (uv.w - uv.y);
  111. for (int i = 0; i < uvs.Length; i++)
  112. {
  113. float u2 = invU * (uvs[i].x - uv.x);
  114. float v2 = invV * (uvs[i].y - uv.y);
  115. float x = u2 * (v.z - v.x) + v.x;
  116. float y = v2 * (v.w - v.y) + v.y;
  117. vh.AddVert(new Vector2(x, y), color32, uvs[i]);
  118. }
  119. ushort[] triangles = overrideSprite.triangles;
  120. for (int i = 0; i < triangles.Length; i += 3)
  121. {
  122. vh.AddTriangle(triangles[i], triangles[i + 1], triangles[i + 2]);
  123. }
  124. }
  125. public static float Cross(Vector2 lhs, Vector2 rhs)
  126. {
  127. return lhs.x * rhs.y - lhs.y * rhs.x;
  128. }
  129. // idea comes from RadialCut
  130. private static List<ushort> LineCut(
  131. List<Vector2> uvs, List<ushort> triangles,
  132. Vector2 start, float angle, Func<Vector2, bool> predict = null)
  133. {
  134. List<ushort> splitTriangles = new List<ushort>();
  135. List<ushort> splitTriCache1 = new List<ushort>();
  136. List<ushort> splitTriCache2 = new List<ushort>();
  137. var offset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
  138. for (int i = 0; i < triangles.Count; i += 3)
  139. {
  140. splitTriCache1.Clear();
  141. splitTriCache2.Clear();
  142. for (int j = 0; j < 3; j++)
  143. {
  144. ushort tri1 = triangles[i + j], tri2 = triangles[i + (j + 1) % 3];
  145. Vector2 uv1 = uvs[tri1], uv2 = uvs[tri2];
  146. float sign1 = Cross(offset, uv1 - start), sign2 = Cross(offset, uv2 - start);
  147. if (sign1 <= 0)
  148. splitTriCache1.Add(tri1);
  149. if (sign1 >= 0)
  150. splitTriCache2.Add(tri1);
  151. if (sign1 * sign2 < 0)
  152. {
  153. // Line Intersects!
  154. var diff = uv2 - uv1;
  155. float t1 = -sign1 / Cross(offset, uv2 - uv1);
  156. var p = uv1 + diff * t1;
  157. ushort idx = (ushort)uvs.Count;
  158. uvs.Add(p);
  159. splitTriCache1.Add(idx);
  160. splitTriCache2.Add(idx);
  161. }
  162. }
  163. if (splitTriCache1.Count >= 3)
  164. {
  165. for (int j = 2; j < splitTriCache1.Count; j++)
  166. {
  167. if(predict != null)
  168. {
  169. Vector2 center = (uvs[splitTriCache1[0]] + uvs[splitTriCache1[j - 1]] + uvs[splitTriCache1[j]]) / 3;
  170. if (!predict(center)) continue;
  171. }
  172. splitTriangles.Add(splitTriCache1[0]);
  173. splitTriangles.Add(splitTriCache1[j - 1]);
  174. splitTriangles.Add(splitTriCache1[j]);
  175. }
  176. }
  177. if (splitTriCache2.Count >= 3)
  178. {
  179. for (int j = 2; j < splitTriCache2.Count; j++)
  180. {
  181. if (predict != null)
  182. {
  183. Vector2 center = (uvs[splitTriCache2[0]] + uvs[splitTriCache2[j - 1]] + uvs[splitTriCache2[j]]) / 3;
  184. if (!predict(center)) continue;
  185. }
  186. splitTriangles.Add(splitTriCache2[0]);
  187. splitTriangles.Add(splitTriCache2[j - 1]);
  188. splitTriangles.Add(splitTriCache2[j]);
  189. }
  190. }
  191. }
  192. return splitTriangles;
  193. }
  194. static readonly Vector2[] s_VertScratch = new Vector2[4];
  195. static readonly Vector2[] s_UVScratch = new Vector2[4];
  196. private static int XSlot(float x)
  197. {
  198. for (int idx = 0; idx < 3; idx++)
  199. {
  200. if (s_UVScratch[idx].x < s_UVScratch[idx + 1].x && x <= s_UVScratch[idx + 1].x)
  201. return idx;
  202. }
  203. return 2;
  204. }
  205. private static int YSlot(float y)
  206. {
  207. for (int idx = 0; idx < 3; idx++)
  208. {
  209. if (s_UVScratch[idx].y < s_UVScratch[idx + 1].y && y <= s_UVScratch[idx + 1].y)
  210. return idx;
  211. }
  212. return 2;
  213. }
  214. private void GenerateSlicedPolySprite(VertexHelper toFill)
  215. {
  216. if (!hasBorder)
  217. {
  218. GenerateSimplePolySprite(toFill, false);
  219. return;
  220. }
  221. Vector4 outer = Sprites.DataUtility.GetOuterUV(overrideSprite);
  222. Vector4 inner = Sprites.DataUtility.GetInnerUV(overrideSprite);
  223. Vector4 padding = Sprites.DataUtility.GetPadding(overrideSprite);
  224. Vector4 border = overrideSprite.border;
  225. Rect rect = GetPixelAdjustedRect();
  226. border = _GetAdjustedBorders(border / pixelsPerUnit, rect);
  227. padding = padding / pixelsPerUnit;
  228. s_VertScratch[0] = new Vector2(padding.x, padding.y);
  229. s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);
  230. s_VertScratch[1].x = border.x;
  231. s_VertScratch[1].y = border.y;
  232. s_VertScratch[2].x = rect.width - border.z;
  233. s_VertScratch[2].y = rect.height - border.w;
  234. for (int i = 0; i < 4; ++i)
  235. {
  236. s_VertScratch[i].x += rect.x;
  237. s_VertScratch[i].y += rect.y;
  238. }
  239. s_UVScratch[0] = new Vector2(outer.x, outer.y);
  240. s_UVScratch[1] = new Vector2(inner.x, inner.y);
  241. s_UVScratch[2] = new Vector2(inner.z, inner.w);
  242. s_UVScratch[3] = new Vector2(outer.z, outer.w);
  243. List<Vector2> uvs = new List<Vector2>(overrideSprite.uv);
  244. List<ushort> triangles = new List<ushort>(overrideSprite.triangles);
  245. var splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.x, inner.y), Mathf.PI / 2);
  246. splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.z, inner.w), 0);
  247. splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.x, inner.y), Mathf.PI / 2);
  248. splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.z, inner.w), 0);
  249. splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.x, inner.y), Mathf.PI / 2);
  250. splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.z, inner.w), 0);
  251. splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.x, inner.y), Mathf.PI / 2);
  252. splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.z, inner.w), 0);
  253. for (int i = 0; i < uvs.Count; i++)
  254. {
  255. int x = XSlot(uvs[i].x);
  256. int y = YSlot(uvs[i].y);
  257. float kX = (uvs[i].x - s_UVScratch[x].x) / (s_UVScratch[x + 1].x - s_UVScratch[x].x);
  258. float kY = (uvs[i].y - s_UVScratch[y].y) / (s_UVScratch[y + 1].y - s_UVScratch[y].y);
  259. Vector2 pos = new Vector2(kX * (s_VertScratch[x + 1].x - s_VertScratch[x].x) + s_VertScratch[x].x,
  260. kY * (s_VertScratch[y + 1].y - s_VertScratch[y].y) + s_VertScratch[y].y);
  261. toFill.AddVert(pos, color, uvs[i]);
  262. }
  263. for (int i = 0; i < splitedTriangles.Count; i += 3)
  264. {
  265. int x = XSlot(uvs[splitedTriangles[i + 0]].x);
  266. int y = YSlot(uvs[splitedTriangles[i + 0]].y);
  267. if (x == 1 && y == 1 && !fillCenter) continue;
  268. toFill.AddTriangle(splitedTriangles[i + 0], splitedTriangles[i + 1], splitedTriangles[i + 2]);
  269. }
  270. }
  271. }
  272. }