SpriteUtilityWindow.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. using System;
  2. using UnityEngine;
  3. using UnityEvent = UnityEngine.Event;
  4. namespace UnityEditor.U2D.Sprites
  5. {
  6. internal class SpriteUtilityWindow : EditorWindow
  7. {
  8. protected class Styles
  9. {
  10. public readonly GUIStyle dragdot = "U2D.dragDot";
  11. public readonly GUIStyle dragdotDimmed = "U2D.dragDotDimmed";
  12. public readonly GUIStyle dragdotactive = "U2D.dragDotActive";
  13. public readonly GUIStyle createRect = "U2D.createRect";
  14. public readonly GUIStyle preToolbar = "preToolbar";
  15. public readonly GUIStyle preButton = "preButton";
  16. public readonly GUIStyle preLabel = "preLabel";
  17. public readonly GUIStyle preSlider = "preSlider";
  18. public readonly GUIStyle preSliderThumb = "preSliderThumb";
  19. public readonly GUIStyle preBackground = "preBackground";
  20. public readonly GUIStyle pivotdotactive = "U2D.pivotDotActive";
  21. public readonly GUIStyle pivotdot = "U2D.pivotDot";
  22. public readonly GUIStyle dragBorderdot = new GUIStyle();
  23. public readonly GUIStyle dragBorderDotActive = new GUIStyle();
  24. public readonly GUIStyle toolbar;
  25. public readonly GUIContent alphaIcon;
  26. public readonly GUIContent RGBIcon;
  27. public readonly GUIStyle notice;
  28. public readonly GUIContent smallMip;
  29. public readonly GUIContent largeMip;
  30. public Styles()
  31. {
  32. toolbar = new GUIStyle(EditorStyles.inspectorBig);
  33. toolbar.margin.top = 0;
  34. toolbar.margin.bottom = 0;
  35. alphaIcon = EditorGUIUtility.IconContent("PreTextureAlpha");
  36. RGBIcon = EditorGUIUtility.IconContent("PreTextureRGB");
  37. preToolbar.border.top = 0;
  38. createRect.border = new RectOffset(3, 3, 3, 3);
  39. notice = new GUIStyle(GUI.skin.label);
  40. notice.alignment = TextAnchor.MiddleCenter;
  41. notice.normal.textColor = Color.yellow;
  42. dragBorderdot.fixedHeight = 5f;
  43. dragBorderdot.fixedWidth = 5f;
  44. dragBorderdot.normal.background = EditorGUIUtility.whiteTexture;
  45. dragBorderDotActive.fixedHeight = dragBorderdot.fixedHeight;
  46. dragBorderDotActive.fixedWidth = dragBorderdot.fixedWidth;
  47. dragBorderDotActive.normal.background = EditorGUIUtility.whiteTexture;
  48. smallMip = EditorGUIUtility.IconContent("PreTextureMipMapLow");
  49. largeMip = EditorGUIUtility.IconContent("PreTextureMipMapHigh");
  50. }
  51. }
  52. protected void InitStyles()
  53. {
  54. if (m_Styles == null)
  55. m_Styles = new Styles();
  56. }
  57. protected Styles m_Styles;
  58. protected const float k_BorderMargin = 10f;
  59. protected const float k_ScrollbarMargin = 16f;
  60. protected const float k_InspectorWindowMargin = 8f;
  61. protected const float k_InspectorWidth = 330f;
  62. protected const float k_MinZoomPercentage = 0.9f;
  63. protected const float k_MaxZoom = 50f;
  64. protected const float k_WheelZoomSpeed = 0.03f;
  65. protected const float k_MouseZoomSpeed = 0.005f;
  66. protected const float k_ToolbarHeight = 17f;
  67. protected ITexture2D m_Texture;
  68. protected ITexture2D m_TextureAlphaOverride;
  69. Rect m_TextureViewRect;
  70. protected Rect m_TextureRect;
  71. [SerializeField]
  72. protected bool m_ShowAlpha = false;
  73. [SerializeField]
  74. protected float m_MipLevel = 0;
  75. [SerializeField]
  76. protected float m_Zoom = -1f;
  77. [SerializeField]
  78. protected Vector2 m_ScrollPosition = new Vector2();
  79. public float zoomLevel
  80. {
  81. get { return m_Zoom; }
  82. set { m_Zoom = Mathf.Clamp(value, GetMinZoom(), k_MaxZoom); }
  83. }
  84. internal Rect textureViewRect
  85. {
  86. get => m_TextureViewRect;
  87. set
  88. {
  89. m_TextureViewRect = value;
  90. zoomLevel = m_Zoom; // update zoom level
  91. }
  92. }
  93. public Vector2 scrollPosition
  94. {
  95. get { return m_ScrollPosition; }
  96. set
  97. {
  98. if (m_Zoom < 0)
  99. m_Zoom = GetMinZoom();
  100. m_ScrollPosition.x = Mathf.Clamp(value.x, maxScrollRect.xMin, maxScrollRect.xMax);
  101. m_ScrollPosition.y = Mathf.Clamp(value.y, maxScrollRect.yMin, maxScrollRect.yMax);
  102. }
  103. }
  104. public bool showAlpha
  105. {
  106. get { return m_ShowAlpha; }
  107. set { m_ShowAlpha = value; }
  108. }
  109. public float mipLevel
  110. {
  111. get { return m_MipLevel; }
  112. set
  113. {
  114. var mipCount = 1;
  115. if (m_Texture != null)
  116. mipCount = Mathf.Max(mipCount, TextureUtil.GetMipmapCount(m_Texture));
  117. m_MipLevel = Mathf.Clamp(value, 0, mipCount - 1);
  118. }
  119. }
  120. protected float GetMinZoom()
  121. {
  122. if (m_Texture == null)
  123. return 1.0f;
  124. // Case 654327: Add k_MaxZoom size to min check to ensure that min zoom is smaller than max zoom
  125. return Mathf.Min(m_TextureViewRect.width / m_Texture.width, m_TextureViewRect.height / m_Texture.height, k_MaxZoom) * k_MinZoomPercentage;
  126. }
  127. protected void HandleZoom()
  128. {
  129. bool zoomMode = UnityEvent.current.alt && UnityEvent.current.button == 1;
  130. if (zoomMode)
  131. {
  132. EditorGUIUtility.AddCursorRect(m_TextureViewRect, MouseCursor.Zoom);
  133. }
  134. if (
  135. ((UnityEvent.current.type == EventType.MouseUp || UnityEvent.current.type == EventType.MouseDown) && zoomMode) ||
  136. ((UnityEvent.current.type == EventType.KeyUp || UnityEvent.current.type == EventType.KeyDown) && UnityEvent.current.keyCode == KeyCode.LeftAlt)
  137. )
  138. {
  139. Repaint();
  140. }
  141. if (UnityEvent.current.type == EventType.ScrollWheel || (UnityEvent.current.type == EventType.MouseDrag && UnityEvent.current.alt && UnityEvent.current.button == 1))
  142. {
  143. float zoomMultiplier = 1f - UnityEvent.current.delta.y * (UnityEvent.current.type == EventType.ScrollWheel ? k_WheelZoomSpeed : -k_MouseZoomSpeed);
  144. // Clamp zoom
  145. float wantedZoom = m_Zoom * zoomMultiplier;
  146. float currentZoom = Mathf.Clamp(wantedZoom, GetMinZoom(), k_MaxZoom);
  147. if (currentZoom != m_Zoom)
  148. {
  149. m_Zoom = currentZoom;
  150. // We need to fix zoomMultiplier if we clamped wantedZoom != currentZoom
  151. if (wantedZoom != currentZoom)
  152. zoomMultiplier /= wantedZoom / currentZoom;
  153. Vector3 textureHalfSize = new Vector2(m_Texture.width, m_Texture.height) * 0.5f;
  154. Vector3 mousePositionWorld = Handles.inverseMatrix.MultiplyPoint3x4(UnityEvent.current.mousePosition + m_ScrollPosition);
  155. Vector3 delta = (mousePositionWorld - textureHalfSize) * (zoomMultiplier - 1f);
  156. m_ScrollPosition += (Vector2)Handles.matrix.MultiplyVector(delta);
  157. UnityEvent.current.Use();
  158. }
  159. }
  160. }
  161. protected void HandlePanning()
  162. {
  163. // You can pan by holding ALT and using left button or NOT holding ALT and using right button. ALT + right is reserved for zooming.
  164. bool panMode = (!UnityEvent.current.alt && UnityEvent.current.button > 0 || UnityEvent.current.alt && UnityEvent.current.button <= 0);
  165. if (panMode && GUIUtility.hotControl == 0)
  166. {
  167. EditorGUIUtility.AddCursorRect(m_TextureViewRect, MouseCursor.Pan);
  168. if (UnityEvent.current.type == EventType.MouseDrag)
  169. {
  170. m_ScrollPosition -= UnityEvent.current.delta;
  171. UnityEvent.current.Use();
  172. }
  173. }
  174. //We need to repaint when entering or exiting the pan mode, so the mouse cursor gets refreshed.
  175. if (
  176. ((UnityEvent.current.type == EventType.MouseUp || UnityEvent.current.type == EventType.MouseDown) && panMode) ||
  177. (UnityEvent.current.type == EventType.KeyUp || UnityEvent.current.type == EventType.KeyDown) && UnityEvent.current.keyCode == KeyCode.LeftAlt
  178. )
  179. {
  180. Repaint();
  181. }
  182. }
  183. // Bounding values for scrollbars. Changes with zoom, because we want min/max scroll to stop at texture edges.
  184. protected Rect maxScrollRect
  185. {
  186. get
  187. {
  188. float halfWidth = m_Texture.width * .5f * m_Zoom;
  189. float halfHeight = m_Texture.height * .5f * m_Zoom;
  190. return new Rect(-halfWidth, -halfHeight, m_TextureViewRect.width + halfWidth * 2f, m_TextureViewRect.height + halfHeight * 2f);
  191. }
  192. }
  193. // Max rect in texture space that can ever be visible
  194. protected Rect maxRect
  195. {
  196. get
  197. {
  198. float marginW = m_TextureViewRect.width * .5f / GetMinZoom();
  199. float marginH = m_TextureViewRect.height * .5f / GetMinZoom();
  200. float left = -marginW;
  201. float top = -marginH;
  202. float width = m_Texture.width + marginW * 2f;
  203. float height = m_Texture.height + marginH * 2f;
  204. return new Rect(left, top, width, height);
  205. }
  206. }
  207. protected void DrawTexturespaceBackground()
  208. {
  209. float size = Mathf.Max(maxRect.width, maxRect.height);
  210. Vector2 offset = new Vector2(maxRect.xMin, maxRect.yMin);
  211. float halfSize = size * .5f;
  212. float alpha = EditorGUIUtility.isProSkin ? 0.15f : 0.08f;
  213. float gridSize = 8f;
  214. SpriteEditorUtility.BeginLines(new Color(0f, 0f, 0f, alpha));
  215. for (float v = 0; v <= size; v += gridSize)
  216. SpriteEditorUtility.DrawLine(new Vector2(-halfSize + v, halfSize + v) + offset, new Vector2(halfSize + v, -halfSize + v) + offset);
  217. SpriteEditorUtility.EndLines();
  218. }
  219. private float Log2(float x)
  220. {
  221. return (float)(System.Math.Log(x) / System.Math.Log(2));
  222. }
  223. protected void DrawTexture()
  224. {
  225. float mipLevel = Mathf.Min(m_MipLevel, TextureUtil.GetMipmapCount(m_Texture) - 1);
  226. FilterMode oldFilter = m_Texture.filterMode;
  227. TextureUtil.SetFilterModeNoDirty(m_Texture, FilterMode.Point);
  228. if (m_ShowAlpha)
  229. {
  230. // check if we have a valid alpha texture
  231. if (m_TextureAlphaOverride != null)
  232. EditorGUI.DrawTextureTransparent(m_TextureRect, m_TextureAlphaOverride, ScaleMode.StretchToFill, 0, mipLevel);
  233. // else use the original texture and display its alpha
  234. else
  235. EditorGUI.DrawTextureAlpha(m_TextureRect, m_Texture, ScaleMode.StretchToFill, 0, mipLevel);
  236. }
  237. else
  238. EditorGUI.DrawTextureTransparent(m_TextureRect, m_Texture, ScaleMode.StretchToFill, 0, mipLevel);
  239. TextureUtil.SetFilterModeNoDirty(m_Texture, oldFilter);
  240. }
  241. protected void DrawScreenspaceBackground()
  242. {
  243. if (UnityEvent.current.type == EventType.Repaint)
  244. m_Styles.preBackground.Draw(m_TextureViewRect, false, false, false, false);
  245. }
  246. protected void HandleScrollbars()
  247. {
  248. Rect horizontalScrollBarPosition = new Rect(m_TextureViewRect.xMin, m_TextureViewRect.yMax, m_TextureViewRect.width, k_ScrollbarMargin);
  249. m_ScrollPosition.x = GUI.HorizontalScrollbar(horizontalScrollBarPosition, m_ScrollPosition.x, m_TextureViewRect.width, maxScrollRect.xMin, maxScrollRect.xMax);
  250. Rect verticalScrollBarPosition = new Rect(m_TextureViewRect.xMax, m_TextureViewRect.yMin, k_ScrollbarMargin, m_TextureViewRect.height);
  251. m_ScrollPosition.y = GUI.VerticalScrollbar(verticalScrollBarPosition, m_ScrollPosition.y, m_TextureViewRect.height, maxScrollRect.yMin, maxScrollRect.yMax);
  252. }
  253. protected void SetupHandlesMatrix()
  254. {
  255. // Offset from top left to center in view space
  256. Vector3 handlesPos = new Vector3(m_TextureRect.x, m_TextureRect.yMax, 0f);
  257. // We flip Y-scale because Unity texture space is bottom-up
  258. Vector3 handlesScale = new Vector3(zoomLevel, -zoomLevel, 1f);
  259. // Handle matrix is for converting between view and texture space coordinates, without taking account the scroll position.
  260. // Scroll position is added separately so we can use it with GUIClip.
  261. Handles.matrix = Matrix4x4.TRS(handlesPos, Quaternion.identity, handlesScale);
  262. }
  263. protected Rect DoAlphaZoomToolbarGUI(Rect area)
  264. {
  265. int mipCount = 1;
  266. if (m_Texture != null)
  267. mipCount = Mathf.Max(mipCount, TextureUtil.GetMipmapCount(m_Texture));
  268. Rect drawArea = new Rect(area.width, 0, 0, area.height);
  269. using (new EditorGUI.DisabledScope(mipCount == 1))
  270. {
  271. drawArea.width = m_Styles.largeMip.image.width;
  272. drawArea.x -= drawArea.width;
  273. GUI.Box(drawArea, m_Styles.largeMip, m_Styles.preLabel);
  274. drawArea.width = EditorGUI.kSliderMinW;
  275. drawArea.x -= drawArea.width;
  276. m_MipLevel = Mathf.Round(GUI.HorizontalSlider(drawArea, m_MipLevel, mipCount - 1, 0, m_Styles.preSlider, m_Styles.preSliderThumb));
  277. drawArea.width = m_Styles.smallMip.image.width;
  278. drawArea.x -= drawArea.width;
  279. GUI.Box(drawArea, m_Styles.smallMip, m_Styles.preLabel);
  280. }
  281. drawArea.width = EditorGUI.kSliderMinW;
  282. drawArea.x -= drawArea.width;
  283. zoomLevel = GUI.HorizontalSlider(drawArea, zoomLevel, GetMinZoom(), k_MaxZoom, m_Styles.preSlider, m_Styles.preSliderThumb);
  284. drawArea.width = EditorGUI.kObjectFieldMiniThumbnailWidth;
  285. drawArea.x -= drawArea.width + EditorGUI.kSpacing;
  286. m_ShowAlpha = GUI.Toggle(drawArea, m_ShowAlpha, m_ShowAlpha ? m_Styles.alphaIcon : m_Styles.RGBIcon, "toolbarButton");
  287. // Returns the area that is not used
  288. return new Rect(area.x, area.y, drawArea.x, area.height);
  289. }
  290. protected void DoTextureGUI()
  291. {
  292. if (m_Texture == null)
  293. return;
  294. // zoom startup init
  295. if (m_Zoom < 0f)
  296. m_Zoom = GetMinZoom();
  297. // Texture rect in view space
  298. m_TextureRect = new Rect(
  299. m_TextureViewRect.width / 2f - (m_Texture.width * m_Zoom / 2f),
  300. m_TextureViewRect.height / 2f - (m_Texture.height * m_Zoom / 2f),
  301. (m_Texture.width * m_Zoom),
  302. (m_Texture.height * m_Zoom)
  303. );
  304. HandleScrollbars();
  305. SetupHandlesMatrix();
  306. DrawScreenspaceBackground();
  307. GUIClip.Push(m_TextureViewRect, -m_ScrollPosition, Vector2.zero, false);
  308. if (UnityEvent.current.type == EventType.Repaint)
  309. {
  310. DrawTexturespaceBackground();
  311. DrawTexture();
  312. DrawGizmos();
  313. }
  314. DoTextureGUIExtras();
  315. GUIClip.Pop();
  316. // Handle this after DoTextureGUIExtras in case user wants any event that is handled by Zoom or Panning
  317. HandleZoom();
  318. HandlePanning();
  319. }
  320. protected virtual void DoTextureGUIExtras()
  321. {
  322. }
  323. protected virtual void DrawGizmos()
  324. {
  325. }
  326. protected void SetNewTexture(Texture2D texture)
  327. {
  328. if (texture != m_Texture)
  329. {
  330. m_Texture = new Texture2DWrapper(texture);
  331. m_Zoom = -1;
  332. m_TextureAlphaOverride = null;
  333. }
  334. }
  335. protected void SetAlphaTextureOverride(Texture2D alphaTexture)
  336. {
  337. if (alphaTexture != m_TextureAlphaOverride)
  338. {
  339. m_TextureAlphaOverride = new Texture2DWrapper(alphaTexture);
  340. m_Zoom = -1;
  341. }
  342. }
  343. internal override void OnResized()
  344. {
  345. if (m_Texture != null && UnityEvent.current != null)
  346. HandleZoom();
  347. base.OnResized();
  348. }
  349. internal static void DrawToolBarWidget(ref Rect drawRect, ref Rect toolbarRect, Action<Rect> drawAction)
  350. {
  351. toolbarRect.width -= drawRect.width;
  352. if (toolbarRect.width < 0)
  353. drawRect.width += toolbarRect.width;
  354. if (drawRect.width > 0)
  355. drawAction(drawRect);
  356. }
  357. } // class
  358. }