UIPrimitiveBase.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. using System;
  2. using System.Collections.Generic;
  3. namespace UnityEngine.UI.Extensions
  4. {
  5. public enum ResolutionMode
  6. {
  7. None,
  8. PerSegment,
  9. PerLine
  10. }
  11. [RequireComponent(typeof(CanvasRenderer))]
  12. public class UIPrimitiveBase : MaskableGraphic, ILayoutElement, ICanvasRaycastFilter
  13. {
  14. static protected Material s_ETC1DefaultUI = null;
  15. List<Vector2> outputList = new List<Vector2>();
  16. [SerializeField] private Sprite m_Sprite;
  17. public Sprite sprite { get { return m_Sprite; } set { if (SetPropertyUtility.SetClass(ref m_Sprite, value)) GeneratedUVs(); SetAllDirty(); } }
  18. [NonSerialized]
  19. private Sprite m_OverrideSprite;
  20. public Sprite overrideSprite { get { return activeSprite; } set { if (SetPropertyUtility.SetClass(ref m_OverrideSprite, value)) GeneratedUVs(); SetAllDirty(); } }
  21. protected Sprite activeSprite { get { return m_OverrideSprite != null ? m_OverrideSprite : sprite; } }
  22. // Not serialized until we support read-enabled sprites better.
  23. internal float m_EventAlphaThreshold = 1;
  24. public float eventAlphaThreshold { get { return m_EventAlphaThreshold; } set { m_EventAlphaThreshold = value; } }
  25. [SerializeField]
  26. private ResolutionMode m_improveResolution;
  27. public ResolutionMode ImproveResolution { get { return m_improveResolution; } set { m_improveResolution = value; SetAllDirty(); } }
  28. [SerializeField]
  29. protected float m_Resolution;
  30. public float Resolution { get { return m_Resolution; } set { m_Resolution = value; SetAllDirty(); } }
  31. [SerializeField]
  32. private bool m_useNativeSize;
  33. public bool UseNativeSize { get { return m_useNativeSize; } set { m_useNativeSize = value; SetAllDirty(); } }
  34. protected UIPrimitiveBase()
  35. {
  36. useLegacyMeshGeneration = false;
  37. }
  38. /// <summary>
  39. /// Default material used to draw everything if no explicit material was specified.
  40. /// </summary>
  41. static public Material defaultETC1GraphicMaterial
  42. {
  43. get
  44. {
  45. if (s_ETC1DefaultUI == null)
  46. s_ETC1DefaultUI = Canvas.GetETC1SupportedCanvasMaterial();
  47. return s_ETC1DefaultUI;
  48. }
  49. }
  50. /// <summary>
  51. /// Image's texture comes from the UnityEngine.Image.
  52. /// </summary>
  53. public override Texture mainTexture
  54. {
  55. get
  56. {
  57. if (activeSprite == null)
  58. {
  59. if (material != null && material.mainTexture != null)
  60. {
  61. return material.mainTexture;
  62. }
  63. return s_WhiteTexture;
  64. }
  65. return activeSprite.texture;
  66. }
  67. }
  68. /// <summary>
  69. /// Whether the Image has a border to work with.
  70. /// </summary>
  71. public bool hasBorder
  72. {
  73. get
  74. {
  75. if (activeSprite != null)
  76. {
  77. Vector4 v = activeSprite.border;
  78. return v.sqrMagnitude > 0f;
  79. }
  80. return false;
  81. }
  82. }
  83. public float pixelsPerUnit
  84. {
  85. get
  86. {
  87. float spritePixelsPerUnit = 100;
  88. if (activeSprite)
  89. spritePixelsPerUnit = activeSprite.pixelsPerUnit;
  90. float referencePixelsPerUnit = 100;
  91. if (canvas)
  92. referencePixelsPerUnit = canvas.referencePixelsPerUnit;
  93. return spritePixelsPerUnit / referencePixelsPerUnit;
  94. }
  95. }
  96. public override Material material
  97. {
  98. get
  99. {
  100. if (m_Material != null)
  101. return m_Material;
  102. if (activeSprite && activeSprite.associatedAlphaSplitTexture != null)
  103. return defaultETC1GraphicMaterial;
  104. return defaultMaterial;
  105. }
  106. set
  107. {
  108. base.material = value;
  109. }
  110. }
  111. protected UIVertex[] SetVbo(Vector2[] vertices, Vector2[] uvs)
  112. {
  113. UIVertex[] vbo = new UIVertex[4];
  114. for (int i = 0; i < vertices.Length; i++)
  115. {
  116. var vert = UIVertex.simpleVert;
  117. vert.color = color;
  118. vert.position = vertices[i];
  119. vert.uv0 = uvs[i];
  120. vbo[i] = vert;
  121. }
  122. return vbo;
  123. }
  124. protected Vector2[] IncreaseResolution(Vector2[] input)
  125. {
  126. return IncreaseResolution(new List<Vector2>(input)).ToArray();
  127. }
  128. protected List<Vector2> IncreaseResolution(List<Vector2> input)
  129. {
  130. outputList.Clear();
  131. switch (ImproveResolution)
  132. {
  133. case ResolutionMode.PerLine:
  134. float totalDistance = 0, increments = 0;
  135. for (int i = 0; i < input.Count - 1; i++)
  136. {
  137. totalDistance += Vector2.Distance(input[i], input[i + 1]);
  138. }
  139. ResolutionToNativeSize(totalDistance);
  140. increments = totalDistance / m_Resolution;
  141. var incrementCount = 0;
  142. for (int i = 0; i < input.Count - 1; i++)
  143. {
  144. var p1 = input[i];
  145. outputList.Add(p1);
  146. var p2 = input[i + 1];
  147. var segmentDistance = Vector2.Distance(p1, p2) / increments;
  148. var incrementTime = 1f / segmentDistance;
  149. for (int j = 0; j < segmentDistance; j++)
  150. {
  151. outputList.Add(Vector2.Lerp(p1, (Vector2)p2, j * incrementTime));
  152. incrementCount++;
  153. }
  154. outputList.Add(p2);
  155. }
  156. break;
  157. case ResolutionMode.PerSegment:
  158. for (int i = 0; i < input.Count - 1; i++)
  159. {
  160. var p1 = input[i];
  161. outputList.Add(p1);
  162. var p2 = input[i + 1];
  163. ResolutionToNativeSize(Vector2.Distance(p1, p2));
  164. increments = 1f / m_Resolution;
  165. for (Single j = 1; j < m_Resolution; j++)
  166. {
  167. outputList.Add(Vector2.Lerp(p1, (Vector2)p2, increments * j));
  168. }
  169. outputList.Add(p2);
  170. }
  171. break;
  172. }
  173. return outputList;
  174. }
  175. protected virtual void GeneratedUVs() { }
  176. protected virtual void ResolutionToNativeSize(float distance) { }
  177. #region ILayoutElement Interface
  178. public virtual void CalculateLayoutInputHorizontal() { }
  179. public virtual void CalculateLayoutInputVertical() { }
  180. public virtual float minWidth { get { return 0; } }
  181. public virtual float preferredWidth
  182. {
  183. get
  184. {
  185. if (overrideSprite == null)
  186. return 0;
  187. return overrideSprite.rect.size.x / pixelsPerUnit;
  188. }
  189. }
  190. public virtual float flexibleWidth { get { return -1; } }
  191. public virtual float minHeight { get { return 0; } }
  192. public virtual float preferredHeight
  193. {
  194. get
  195. {
  196. if (overrideSprite == null)
  197. return 0;
  198. return overrideSprite.rect.size.y / pixelsPerUnit;
  199. }
  200. }
  201. public virtual float flexibleHeight { get { return -1; } }
  202. public virtual int layoutPriority { get { return 0; } }
  203. #endregion
  204. #region ICanvasRaycastFilter Interface
  205. public virtual bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
  206. {
  207. // add test for line check
  208. if (m_EventAlphaThreshold >= 1)
  209. return true;
  210. Sprite sprite = overrideSprite;
  211. if (sprite == null)
  212. return true;
  213. Vector2 local;
  214. RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out local);
  215. Rect rect = GetPixelAdjustedRect();
  216. // Convert to have lower left corner as reference point.
  217. local.x += rectTransform.pivot.x * rect.width;
  218. local.y += rectTransform.pivot.y * rect.height;
  219. local = MapCoordinate(local, rect);
  220. //test local coord with Mesh
  221. // Normalize local coordinates.
  222. Rect spriteRect = sprite.textureRect;
  223. Vector2 normalized = new Vector2(local.x / spriteRect.width, local.y / spriteRect.height);
  224. // Convert to texture space.
  225. float x = Mathf.Lerp(spriteRect.x, spriteRect.xMax, normalized.x) / sprite.texture.width;
  226. float y = Mathf.Lerp(spriteRect.y, spriteRect.yMax, normalized.y) / sprite.texture.height;
  227. try
  228. {
  229. return sprite.texture.GetPixelBilinear(x, y).a >= m_EventAlphaThreshold;
  230. }
  231. catch (UnityException e)
  232. {
  233. Debug.LogError("Using clickAlphaThreshold lower than 1 on Image whose sprite texture cannot be read. " + e.Message + " Also make sure to disable sprite packing for this sprite.", this);
  234. return true;
  235. }
  236. }
  237. /// <summary>
  238. /// Return image adjusted position
  239. /// **Copied from Unity's Image component for now and simplified for UI Extensions primitives
  240. /// </summary>
  241. /// <param name="local"></param>
  242. /// <param name="rect"></param>
  243. /// <returns></returns>
  244. private Vector2 MapCoordinate(Vector2 local, Rect rect)
  245. {
  246. Rect spriteRect = sprite.rect;
  247. //if (type == Type.Simple || type == Type.Filled)
  248. return new Vector2(local.x * rect.width, local.y * rect.height);
  249. //Vector4 border = sprite.border;
  250. //Vector4 adjustedBorder = GetAdjustedBorders(border / pixelsPerUnit, rect);
  251. //for (int i = 0; i < 2; i++)
  252. //{
  253. // if (local[i] <= adjustedBorder[i])
  254. // continue;
  255. // if (rect.size[i] - local[i] <= adjustedBorder[i + 2])
  256. // {
  257. // local[i] -= (rect.size[i] - spriteRect.size[i]);
  258. // continue;
  259. // }
  260. // if (type == Type.Sliced)
  261. // {
  262. // float lerp = Mathf.InverseLerp(adjustedBorder[i], rect.size[i] - adjustedBorder[i + 2], local[i]);
  263. // local[i] = Mathf.Lerp(border[i], spriteRect.size[i] - border[i + 2], lerp);
  264. // continue;
  265. // }
  266. // else
  267. // {
  268. // local[i] -= adjustedBorder[i];
  269. // local[i] = Mathf.Repeat(local[i], spriteRect.size[i] - border[i] - border[i + 2]);
  270. // local[i] += border[i];
  271. // continue;
  272. // }
  273. //}
  274. //return local;
  275. }
  276. Vector4 GetAdjustedBorders(Vector4 border, Rect rect)
  277. {
  278. for (int axis = 0; axis <= 1; axis++)
  279. {
  280. // If the rect is smaller than the combined borders, then there's not room for the borders at their normal size.
  281. // In order to avoid artefact's with overlapping borders, we scale the borders down to fit.
  282. float combinedBorders = border[axis] + border[axis + 2];
  283. if (rect.size[axis] < combinedBorders && combinedBorders != 0)
  284. {
  285. float borderScaleRatio = rect.size[axis] / combinedBorders;
  286. border[axis] *= borderScaleRatio;
  287. border[axis + 2] *= borderScaleRatio;
  288. }
  289. }
  290. return border;
  291. }
  292. #endregion
  293. #region onEnable
  294. protected override void OnEnable()
  295. {
  296. base.OnEnable();
  297. SetAllDirty();
  298. }
  299. #endregion
  300. }
  301. }