426 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			426 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using UnityEngine;
 | |
| using UnityEvent = UnityEngine.Event;
 | |
| 
 | |
| namespace UnityEditor.U2D.Sprites
 | |
| {
 | |
|     internal class SpriteUtilityWindow : EditorWindow
 | |
|     {
 | |
|         protected class Styles
 | |
|         {
 | |
|             public readonly GUIStyle dragdot = "U2D.dragDot";
 | |
|             public readonly GUIStyle dragdotDimmed = "U2D.dragDotDimmed";
 | |
|             public readonly GUIStyle dragdotactive = "U2D.dragDotActive";
 | |
|             public readonly GUIStyle createRect = "U2D.createRect";
 | |
|             public readonly GUIStyle preToolbar = "preToolbar";
 | |
|             public readonly GUIStyle preButton = "preButton";
 | |
|             public readonly GUIStyle preLabel = "preLabel";
 | |
|             public readonly GUIStyle preSlider = "preSlider";
 | |
|             public readonly GUIStyle preSliderThumb = "preSliderThumb";
 | |
|             public readonly GUIStyle preBackground = "preBackground";
 | |
|             public readonly GUIStyle pivotdotactive = "U2D.pivotDotActive";
 | |
|             public readonly GUIStyle pivotdot = "U2D.pivotDot";
 | |
| 
 | |
|             public readonly GUIStyle dragBorderdot = new GUIStyle();
 | |
|             public readonly GUIStyle dragBorderDotActive = new GUIStyle();
 | |
| 
 | |
|             public readonly GUIStyle toolbar;
 | |
|             public readonly GUIContent alphaIcon;
 | |
|             public readonly GUIContent RGBIcon;
 | |
|             public readonly GUIStyle notice;
 | |
| 
 | |
|             public readonly GUIContent smallMip;
 | |
|             public readonly GUIContent largeMip;
 | |
| 
 | |
|             public Styles()
 | |
|             {
 | |
|                 toolbar = new GUIStyle(EditorStyles.inspectorBig);
 | |
|                 toolbar.margin.top = 0;
 | |
|                 toolbar.margin.bottom = 0;
 | |
|                 alphaIcon = EditorGUIUtility.IconContent("PreTextureAlpha");
 | |
|                 RGBIcon = EditorGUIUtility.IconContent("PreTextureRGB");
 | |
|                 preToolbar.border.top = 0;
 | |
|                 createRect.border = new RectOffset(3, 3, 3, 3);
 | |
| 
 | |
|                 notice = new GUIStyle(GUI.skin.label);
 | |
|                 notice.alignment = TextAnchor.MiddleCenter;
 | |
|                 notice.normal.textColor = Color.yellow;
 | |
| 
 | |
|                 dragBorderdot.fixedHeight = 5f;
 | |
|                 dragBorderdot.fixedWidth = 5f;
 | |
|                 dragBorderdot.normal.background = EditorGUIUtility.whiteTexture;
 | |
| 
 | |
|                 dragBorderDotActive.fixedHeight = dragBorderdot.fixedHeight;
 | |
|                 dragBorderDotActive.fixedWidth = dragBorderdot.fixedWidth;
 | |
|                 dragBorderDotActive.normal.background = EditorGUIUtility.whiteTexture;
 | |
| 
 | |
|                 smallMip = EditorGUIUtility.IconContent("PreTextureMipMapLow");
 | |
|                 largeMip = EditorGUIUtility.IconContent("PreTextureMipMapHigh");
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         protected void InitStyles()
 | |
|         {
 | |
|             if (m_Styles == null)
 | |
|                 m_Styles = new Styles();
 | |
|         }
 | |
| 
 | |
|         protected Styles m_Styles;
 | |
| 
 | |
|         protected const float k_BorderMargin = 10f;
 | |
|         protected const float k_ScrollbarMargin = 16f;
 | |
|         protected const float k_InspectorWindowMargin = 8f;
 | |
|         protected const float k_InspectorWidth = 330f;
 | |
|         protected const float k_MinZoomPercentage = 0.9f;
 | |
|         protected const float k_MaxZoom = 50f;
 | |
|         protected const float k_WheelZoomSpeed = 0.03f;
 | |
|         protected const float k_MouseZoomSpeed = 0.005f;
 | |
|         protected const float k_ToolbarHeight = 17f;
 | |
| 
 | |
|         protected ITexture2D m_Texture;
 | |
|         protected ITexture2D m_TextureAlphaOverride;
 | |
|         Rect m_TextureViewRect;
 | |
|         protected Rect m_TextureRect;
 | |
| 
 | |
|         [SerializeField]
 | |
|         protected bool m_ShowAlpha = false;
 | |
|         [SerializeField]
 | |
|         protected float m_MipLevel = 0;
 | |
|         [SerializeField]
 | |
|         protected float m_Zoom = -1f;
 | |
|         [SerializeField]
 | |
|         protected Vector2 m_ScrollPosition = new Vector2();
 | |
| 
 | |
|         public float zoomLevel
 | |
|         {
 | |
|             get { return m_Zoom; }
 | |
|             set { m_Zoom = Mathf.Clamp(value, GetMinZoom(), k_MaxZoom); }
 | |
|         }
 | |
| 
 | |
|         internal Rect textureViewRect
 | |
|         {
 | |
|             get => m_TextureViewRect;
 | |
|             set
 | |
|             {
 | |
|                 m_TextureViewRect = value;
 | |
|                 zoomLevel = m_Zoom; // update zoom level
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public Vector2 scrollPosition
 | |
|         {
 | |
|             get { return m_ScrollPosition; }
 | |
|             set
 | |
|             {
 | |
|                 if (m_Zoom < 0)
 | |
|                     m_Zoom = GetMinZoom();
 | |
| 
 | |
|                 m_ScrollPosition.x = Mathf.Clamp(value.x, maxScrollRect.xMin, maxScrollRect.xMax);
 | |
|                 m_ScrollPosition.y = Mathf.Clamp(value.y, maxScrollRect.yMin, maxScrollRect.yMax);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public bool showAlpha
 | |
|         {
 | |
|             get { return m_ShowAlpha; }
 | |
|             set { m_ShowAlpha = value; }
 | |
|         }
 | |
| 
 | |
|         public float mipLevel
 | |
|         {
 | |
|             get { return m_MipLevel; }
 | |
|             set
 | |
|             {
 | |
|                 var mipCount = 1;
 | |
|                 if (m_Texture != null)
 | |
|                     mipCount = Mathf.Max(mipCount, TextureUtil.GetMipmapCount(m_Texture));
 | |
|                 m_MipLevel = Mathf.Clamp(value, 0, mipCount - 1);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         protected float GetMinZoom()
 | |
|         {
 | |
|             if (m_Texture == null)
 | |
|                 return 1.0f;
 | |
|             // Case 654327: Add k_MaxZoom size to min check to ensure that min zoom is smaller than max zoom
 | |
|             return Mathf.Min(m_TextureViewRect.width / m_Texture.width, m_TextureViewRect.height / m_Texture.height, k_MaxZoom) * k_MinZoomPercentage;
 | |
|         }
 | |
| 
 | |
|         protected void HandleZoom()
 | |
|         {
 | |
|             bool zoomMode = UnityEvent.current.alt && UnityEvent.current.button == 1;
 | |
|             if (zoomMode)
 | |
|             {
 | |
|                 EditorGUIUtility.AddCursorRect(m_TextureViewRect, MouseCursor.Zoom);
 | |
|             }
 | |
| 
 | |
|             if (
 | |
|                 ((UnityEvent.current.type == EventType.MouseUp || UnityEvent.current.type == EventType.MouseDown) && zoomMode) ||
 | |
|                 ((UnityEvent.current.type == EventType.KeyUp || UnityEvent.current.type == EventType.KeyDown) && UnityEvent.current.keyCode == KeyCode.LeftAlt)
 | |
|             )
 | |
|             {
 | |
|                 Repaint();
 | |
|             }
 | |
| 
 | |
|             if (UnityEvent.current.type == EventType.ScrollWheel || (UnityEvent.current.type == EventType.MouseDrag && UnityEvent.current.alt && UnityEvent.current.button == 1))
 | |
|             {
 | |
|                 float zoomMultiplier = 1f - UnityEvent.current.delta.y * (UnityEvent.current.type == EventType.ScrollWheel ? k_WheelZoomSpeed : -k_MouseZoomSpeed);
 | |
| 
 | |
|                 // Clamp zoom
 | |
|                 float wantedZoom = m_Zoom * zoomMultiplier;
 | |
| 
 | |
|                 float currentZoom = Mathf.Clamp(wantedZoom, GetMinZoom(), k_MaxZoom);
 | |
| 
 | |
|                 if (currentZoom != m_Zoom)
 | |
|                 {
 | |
|                     m_Zoom = currentZoom;
 | |
| 
 | |
|                     // We need to fix zoomMultiplier if we clamped wantedZoom != currentZoom
 | |
|                     if (wantedZoom != currentZoom)
 | |
|                         zoomMultiplier /= wantedZoom / currentZoom;
 | |
| 
 | |
|                     Vector3 textureHalfSize = new Vector2(m_Texture.width, m_Texture.height) * 0.5f;
 | |
|                     Vector3 mousePositionWorld = Handles.inverseMatrix.MultiplyPoint3x4(UnityEvent.current.mousePosition + m_ScrollPosition);
 | |
|                     Vector3 delta = (mousePositionWorld - textureHalfSize) * (zoomMultiplier - 1f);
 | |
| 
 | |
|                     m_ScrollPosition += (Vector2)Handles.matrix.MultiplyVector(delta);
 | |
| 
 | |
|                     UnityEvent.current.Use();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         protected void HandlePanning()
 | |
|         {
 | |
|             // You can pan by holding ALT and using left button or NOT holding ALT and using right button. ALT + right is reserved for zooming.
 | |
|             bool panMode = (!UnityEvent.current.alt && UnityEvent.current.button > 0 || UnityEvent.current.alt && UnityEvent.current.button <= 0);
 | |
|             if (panMode && GUIUtility.hotControl == 0)
 | |
|             {
 | |
|                 EditorGUIUtility.AddCursorRect(m_TextureViewRect, MouseCursor.Pan);
 | |
| 
 | |
|                 if (UnityEvent.current.type == EventType.MouseDrag)
 | |
|                 {
 | |
|                     m_ScrollPosition -= UnityEvent.current.delta;
 | |
|                     UnityEvent.current.Use();
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             //We need to repaint when entering or exiting the pan mode, so the mouse cursor gets refreshed.
 | |
|             if (
 | |
|                 ((UnityEvent.current.type == EventType.MouseUp || UnityEvent.current.type == EventType.MouseDown) && panMode) ||
 | |
|                 (UnityEvent.current.type == EventType.KeyUp || UnityEvent.current.type == EventType.KeyDown)  && UnityEvent.current.keyCode == KeyCode.LeftAlt
 | |
|             )
 | |
|             {
 | |
|                 Repaint();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Bounding values for scrollbars. Changes with zoom, because we want min/max scroll to stop at texture edges.
 | |
|         protected Rect maxScrollRect
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 float halfWidth = m_Texture.width * .5f * m_Zoom;
 | |
|                 float halfHeight = m_Texture.height * .5f * m_Zoom;
 | |
|                 return new Rect(-halfWidth, -halfHeight, m_TextureViewRect.width + halfWidth * 2f, m_TextureViewRect.height + halfHeight * 2f);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Max rect in texture space that can ever be visible
 | |
|         protected Rect maxRect
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 float marginW = m_TextureViewRect.width * .5f / GetMinZoom();
 | |
|                 float marginH = m_TextureViewRect.height * .5f / GetMinZoom();
 | |
|                 float left = -marginW;
 | |
|                 float top = -marginH;
 | |
|                 float width = m_Texture.width + marginW * 2f;
 | |
|                 float height = m_Texture.height + marginH * 2f;
 | |
|                 return new Rect(left, top, width, height);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         protected void DrawTexturespaceBackground()
 | |
|         {
 | |
|             float size = Mathf.Max(maxRect.width, maxRect.height);
 | |
|             Vector2 offset = new Vector2(maxRect.xMin, maxRect.yMin);
 | |
| 
 | |
|             float halfSize = size * .5f;
 | |
|             float alpha = EditorGUIUtility.isProSkin ? 0.15f : 0.08f;
 | |
|             float gridSize = 8f;
 | |
| 
 | |
|             SpriteEditorUtility.BeginLines(new Color(0f, 0f, 0f, alpha));
 | |
|             for (float v = 0; v <= size; v += gridSize)
 | |
|                 SpriteEditorUtility.DrawLine(new Vector2(-halfSize + v, halfSize + v) + offset, new Vector2(halfSize + v, -halfSize + v) + offset);
 | |
|             SpriteEditorUtility.EndLines();
 | |
|         }
 | |
| 
 | |
|         private float Log2(float x)
 | |
|         {
 | |
|             return (float)(System.Math.Log(x) / System.Math.Log(2));
 | |
|         }
 | |
| 
 | |
|         protected void DrawTexture()
 | |
|         {
 | |
|             float mipLevel = Mathf.Min(m_MipLevel, TextureUtil.GetMipmapCount(m_Texture) - 1);
 | |
| 
 | |
|             if (m_ShowAlpha)
 | |
|             {
 | |
|                 // check if we have a valid alpha texture
 | |
|                 if (m_TextureAlphaOverride != null)
 | |
|                     EditorGUI.DrawTextureTransparent(m_TextureRect, m_TextureAlphaOverride, ScaleMode.StretchToFill, 0, mipLevel);
 | |
|                 // else use the original texture and display its alpha
 | |
|                 else
 | |
|                     EditorGUI.DrawTextureAlpha(m_TextureRect, m_Texture, ScaleMode.StretchToFill, 0, mipLevel);
 | |
|             }
 | |
|             else
 | |
|                 EditorGUI.DrawTextureTransparent(m_TextureRect, m_Texture, ScaleMode.StretchToFill, 0, mipLevel);
 | |
|         }
 | |
| 
 | |
|         protected void DrawScreenspaceBackground()
 | |
|         {
 | |
|             if (UnityEvent.current.type == EventType.Repaint)
 | |
|                 m_Styles.preBackground.Draw(m_TextureViewRect, false, false, false, false);
 | |
|         }
 | |
| 
 | |
|         protected void HandleScrollbars()
 | |
|         {
 | |
|             Rect horizontalScrollBarPosition = new Rect(m_TextureViewRect.xMin, m_TextureViewRect.yMax, m_TextureViewRect.width, k_ScrollbarMargin);
 | |
|             m_ScrollPosition.x = GUI.HorizontalScrollbar(horizontalScrollBarPosition, m_ScrollPosition.x, m_TextureViewRect.width, maxScrollRect.xMin, maxScrollRect.xMax);
 | |
| 
 | |
|             Rect verticalScrollBarPosition = new Rect(m_TextureViewRect.xMax, m_TextureViewRect.yMin, k_ScrollbarMargin, m_TextureViewRect.height);
 | |
|             m_ScrollPosition.y = GUI.VerticalScrollbar(verticalScrollBarPosition, m_ScrollPosition.y, m_TextureViewRect.height, maxScrollRect.yMin, maxScrollRect.yMax);
 | |
|         }
 | |
| 
 | |
|         protected void SetupHandlesMatrix()
 | |
|         {
 | |
|             // Offset from top left to center in view space
 | |
|             Vector3 handlesPos = new Vector3(m_TextureRect.x, m_TextureRect.yMax, 0f);
 | |
|             // We flip Y-scale because Unity texture space is bottom-up
 | |
|             Vector3 handlesScale = new Vector3(zoomLevel, -zoomLevel, 1f);
 | |
| 
 | |
|             // Handle matrix is for converting between view and texture space coordinates, without taking account the scroll position.
 | |
|             // Scroll position is added separately so we can use it with GUIClip.
 | |
|             Handles.matrix = Matrix4x4.TRS(handlesPos, Quaternion.identity, handlesScale);
 | |
|         }
 | |
| 
 | |
|         protected Rect DoAlphaZoomToolbarGUI(Rect area)
 | |
|         {
 | |
|             int mipCount = 1;
 | |
|             if (m_Texture != null)
 | |
|                 mipCount = Mathf.Max(mipCount, TextureUtil.GetMipmapCount(m_Texture));
 | |
| 
 | |
|             Rect drawArea = new Rect(area.width, 0, 0, area.height);
 | |
|             using (new EditorGUI.DisabledScope(mipCount == 1))
 | |
|             {
 | |
|                 drawArea.width = m_Styles.largeMip.image.width;
 | |
|                 drawArea.x -= drawArea.width;
 | |
|                 GUI.Box(drawArea, m_Styles.largeMip, m_Styles.preLabel);
 | |
| 
 | |
|                 drawArea.width = EditorGUI.kSliderMinW;
 | |
|                 drawArea.x -= drawArea.width;
 | |
|                 m_MipLevel = Mathf.Round(GUI.HorizontalSlider(drawArea, m_MipLevel, mipCount - 1, 0, m_Styles.preSlider, m_Styles.preSliderThumb));
 | |
| 
 | |
|                 drawArea.width = m_Styles.smallMip.image.width;
 | |
|                 drawArea.x -= drawArea.width;
 | |
|                 GUI.Box(drawArea, m_Styles.smallMip, m_Styles.preLabel);
 | |
|             }
 | |
| 
 | |
|             drawArea.width = EditorGUI.kSliderMinW;
 | |
|             drawArea.x -= drawArea.width;
 | |
|             zoomLevel = GUI.HorizontalSlider(drawArea, zoomLevel, GetMinZoom(), k_MaxZoom, m_Styles.preSlider, m_Styles.preSliderThumb);
 | |
| 
 | |
|             drawArea.width = EditorGUI.kObjectFieldMiniThumbnailWidth;
 | |
|             drawArea.x -= drawArea.width + EditorGUI.kSpacing;
 | |
|             m_ShowAlpha = GUI.Toggle(drawArea, m_ShowAlpha, m_ShowAlpha ? m_Styles.alphaIcon : m_Styles.RGBIcon, "toolbarButton");
 | |
| 
 | |
|             // Returns the area that is not used
 | |
|             return new Rect(area.x, area.y, drawArea.x, area.height);
 | |
|         }
 | |
| 
 | |
|         protected void DoTextureGUI()
 | |
|         {
 | |
|             if (m_Texture == null)
 | |
|                 return;
 | |
| 
 | |
|             // zoom startup init
 | |
|             if (m_Zoom < 0f)
 | |
|                 m_Zoom = GetMinZoom();
 | |
| 
 | |
|             // Texture rect in view space
 | |
|             m_TextureRect = new Rect(
 | |
|                 m_TextureViewRect.width / 2f - (m_Texture.width * m_Zoom / 2f),
 | |
|                 m_TextureViewRect.height / 2f - (m_Texture.height * m_Zoom / 2f),
 | |
|                 (m_Texture.width * m_Zoom),
 | |
|                 (m_Texture.height * m_Zoom)
 | |
|             );
 | |
| 
 | |
|             HandleScrollbars();
 | |
|             SetupHandlesMatrix();
 | |
|             DrawScreenspaceBackground();
 | |
| 
 | |
|             GUIClip.Push(m_TextureViewRect, -m_ScrollPosition, Vector2.zero, false);
 | |
| 
 | |
|             if (UnityEvent.current.type == EventType.Repaint)
 | |
|             {
 | |
|                 DrawTexturespaceBackground();
 | |
|                 DrawTexture();
 | |
|                 DrawGizmos();
 | |
|             }
 | |
| 
 | |
|             DoTextureGUIExtras();
 | |
| 
 | |
|             GUIClip.Pop();
 | |
| 
 | |
|             // Handle this after DoTextureGUIExtras in case user wants any event that is handled by Zoom or Panning
 | |
|             HandleZoom();
 | |
|             HandlePanning();
 | |
|         }
 | |
| 
 | |
|         protected virtual void DoTextureGUIExtras()
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         protected virtual void DrawGizmos()
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         protected void SetNewTexture(Texture2D texture)
 | |
|         {
 | |
|             if (texture != m_Texture)
 | |
|             {
 | |
|                 m_Texture = new Texture2DWrapper(texture);
 | |
|                 m_Zoom = -1;
 | |
|                 m_TextureAlphaOverride = null;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         protected void SetAlphaTextureOverride(Texture2D alphaTexture)
 | |
|         {
 | |
|             if (alphaTexture != m_TextureAlphaOverride)
 | |
|             {
 | |
|                 m_TextureAlphaOverride = new Texture2DWrapper(alphaTexture);
 | |
|                 m_Zoom = -1;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal override void OnResized()
 | |
|         {
 | |
|             if (m_Texture != null && UnityEvent.current != null)
 | |
|                 HandleZoom();
 | |
|             base.OnResized();
 | |
|         }
 | |
| 
 | |
|         internal static void DrawToolBarWidget(ref Rect drawRect, ref Rect toolbarRect, Action<Rect> drawAction)
 | |
|         {
 | |
|             toolbarRect.width -= drawRect.width;
 | |
|             if (toolbarRect.width < 0)
 | |
|                 drawRect.width += toolbarRect.width;
 | |
| 
 | |
|             if (drawRect.width > 0)
 | |
|                 drawAction(drawRect);
 | |
|         }
 | |
|     } // class
 | |
| }
 |