You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
430 lines
17 KiB
430 lines
17 KiB
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); |
|
|
|
FilterMode oldFilter = m_Texture.filterMode; |
|
TextureUtil.SetFilterModeNoDirty(m_Texture, FilterMode.Point); |
|
|
|
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); |
|
|
|
TextureUtil.SetFilterModeNoDirty(m_Texture, oldFilter); |
|
} |
|
|
|
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 |
|
}
|
|
|