testss
@@ -0,0 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0.0] - 2020-09-22
|
||||
###Added
|
||||
- Added confirmation dialog when user pressed on Apply/Revert button on the Sprite Editor Window. This can be enabled/disabled in Preferences
|
||||
|
||||
## [1.0.0] - 2019-01-25
|
||||
###Added
|
||||
- This is the first release of Sprite Editor, as a Package
|
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("Unity.2D.Sprite.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.2D.Sprite.Package.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("Assembly-CSharp-Editor-testable")]
|
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal interface IAssetDatabase
|
||||
{
|
||||
string GetAssetPath(Object o);
|
||||
AssetImporter GetAssetImporterFromPath(string path);
|
||||
}
|
||||
|
||||
internal class AssetDatabaseSystem : IAssetDatabase
|
||||
{
|
||||
public string GetAssetPath(Object o)
|
||||
{
|
||||
return AssetDatabase.GetAssetPath(o);
|
||||
}
|
||||
|
||||
public AssetImporter GetAssetImporterFromPath(string path)
|
||||
{
|
||||
return AssetImporter.GetAtPath(path);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
using UnityEngine;
|
||||
using UnityEvent = UnityEngine.Event;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal interface IEvent
|
||||
{
|
||||
EventType type { get; }
|
||||
string commandName { get; }
|
||||
bool control { get; }
|
||||
bool alt { get; }
|
||||
bool shift { get; }
|
||||
KeyCode keyCode { get; }
|
||||
Vector2 mousePosition { get; }
|
||||
int button { get; }
|
||||
EventModifiers modifiers { get; }
|
||||
EventType GetTypeForControl(int id);
|
||||
|
||||
void Use();
|
||||
}
|
||||
|
||||
internal class Event : IEvent
|
||||
{
|
||||
UnityEvent m_Event;
|
||||
|
||||
public Event()
|
||||
{
|
||||
m_Event = UnityEvent.current;
|
||||
}
|
||||
|
||||
public EventType type
|
||||
{
|
||||
get { return m_Event.type; }
|
||||
}
|
||||
|
||||
public string commandName
|
||||
{
|
||||
get { return m_Event.commandName; }
|
||||
}
|
||||
|
||||
public bool control
|
||||
{
|
||||
get { return m_Event.control; }
|
||||
}
|
||||
|
||||
public bool alt
|
||||
{
|
||||
get { return m_Event.alt; }
|
||||
}
|
||||
|
||||
public bool shift
|
||||
{
|
||||
get { return m_Event.shift; }
|
||||
}
|
||||
|
||||
public KeyCode keyCode
|
||||
{
|
||||
get { return m_Event.keyCode; }
|
||||
}
|
||||
|
||||
public Vector2 mousePosition
|
||||
{
|
||||
get { return m_Event.mousePosition; }
|
||||
}
|
||||
|
||||
public int button
|
||||
{
|
||||
get { return m_Event.button; }
|
||||
}
|
||||
|
||||
public void Use()
|
||||
{
|
||||
m_Event.Use();
|
||||
}
|
||||
|
||||
public EventModifiers modifiers
|
||||
{
|
||||
get { return m_Event.modifiers; }
|
||||
}
|
||||
|
||||
public EventType GetTypeForControl(int id)
|
||||
{
|
||||
return m_Event.GetTypeForControl(id);
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IEventSystem
|
||||
{
|
||||
IEvent current { get; }
|
||||
}
|
||||
|
||||
internal class EventSystem : IEventSystem
|
||||
{
|
||||
public IEvent current
|
||||
{
|
||||
get { return new Event(); }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
namespace UnityEngine.U2D.Sprites
|
||||
{
|
||||
internal interface IGL
|
||||
{
|
||||
void PushMatrix();
|
||||
void PopMatrix();
|
||||
void MultMatrix(Matrix4x4 m);
|
||||
void Begin(int mode);
|
||||
void End();
|
||||
void Color(Color c);
|
||||
void Vertex(Vector3 v);
|
||||
}
|
||||
|
||||
internal class GLSystem : IGL
|
||||
{
|
||||
static IGL m_GLSystem;
|
||||
internal static void SetSystem(IGL system)
|
||||
{
|
||||
m_GLSystem = system;
|
||||
}
|
||||
|
||||
internal static IGL GetSystem()
|
||||
{
|
||||
if (m_GLSystem == null)
|
||||
m_GLSystem = new GLSystem();
|
||||
return m_GLSystem;
|
||||
}
|
||||
|
||||
public void PushMatrix()
|
||||
{
|
||||
GL.PushMatrix();
|
||||
}
|
||||
|
||||
public void PopMatrix()
|
||||
{
|
||||
GL.PopMatrix();
|
||||
}
|
||||
|
||||
public void MultMatrix(Matrix4x4 m)
|
||||
{
|
||||
GL.MultMatrix(m);
|
||||
}
|
||||
|
||||
public void Begin(int mode)
|
||||
{
|
||||
GL.Begin(mode);
|
||||
}
|
||||
|
||||
public void End()
|
||||
{
|
||||
GL.End();
|
||||
}
|
||||
|
||||
public void Color(Color c)
|
||||
{
|
||||
GL.Color(c);
|
||||
}
|
||||
|
||||
public void Vertex(Vector3 v)
|
||||
{
|
||||
GL.Vertex(v);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal interface IGUIUtility
|
||||
{
|
||||
int GetPermanentControlID();
|
||||
int hotControl { get; set; }
|
||||
int keyboardControl { get; set; }
|
||||
int GetControlID(int hint, FocusType focus);
|
||||
}
|
||||
|
||||
internal class GUIUtilitySystem : IGUIUtility
|
||||
{
|
||||
public int GetPermanentControlID()
|
||||
{
|
||||
return GUIUtility.GetPermanentControlID();
|
||||
}
|
||||
|
||||
public int hotControl
|
||||
{
|
||||
get
|
||||
{
|
||||
return GUIUtility.hotControl;
|
||||
}
|
||||
set
|
||||
{
|
||||
GUIUtility.hotControl = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int keyboardControl
|
||||
{
|
||||
get
|
||||
{
|
||||
return GUIUtility.keyboardControl;
|
||||
}
|
||||
set
|
||||
{
|
||||
GUIUtility.keyboardControl = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetControlID(int hint, FocusType focus)
|
||||
{
|
||||
return GUIUtility.GetControlID(hint, focus);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
using UnityEngine;
|
||||
using UnityHandles = UnityEditor.Handles;
|
||||
using UnityTexture2D = UnityEngine.Texture2D;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal interface IHandles
|
||||
{
|
||||
Color color { get; set; }
|
||||
Matrix4x4 matrix { get; set; }
|
||||
|
||||
Vector3[] MakeBezierPoints(Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, int division);
|
||||
|
||||
void DrawAAPolyLine(ITexture2D lineTex, float width, params Vector3[] points);
|
||||
void DrawAAPolyLine(ITexture2D lineTex, params Vector3[] points);
|
||||
|
||||
void DrawLine(Vector3 p1, Vector3 p2);
|
||||
|
||||
void SetDiscSectionPoints(Vector3[] dest, Vector3 center, Vector3 normal, Vector3 from, float angle, float radius);
|
||||
}
|
||||
|
||||
internal class HandlesSystem : IHandles
|
||||
{
|
||||
static IHandles m_System;
|
||||
|
||||
static public void SetSystem(IHandles system)
|
||||
{
|
||||
m_System = system;
|
||||
}
|
||||
|
||||
static public IHandles GetSystem()
|
||||
{
|
||||
if (m_System == null)
|
||||
m_System = new HandlesSystem();
|
||||
return m_System;
|
||||
}
|
||||
|
||||
public Color color
|
||||
{
|
||||
get { return UnityHandles.color; }
|
||||
set { UnityHandles.color = value; }
|
||||
}
|
||||
public Matrix4x4 matrix
|
||||
{
|
||||
get { return UnityHandles.matrix; }
|
||||
set { UnityHandles.matrix = value; }
|
||||
}
|
||||
|
||||
public Vector3[] MakeBezierPoints(Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, int division)
|
||||
{
|
||||
return UnityHandles.MakeBezierPoints(startPosition, endPosition, startTangent, endTangent, division);
|
||||
}
|
||||
|
||||
public void DrawAAPolyLine(ITexture2D lineTex, float width, params Vector3[] points)
|
||||
{
|
||||
UnityHandles.DrawAAPolyLine((UnityTexture2D)lineTex, width, points);
|
||||
}
|
||||
|
||||
public void DrawAAPolyLine(ITexture2D lineTex, params Vector3[] points)
|
||||
{
|
||||
UnityHandles.DrawAAPolyLine((UnityTexture2D)lineTex, points);
|
||||
}
|
||||
|
||||
public void DrawLine(Vector3 p1, Vector3 p2)
|
||||
{
|
||||
UnityHandles.DrawLine(p1, p2);
|
||||
}
|
||||
|
||||
public void SetDiscSectionPoints(Vector3[] dest, Vector3 center, Vector3 normal, Vector3 from, float angle, float radius)
|
||||
{
|
||||
UnityHandles.SetDiscSectionPoints(dest, center, normal, from, angle, radius);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.U2D;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
/// <summary>An interface that allows Sprite Editor Window to edit Sprite data for user custom importer.</summary>
|
||||
/// <remarks>Implement this interface for [[ScriptedImporter]] to leverage on Sprite Editor Window to edit Sprite data.</remarks>
|
||||
public interface ISpriteEditorDataProvider
|
||||
{
|
||||
/// <summary>SpriteImportMode to indicate how Sprite data will be imported.</summary>
|
||||
SpriteImportMode spriteImportMode { get; }
|
||||
/// <summary>The number of pixels in the sprite that correspond to one unit in world space.</summary>
|
||||
float pixelsPerUnit { get; }
|
||||
/// <summary>The object that this data provider is acquiring its data from.</summary>
|
||||
UnityObject targetObject { get; }
|
||||
/// <summary>Returns an array of SpriteRect representing Sprite data the provider has.</summary>
|
||||
/// <returns>Array of SpriteRect.</returns>
|
||||
SpriteRect[] GetSpriteRects();
|
||||
/// <summary>Sets the data provider's current SpriteRect.</summary>
|
||||
/// <param name="spriteRects">Updated array of SpriteRect.</param>
|
||||
void SetSpriteRects(SpriteRect[] spriteRects);
|
||||
/// <summary>Applying any changed data.</summary>
|
||||
void Apply();
|
||||
/// <summary>Allows the data provider to initialize any data if needed.</summary>
|
||||
void InitSpriteEditorDataProvider();
|
||||
/// <summary>Gets other data providers that might be supported by ISpriteEditorDataProvider.targetObject.</summary>
|
||||
/// <typeparam name="T">The data provider type to acquire.</typeparam>
|
||||
/// <returns>Data provider type.</returns>
|
||||
T GetDataProvider<T>() where T : class;
|
||||
/// <summary>Queries if ISpriteEditorDataProvider.targetObject supports the data provider type.</summary>
|
||||
/// <param name="type">Data provider type.</param>
|
||||
/// <returns>True if supports, false otherwise.</returns>
|
||||
bool HasDataProvider(Type type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data Provider interface that deals with Sprite Bone data.
|
||||
/// </summary>
|
||||
public interface ISpriteBoneDataProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the list of SpriteBone for the corresponding Sprite ID.
|
||||
/// </summary>
|
||||
/// <param name="guid">Sprite ID.</param>
|
||||
/// <returns>The list of SpriteBone associated with the Sprite</returns>
|
||||
List<SpriteBone> GetBones(GUID guid);
|
||||
|
||||
/// <summary>Sets a new set of SpriteBone for the corresponding Sprite ID.</summary>
|
||||
/// <param name = "guid" > Sprite ID.</param>
|
||||
/// <param name = "bones" > List of SpriteBone to associate with the Sprite.</param>
|
||||
void SetBones(GUID guid, List<SpriteBone> bones);
|
||||
}
|
||||
|
||||
/// <summary>Data provider that provides the outline data for SpriteRect.</summary>
|
||||
/// <remarks>The outline data is used to tessellate a Sprite's mesh.</remarks>
|
||||
public interface ISpriteOutlineDataProvider
|
||||
{
|
||||
/// <summary>Given a GUID, returns the outline data used for tessellating the SpriteRect.</summary>
|
||||
/// <param name="guid">GUID of the SpriteRect.</param>
|
||||
/// <returns>Outline data for theSpriteRect.</returns>
|
||||
List<Vector2[]> GetOutlines(GUID guid);
|
||||
/// <summary>Given a GUID, sets the outline data used for tessellating the SpriteRect.</summary>
|
||||
/// <param name="guid">GUID of the SpriteRect.</param>
|
||||
/// <param name="data">Outline data for theSpriteRect.</param>
|
||||
void SetOutlines(GUID guid, List<Vector2[]> data);
|
||||
/// <summary>Given a GUID, returns the tessellation detail.Tessellation value should be between 0 to 1.</summary>
|
||||
/// <param name = "guid" > GUID of the SpriteRect.</param>
|
||||
/// <returns>The tessellation value.</returns>
|
||||
float GetTessellationDetail(GUID guid);
|
||||
/// <summary>Given a GUID, sets the tessellation detail.Tessellation value should be between 0 to 1.</summary>
|
||||
/// <param name = "guid" > GUID of the SpriteRect.</param>
|
||||
/// <param name="value">The tessellation value.</param>
|
||||
void SetTessellationDetail(GUID guid, float value);
|
||||
}
|
||||
|
||||
/// <summary>Data provider that provides the Physics outline data for SpriteRect.</summary>
|
||||
/// <remarks>Uses the outline data to generate the Sprite's Physics shape for Polygon Collider 2D.</remarks>
|
||||
public interface ISpritePhysicsOutlineDataProvider
|
||||
{
|
||||
/// <summary>Given a GUID, returns the Physics outline data used for the SpriteRect.</summary>
|
||||
/// <param name="guid">GUID of the SpriteRect.</param>
|
||||
/// <returns>Physics outline data for the SpriteRect.</returns>
|
||||
List<Vector2[]> GetOutlines(GUID guid);
|
||||
/// <summary>Given a GUID, sets the Physics outline data used for the SpriteRect.</summary>
|
||||
/// <param name="guid">GUID of the SpriteRect.</param>
|
||||
/// <param name="data">Physics outline data for the SpriteRect.</param>
|
||||
void SetOutlines(GUID guid, List<Vector2[]> data);
|
||||
/// <summary>Given a GUID, returns the tessellation detail.Tessellation value should be between 0 to 1.</summary>
|
||||
/// <param name = "guid" > GUID of the SpriteRect.</param>
|
||||
/// <returns>The tessellation value.</returns>
|
||||
float GetTessellationDetail(GUID guid);
|
||||
/// <summary>Given a GUID, sets the tessellation detail.Tessellation value should be between 0 to 1.</summary>
|
||||
/// <param name = "guid" > GUID of the SpriteRect.</param>
|
||||
/// <param name="value">The tessellation value.</param>
|
||||
void SetTessellationDetail(GUID guid, float value);
|
||||
}
|
||||
|
||||
/// <summary>Data provider that provides texture data needed for Sprite Editor Window.</summary>
|
||||
public interface ITextureDataProvider
|
||||
{
|
||||
/// <summary>Texture2D representation of the data provider.</summary>
|
||||
Texture2D texture { get; }
|
||||
/// <summary>Texture2D that represents the preview for ITextureDataProvider.texture.</summary>
|
||||
Texture2D previewTexture { get; }
|
||||
/// <summary>The actual width and height of the texture data.</summary>
|
||||
/// <param name="width">Out value for width.</param>
|
||||
/// <param name="height">Out value for height.</param>
|
||||
void GetTextureActualWidthAndHeight(out int width , out int height);
|
||||
/// <summary>Readable version of ITextureProvider.texture.</summary>
|
||||
/// <returns>Texture2D that is readable.</returns>
|
||||
Texture2D GetReadableTexture2D();
|
||||
}
|
||||
|
||||
/// <summary>Data provider that provides secoondary texture data needed for Sprite Editor Window.</summary>
|
||||
public interface ISecondaryTextureDataProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Get set method for an array of SecondarySpriteTexture in the Data Provider
|
||||
/// </summary>
|
||||
SecondarySpriteTexture[] textures { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>A structure that contains meta data about vertices in a Sprite.</summary>
|
||||
[Serializable]
|
||||
public struct Vertex2DMetaData
|
||||
{
|
||||
/// <summary>The position of the vertex.</summary>
|
||||
public Vector2 position;
|
||||
/// <summary>The BoneWeight of the vertex.</summary>
|
||||
public BoneWeight boneWeight;
|
||||
}
|
||||
|
||||
/// <summary>Data Provider interface that deals with Sprite mesh data.</summary>
|
||||
public interface ISpriteMeshDataProvider
|
||||
{
|
||||
/// <summary>Returns the list of vertex datas for the corresponding Sprite ID.</summary>
|
||||
/// <param name = "guid" > Sprite ID.</param>
|
||||
Vertex2DMetaData[] GetVertices(GUID guid);
|
||||
/// <summary>Sets a new list of vertices for the corresponding Sprite ID.</summary>
|
||||
/// <param name = "guid" > Sprite ID.</param>
|
||||
void SetVertices(GUID guid, Vertex2DMetaData[] vertices);
|
||||
/// <summary>Returns the list of mesh index for the corresponding Sprite ID.</summary>
|
||||
/// <param name = "guid" > Sprite ID.</param>
|
||||
int[] GetIndices(GUID guid);
|
||||
/// <summary>Sets a new list of indices for the corresponding Sprite ID.</summary>
|
||||
/// <param name = "guid" > Sprite ID.</param>
|
||||
void SetIndices(GUID guid, int[] indices);
|
||||
/// <summary>Returns the list of mesh edges for the corresponding Sprite ID.</summary>
|
||||
/// <param name = "guid" > Sprite ID.</param>
|
||||
Vector2Int[] GetEdges(GUID guid);
|
||||
/// <summary>Sets a new list of edges for the corresponding Sprite ID.</summary>
|
||||
/// <param name = "guid" > Sprite ID.</param>
|
||||
void SetEdges(GUID guid, Vector2Int[] edges);
|
||||
}
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
using UnityEngine;
|
||||
using UnityTexture2D = UnityEngine.Texture2D;
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal abstract class ITexture2D
|
||||
{
|
||||
public abstract int width { get; }
|
||||
public abstract int height { get; }
|
||||
public abstract TextureFormat format { get; }
|
||||
public abstract Color32[] GetPixels32();
|
||||
public abstract FilterMode filterMode { get; set; }
|
||||
public abstract string name { get; }
|
||||
public abstract void SetPixels(Color[] c);
|
||||
public abstract void Apply();
|
||||
public abstract float mipMapBias { get; }
|
||||
|
||||
public static bool operator==(ITexture2D t1, ITexture2D t2)
|
||||
{
|
||||
if (object.ReferenceEquals(t1, null))
|
||||
{
|
||||
return object.ReferenceEquals(t2, null) || t2 == null;
|
||||
}
|
||||
|
||||
return t1.Equals(t2);
|
||||
}
|
||||
|
||||
public static bool operator!=(ITexture2D t1, ITexture2D t2)
|
||||
{
|
||||
if (object.ReferenceEquals(t1, null))
|
||||
{
|
||||
return !object.ReferenceEquals(t2, null) && t2 != null;
|
||||
}
|
||||
|
||||
return !t1.Equals(t2);
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static implicit operator UnityEngine.Object(ITexture2D t)
|
||||
{
|
||||
return object.ReferenceEquals(t, null) ? null : t.ToUnityObject();
|
||||
}
|
||||
|
||||
public static implicit operator UnityEngine.Texture2D(ITexture2D t)
|
||||
{
|
||||
return object.ReferenceEquals(t, null) ? null : t.ToUnityTexture();
|
||||
}
|
||||
|
||||
protected abstract UnityEngine.Object ToUnityObject();
|
||||
protected abstract UnityEngine.Texture2D ToUnityTexture();
|
||||
}
|
||||
|
||||
internal class Texture2DWrapper : ITexture2D
|
||||
{
|
||||
UnityTexture2D m_Texture;
|
||||
|
||||
public Texture2DWrapper(UnityTexture2D texture)
|
||||
{
|
||||
m_Texture = texture;
|
||||
}
|
||||
|
||||
public override int width
|
||||
{
|
||||
get { return m_Texture.width; }
|
||||
}
|
||||
|
||||
public override int height
|
||||
{
|
||||
get { return m_Texture.height; }
|
||||
}
|
||||
|
||||
public override TextureFormat format
|
||||
{
|
||||
get { return m_Texture.format; }
|
||||
}
|
||||
|
||||
public override Color32[] GetPixels32()
|
||||
{
|
||||
return m_Texture.GetPixels32();
|
||||
}
|
||||
|
||||
public override FilterMode filterMode
|
||||
{
|
||||
get { return m_Texture.filterMode; }
|
||||
set { m_Texture.filterMode = value; }
|
||||
}
|
||||
|
||||
public override float mipMapBias
|
||||
{
|
||||
get { return m_Texture.mipMapBias; }
|
||||
}
|
||||
|
||||
public override string name
|
||||
{
|
||||
get { return m_Texture.name; }
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
Texture2DWrapper t = other as Texture2DWrapper;
|
||||
if (object.ReferenceEquals(t, null))
|
||||
return m_Texture == null;
|
||||
return m_Texture == t.m_Texture;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_Texture.GetHashCode();
|
||||
}
|
||||
|
||||
public override void SetPixels(Color[] c)
|
||||
{
|
||||
m_Texture.SetPixels(c);
|
||||
}
|
||||
|
||||
public override void Apply()
|
||||
{
|
||||
m_Texture.Apply();
|
||||
}
|
||||
|
||||
protected override UnityEngine.Object ToUnityObject()
|
||||
{
|
||||
return m_Texture;
|
||||
}
|
||||
|
||||
protected override UnityEngine.Texture2D ToUnityTexture()
|
||||
{
|
||||
return m_Texture;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal interface IUndoSystem
|
||||
{
|
||||
void RegisterUndoCallback(Undo.UndoRedoCallback undoCallback);
|
||||
void UnregisterUndoCallback(Undo.UndoRedoCallback undoCallback);
|
||||
void RegisterCompleteObjectUndo(ScriptableObject obj, string undoText);
|
||||
void ClearUndo(ScriptableObject obj);
|
||||
}
|
||||
|
||||
internal class UndoSystem : IUndoSystem
|
||||
{
|
||||
public void RegisterUndoCallback(Undo.UndoRedoCallback undoCallback)
|
||||
{
|
||||
Undo.undoRedoPerformed += undoCallback;
|
||||
}
|
||||
|
||||
public void UnregisterUndoCallback(Undo.UndoRedoCallback undoCallback)
|
||||
{
|
||||
Undo.undoRedoPerformed -= undoCallback;
|
||||
}
|
||||
|
||||
public void RegisterCompleteObjectUndo(ScriptableObject so, string undoText)
|
||||
{
|
||||
if (so != null)
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(so, undoText);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearUndo(ScriptableObject so)
|
||||
{
|
||||
if (so != null)
|
||||
{
|
||||
Undo.ClearUndo(so);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 879 B |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D
|
||||
{
|
||||
static internal class ItemCreationUtility
|
||||
{
|
||||
static internal Action<int, ProjectWindowCallback.EndNameEditAction, string, Texture2D, string> StartNewAssetNameEditingDelegate = ProjectWindowUtil.StartNameEditingIfProjectWindowExists;
|
||||
|
||||
static public GameObject CreateGameObject(string name, MenuCommand menuCommand, params Type[] components)
|
||||
{
|
||||
var parent = menuCommand.context as GameObject;
|
||||
var newGO = ObjectFactory.CreateGameObject(name, components);
|
||||
newGO.name = name;
|
||||
Selection.activeObject = newGO;
|
||||
GOCreationCommands.Place(newGO, parent);
|
||||
if (EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D)
|
||||
{
|
||||
var position = newGO.transform.position;
|
||||
position.z = 0;
|
||||
newGO.transform.position = position;
|
||||
}
|
||||
Undo.RegisterCreatedObjectUndo(newGO, string.Format("Create {0}", name));
|
||||
return newGO;
|
||||
}
|
||||
|
||||
static public T CreateAssetObjectFromTemplate<T>(string sourcePath) where T : UnityEngine.Object
|
||||
{
|
||||
var assetSelectionPath = AssetDatabase.GetAssetPath(Selection.activeObject);
|
||||
var isFolder = false;
|
||||
if (!string.IsNullOrEmpty(assetSelectionPath))
|
||||
isFolder = File.GetAttributes(assetSelectionPath).HasFlag(FileAttributes.Directory);
|
||||
var path = ProjectWindowUtil.GetActiveFolderPath();
|
||||
if (isFolder)
|
||||
{
|
||||
path = assetSelectionPath;
|
||||
}
|
||||
var fileName = Path.GetFileName(sourcePath);
|
||||
var destName = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(path, fileName));
|
||||
var icon = EditorGUIUtility.IconContent<T>().image as Texture2D;
|
||||
StartNewAssetNameEditing(sourcePath, destName, icon, ProjectBrowser.kAssetCreationInstanceID_ForNonExistingAssets);
|
||||
return Selection.activeObject as T;
|
||||
}
|
||||
|
||||
static public T CreateAssetObject<T>(string name) where T : UnityEngine.Object
|
||||
{
|
||||
var assetSelectionPath = AssetDatabase.GetAssetPath(Selection.activeObject);
|
||||
var isFolder = false;
|
||||
if (!string.IsNullOrEmpty(assetSelectionPath))
|
||||
isFolder = File.GetAttributes(assetSelectionPath).HasFlag(FileAttributes.Directory);
|
||||
var path = ProjectWindowUtil.GetActiveFolderPath();
|
||||
if (isFolder)
|
||||
{
|
||||
path = assetSelectionPath;
|
||||
}
|
||||
var destName = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(path, name));
|
||||
var newObject = Activator.CreateInstance<T>();
|
||||
var icon = EditorGUIUtility.IconContent<T>().image as Texture2D;
|
||||
StartNewAssetNameEditing(null, destName, icon, newObject.GetInstanceID());
|
||||
return Selection.activeObject as T;
|
||||
}
|
||||
|
||||
static private void StartNewAssetNameEditing(string source, string dest, Texture2D icon, int instanceId)
|
||||
{
|
||||
var action = ScriptableObject.CreateInstance<CreateAssetEndNameEditAction>();
|
||||
StartNewAssetNameEditingDelegate(instanceId, action, dest, icon, source);
|
||||
}
|
||||
|
||||
internal class CreateAssetEndNameEditAction : ProjectWindowCallback.EndNameEditAction
|
||||
{
|
||||
public override void Action(int instanceId, string pathName, string resourceFile)
|
||||
{
|
||||
var uniqueName = AssetDatabase.GenerateUniqueAssetPath(pathName);
|
||||
if (instanceId == ProjectBrowser.kAssetCreationInstanceID_ForNonExistingAssets && !string.IsNullOrEmpty(resourceFile))
|
||||
{
|
||||
AssetDatabase.CopyAsset(resourceFile, uniqueName);
|
||||
instanceId = AssetDatabase.LoadMainAssetAtPath(uniqueName).GetInstanceID();
|
||||
}
|
||||
else
|
||||
{
|
||||
var obj = EditorUtility.InstanceIDToObject(instanceId);
|
||||
AssetDatabase.CreateAsset(obj, uniqueName);
|
||||
}
|
||||
ProjectWindowUtil.FrameObjectInProjectWindow(instanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,176 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.U2D;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.U2D
|
||||
{
|
||||
static internal class MenuItems
|
||||
{
|
||||
const int k_SpriteAssetMenuPriority = 1;
|
||||
const int k_SpriteAtlasAssetMenuPriority = k_SpriteAssetMenuPriority + 11;
|
||||
|
||||
const int k_SpriteGameObjectMenuPriority = 1;
|
||||
const int k_PhysicsGameObjectMenuPriority = 2;
|
||||
const int k_SpriteMaskGameObjectMenuPriority = 6;
|
||||
|
||||
[MenuItem("Assets/Create/2D/Sprites/Square", priority = k_SpriteAssetMenuPriority)]
|
||||
static void AssetsCreateSpritesSquare(MenuCommand menuCommand)
|
||||
{
|
||||
ItemCreationUtility.CreateAssetObjectFromTemplate<Texture2D>("Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/Square.png");
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/2D/Sprites/Circle", priority = k_SpriteAssetMenuPriority)]
|
||||
static void AssetsCreateSpritesCircle(MenuCommand menuCommand)
|
||||
{
|
||||
ItemCreationUtility.CreateAssetObjectFromTemplate<Texture2D>("Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/Circle.png");
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/2D/Sprites/Capsule", priority = k_SpriteAssetMenuPriority)]
|
||||
static void AssetsCreateSpritesCapsule(MenuCommand menuCommand)
|
||||
{
|
||||
ItemCreationUtility.CreateAssetObjectFromTemplate<Texture2D>("Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/Capsule.png");
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/2D/Sprites/Isometric Diamond", priority = k_SpriteAssetMenuPriority)]
|
||||
static void AssetsCreateSpritesIsometricDiamond(MenuCommand menuCommand)
|
||||
{
|
||||
ItemCreationUtility.CreateAssetObjectFromTemplate<Texture2D>("Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/IsometricDiamond.png");
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/2D/Sprites/Hexagon Flat-Top", priority = k_SpriteAssetMenuPriority)]
|
||||
static void AssetsCreateSpritesHexagonFlatTop(MenuCommand menuCommand)
|
||||
{
|
||||
ItemCreationUtility.CreateAssetObjectFromTemplate<Texture2D>("Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/HexagonFlatTop.png");
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/2D/Sprites/Hexagon Pointed-Top", priority = k_SpriteAssetMenuPriority)]
|
||||
static void AssetsCreateSpritesHexagonPointedTop(MenuCommand menuCommand)
|
||||
{
|
||||
ItemCreationUtility.CreateAssetObjectFromTemplate<Texture2D>("Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/HexagonPointedTop.png");
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/2D/Sprites/9-Sliced", priority = k_SpriteAssetMenuPriority)]
|
||||
static void AssetsCreateSprites9Sliced(MenuCommand menuCommand)
|
||||
{
|
||||
ItemCreationUtility.CreateAssetObjectFromTemplate<Texture2D>("Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/9Sliced.png");
|
||||
}
|
||||
|
||||
internal class DoCreateSpriteAtlas : ProjectWindowCallback.EndNameEditAction
|
||||
{
|
||||
public int sides;
|
||||
public override void Action(int instanceId, string pathName, string resourceFile)
|
||||
{
|
||||
var spriteAtlasAsset = new SpriteAtlasAsset();
|
||||
|
||||
UnityEditorInternal.InternalEditorUtility.SaveToSerializedFileAndForget(new Object[] { spriteAtlasAsset }, pathName, true);
|
||||
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
static private void CreateSpriteAtlas()
|
||||
{
|
||||
var icon = EditorGUIUtility.IconContent<SpriteAtlasAsset>().image as Texture2D;
|
||||
DoCreateSpriteAtlas action = ScriptableObject.CreateInstance<DoCreateSpriteAtlas>();
|
||||
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, action, "New Sprite Atlas.spriteatlasv2", icon, null);
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/2D/Sprite Atlas", priority = k_SpriteAtlasAssetMenuPriority)]
|
||||
static void AssetsCreateSpriteAtlas(MenuCommand menuCommand)
|
||||
{
|
||||
if (EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2)
|
||||
CreateSpriteAtlas();
|
||||
else
|
||||
ItemCreationUtility.CreateAssetObject<SpriteAtlas>("New Sprite Atlas.spriteatlas");
|
||||
}
|
||||
|
||||
static GameObject CreateSpriteRendererGameObject(string name, string spritePath, MenuCommand menuCommand)
|
||||
{
|
||||
var go = ItemCreationUtility.CreateGameObject(name, menuCommand, new[] {typeof(SpriteRenderer)});
|
||||
var sr = go.GetComponent<SpriteRenderer>();
|
||||
sr.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(spritePath);
|
||||
return go;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/2D Object/Sprites/Square", priority = k_SpriteGameObjectMenuPriority)]
|
||||
static void GameObjectCreateSpritesSquare(MenuCommand menuCommand)
|
||||
{
|
||||
CreateSpriteRendererGameObject("Square", "Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/Square.png", menuCommand);
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/2D Object/Sprites/Circle", priority = k_SpriteGameObjectMenuPriority)]
|
||||
static void GameObjectCreateSpritesCircle(MenuCommand menuCommand)
|
||||
{
|
||||
CreateSpriteRendererGameObject("Circle", "Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/Circle.png", menuCommand);
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/2D Object/Sprites/Capsule", priority = k_SpriteGameObjectMenuPriority)]
|
||||
static void GameObjectCreateSpritesCapsule(MenuCommand menuCommand)
|
||||
{
|
||||
CreateSpriteRendererGameObject("Capsule", "Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/Capsule.png", menuCommand);
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/2D Object/Sprites/Isometric Diamond", priority = k_SpriteGameObjectMenuPriority)]
|
||||
static void GameObjectCreateSpritesIsometricDiamond(MenuCommand menuCommand)
|
||||
{
|
||||
CreateSpriteRendererGameObject("Isometric Diamond", "Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/IsometricDiamond.png", menuCommand);
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/2D Object/Sprites/Hexagon Flat-Top", priority = k_SpriteGameObjectMenuPriority)]
|
||||
static void GameObjectCreateSpritesHexagonFlatTop(MenuCommand menuCommand)
|
||||
{
|
||||
CreateSpriteRendererGameObject("Hexagon Flat-Top", "Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/HexagonFlatTop.png", menuCommand);
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/2D Object/Sprites/Hexagon Pointed-Top", priority = k_SpriteGameObjectMenuPriority)]
|
||||
static void GameObjectCreateSpritesHexagonPointedTop(MenuCommand menuCommand)
|
||||
{
|
||||
CreateSpriteRendererGameObject("Hexagon Pointed-Top", "Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/HexagonPointedTop.png", menuCommand);
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/2D Object/Sprites/9-Sliced", priority = k_SpriteGameObjectMenuPriority)]
|
||||
static void GameObjectCreateSprites9Sliced(MenuCommand menuCommand)
|
||||
{
|
||||
var go = CreateSpriteRendererGameObject("9-Sliced", "Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/9Sliced.png", menuCommand);
|
||||
var sr = go.GetComponent<SpriteRenderer>();
|
||||
if (sr.drawMode == SpriteDrawMode.Simple)
|
||||
{
|
||||
sr.drawMode = SpriteDrawMode.Tiled;
|
||||
sr.tileMode = SpriteTileMode.Continuous;
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/2D Object/Physics/Static Sprite", priority = k_PhysicsGameObjectMenuPriority)]
|
||||
static void GameObjectCreatePhysicsStaticSprite(MenuCommand menuCommand)
|
||||
{
|
||||
var go = ItemCreationUtility.CreateGameObject("Static Sprite", menuCommand, new[] {typeof(SpriteRenderer), typeof(BoxCollider2D), typeof(Rigidbody2D)});
|
||||
var sr = go.GetComponent<SpriteRenderer>();
|
||||
if (sr.sprite == null)
|
||||
sr.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(
|
||||
"Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/Square.png");
|
||||
var rigidBody = go.GetComponent<Rigidbody2D>();
|
||||
rigidBody.bodyType = RigidbodyType2D.Static;
|
||||
var boxCollider2D = go.GetComponent<BoxCollider2D>();
|
||||
boxCollider2D.size = sr.sprite.rect.size / sr.sprite.pixelsPerUnit;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/2D Object/Physics/Dynamic Sprite", priority = k_PhysicsGameObjectMenuPriority)]
|
||||
static void GameObjectCreatePhysicsDynamicSprite(MenuCommand menuCommand)
|
||||
{
|
||||
var go = ItemCreationUtility.CreateGameObject("Dynamic Sprite", menuCommand, new[] {typeof(SpriteRenderer), typeof(CircleCollider2D), typeof(Rigidbody2D)});
|
||||
var sr = go.GetComponent<SpriteRenderer>();
|
||||
if (sr.sprite == null)
|
||||
sr.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(
|
||||
"Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/v2/Circle.png");
|
||||
var rigidBody = go.GetComponent<Rigidbody2D>();
|
||||
rigidBody.bodyType = RigidbodyType2D.Dynamic;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/2D Object/Sprite Mask", priority = k_SpriteMaskGameObjectMenuPriority)]
|
||||
static void GameObjectCreateSpriteMask(MenuCommand menuCommand)
|
||||
{
|
||||
var go = ItemCreationUtility.CreateGameObject("Sprite Mask", menuCommand, new[] {typeof(SpriteMask)});
|
||||
go.GetComponent<SpriteMask>().sprite = AssetDatabase.LoadAssetAtPath<Sprite>(
|
||||
"Packages/com.unity.2d.sprite/Editor/ObjectMenuCreation/DefaultAssets/Textures/CircleMask.png");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor.Sprites;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites.Obsolete
|
||||
{
|
||||
public static class ObsoleteSupport
|
||||
{
|
||||
[Obsolete("Sprite Packer is no longer supported. Consider switching to the new Sprite Altas System")]
|
||||
public static void ShowSpritePacker()
|
||||
{
|
||||
EditorWindow.GetWindow<PackerWindow>();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,230 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEvent = UnityEngine.Event;
|
||||
using SelectionType = UnityEditor.U2D.Sprites.ShapeEditor.SelectionType;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal class ShapeEditorRectSelectionTool
|
||||
{
|
||||
Vector2 m_SelectStartPoint;
|
||||
Vector2 m_SelectMousePoint;
|
||||
bool m_RectSelecting;
|
||||
int m_RectSelectionID;
|
||||
const float k_MinSelectionSize = 6f;
|
||||
|
||||
public event Action<Rect, SelectionType> RectSelect = (i, p) => {};
|
||||
public event Action ClearSelection = () => {};
|
||||
|
||||
public ShapeEditorRectSelectionTool(IGUIUtility gu)
|
||||
{
|
||||
guiUtility = gu;
|
||||
m_RectSelectionID = guiUtility.GetPermanentControlID();
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
var evt = UnityEvent.current;
|
||||
|
||||
Handles.BeginGUI();
|
||||
|
||||
Vector2 mousePos = evt.mousePosition;
|
||||
int id = m_RectSelectionID;
|
||||
|
||||
switch (evt.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.Layout:
|
||||
case EventType.MouseMove:
|
||||
if (!Tools.viewToolActive)
|
||||
HandleUtility.AddDefaultControl(id);
|
||||
break;
|
||||
|
||||
case EventType.MouseDown:
|
||||
if (HandleUtility.nearestControl == id && evt.button == 0)
|
||||
{
|
||||
guiUtility.hotControl = id;
|
||||
m_SelectStartPoint = mousePos;
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
if (guiUtility.hotControl == id)
|
||||
{
|
||||
if (!m_RectSelecting && (mousePos - m_SelectStartPoint).magnitude > k_MinSelectionSize)
|
||||
{
|
||||
m_RectSelecting = true;
|
||||
}
|
||||
if (m_RectSelecting)
|
||||
{
|
||||
m_SelectMousePoint = mousePos;
|
||||
|
||||
SelectionType type = SelectionType.Normal;
|
||||
if (UnityEvent.current.control)
|
||||
type = SelectionType.Subtractive;
|
||||
else if (UnityEvent.current.shift)
|
||||
type = SelectionType.Additive;
|
||||
RectSelect(EditorGUIExt.FromToRect(m_SelectStartPoint, m_SelectMousePoint), type);
|
||||
}
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.Repaint:
|
||||
if (guiUtility.hotControl == id && m_RectSelecting)
|
||||
{
|
||||
EditorStyles.selectionRect.Draw(EditorGUIExt.FromToRect(m_SelectStartPoint, m_SelectMousePoint), GUIContent.none,
|
||||
false, false, false, false);
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseUp:
|
||||
if (guiUtility.hotControl == id && evt.button == 0)
|
||||
{
|
||||
guiUtility.hotControl = 0;
|
||||
guiUtility.keyboardControl = 0;
|
||||
if (m_RectSelecting)
|
||||
{
|
||||
m_SelectMousePoint = new Vector2(mousePos.x, mousePos.y);
|
||||
|
||||
SelectionType type = SelectionType.Normal;
|
||||
if (UnityEvent.current.control)
|
||||
type = SelectionType.Subtractive;
|
||||
else if (UnityEvent.current.shift)
|
||||
type = SelectionType.Additive;
|
||||
|
||||
RectSelect(EditorGUIExt.FromToRect(m_SelectStartPoint, m_SelectMousePoint), type);
|
||||
|
||||
m_RectSelecting = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearSelection();
|
||||
}
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Handles.EndGUI();
|
||||
}
|
||||
|
||||
public bool isSelecting
|
||||
{
|
||||
get { return guiUtility.hotControl == m_RectSelectionID; }
|
||||
}
|
||||
|
||||
IGUIUtility guiUtility
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: For now we copy-paste from RectSelection. Refactor to avoid duplicate codes.
|
||||
internal class ShapeEditorSelection : IEnumerable<int>
|
||||
{
|
||||
HashSet<int> m_SelectedPoints = new HashSet<int>();
|
||||
ShapeEditor m_ShapeEditor;
|
||||
|
||||
public ShapeEditorSelection(ShapeEditor owner)
|
||||
{
|
||||
m_ShapeEditor = owner;
|
||||
}
|
||||
|
||||
public bool Contains(int i)
|
||||
{
|
||||
return m_SelectedPoints.Contains(i);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_SelectedPoints.Count; }
|
||||
}
|
||||
|
||||
public void DeleteSelection()
|
||||
{
|
||||
var sorted = m_SelectedPoints.OrderByDescending(x => x);
|
||||
foreach (int selectedIndex in sorted)
|
||||
{
|
||||
m_ShapeEditor.RemovePointAt(selectedIndex);
|
||||
}
|
||||
if (m_ShapeEditor.activePoint >= m_ShapeEditor.GetPointsCount())
|
||||
m_ShapeEditor.activePoint = m_ShapeEditor.GetPointsCount() - 1;
|
||||
m_SelectedPoints.Clear();
|
||||
}
|
||||
|
||||
public void MoveSelection(Vector3 delta)
|
||||
{
|
||||
if (delta.sqrMagnitude < float.Epsilon)
|
||||
return;
|
||||
|
||||
foreach (int selectedIndex in m_SelectedPoints)
|
||||
{
|
||||
m_ShapeEditor.SetPointPosition(selectedIndex, m_ShapeEditor.GetPointPosition(selectedIndex) + delta);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_SelectedPoints.Clear();
|
||||
if (m_ShapeEditor != null)
|
||||
m_ShapeEditor.activePoint = -1;
|
||||
}
|
||||
|
||||
public void SelectPoint(int i, SelectionType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SelectionType.Additive:
|
||||
m_ShapeEditor.activePoint = i;
|
||||
m_SelectedPoints.Add(i);
|
||||
break;
|
||||
case SelectionType.Subtractive:
|
||||
m_ShapeEditor.activePoint = i > 0 ? i - 1 : 0;
|
||||
m_SelectedPoints.Remove(i);
|
||||
break;
|
||||
case SelectionType.Normal:
|
||||
m_SelectedPoints.Clear();
|
||||
m_ShapeEditor.activePoint = i;
|
||||
m_SelectedPoints.Add(i);
|
||||
break;
|
||||
default:
|
||||
m_ShapeEditor.activePoint = i; break;
|
||||
}
|
||||
m_ShapeEditor.Repaint();
|
||||
}
|
||||
|
||||
public void RectSelect(Rect rect, SelectionType type)
|
||||
{
|
||||
if (type == SelectionType.Normal)
|
||||
{
|
||||
m_SelectedPoints.Clear();
|
||||
m_ShapeEditor.activePoint = -1;
|
||||
type = SelectionType.Additive;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_ShapeEditor.GetPointsCount(); i++)
|
||||
{
|
||||
var p0 = m_ShapeEditor.GetPointPosition(i);
|
||||
if (rect.Contains(p0))
|
||||
{
|
||||
SelectPoint(i, type);
|
||||
}
|
||||
}
|
||||
m_ShapeEditor.Repaint();
|
||||
}
|
||||
|
||||
public HashSet<int> indices { get { return m_SelectedPoints; } }
|
||||
|
||||
public IEnumerator<int> GetEnumerator()
|
||||
{
|
||||
return m_SelectedPoints.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
} // namespace
|
@@ -0,0 +1,360 @@
|
||||
using UnityEngine;
|
||||
using UnityEvent = UnityEngine.Event;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal static class SpriteEditorHandles
|
||||
{
|
||||
private static Vector2 s_CurrentMousePosition;
|
||||
private static Vector2 s_DragStartScreenPosition;
|
||||
private static Vector2 s_DragScreenOffset;
|
||||
|
||||
static internal Vector2 PointSlider(Vector2 pos, MouseCursor cursor, GUIStyle dragDot, GUIStyle dragDotActive)
|
||||
{
|
||||
int id = GUIUtility.GetControlID("Slider1D".GetHashCode(), FocusType.Keyboard);
|
||||
Vector2 screenVal = Handles.matrix.MultiplyPoint(pos);
|
||||
Rect handleScreenPos = new Rect(
|
||||
screenVal.x - dragDot.fixedWidth * .5f,
|
||||
screenVal.y - dragDot.fixedHeight * .5f,
|
||||
dragDot.fixedWidth,
|
||||
dragDot.fixedHeight
|
||||
);
|
||||
|
||||
var evt = UnityEvent.current;
|
||||
switch (evt.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.Repaint:
|
||||
if (GUIUtility.hotControl == id)
|
||||
dragDotActive.Draw(handleScreenPos, GUIContent.none, id);
|
||||
else
|
||||
dragDot.Draw(handleScreenPos, GUIContent.none, id);
|
||||
break;
|
||||
}
|
||||
return ScaleSlider(pos, cursor, handleScreenPos);
|
||||
}
|
||||
|
||||
static internal Vector2 ScaleSlider(Vector2 pos, MouseCursor cursor, Rect cursorRect)
|
||||
{
|
||||
int id = GUIUtility.GetControlID("Slider1D".GetHashCode(), FocusType.Keyboard);
|
||||
return ScaleSlider(id, pos, cursor, cursorRect);
|
||||
}
|
||||
|
||||
static private Vector2 ScaleSlider(int id, Vector2 pos, MouseCursor cursor, Rect cursorRect)
|
||||
{
|
||||
Vector2 screenVal = Handles.matrix.MultiplyPoint(pos);
|
||||
|
||||
var evt = UnityEvent.current;
|
||||
switch (evt.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
// am I closest to the thingy?
|
||||
if (evt.button == 0 &&
|
||||
cursorRect.Contains(UnityEvent.current.mousePosition) &&
|
||||
!evt.alt)
|
||||
{
|
||||
GUIUtility.hotControl = GUIUtility.keyboardControl = id; // Grab mouse focus
|
||||
s_CurrentMousePosition = evt.mousePosition;
|
||||
s_DragStartScreenPosition = evt.mousePosition;
|
||||
s_DragScreenOffset = s_CurrentMousePosition - screenVal;
|
||||
evt.Use();
|
||||
EditorGUIUtility.SetWantsMouseJumping(1);
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
s_CurrentMousePosition += evt.delta;
|
||||
Vector2 oldPos = pos;
|
||||
pos = Handles.inverseMatrix.MultiplyPoint(s_CurrentMousePosition);
|
||||
if (!Mathf.Approximately((oldPos - pos).magnitude, 0f))
|
||||
GUI.changed = true;
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == id && (evt.button == 0 || evt.button == 2))
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
EditorGUIUtility.SetWantsMouseJumping(0);
|
||||
}
|
||||
break;
|
||||
case EventType.KeyDown:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Escape)
|
||||
{
|
||||
pos = Handles.inverseMatrix.MultiplyPoint(s_DragStartScreenPosition - s_DragScreenOffset);
|
||||
GUIUtility.hotControl = 0;
|
||||
GUI.changed = true;
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.Repaint:
|
||||
if (GUIUtility.hotControl == 0 || GUIUtility.hotControl == id)
|
||||
EditorGUIUtility.AddCursorRect(cursorRect, cursor, id);
|
||||
break;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
static internal Vector2 PivotSlider(Rect sprite, Vector2 pos, GUIStyle pivotDot, GUIStyle pivotDotActive)
|
||||
{
|
||||
int id = GUIUtility.GetControlID("Slider1D".GetHashCode(), FocusType.Keyboard);
|
||||
|
||||
// Convert from normalized space to texture space
|
||||
pos = new Vector2(sprite.xMin + sprite.width * pos.x, sprite.yMin + sprite.height * pos.y);
|
||||
|
||||
Vector2 screenVal = Handles.matrix.MultiplyPoint(pos);
|
||||
|
||||
Rect handleScreenPos = new Rect(
|
||||
screenVal.x - pivotDot.fixedWidth * .5f,
|
||||
screenVal.y - pivotDot.fixedHeight * .5f,
|
||||
pivotDotActive.fixedWidth,
|
||||
pivotDotActive.fixedHeight
|
||||
);
|
||||
|
||||
var evt = UnityEvent.current;
|
||||
switch (evt.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
// am I closest to the thingy?
|
||||
if (evt.button == 0 && handleScreenPos.Contains(UnityEvent.current.mousePosition) && !evt.alt)
|
||||
{
|
||||
GUIUtility.hotControl = GUIUtility.keyboardControl = id; // Grab mouse focus
|
||||
s_CurrentMousePosition = evt.mousePosition;
|
||||
s_DragStartScreenPosition = evt.mousePosition;
|
||||
Vector2 rectScreenCenter = Handles.matrix.MultiplyPoint(pos);
|
||||
s_DragScreenOffset = s_CurrentMousePosition - rectScreenCenter;
|
||||
evt.Use();
|
||||
EditorGUIUtility.SetWantsMouseJumping(1);
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
s_CurrentMousePosition += evt.delta;
|
||||
Vector2 oldPos = pos;
|
||||
Vector3 scrPos = Handles.inverseMatrix.MultiplyPoint(s_CurrentMousePosition - s_DragScreenOffset);
|
||||
pos = new Vector2(scrPos.x, scrPos.y);
|
||||
if (!Mathf.Approximately((oldPos - pos).magnitude, 0f))
|
||||
GUI.changed = true;
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == id && (evt.button == 0 || evt.button == 2))
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
EditorGUIUtility.SetWantsMouseJumping(0);
|
||||
}
|
||||
break;
|
||||
case EventType.KeyDown:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Escape)
|
||||
{
|
||||
pos = Handles.inverseMatrix.MultiplyPoint(s_DragStartScreenPosition - s_DragScreenOffset);
|
||||
GUIUtility.hotControl = 0;
|
||||
GUI.changed = true;
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.Repaint:
|
||||
EditorGUIUtility.AddCursorRect(handleScreenPos, MouseCursor.Arrow, id);
|
||||
|
||||
if (GUIUtility.hotControl == id)
|
||||
pivotDotActive.Draw(handleScreenPos, GUIContent.none, id);
|
||||
else
|
||||
pivotDot.Draw(handleScreenPos, GUIContent.none, id);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Convert from texture space back to normalized space
|
||||
pos = new Vector2((pos.x - sprite.xMin) / sprite.width, (pos.y - sprite.yMin) / sprite.height);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static internal Rect SliderRect(Rect pos)
|
||||
{
|
||||
int id = GUIUtility.GetControlID("SliderRect".GetHashCode(), FocusType.Keyboard);
|
||||
|
||||
var evt = UnityEvent.current;
|
||||
|
||||
// SpriteEditorWindow is telling us we got selected and so we fake a mousedown on our Repaint event to get "one-click dragging" going on
|
||||
if (SpriteEditorWindow.s_OneClickDragStarted && evt.type == EventType.Repaint)
|
||||
{
|
||||
HandleSliderRectMouseDown(id, evt, pos);
|
||||
SpriteEditorWindow.s_OneClickDragStarted = false;
|
||||
}
|
||||
|
||||
switch (evt.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
// am I closest to the thingy?
|
||||
if (evt.button == 0 && pos.Contains(Handles.inverseMatrix.MultiplyPoint(UnityEvent.current.mousePosition)) && !evt.alt)
|
||||
{
|
||||
HandleSliderRectMouseDown(id, evt, pos);
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
s_CurrentMousePosition += evt.delta;
|
||||
|
||||
Vector2 oldCenter = pos.center;
|
||||
pos.center = Handles.inverseMatrix.MultiplyPoint(s_CurrentMousePosition - s_DragScreenOffset);
|
||||
if (!Mathf.Approximately((oldCenter - pos.center).magnitude, 0f))
|
||||
GUI.changed = true;
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == id && (evt.button == 0 || evt.button == 2))
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
EditorGUIUtility.SetWantsMouseJumping(0);
|
||||
}
|
||||
break;
|
||||
case EventType.KeyDown:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Escape)
|
||||
{
|
||||
pos.center = Handles.inverseMatrix.MultiplyPoint(s_DragStartScreenPosition - s_DragScreenOffset);
|
||||
GUIUtility.hotControl = 0;
|
||||
GUI.changed = true;
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.Repaint:
|
||||
Vector2 topleft = Handles.inverseMatrix.MultiplyPoint(new Vector2(pos.xMin, pos.yMin));
|
||||
Vector2 bottomright = Handles.inverseMatrix.MultiplyPoint(new Vector2(pos.xMax, pos.yMax));
|
||||
EditorGUIUtility.AddCursorRect(new Rect(topleft.x, topleft.y, bottomright.x - topleft.x, bottomright.y - topleft.y), MouseCursor.Arrow, id);
|
||||
break;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static internal void HandleSliderRectMouseDown(int id, UnityEvent evt, Rect pos)
|
||||
{
|
||||
GUIUtility.hotControl = GUIUtility.keyboardControl = id; // Grab mouse focus
|
||||
s_CurrentMousePosition = evt.mousePosition;
|
||||
s_DragStartScreenPosition = evt.mousePosition;
|
||||
|
||||
Vector2 rectScreenCenter = Handles.matrix.MultiplyPoint(pos.center);
|
||||
s_DragScreenOffset = s_CurrentMousePosition - rectScreenCenter;
|
||||
|
||||
EditorGUIUtility.SetWantsMouseJumping(1);
|
||||
}
|
||||
|
||||
static int s_RectSelectionID = GUIUtility.GetPermanentControlID();
|
||||
|
||||
static internal Rect RectCreator(Rect textureArea, GUIStyle rectStyle)
|
||||
{
|
||||
var evt = UnityEvent.current;
|
||||
Vector2 mousePos = evt.mousePosition;
|
||||
int id = s_RectSelectionID;
|
||||
Rect result = new Rect();
|
||||
|
||||
switch (evt.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
if (evt.button == 0)
|
||||
{
|
||||
GUIUtility.hotControl = id;
|
||||
|
||||
// Make sure that the starting position is clamped to inside texture area
|
||||
s_DragStartScreenPosition = Handles.inverseMatrix.MultiplyPoint(mousePos);
|
||||
|
||||
s_DragStartScreenPosition.x = Mathf.Min(Mathf.Max(s_DragStartScreenPosition.x, textureArea.xMin), textureArea.xMax);
|
||||
s_DragStartScreenPosition.y = Mathf.Min(Mathf.Max(s_DragStartScreenPosition.y, textureArea.yMin), textureArea.yMax);
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.Repaint:
|
||||
{
|
||||
var startDragPoint = Handles.matrix.MultiplyPoint(s_DragStartScreenPosition);
|
||||
if (GUIUtility.hotControl == id && ValidRect(startDragPoint, mousePos))
|
||||
{
|
||||
// TODO: use rectstyle
|
||||
//rectStyle.Draw (GetCurrentRect (true, textureWidth, textureHeight, s_DragStartScreenPosition, s_CurrentMousePosition), GUIContent.none, false, false, false, false);
|
||||
SpriteEditorUtility.BeginLines(Color.green * 1.5f);
|
||||
SpriteEditorUtility.DrawBox(GetCurrentRect(false, textureArea, startDragPoint, mousePos));
|
||||
SpriteEditorUtility.EndLines();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == id && evt.button == 0)
|
||||
{
|
||||
var startDragPoint = Handles.matrix.MultiplyPoint(s_DragStartScreenPosition);
|
||||
if (ValidRect(startDragPoint, mousePos))
|
||||
{
|
||||
result = GetCurrentRect(false, textureArea, startDragPoint, mousePos);
|
||||
GUI.changed = true;
|
||||
evt.Use();
|
||||
}
|
||||
|
||||
GUIUtility.hotControl = 0;
|
||||
}
|
||||
break;
|
||||
case EventType.KeyDown:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Escape)
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
GUI.changed = true;
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static internal Rect RectCreator(float textureWidth, float textureHeight, GUIStyle rectStyle)
|
||||
{
|
||||
return RectCreator(new Rect(0, 0, textureWidth, textureHeight), rectStyle);
|
||||
}
|
||||
|
||||
static private bool ValidRect(Vector2 startPoint, Vector2 endPoint)
|
||||
{
|
||||
return Mathf.Abs((endPoint - startPoint).x) > 5f && Mathf.Abs((endPoint - startPoint).y) > 5f;
|
||||
}
|
||||
|
||||
static private Rect GetCurrentRect(bool screenSpace, Rect clampArea, Vector2 startPoint, Vector2 endPoint)
|
||||
{
|
||||
Rect r = EditorGUIExt.FromToRect(Handles.inverseMatrix.MultiplyPoint(startPoint), Handles.inverseMatrix.MultiplyPoint(endPoint));
|
||||
r = SpriteEditorUtility.ClampedRect(SpriteEditorUtility.RoundToInt(r), clampArea, false);
|
||||
|
||||
if (screenSpace)
|
||||
{
|
||||
Vector2 topleft = Handles.matrix.MultiplyPoint(new Vector2(r.xMin, r.yMin));
|
||||
Vector2 bottomright = Handles.matrix.MultiplyPoint(new Vector2(r.xMax, r.yMax));
|
||||
|
||||
r = new Rect(topleft.x, topleft.y, bottomright.x - topleft.x, bottomright.y - topleft.y);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,514 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEvent = UnityEngine.Event;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
[Serializable]
|
||||
internal class SpriteEditorMenuSetting : ScriptableObject
|
||||
{
|
||||
public enum SlicingType
|
||||
{
|
||||
Automatic = 0,
|
||||
GridByCellSize = 1,
|
||||
GridByCellCount = 2,
|
||||
IsometricGrid = 3
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
public Vector2 gridCellCount = new Vector2(1, 1);
|
||||
[SerializeField]
|
||||
public Vector2 gridSpriteSize = new Vector2(64, 64);
|
||||
[SerializeField]
|
||||
public Vector2 gridSpriteOffset = new Vector2(0, 0);
|
||||
[SerializeField]
|
||||
public Vector2 gridSpritePadding = new Vector2(0, 0);
|
||||
[SerializeField]
|
||||
public Vector2 pivot = Vector2.zero;
|
||||
[SerializeField]
|
||||
public int autoSlicingMethod = (int)SpriteFrameModule.AutoSlicingMethod.DeleteAll;
|
||||
[SerializeField]
|
||||
public int spriteAlignment;
|
||||
[SerializeField]
|
||||
public SlicingType slicingType;
|
||||
[SerializeField]
|
||||
public bool keepEmptyRects;
|
||||
[SerializeField]
|
||||
public bool isAlternate;
|
||||
}
|
||||
|
||||
internal class SpriteEditorMenu : EditorWindow
|
||||
{
|
||||
private static Styles s_Styles;
|
||||
private static long s_LastClosedTime;
|
||||
private static SpriteEditorMenuSetting s_Setting;
|
||||
private ITextureDataProvider m_TextureDataProvider;
|
||||
private SpriteFrameModule m_SpriteFrameModule;
|
||||
private List<Rect> m_PotentialRects;
|
||||
|
||||
private class Styles
|
||||
{
|
||||
public GUIStyle background = "grey_border";
|
||||
public GUIStyle notice;
|
||||
|
||||
public Styles()
|
||||
{
|
||||
notice = new GUIStyle(GUI.skin.label);
|
||||
notice.alignment = TextAnchor.MiddleCenter;
|
||||
notice.wordWrap = true;
|
||||
}
|
||||
|
||||
public readonly GUIContent[] spriteAlignmentOptions =
|
||||
{
|
||||
EditorGUIUtility.TrTextContent("Center"),
|
||||
EditorGUIUtility.TrTextContent("Top Left"),
|
||||
EditorGUIUtility.TrTextContent("Top"),
|
||||
EditorGUIUtility.TrTextContent("Top Right"),
|
||||
EditorGUIUtility.TrTextContent("Left"),
|
||||
EditorGUIUtility.TrTextContent("Right"),
|
||||
EditorGUIUtility.TrTextContent("Bottom Left"),
|
||||
EditorGUIUtility.TrTextContent("Bottom"),
|
||||
EditorGUIUtility.TrTextContent("Bottom Right"),
|
||||
EditorGUIUtility.TrTextContent("Custom")
|
||||
};
|
||||
|
||||
public readonly GUIContent[] slicingMethodOptions =
|
||||
{
|
||||
EditorGUIUtility.TrTextContent("Delete Existing", "Delete all existing sprite assets before the slicing operation"),
|
||||
EditorGUIUtility.TrTextContent("Smart", "Try to match existing sprite rects to sliced rects from the slicing operation"),
|
||||
EditorGUIUtility.TrTextContent("Safe", "Keep existing sprite rects intact")
|
||||
};
|
||||
|
||||
public readonly GUIContent methodLabel = EditorGUIUtility.TrTextContent("Method");
|
||||
public readonly GUIContent pivotLabel = EditorGUIUtility.TrTextContent("Pivot");
|
||||
public readonly GUIContent typeLabel = EditorGUIUtility.TrTextContent("Type");
|
||||
public readonly GUIContent sliceButtonLabel = EditorGUIUtility.TrTextContent("Slice");
|
||||
public readonly GUIContent columnAndRowLabel = EditorGUIUtility.TrTextContent("Column & Row");
|
||||
public readonly GUIContent columnLabel = EditorGUIUtility.TextContent("C");
|
||||
public readonly GUIContent rowLabel = EditorGUIUtility.TextContent("R");
|
||||
public readonly GUIContent pixelSizeLabel = EditorGUIUtility.TrTextContent("Pixel Size");
|
||||
public readonly GUIContent xLabel = EditorGUIUtility.TextContent("X");
|
||||
public readonly GUIContent yLabel = EditorGUIUtility.TextContent("Y");
|
||||
public readonly GUIContent offsetLabel = EditorGUIUtility.TrTextContent("Offset");
|
||||
public readonly GUIContent paddingLabel = EditorGUIUtility.TrTextContent("Padding");
|
||||
public readonly GUIContent automaticSlicingHintLabel = EditorGUIUtility.TrTextContent("To obtain more accurate slicing results, manual slicing is recommended!");
|
||||
public readonly GUIContent customPivotLabel = EditorGUIUtility.TrTextContent("Custom Pivot");
|
||||
public readonly GUIContent keepEmptyRectsLabel = EditorGUIUtility.TrTextContent("Keep Empty Rects");
|
||||
public readonly GUIContent isAlternateLabel = EditorGUIUtility.TrTextContent("Is Alternate");
|
||||
}
|
||||
|
||||
internal List<Rect> GetPotentialRects()
|
||||
{
|
||||
if (m_PotentialRects == null)
|
||||
m_PotentialRects = new List<Rect>();
|
||||
m_PotentialRects.Clear();
|
||||
switch (s_Setting.slicingType)
|
||||
{
|
||||
case SpriteEditorMenuSetting.SlicingType.Automatic:
|
||||
// Do not show rects for Automatic
|
||||
break;
|
||||
case SpriteEditorMenuSetting.SlicingType.GridByCellCount:
|
||||
DetermineGridCellSizeWithCellCount(out var cellSize);
|
||||
m_PotentialRects.AddRange(m_SpriteFrameModule.GetGridRects(cellSize
|
||||
, s_Setting.gridSpriteOffset
|
||||
, s_Setting.gridSpritePadding
|
||||
, true));
|
||||
break;
|
||||
case SpriteEditorMenuSetting.SlicingType.GridByCellSize:
|
||||
m_PotentialRects.AddRange(m_SpriteFrameModule.GetGridRects(s_Setting.gridSpriteSize
|
||||
, s_Setting.gridSpriteOffset
|
||||
, s_Setting.gridSpritePadding
|
||||
, true));
|
||||
break;
|
||||
case SpriteEditorMenuSetting.SlicingType.IsometricGrid:
|
||||
m_PotentialRects.AddRange(m_SpriteFrameModule.GetIsometricRects(s_Setting.gridSpriteSize
|
||||
, s_Setting.gridSpriteOffset
|
||||
, s_Setting.isAlternate
|
||||
, true));
|
||||
break;
|
||||
}
|
||||
|
||||
return m_PotentialRects;
|
||||
}
|
||||
|
||||
private void Init(Rect buttonRect, SpriteFrameModule sf, ITextureDataProvider dataProvider)
|
||||
{
|
||||
// Create for once if setting was not created before.
|
||||
if (s_Setting == null)
|
||||
s_Setting = CreateInstance<SpriteEditorMenuSetting>();
|
||||
|
||||
m_SpriteFrameModule = sf;
|
||||
m_TextureDataProvider = dataProvider;
|
||||
|
||||
buttonRect = GUIUtility.GUIToScreenRect(buttonRect);
|
||||
float windowHeight = 195;
|
||||
var windowSize = new Vector2(300, windowHeight);
|
||||
ShowAsDropDown(buttonRect, windowSize);
|
||||
|
||||
Undo.undoRedoPerformed += UndoRedoPerformed;
|
||||
|
||||
RectSettingsDirty();
|
||||
}
|
||||
|
||||
private void UndoRedoPerformed()
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
AssemblyReloadEvents.beforeAssemblyReload += Close;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
AssemblyReloadEvents.beforeAssemblyReload -= Close;
|
||||
Undo.undoRedoPerformed -= UndoRedoPerformed;
|
||||
s_LastClosedTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
|
||||
m_SpriteFrameModule.potentialRects = null;
|
||||
m_SpriteFrameModule.spriteEditor.RequestRepaint();
|
||||
}
|
||||
|
||||
private void RectSettingsDirty()
|
||||
{
|
||||
m_SpriteFrameModule.potentialRects = GetPotentialRects();
|
||||
m_SpriteFrameModule.spriteEditor.RequestRepaint();
|
||||
}
|
||||
|
||||
internal static bool ShowAtPosition(Rect buttonRect, SpriteFrameModule sf, ITextureDataProvider textureProvider)
|
||||
{
|
||||
// We could not use realtimeSinceStartUp since it is set to 0 when entering/exitting playmode, we assume an increasing time when comparing time.
|
||||
long nowMilliSeconds = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
|
||||
bool justClosed = nowMilliSeconds < s_LastClosedTime + 50;
|
||||
if (!justClosed)
|
||||
{
|
||||
if (UnityEvent.current != null) // Event.current can be null during integration test
|
||||
UnityEvent.current.Use();
|
||||
|
||||
SpriteEditorMenu spriteEditorMenu = CreateInstance<SpriteEditorMenu>();
|
||||
spriteEditorMenu.Init(buttonRect, sf, textureProvider);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (s_Styles == null)
|
||||
s_Styles = new Styles();
|
||||
|
||||
// Leave some space above the elements
|
||||
GUILayout.Space(4);
|
||||
|
||||
EditorGUIUtility.labelWidth = 124f;
|
||||
EditorGUIUtility.wideMode = true;
|
||||
|
||||
GUI.Label(new Rect(0, 0, position.width, position.height), GUIContent.none, s_Styles.background);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
SpriteEditorMenuSetting.SlicingType slicingType = s_Setting.slicingType;
|
||||
slicingType = (SpriteEditorMenuSetting.SlicingType)EditorGUILayout.EnumPopup(s_Styles.typeLabel, slicingType);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Change slicing type");
|
||||
s_Setting.slicingType = slicingType;
|
||||
RectSettingsDirty();
|
||||
}
|
||||
switch (slicingType)
|
||||
{
|
||||
case SpriteEditorMenuSetting.SlicingType.GridByCellSize:
|
||||
case SpriteEditorMenuSetting.SlicingType.GridByCellCount:
|
||||
OnGridGUI();
|
||||
break;
|
||||
case SpriteEditorMenuSetting.SlicingType.Automatic:
|
||||
OnAutomaticGUI();
|
||||
break;
|
||||
case SpriteEditorMenuSetting.SlicingType.IsometricGrid:
|
||||
OnIsometricGridGUI();
|
||||
break;
|
||||
}
|
||||
|
||||
DoPivotGUI();
|
||||
GUILayout.Space(2f);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int slicingMethod = s_Setting.autoSlicingMethod;
|
||||
slicingMethod = EditorGUILayout.Popup(s_Styles.methodLabel, slicingMethod, s_Styles.slicingMethodOptions);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Change Slicing Method");
|
||||
s_Setting.autoSlicingMethod = slicingMethod;
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(EditorGUIUtility.labelWidth + 4);
|
||||
if (GUILayout.Button(s_Styles.sliceButtonLabel))
|
||||
DoSlicing();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DoSlicing()
|
||||
{
|
||||
switch (s_Setting.slicingType)
|
||||
{
|
||||
case SpriteEditorMenuSetting.SlicingType.GridByCellCount:
|
||||
case SpriteEditorMenuSetting.SlicingType.GridByCellSize:
|
||||
DoGridSlicing();
|
||||
break;
|
||||
case SpriteEditorMenuSetting.SlicingType.Automatic:
|
||||
DoAutomaticSlicing();
|
||||
break;
|
||||
case SpriteEditorMenuSetting.SlicingType.IsometricGrid:
|
||||
DoIsometricGridSlicing();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void TwoIntFields(GUIContent label, GUIContent labelX, GUIContent labelY, ref int x, ref int y)
|
||||
{
|
||||
float height = EditorGUI.kSingleLineHeight;
|
||||
Rect rect = GUILayoutUtility.GetRect(EditorGUILayout.kLabelFloatMinW, EditorGUILayout.kLabelFloatMaxW, height, height, EditorStyles.numberField);
|
||||
|
||||
Rect labelRect = rect;
|
||||
labelRect.width = EditorGUIUtility.labelWidth;
|
||||
labelRect.height = EditorGUI.kSingleLineHeight;
|
||||
|
||||
GUI.Label(labelRect, label);
|
||||
|
||||
Rect fieldRect = rect;
|
||||
fieldRect.width -= EditorGUIUtility.labelWidth;
|
||||
fieldRect.height = EditorGUI.kSingleLineHeight;
|
||||
fieldRect.x += EditorGUIUtility.labelWidth;
|
||||
fieldRect.width /= 2;
|
||||
fieldRect.width -= 2;
|
||||
|
||||
EditorGUIUtility.labelWidth = 12;
|
||||
|
||||
x = EditorGUI.IntField(fieldRect, labelX, x);
|
||||
fieldRect.x += fieldRect.width + 3;
|
||||
y = EditorGUI.IntField(fieldRect, labelY, y);
|
||||
|
||||
EditorGUIUtility.labelWidth = labelRect.width;
|
||||
}
|
||||
|
||||
private void OnGridGUI()
|
||||
{
|
||||
int width, height;
|
||||
m_TextureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||||
var texture = m_TextureDataProvider.GetReadableTexture2D();
|
||||
int maxWidth = texture != null ? width : 4096;
|
||||
int maxHeight = texture != null ? height : 4096;
|
||||
|
||||
if (s_Setting.slicingType == SpriteEditorMenuSetting.SlicingType.GridByCellCount)
|
||||
{
|
||||
int x = (int)s_Setting.gridCellCount.x;
|
||||
int y = (int)s_Setting.gridCellCount.y;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
TwoIntFields(s_Styles.columnAndRowLabel, s_Styles.columnLabel, s_Styles.rowLabel, ref x, ref y);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Change column & row");
|
||||
|
||||
s_Setting.gridCellCount.x = Mathf.Clamp(x, 1, maxWidth);
|
||||
s_Setting.gridCellCount.y = Mathf.Clamp(y, 1, maxHeight);
|
||||
RectSettingsDirty();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int x = (int)s_Setting.gridSpriteSize.x;
|
||||
int y = (int)s_Setting.gridSpriteSize.y;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
TwoIntFields(s_Styles.pixelSizeLabel, s_Styles.xLabel, s_Styles.yLabel, ref x, ref y);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Change grid size");
|
||||
|
||||
s_Setting.gridSpriteSize.x = Mathf.Clamp(x, 1, maxWidth);
|
||||
s_Setting.gridSpriteSize.y = Mathf.Clamp(y, 1, maxHeight);
|
||||
RectSettingsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x = (int)s_Setting.gridSpriteOffset.x;
|
||||
int y = (int)s_Setting.gridSpriteOffset.y;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
TwoIntFields(s_Styles.offsetLabel, s_Styles.xLabel, s_Styles.yLabel, ref x, ref y);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Change grid offset");
|
||||
|
||||
s_Setting.gridSpriteOffset.x = Mathf.Clamp(x, 0, maxWidth - s_Setting.gridSpriteSize.x);
|
||||
s_Setting.gridSpriteOffset.y = Mathf.Clamp(y, 0, maxHeight - s_Setting.gridSpriteSize.y);
|
||||
RectSettingsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x = (int)s_Setting.gridSpritePadding.x;
|
||||
int y = (int)s_Setting.gridSpritePadding.y;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
TwoIntFields(s_Styles.paddingLabel, s_Styles.xLabel, s_Styles.yLabel, ref x, ref y);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Change grid padding");
|
||||
|
||||
s_Setting.gridSpritePadding.x = Mathf.Clamp(x, 0, maxWidth);
|
||||
s_Setting.gridSpritePadding.y = Mathf.Clamp(y, 0, maxHeight);
|
||||
RectSettingsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
bool keepEmptyRects = s_Setting.keepEmptyRects;
|
||||
keepEmptyRects = EditorGUILayout.Toggle(s_Styles.keepEmptyRectsLabel, keepEmptyRects);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Keep Empty Rects");
|
||||
s_Setting.keepEmptyRects = keepEmptyRects;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAutomaticGUI()
|
||||
{
|
||||
float spacing = 38f;
|
||||
var texture = m_TextureDataProvider.GetReadableTexture2D();
|
||||
if (texture != null && UnityEditor.TextureUtil.IsCompressedTextureFormat(texture.format))
|
||||
{
|
||||
EditorGUILayout.LabelField(s_Styles.automaticSlicingHintLabel, s_Styles.notice);
|
||||
spacing -= 31f;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnIsometricGridGUI()
|
||||
{
|
||||
int width, height;
|
||||
m_TextureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||||
var texture = m_TextureDataProvider.GetReadableTexture2D();
|
||||
int maxWidth = texture != null ? width : 4096;
|
||||
int maxHeight = texture != null ? height : 4096;
|
||||
|
||||
{
|
||||
int x = (int)s_Setting.gridSpriteSize.x;
|
||||
int y = (int)s_Setting.gridSpriteSize.y;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
TwoIntFields(s_Styles.pixelSizeLabel, s_Styles.xLabel, s_Styles.yLabel, ref x, ref y);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Change grid size");
|
||||
|
||||
s_Setting.gridSpriteSize.x = Mathf.Clamp(x, 1, maxWidth);
|
||||
s_Setting.gridSpriteSize.y = Mathf.Clamp(y, 1, maxHeight);
|
||||
RectSettingsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int x = (int)s_Setting.gridSpriteOffset.x;
|
||||
int y = (int)s_Setting.gridSpriteOffset.y;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
TwoIntFields(s_Styles.offsetLabel, s_Styles.xLabel, s_Styles.yLabel, ref x, ref y);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Change grid offset");
|
||||
|
||||
s_Setting.gridSpriteOffset.x = Mathf.Clamp(x, 0, maxWidth - s_Setting.gridSpriteSize.x);
|
||||
s_Setting.gridSpriteOffset.y = Mathf.Clamp(y, 0, maxHeight - s_Setting.gridSpriteSize.y);
|
||||
RectSettingsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
bool keepEmptyRects = s_Setting.keepEmptyRects;
|
||||
keepEmptyRects = EditorGUILayout.Toggle(s_Styles.keepEmptyRectsLabel, keepEmptyRects);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Keep Empty Rects");
|
||||
s_Setting.keepEmptyRects = keepEmptyRects;
|
||||
}
|
||||
EditorGUI.BeginChangeCheck();
|
||||
bool isAlternate = s_Setting.isAlternate;
|
||||
isAlternate = EditorGUILayout.Toggle(s_Styles.isAlternateLabel, isAlternate);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Is Alternate");
|
||||
s_Setting.isAlternate = isAlternate;
|
||||
RectSettingsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
private void DoPivotGUI()
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int alignment = s_Setting.spriteAlignment;
|
||||
alignment = EditorGUILayout.Popup(s_Styles.pivotLabel, alignment, s_Styles.spriteAlignmentOptions);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Change Alignment");
|
||||
s_Setting.spriteAlignment = alignment;
|
||||
s_Setting.pivot = SpriteEditorUtility.GetPivotValue((SpriteAlignment)alignment, s_Setting.pivot);
|
||||
}
|
||||
|
||||
Vector2 pivot = s_Setting.pivot;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
using (new EditorGUI.DisabledScope(alignment != (int)SpriteAlignment.Custom))
|
||||
{
|
||||
pivot = EditorGUILayout.Vector2Field(s_Styles.customPivotLabel, pivot);
|
||||
}
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(s_Setting, "Change custom pivot");
|
||||
|
||||
s_Setting.pivot = pivot;
|
||||
}
|
||||
}
|
||||
|
||||
private void DoAutomaticSlicing()
|
||||
{
|
||||
// 4 seems to be a pretty nice min size for a automatic sprite slicing. It used to be exposed to the slicing dialog, but it is actually better workflow to slice&crop manually than find a suitable size number
|
||||
m_SpriteFrameModule.DoAutomaticSlicing(4, s_Setting.spriteAlignment, s_Setting.pivot, (SpriteFrameModule.AutoSlicingMethod)s_Setting.autoSlicingMethod);
|
||||
}
|
||||
|
||||
private void DoGridSlicing()
|
||||
{
|
||||
if (s_Setting.slicingType == SpriteEditorMenuSetting.SlicingType.GridByCellCount)
|
||||
SetGridCellSizeWithCellCount();
|
||||
|
||||
m_SpriteFrameModule.DoGridSlicing(s_Setting.gridSpriteSize, s_Setting.gridSpriteOffset, s_Setting.gridSpritePadding, s_Setting.spriteAlignment, s_Setting.pivot, (SpriteFrameModule.AutoSlicingMethod)s_Setting.autoSlicingMethod, s_Setting.keepEmptyRects);
|
||||
}
|
||||
|
||||
private void DoIsometricGridSlicing()
|
||||
{
|
||||
m_SpriteFrameModule.DoIsometricGridSlicing(s_Setting.gridSpriteSize, s_Setting.gridSpriteOffset, s_Setting.spriteAlignment, s_Setting.pivot, (SpriteFrameModule.AutoSlicingMethod)s_Setting.autoSlicingMethod, s_Setting.keepEmptyRects, s_Setting.isAlternate);
|
||||
}
|
||||
|
||||
private void SetGridCellSizeWithCellCount()
|
||||
{
|
||||
DetermineGridCellSizeWithCellCount(out var cellSize);
|
||||
s_Setting.gridSpriteSize = cellSize;
|
||||
}
|
||||
|
||||
private void DetermineGridCellSizeWithCellCount(out Vector2 cellSize)
|
||||
{
|
||||
int width, height;
|
||||
m_TextureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||||
var texture = m_TextureDataProvider.GetReadableTexture2D();
|
||||
int maxWidth = texture != null ? width : 4096;
|
||||
int maxHeight = texture != null ? height : 4096;
|
||||
|
||||
cellSize.x = (maxWidth - s_Setting.gridSpriteOffset.x - (s_Setting.gridSpritePadding.x * s_Setting.gridCellCount.x)) / s_Setting.gridCellCount.x;
|
||||
cellSize.y = (maxHeight - s_Setting.gridSpriteOffset.y - (s_Setting.gridSpritePadding.y * s_Setting.gridCellCount.y)) / s_Setting.gridCellCount.y;
|
||||
|
||||
cellSize.x = Mathf.Clamp(cellSize.x, 1, maxWidth);
|
||||
cellSize.y = Mathf.Clamp(cellSize.y, 1, maxHeight);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,179 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEvent = UnityEngine.Event;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
static internal class SpriteEditorUtility
|
||||
{
|
||||
public static Vector2 GetPivotValue(SpriteAlignment alignment, Vector2 customOffset)
|
||||
{
|
||||
switch (alignment)
|
||||
{
|
||||
case SpriteAlignment.BottomLeft:
|
||||
return new Vector2(0f, 0f);
|
||||
case SpriteAlignment.BottomCenter:
|
||||
return new Vector2(0.5f, 0f);
|
||||
case SpriteAlignment.BottomRight:
|
||||
return new Vector2(1f, 0f);
|
||||
|
||||
case SpriteAlignment.LeftCenter:
|
||||
return new Vector2(0f, 0.5f);
|
||||
case SpriteAlignment.Center:
|
||||
return new Vector2(0.5f, 0.5f);
|
||||
case SpriteAlignment.RightCenter:
|
||||
return new Vector2(1f, 0.5f);
|
||||
|
||||
case SpriteAlignment.TopLeft:
|
||||
return new Vector2(0f, 1f);
|
||||
case SpriteAlignment.TopCenter:
|
||||
return new Vector2(0.5f, 1f);
|
||||
case SpriteAlignment.TopRight:
|
||||
return new Vector2(1f, 1f);
|
||||
|
||||
case SpriteAlignment.Custom:
|
||||
return customOffset;
|
||||
}
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
public static Rect RoundedRect(Rect rect)
|
||||
{
|
||||
return new Rect(
|
||||
Mathf.RoundToInt(rect.xMin),
|
||||
Mathf.RoundToInt(rect.yMin),
|
||||
Mathf.RoundToInt(rect.width),
|
||||
Mathf.RoundToInt(rect.height)
|
||||
);
|
||||
}
|
||||
|
||||
public static Rect RoundToInt(Rect r)
|
||||
{
|
||||
r.xMin = Mathf.RoundToInt(r.xMin);
|
||||
r.yMin = Mathf.RoundToInt(r.yMin);
|
||||
r.xMax = Mathf.RoundToInt(r.xMax);
|
||||
r.yMax = Mathf.RoundToInt(r.yMax);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Rect ClampedRect(Rect rect, Rect clamp, bool maintainSize)
|
||||
{
|
||||
Rect r = new Rect(rect);
|
||||
|
||||
if (maintainSize)
|
||||
{
|
||||
Vector2 center = rect.center;
|
||||
if (center.x + Mathf.Abs(rect.width) * .5f > clamp.xMax)
|
||||
center.x = clamp.xMax - rect.width * .5f;
|
||||
if (center.x - Mathf.Abs(rect.width) * .5f < clamp.xMin)
|
||||
center.x = clamp.xMin + rect.width * .5f;
|
||||
if (center.y + Mathf.Abs(rect.height) * .5f > clamp.yMax)
|
||||
center.y = clamp.yMax - rect.height * .5f;
|
||||
if (center.y - Mathf.Abs(rect.height) * .5f < clamp.yMin)
|
||||
center.y = clamp.yMin + rect.height * .5f;
|
||||
r.center = center;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (r.width > 0f)
|
||||
{
|
||||
r.xMin = Mathf.Max(rect.xMin, clamp.xMin);
|
||||
r.xMax = Mathf.Min(rect.xMax, clamp.xMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
r.xMin = Mathf.Min(rect.xMin, clamp.xMax);
|
||||
r.xMax = Mathf.Max(rect.xMax, clamp.xMin);
|
||||
}
|
||||
if (r.height > 0f)
|
||||
{
|
||||
r.yMin = Mathf.Max(rect.yMin, clamp.yMin);
|
||||
r.yMax = Mathf.Min(rect.yMax, clamp.yMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
r.yMin = Mathf.Min(rect.yMin, clamp.yMax);
|
||||
r.yMax = Mathf.Max(rect.yMax, clamp.yMin);
|
||||
}
|
||||
}
|
||||
|
||||
r.width = Mathf.Abs(r.width);
|
||||
r.height = Mathf.Abs(r.height);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public static void DrawBox(Rect position)
|
||||
{
|
||||
var points0 = new Vector3(position.xMin, position.yMin, 0f);
|
||||
var points1 = new Vector3(position.xMax, position.yMin, 0f);
|
||||
var points2 = new Vector3(position.xMax, position.yMax, 0f);
|
||||
var points3 = new Vector3(position.xMin, position.yMax, 0f);
|
||||
|
||||
DrawLine(points0, points1);
|
||||
DrawLine(points1, points2);
|
||||
DrawLine(points2, points3);
|
||||
DrawLine(points3, points0);
|
||||
}
|
||||
|
||||
public static void DrawLine(Vector3 p1, Vector3 p2)
|
||||
{
|
||||
GL.Vertex(p1);
|
||||
GL.Vertex(p2);
|
||||
}
|
||||
|
||||
public static void BeginLines(Color color)
|
||||
{
|
||||
Assert.IsTrue(UnityEvent.current.type == EventType.Repaint);
|
||||
HandleUtility.ApplyWireMaterial();
|
||||
GL.PushMatrix();
|
||||
GL.MultMatrix(Handles.matrix);
|
||||
GL.Begin(GL.LINES);
|
||||
GL.Color(color);
|
||||
}
|
||||
|
||||
public static void EndLines()
|
||||
{
|
||||
Assert.IsTrue(UnityEvent.current.type == EventType.Repaint);
|
||||
GL.End();
|
||||
GL.PopMatrix();
|
||||
}
|
||||
|
||||
public static void FourIntFields(Vector2 rectSize, GUIContent label, GUIContent labelX, GUIContent labelY, GUIContent labelZ, GUIContent labelW, ref int x, ref int y, ref int z, ref int w)
|
||||
{
|
||||
Rect rect = GUILayoutUtility.GetRect(rectSize.x, rectSize.y);
|
||||
|
||||
Rect labelRect = rect;
|
||||
labelRect.width = EditorGUIUtility.labelWidth;
|
||||
labelRect.height = EditorGUI.kSingleLineHeight;
|
||||
|
||||
GUI.Label(labelRect, label);
|
||||
|
||||
Rect fieldRect = rect;
|
||||
fieldRect.width -= EditorGUIUtility.labelWidth;
|
||||
fieldRect.height = EditorGUI.kSingleLineHeight;
|
||||
fieldRect.x += EditorGUIUtility.labelWidth;
|
||||
fieldRect.width /= 2;
|
||||
fieldRect.width -= EditorGUI.kSpacingSubLabel;
|
||||
|
||||
float oldLabelWidth = EditorGUIUtility.labelWidth;
|
||||
EditorGUIUtility.labelWidth = EditorGUI.kMiniLabelW;
|
||||
|
||||
GUI.SetNextControlName("FourIntFields_x");
|
||||
x = EditorGUI.IntField(fieldRect, labelX, x);
|
||||
fieldRect.x += fieldRect.width + EditorGUI.kSpacing;
|
||||
GUI.SetNextControlName("FourIntFields_y");
|
||||
y = EditorGUI.IntField(fieldRect, labelY, y);
|
||||
fieldRect.y += EditorGUI.kSingleLineHeight + EditorGUI.kVerticalSpacingMultiField;
|
||||
fieldRect.x -= fieldRect.width + EditorGUI.kSpacing;
|
||||
GUI.SetNextControlName("FourIntFields_z");
|
||||
z = EditorGUI.IntField(fieldRect, labelZ, z);
|
||||
fieldRect.x += fieldRect.width + EditorGUI.kSpacing;
|
||||
GUI.SetNextControlName("FourIntFields_w");
|
||||
w = EditorGUI.IntField(fieldRect, labelW, w);
|
||||
|
||||
EditorGUIUtility.labelWidth = oldLabelWidth;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal class SpriteEditorWindowSettings : SettingsProvider
|
||||
{
|
||||
public const string kSettingsUniqueKey = "UnityEditor.U2D.Sprites/SpriteEditorWindow";
|
||||
public const string kShowRevertConfirmation = kSettingsUniqueKey + "RevertConfirmation";
|
||||
public const string kShowApplyConfirmation = kSettingsUniqueKey + "ApplyConfirmation";
|
||||
public static readonly GUIContent kShowRevertConfirmationLabel = EditorGUIUtility.TrTextContent("Show Revert Confirmation");
|
||||
public static readonly GUIContent kShowApplyConfirmationLabel = EditorGUIUtility.TrTextContent("Show Apply Confirmation");
|
||||
|
||||
public SpriteEditorWindowSettings() : base("Preferences/2D/Sprite Editor Window", SettingsScope.User)
|
||||
{
|
||||
guiHandler = OnGUI;
|
||||
}
|
||||
|
||||
[SettingsProvider]
|
||||
private static SettingsProvider CreateSettingsProvider()
|
||||
{
|
||||
return new SpriteEditorWindowSettings()
|
||||
{
|
||||
guiHandler = SettingsGUI
|
||||
};
|
||||
}
|
||||
|
||||
private static void SettingsGUI(string searchContext)
|
||||
{
|
||||
showApplyConfirmation = EditorGUILayout.Toggle(kShowApplyConfirmationLabel, showApplyConfirmation);
|
||||
showRevertConfirmation = EditorGUILayout.Toggle(kShowRevertConfirmationLabel, showRevertConfirmation);
|
||||
}
|
||||
|
||||
public static bool showRevertConfirmation
|
||||
{
|
||||
get { return EditorPrefs.GetBool(kShowRevertConfirmation, false); }
|
||||
set { EditorPrefs.SetBool(kShowRevertConfirmation, value); }
|
||||
}
|
||||
|
||||
public static bool showApplyConfirmation
|
||||
{
|
||||
get { return EditorPrefs.GetBool(kShowApplyConfirmation, false); }
|
||||
set { EditorPrefs.SetBool(kShowApplyConfirmation, value); }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor
|
||||
{
|
||||
/// <summary>Abstract class that is used by systems to encapsulate Sprite data representation. Currently this is used by Sprite Editor Window.</summary>
|
||||
[Serializable]
|
||||
public class SpriteRect
|
||||
{
|
||||
[SerializeField]
|
||||
string m_Name;
|
||||
|
||||
[SerializeField]
|
||||
string m_OriginalName;
|
||||
|
||||
[SerializeField]
|
||||
Vector2 m_Pivot;
|
||||
|
||||
[SerializeField]
|
||||
SpriteAlignment m_Alignment;
|
||||
|
||||
[SerializeField]
|
||||
Vector4 m_Border;
|
||||
|
||||
[SerializeField]
|
||||
Rect m_Rect;
|
||||
|
||||
[SerializeField]
|
||||
string m_SpriteID;
|
||||
|
||||
[SerializeField]
|
||||
internal long m_InternalID;
|
||||
|
||||
internal bool m_RegisterInternalID;
|
||||
|
||||
GUID m_GUID;
|
||||
|
||||
// <summary>The name of the Sprite data.</summary>
|
||||
public string name
|
||||
{
|
||||
get { return m_Name; }
|
||||
set { m_Name = value; }
|
||||
}
|
||||
|
||||
// <summary>Vector2value representing the pivot for the Sprite data.</summary>
|
||||
public Vector2 pivot
|
||||
{
|
||||
get { return m_Pivot; }
|
||||
set { m_Pivot = value; }
|
||||
}
|
||||
|
||||
/// <summary>SpriteAlignment that represents the pivot value for the Sprite data.</summary>
|
||||
public SpriteAlignment alignment
|
||||
{
|
||||
get { return m_Alignment; }
|
||||
set { m_Alignment = value; }
|
||||
}
|
||||
|
||||
/// <summary>Returns a Vector4 that represents the border of the Sprite data.</summary>
|
||||
public Vector4 border
|
||||
{
|
||||
get { return m_Border; }
|
||||
set { m_Border = value; }
|
||||
}
|
||||
|
||||
// <summary>Rect value that represents the position and size of the Sprite data.</summary>
|
||||
public Rect rect
|
||||
{
|
||||
get { return m_Rect; }
|
||||
set { m_Rect = value; }
|
||||
}
|
||||
|
||||
internal string originalName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_OriginalName == null)
|
||||
{
|
||||
m_OriginalName = name;
|
||||
}
|
||||
return m_OriginalName;
|
||||
}
|
||||
|
||||
set { m_OriginalName = value; }
|
||||
}
|
||||
|
||||
// <summary>GUID to uniquely identify the SpriteRect data. This will be populated to Sprite.spriteID to identify the SpriteRect used to generate the Sprite.</summary>
|
||||
public GUID spriteID
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateGUID();
|
||||
return m_GUID;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_GUID = value;
|
||||
m_SpriteID = m_GUID.ToString();
|
||||
ValidateGUID();
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateGUID()
|
||||
{
|
||||
if (m_GUID.Empty())
|
||||
{
|
||||
// We can't use ISerializationCallbackReceiver because we will hit into Script serialization errors
|
||||
m_GUID = new GUID(m_SpriteID);
|
||||
if (m_GUID.Empty())
|
||||
{
|
||||
m_GUID = GUID.Generate();
|
||||
m_SpriteID = m_GUID.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Helper method to get SpriteRect.spriteID from a SerializedProperty.</summary>
|
||||
/// <param name="sp">The SerializedProperty to acquire from.</param>
|
||||
/// <returns>GUID for the SpriteRect.</returns>
|
||||
public static GUID GetSpriteIDFromSerializedProperty(SerializedProperty sp)
|
||||
{
|
||||
return new GUID(sp.FindPropertyRelative("m_SpriteID").stringValue);
|
||||
}
|
||||
|
||||
internal long internalID
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_InternalID;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_InternalID = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SpriteRectCache : ScriptableObject
|
||||
{
|
||||
[SerializeField]
|
||||
private List<SpriteRect> m_Rects;
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_Rects != null ? m_Rects.Count : 0; }
|
||||
}
|
||||
|
||||
public SpriteRect RectAt(int i)
|
||||
{
|
||||
return i >= Count || i < 0 ? null : m_Rects[i];
|
||||
}
|
||||
|
||||
public void AddRect(SpriteRect r)
|
||||
{
|
||||
if (m_Rects != null)
|
||||
m_Rects.Add(r);
|
||||
}
|
||||
|
||||
public void RemoveRect(SpriteRect r)
|
||||
{
|
||||
if (m_Rects != null)
|
||||
m_Rects.RemoveAll(x => x.spriteID == r.spriteID);
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
if (m_Rects != null)
|
||||
m_Rects.Clear();
|
||||
}
|
||||
|
||||
public int GetIndex(SpriteRect spriteRect)
|
||||
{
|
||||
if (m_Rects != null && spriteRect != null)
|
||||
return m_Rects.FindIndex(p => p.spriteID == spriteRect.spriteID);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public bool Contains(SpriteRect spriteRect)
|
||||
{
|
||||
if (m_Rects != null && spriteRect != null)
|
||||
return m_Rects.Find(x => x.spriteID == spriteRect.spriteID) != null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (m_Rects == null)
|
||||
m_Rects = new List<SpriteRect>();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,430 @@
|
||||
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
|
||||
}
|
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
/// <summary>Base class the Sprite Editor Window custom module inherits from.</summary>
|
||||
/// <remarks>Sprite Editor Window functionality can be extended by providing custom module. By inheriting from SpriteEditorModuleBase, user will be able to activate the module's functionality from Sprite Editor Window.
|
||||
/// </remarks>
|
||||
public abstract class SpriteEditorModuleBase
|
||||
{
|
||||
/// <summary>The ISpriteEditor instance that instantiated the module.</summary>
|
||||
/// <value>An instance of ISpriteEditor</value>
|
||||
public ISpriteEditor spriteEditor { get; internal set; }
|
||||
/// <summary>The module name to display in Sprite Editor Window.</summary>
|
||||
/// <value>String to represent the name of the module</value>
|
||||
public abstract string moduleName { get; }
|
||||
/// <summary>Indicates if the module can be activated with the current ISpriteEditor state.</summary>
|
||||
/// <returns>Return true if the module can be activated.</returns>
|
||||
public abstract bool CanBeActivated();
|
||||
/// <summary>Implement this to draw on the Sprite Editor Window.</summary>
|
||||
/// <remarks>Called after Sprite Editor Window draws texture preview of the Asset.Use this to draw gizmos for Sprite data.</remarks>
|
||||
public abstract void DoMainGUI();
|
||||
/// <summary>Implement this to create a custom toolbar.</summary>
|
||||
/// <param name = "drawArea" > Area for drawing tool bar.</param>
|
||||
public abstract void DoToolbarGUI(Rect drawArea);
|
||||
/// <summary>This is called when the user activates the module.</summary>
|
||||
/// <remarks>When user switches to the module via Sprite Editor Window, this method will be called for the module to setup.</remarks>
|
||||
public abstract void OnModuleActivate();
|
||||
/// <summary>This is called when user switches to another module.</summary>
|
||||
/// <remarks>When user switches to the another module in Sprite Editor Window, this method will be called for the module to clean up.</remarks>
|
||||
public abstract void OnModuleDeactivate();
|
||||
/// <summary>Implement this to draw widgets in Sprite Editor Window.</summary>
|
||||
/// <remarks>This method is called last to allow drawing of widgets.</remarks>
|
||||
public abstract void DoPostGUI();
|
||||
/// <summary>This is called when user clicks on the Apply or Revert button in Sprite Editor Window.</summary>
|
||||
/// <param name="apply">True when user wants to apply the data, false when user wants to revert.</param>
|
||||
/// <returns>Return true to trigger a reimport.</returns>
|
||||
public abstract bool ApplyRevert(bool apply);
|
||||
}
|
||||
|
||||
/// <summary>Interface that defines the functionality available for classes that inherits SpriteEditorModuleBase.</summary>
|
||||
/// <remarks>Used by Sprite Editor Window to encapsulate functionality accessible for Sprite Editor modules when editing Sprite data.</remarks>
|
||||
public interface ISpriteEditor
|
||||
{
|
||||
/// <summary>Sets current available Sprite rects.</summary>
|
||||
List<SpriteRect> spriteRects { set; }
|
||||
/// <summary>The current selected Sprite rect data.</summary>
|
||||
SpriteRect selectedSpriteRect { get; set; }
|
||||
/// <summary>Indicates if ISpriteEditor should be interested in mouse move events.</summary>
|
||||
bool enableMouseMoveEvent { set; }
|
||||
/// <summary>Indicates that if Sprite data editing should be disabled; for example when the Editor is in Play Mode.</summary>
|
||||
bool editingDisabled { get; }
|
||||
/// <summary>Property that defines the window's current screen position and size.</summary>
|
||||
Rect windowDimension { get; }
|
||||
/// <summary>
|
||||
/// Gets data provider that is supported by the current selected object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Data provider type</typeparam>
|
||||
/// <returns>Instance of the data provider</returns>
|
||||
T GetDataProvider<T>() where T : class;
|
||||
/// <summary>The method updates ISpriteEditor.selectedSpriteRect based on current mouse down event and ISpriteEditor.spriteRects available.</summary>
|
||||
/// <returns>Returns true when ISpriteEditor.selectedSpriteRect is changed.</returns>
|
||||
bool HandleSpriteSelection();
|
||||
/// <summary>Request to repaint the current view.</summary>
|
||||
void RequestRepaint();
|
||||
/// <summary>Indicates that there has been a change of data. In Sprite Editor Window, this enables the 'Apply' and 'Revert' button.</summary>
|
||||
void SetDataModified();
|
||||
/// <summary>The method will inform current active SpriteEditorModuleBase to apply or revert any data changes.</summary>
|
||||
void ApplyOrRevertModification(bool apply);
|
||||
/// <summary>
|
||||
/// Returns a VisualElement for attaching child VisualElement onto the main view of a ISpriteEditor.
|
||||
/// </summary>
|
||||
/// <returns>Root VisualElement for the main view.</returns>
|
||||
/// <remarks>This method returns the root VisualElement for SpriteEditorModuleBase which uses the UIElement instead of IMGUI for its UI.A VisualElement that is added to this container has the same draw order as SpriteEditorModuleBase.DoPostGUI.</remarks>
|
||||
VisualElement GetMainVisualContainer();
|
||||
/// <summary>Sets a custom texture to be used by the ISpriteEditor during setup of the editing space.</summary>
|
||||
/// <param name = "texture" > The custom preview texture.</param>
|
||||
/// <param name = "width" > The width dimension to render the preview texture.</param>
|
||||
/// <param name = "height" > The height dimension to render the preview texture.</param>
|
||||
/// <remarks>When the method is called, the editing space's dimensions are set to the width and height values, affecting operations such as Zoom and Pan in the ISpriteEditor view. The preview texture is rendered as the background of the editing space.</remarks>
|
||||
void SetPreviewTexture(Texture2D texture, int width, int height);
|
||||
/// <summary> Resets the zoom and scroll of the Sprite Editor Windows.</summary>
|
||||
void ResetZoomAndScroll();
|
||||
/// <summary>Current zoom level of the ISpriteEditor view. </summary>
|
||||
float zoomLevel { get; set; }
|
||||
/// <summary>Current scroll position of the ISpriteEditor view. Determines the viewing location of the Texture displayed</summary>
|
||||
Vector2 scrollPosition { get; set; }
|
||||
/// <summary>Whether the ISpriteEditor view is visualizing the Alpha of the Texture displayed</summary>
|
||||
bool showAlpha { get; set; }
|
||||
/// <summary>The current Mip Level of the Texture displayed in ISpriteEditor view</summary>
|
||||
float mipLevel { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Use this attribute on a class that inherits from SpriteEditorModuleBase to indicate what data provider it needs.</summary>
|
||||
/// <remarks>When you use this attribute, Sprite Editor Window will only make the module available for selection if ISpriteEditorDataProvider.HasDataProvider returns true for all the data providers the module needs.
|
||||
/// <code>
|
||||
/// using UnityEditor.U2D;
|
||||
/// using UnityEngine;
|
||||
///
|
||||
/// [RequireSpriteDataProvider(typeof(ISpriteOutlineDataProvider), typeof(ITextureDataProvider))]
|
||||
/// public class MySpriteEditorCustomModule : SpriteEditorModuleBase
|
||||
/// {
|
||||
/// public override string moduleName
|
||||
/// {
|
||||
/// get { return "MySpriteEditorCustomModule"; }
|
||||
/// }
|
||||
/// public override bool ApplyRevert(bool apply)
|
||||
/// {
|
||||
/// return true;
|
||||
/// }
|
||||
/// public override bool CanBeActivated()
|
||||
/// {
|
||||
/// return true;
|
||||
/// }
|
||||
/// public override void DoMainGUI() { }
|
||||
/// public override void DoToolbarGUI(Rect drawArea) { }
|
||||
/// public override void OnModuleActivate()
|
||||
/// {
|
||||
/// var outlineProvider = spriteEditor.GetDataProvider<ISpriteOutlineDataProvider>();
|
||||
/// var spriteRects = spriteEditor.GetDataProvider<ISpriteEditorDataProvider>().GetSpriteRects();
|
||||
/// foreach (var spriteRect in spriteRects)
|
||||
/// {
|
||||
/// // Access outline data
|
||||
/// Debug.Log(outlineProvider.GetOutlines(spriteRect.spriteID));
|
||||
/// }
|
||||
/// }
|
||||
/// public override void OnModuleDeactivate() { }
|
||||
/// public override void DoPostGUI() { }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public class RequireSpriteDataProviderAttribute : Attribute
|
||||
{
|
||||
Type[] m_Types;
|
||||
|
||||
/// <summary>Use the attribute to indicate the custom data provider that SpriteEditorBaseModule needs.</summary>
|
||||
/// <param name="types">Data provider type.</param>
|
||||
public RequireSpriteDataProviderAttribute(params Type[] types)
|
||||
{
|
||||
m_Types = types;
|
||||
}
|
||||
|
||||
internal bool ContainsAllType(ISpriteEditorDataProvider provider)
|
||||
{
|
||||
return provider == null ? false : m_Types.Where(x =>
|
||||
{
|
||||
return provider.HasDataProvider(x);
|
||||
}).Count() == m_Types.Length;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine.U2D;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal class SpriteDataExt : SpriteRect
|
||||
{
|
||||
public float tessellationDetail = 0;
|
||||
|
||||
// The following lists are to be left un-initialized.
|
||||
// If they never loaded or assign explicitly, we avoid writing empty list to metadata.
|
||||
public List<Vector2[]> spriteOutline;
|
||||
public List<Vertex2DMetaData> vertices;
|
||||
public List<int> indices;
|
||||
public List<Vector2Int> edges;
|
||||
public List<Vector2[]> spritePhysicsOutline;
|
||||
public List<SpriteBone> spriteBone;
|
||||
|
||||
internal SpriteDataExt(SerializedObject so)
|
||||
{
|
||||
var ti = so.targetObject as TextureImporter;
|
||||
name = Path.GetFileNameWithoutExtension(ti.assetPath);
|
||||
alignment = (SpriteAlignment)so.FindProperty("m_Alignment").intValue;
|
||||
border = ti.spriteBorder;
|
||||
pivot = SpriteEditorUtility.GetPivotValue(alignment, ti.spritePivot);
|
||||
tessellationDetail = so.FindProperty("m_SpriteTessellationDetail").floatValue;
|
||||
|
||||
int width = 0, height = 0;
|
||||
ti.GetWidthAndHeight(ref width, ref height);
|
||||
rect = new Rect(0, 0, width, height);
|
||||
|
||||
var guidSP = so.FindProperty("m_SpriteSheet.m_SpriteID");
|
||||
spriteID = new GUID(guidSP.stringValue);
|
||||
|
||||
internalID = so.FindProperty("m_SpriteSheet.m_InternalID").longValue;
|
||||
}
|
||||
|
||||
internal SpriteDataExt(SerializedProperty sp)
|
||||
{
|
||||
rect = sp.FindPropertyRelative("m_Rect").rectValue;
|
||||
border = sp.FindPropertyRelative("m_Border").vector4Value;
|
||||
name = sp.FindPropertyRelative("m_Name").stringValue;
|
||||
alignment = (SpriteAlignment)sp.FindPropertyRelative("m_Alignment").intValue;
|
||||
pivot = SpriteEditorUtility.GetPivotValue(alignment, sp.FindPropertyRelative("m_Pivot").vector2Value);
|
||||
tessellationDetail = sp.FindPropertyRelative("m_TessellationDetail").floatValue;
|
||||
spriteID = new GUID(sp.FindPropertyRelative("m_SpriteID").stringValue);
|
||||
internalID = sp.FindPropertyRelative("m_InternalID").longValue;
|
||||
}
|
||||
|
||||
internal SpriteDataExt(SpriteRect sr)
|
||||
{
|
||||
originalName = sr.originalName;
|
||||
name = sr.name;
|
||||
border = sr.border;
|
||||
tessellationDetail = 0;
|
||||
rect = sr.rect;
|
||||
spriteID = sr.spriteID;
|
||||
internalID = sr.internalID;
|
||||
alignment = sr.alignment;
|
||||
pivot = sr.pivot;
|
||||
spriteOutline = new List<Vector2[]>();
|
||||
vertices = new List<Vertex2DMetaData>();
|
||||
indices = new List<int>();
|
||||
edges = new List<Vector2Int>();
|
||||
spritePhysicsOutline = new List<Vector2[]>();
|
||||
spriteBone = new List<SpriteBone>();
|
||||
}
|
||||
|
||||
public void Apply(SerializedObject so)
|
||||
{
|
||||
so.FindProperty("m_Alignment").intValue = (int)alignment;
|
||||
so.FindProperty("m_SpriteBorder").vector4Value = border;
|
||||
so.FindProperty("m_SpritePivot").vector2Value = pivot;
|
||||
so.FindProperty("m_SpriteTessellationDetail").floatValue = tessellationDetail;
|
||||
so.FindProperty("m_SpriteSheet.m_SpriteID").stringValue = spriteID.ToString();
|
||||
so.FindProperty("m_SpriteSheet.m_InternalID").longValue = internalID;
|
||||
|
||||
var sp = so.FindProperty("m_SpriteSheet");
|
||||
if (spriteBone != null)
|
||||
SpriteBoneDataTransfer.Apply(sp, spriteBone);
|
||||
if (spriteOutline != null)
|
||||
SpriteOutlineDataTransfer.Apply(sp, spriteOutline);
|
||||
if (spritePhysicsOutline != null)
|
||||
SpritePhysicsOutlineDataTransfer.Apply(sp, spritePhysicsOutline);
|
||||
if (vertices != null)
|
||||
SpriteMeshDataTransfer.Apply(sp, vertices, indices, edges);
|
||||
}
|
||||
|
||||
public void Apply(SerializedProperty sp)
|
||||
{
|
||||
sp.FindPropertyRelative("m_Rect").rectValue = rect;
|
||||
sp.FindPropertyRelative("m_Name").stringValue = name;
|
||||
sp.FindPropertyRelative("m_Border").vector4Value = border;
|
||||
sp.FindPropertyRelative("m_Alignment").intValue = (int)alignment;
|
||||
sp.FindPropertyRelative("m_Pivot").vector2Value = pivot;
|
||||
sp.FindPropertyRelative("m_TessellationDetail").floatValue = tessellationDetail;
|
||||
sp.FindPropertyRelative("m_SpriteID").stringValue = spriteID.ToString();
|
||||
sp.FindPropertyRelative("m_InternalID").longValue = internalID;
|
||||
|
||||
if (spriteBone != null)
|
||||
SpriteBoneDataTransfer.Apply(sp, spriteBone);
|
||||
if (spriteOutline != null)
|
||||
SpriteOutlineDataTransfer.Apply(sp, spriteOutline);
|
||||
if (spritePhysicsOutline != null)
|
||||
SpritePhysicsOutlineDataTransfer.Apply(sp, spritePhysicsOutline);
|
||||
if (vertices != null)
|
||||
SpriteMeshDataTransfer.Apply(sp, vertices, indices, edges);
|
||||
}
|
||||
|
||||
public void CopyFromSpriteRect(SpriteRect spriteRect)
|
||||
{
|
||||
alignment = spriteRect.alignment;
|
||||
border = spriteRect.border;
|
||||
name = spriteRect.name;
|
||||
pivot = spriteRect.pivot;
|
||||
rect = spriteRect.rect;
|
||||
spriteID = spriteRect.spriteID;
|
||||
internalID = spriteRect.internalID;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,551 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEditorInternal;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityTexture2D = UnityEngine.Texture2D;
|
||||
using UnityEditor.ShortcutManagement;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
[RequireSpriteDataProvider(typeof(ITextureDataProvider))]
|
||||
internal partial class SpriteFrameModule : SpriteFrameModuleBase
|
||||
{
|
||||
public enum AutoSlicingMethod
|
||||
{
|
||||
DeleteAll = 0,
|
||||
Smart = 1,
|
||||
Safe = 2
|
||||
}
|
||||
|
||||
private bool[] m_AlphaPixelCache;
|
||||
SpriteFrameModuleContext m_SpriteFrameModuleContext;
|
||||
|
||||
private const float kOverlapTolerance = 0.00001f;
|
||||
private StringBuilder m_SpriteNameStringBuilder;
|
||||
|
||||
private List<Rect> m_PotentialRects;
|
||||
public List<Rect> potentialRects
|
||||
{
|
||||
set => m_PotentialRects = value;
|
||||
}
|
||||
|
||||
public SpriteFrameModule(ISpriteEditor sw, IEventSystem es, IUndoSystem us, IAssetDatabase ad) :
|
||||
base("Sprite Editor", sw, es, us, ad)
|
||||
{}
|
||||
|
||||
class SpriteFrameModuleContext : IShortcutToolContext
|
||||
{
|
||||
SpriteFrameModule m_SpriteFrameModule;
|
||||
|
||||
public SpriteFrameModuleContext(SpriteFrameModule spriteFrame)
|
||||
{
|
||||
m_SpriteFrameModule = spriteFrame;
|
||||
}
|
||||
|
||||
public bool active
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public SpriteFrameModule spriteFrameModule
|
||||
{
|
||||
get { return m_SpriteFrameModule; }
|
||||
}
|
||||
}
|
||||
|
||||
[FormerlyPrefKeyAs("Sprite Editor/Trim", "#t")]
|
||||
[Shortcut("Sprite Editor/Trim", typeof(SpriteFrameModuleContext), KeyCode.T, ShortcutModifiers.Shift)]
|
||||
static void ShortcutTrim(ShortcutArguments args)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(GUI.GetNameOfFocusedControl()))
|
||||
return;
|
||||
var spriteFrameContext = (SpriteFrameModuleContext)args.context;
|
||||
spriteFrameContext.spriteFrameModule.TrimAlpha();
|
||||
spriteFrameContext.spriteFrameModule.spriteEditor.RequestRepaint();
|
||||
}
|
||||
|
||||
public override void OnModuleActivate()
|
||||
{
|
||||
base.OnModuleActivate();
|
||||
spriteEditor.enableMouseMoveEvent = true;
|
||||
m_SpriteFrameModuleContext = new SpriteFrameModuleContext(this);
|
||||
ShortcutIntegration.instance.contextManager.RegisterToolContext(m_SpriteFrameModuleContext);
|
||||
m_SpriteNameStringBuilder = new StringBuilder(GetSpriteNamePrefix() + "_");
|
||||
m_PotentialRects = null;
|
||||
}
|
||||
|
||||
public override void OnModuleDeactivate()
|
||||
{
|
||||
base.OnModuleDeactivate();
|
||||
ShortcutIntegration.instance.contextManager.DeregisterToolContext(m_SpriteFrameModuleContext);
|
||||
m_PotentialRects = null;
|
||||
m_AlphaPixelCache = null;
|
||||
}
|
||||
|
||||
public static SpriteImportMode GetSpriteImportMode(ISpriteEditorDataProvider dataProvider)
|
||||
{
|
||||
return dataProvider == null ? SpriteImportMode.None : dataProvider.spriteImportMode;
|
||||
}
|
||||
|
||||
public override bool CanBeActivated()
|
||||
{
|
||||
return GetSpriteImportMode(spriteEditor.GetDataProvider<ISpriteEditorDataProvider>()) != SpriteImportMode.Polygon;
|
||||
}
|
||||
|
||||
private string GenerateSpriteNameWithIndex(int startIndex)
|
||||
{
|
||||
int originalLength = m_SpriteNameStringBuilder.Length;
|
||||
m_SpriteNameStringBuilder.Append(startIndex);
|
||||
var name = m_SpriteNameStringBuilder.ToString();
|
||||
m_SpriteNameStringBuilder.Length = originalLength;
|
||||
return name;
|
||||
}
|
||||
|
||||
// 1. Find top-most rectangle
|
||||
// 2. Sweep it vertically to find out all rects from that "row"
|
||||
// 3. goto 1.
|
||||
// This will give us nicely sorted left->right top->down list of rectangles
|
||||
// Works for most sprite sheets pretty nicely
|
||||
private List<Rect> SortRects(List<Rect> rects)
|
||||
{
|
||||
List<Rect> result = new List<Rect>();
|
||||
|
||||
while (rects.Count > 0)
|
||||
{
|
||||
// Because the slicing algorithm works from bottom-up, the topmost rect is the last one in the array
|
||||
Rect r = rects[rects.Count - 1];
|
||||
Rect sweepRect = new Rect(0, r.yMin, textureActualWidth, r.height);
|
||||
|
||||
List<Rect> rowRects = RectSweep(rects, sweepRect);
|
||||
|
||||
if (rowRects.Count > 0)
|
||||
result.AddRange(rowRects);
|
||||
else
|
||||
{
|
||||
// We didn't find any rects, just dump the remaining rects and continue
|
||||
result.AddRange(rects);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Rect> RectSweep(List<Rect> rects, Rect sweepRect)
|
||||
{
|
||||
if (rects == null || rects.Count == 0)
|
||||
return new List<Rect>();
|
||||
|
||||
List<Rect> containedRects = new List<Rect>();
|
||||
|
||||
foreach (Rect rect in rects)
|
||||
{
|
||||
if (rect.Overlaps(sweepRect))
|
||||
containedRects.Add(rect);
|
||||
}
|
||||
|
||||
// Remove found rects from original list
|
||||
foreach (Rect rect in containedRects)
|
||||
rects.Remove(rect);
|
||||
|
||||
// Sort found rects by x position
|
||||
containedRects.Sort((a, b) => a.x.CompareTo(b.x));
|
||||
|
||||
return containedRects;
|
||||
}
|
||||
|
||||
private int AddSprite(Rect frame, int alignment, Vector2 pivot, AutoSlicingMethod slicingMethod, int originalCount, ref int nameIndex)
|
||||
{
|
||||
int outSprite = -1;
|
||||
switch (slicingMethod)
|
||||
{
|
||||
case AutoSlicingMethod.DeleteAll:
|
||||
{
|
||||
while (outSprite == -1)
|
||||
{
|
||||
outSprite = AddSprite(frame, alignment, pivot, GenerateSpriteNameWithIndex(nameIndex++), Vector4.zero, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AutoSlicingMethod.Smart:
|
||||
{
|
||||
outSprite = GetExistingOverlappingSprite(frame, originalCount, true);
|
||||
if (outSprite != -1)
|
||||
{
|
||||
var existingRect = m_RectsCache.spriteRects[outSprite];
|
||||
existingRect.rect = frame;
|
||||
existingRect.alignment = (SpriteAlignment)alignment;
|
||||
existingRect.pivot = pivot;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (outSprite == -1)
|
||||
{
|
||||
outSprite = AddSprite(frame, alignment, pivot, GenerateSpriteNameWithIndex(nameIndex++), Vector4.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AutoSlicingMethod.Safe:
|
||||
{
|
||||
outSprite = GetExistingOverlappingSprite(frame, originalCount);
|
||||
while (outSprite == -1)
|
||||
{
|
||||
outSprite = AddSprite(frame, alignment, pivot, GenerateSpriteNameWithIndex(nameIndex++), Vector4.zero);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return outSprite;
|
||||
}
|
||||
|
||||
private int GetExistingOverlappingSprite(Rect rect, int originalCount, bool bestFit = false)
|
||||
{
|
||||
var count = Math.Min(originalCount, m_RectsCache.spriteRects.Count);
|
||||
int bestRect = -1;
|
||||
float rectArea = rect.width * rect.height;
|
||||
if (rectArea < kOverlapTolerance)
|
||||
return bestRect;
|
||||
|
||||
float bestRatio = float.MaxValue;
|
||||
float bestArea = float.MaxValue;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Rect existingRect = m_RectsCache.spriteRects[i].rect;
|
||||
if (existingRect.Overlaps(rect))
|
||||
{
|
||||
if (bestFit)
|
||||
{
|
||||
float dx = Math.Min(rect.xMax, existingRect.xMax) - Math.Max(rect.xMin, existingRect.xMin);
|
||||
float dy = Math.Min(rect.yMax, existingRect.yMax) - Math.Max(rect.yMin, existingRect.yMin);
|
||||
float overlapArea = dx * dy;
|
||||
float overlapRatio = Math.Abs((overlapArea / rectArea) - 1.0f);
|
||||
float existingArea = existingRect.width * existingRect.height;
|
||||
if (overlapRatio < bestRatio || (overlapRatio < kOverlapTolerance && existingArea < bestArea))
|
||||
{
|
||||
bestRatio = overlapRatio;
|
||||
if (overlapRatio < kOverlapTolerance)
|
||||
bestArea = existingArea;
|
||||
bestRect = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bestRect = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestRect;
|
||||
}
|
||||
|
||||
private bool PixelHasAlpha(int x, int y, UnityTexture2D texture)
|
||||
{
|
||||
if (m_AlphaPixelCache == null)
|
||||
{
|
||||
m_AlphaPixelCache = new bool[texture.width * texture.height];
|
||||
Color32[] pixels = texture.GetPixels32();
|
||||
|
||||
for (int i = 0; i < pixels.Length; i++)
|
||||
m_AlphaPixelCache[i] = pixels[i].a != 0;
|
||||
}
|
||||
int index = y * (int)texture.width + x;
|
||||
return m_AlphaPixelCache[index];
|
||||
}
|
||||
|
||||
private int AddSprite(Rect rect, int alignment, Vector2 pivot, string name, Vector4 border, bool uniqueNameCheck = true)
|
||||
{
|
||||
var sed = spriteEditor.GetDataProvider<ISpriteEditorDataProvider>();
|
||||
long internalID = AssetImporter.MakeLocalFileIDWithHash(spriteType.persistentTypeID, name, 0);
|
||||
if (m_RectsCache.HasName(name))
|
||||
return -1;
|
||||
if (m_RectsCache.HasInternalID(internalID))
|
||||
return -1;
|
||||
|
||||
SpriteRect spriteRect = new SpriteRect();
|
||||
spriteRect.rect = rect;
|
||||
spriteRect.alignment = (SpriteAlignment)alignment;
|
||||
spriteRect.pivot = pivot;
|
||||
spriteRect.name = name;
|
||||
spriteRect.originalName = spriteRect.name;
|
||||
spriteRect.border = border;
|
||||
|
||||
spriteRect.internalID = internalID;
|
||||
spriteRect.spriteID = GUID.CreateGUIDFromSInt64(internalID);
|
||||
|
||||
// check if someone is using the internal id, if so, we change it to us.
|
||||
// Only TextureImporter needs this now.
|
||||
var ai = sed.targetObject as TextureImporter;
|
||||
var oldName = "";
|
||||
if (ai != null && ai.GetNameFromInternalIDMap(internalID, ref oldName))
|
||||
{
|
||||
if (string.IsNullOrEmpty(oldName))
|
||||
return -1;
|
||||
spriteRect.originalName = oldName;
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteRect.m_RegisterInternalID = true;
|
||||
}
|
||||
|
||||
m_RectsCache.Add(spriteRect);
|
||||
spriteEditor.SetDataModified();
|
||||
|
||||
return m_RectsCache.spriteRects.Count - 1;
|
||||
}
|
||||
|
||||
private string GetSpriteNamePrefix()
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(spriteAssetPath);
|
||||
}
|
||||
|
||||
public void DoAutomaticSlicing(int minimumSpriteSize, int alignment, Vector2 pivot, AutoSlicingMethod slicingMethod)
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Automatic Slicing");
|
||||
|
||||
if (slicingMethod == AutoSlicingMethod.DeleteAll)
|
||||
m_RectsCache.Clear();
|
||||
|
||||
var textureToUse = GetTextureToSlice();
|
||||
List<Rect> frames = new List<Rect>(InternalSpriteUtility.GenerateAutomaticSpriteRectangles((UnityTexture2D)textureToUse, minimumSpriteSize, 0));
|
||||
frames = SortRects(frames);
|
||||
int index = 0;
|
||||
int originalCount = m_RectsCache.spriteRects.Count;
|
||||
|
||||
foreach (Rect frame in frames)
|
||||
AddSprite(frame, alignment, pivot, slicingMethod, originalCount, ref index);
|
||||
|
||||
selected = null;
|
||||
spriteEditor.SetDataModified();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
UnityTexture2D GetTextureToSlice()
|
||||
{
|
||||
int width, height;
|
||||
m_TextureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||||
var readableTexture = m_TextureDataProvider.GetReadableTexture2D();
|
||||
if (readableTexture == null || (readableTexture.width == width && readableTexture.height == height))
|
||||
return readableTexture;
|
||||
// we want to slice based on the original texture slice. Upscale the imported texture
|
||||
var texture = UnityEditor.SpriteUtility.CreateTemporaryDuplicate(readableTexture, width, height);
|
||||
return texture;
|
||||
}
|
||||
|
||||
public IEnumerable<Rect> GetGridRects(Vector2 size, Vector2 offset, Vector2 padding, bool keepEmptyRects)
|
||||
{
|
||||
var textureToUse = GetTextureToSlice();
|
||||
return InternalSpriteUtility.GenerateGridSpriteRectangles((UnityTexture2D)textureToUse, offset, size, padding, keepEmptyRects);
|
||||
}
|
||||
|
||||
public void DoGridSlicing(Vector2 size, Vector2 offset, Vector2 padding, int alignment, Vector2 pivot, AutoSlicingMethod slicingMethod, bool keepEmptyRects = false)
|
||||
{
|
||||
var frames = GetGridRects(size, offset, padding, keepEmptyRects);
|
||||
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Grid Slicing");
|
||||
if (slicingMethod == AutoSlicingMethod.DeleteAll)
|
||||
m_RectsCache.Clear();
|
||||
|
||||
int index = 0;
|
||||
int originalCount = m_RectsCache.spriteRects.Count;
|
||||
foreach (Rect frame in frames)
|
||||
AddSprite(frame, alignment, pivot, slicingMethod, originalCount, ref index);
|
||||
|
||||
selected = null;
|
||||
spriteEditor.SetDataModified();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
public IEnumerable<Rect> GetIsometricRects(Vector2 size, Vector2 offset, bool isAlternate, bool keepEmptyRects)
|
||||
{
|
||||
var textureToUse = GetTextureToSlice();
|
||||
var gradient = (size.x / 2) / (size.y / 2);
|
||||
bool isAlt = isAlternate;
|
||||
float x = offset.x;
|
||||
if (isAlt)
|
||||
x += size.x / 2;
|
||||
float y = textureToUse.height - offset.y;
|
||||
while (y - size.y >= 0)
|
||||
{
|
||||
while (x + size.x <= textureToUse.width)
|
||||
{
|
||||
var rect = new Rect(x, y - size.y, size.x, size.y);
|
||||
if (!keepEmptyRects)
|
||||
{
|
||||
int sx = (int)rect.x;
|
||||
int sy = (int)rect.y;
|
||||
int width = (int)size.x;
|
||||
int odd = ((int)size.y) % 2;
|
||||
int topY = ((int)size.y / 2) - 1;
|
||||
int bottomY = topY + odd;
|
||||
int totalPixels = 0;
|
||||
int alphaPixels = 0;
|
||||
{
|
||||
for (int ry = 0; ry <= topY; ry++)
|
||||
{
|
||||
var pixelOffset = Mathf.CeilToInt(gradient * ry);
|
||||
for (int rx = pixelOffset; rx < width - pixelOffset; ++rx)
|
||||
{
|
||||
if (PixelHasAlpha(sx + rx, sy + topY - ry, textureToUse))
|
||||
alphaPixels++;
|
||||
if (PixelHasAlpha(sx + rx, sy + bottomY + ry, textureToUse))
|
||||
alphaPixels++;
|
||||
totalPixels += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (odd > 0)
|
||||
{
|
||||
int ry = topY + 1;
|
||||
for (int rx = 0; rx < size.x; ++rx)
|
||||
{
|
||||
if (PixelHasAlpha(sx + rx, sy + ry, textureToUse))
|
||||
alphaPixels++;
|
||||
totalPixels++;
|
||||
}
|
||||
}
|
||||
if (totalPixels > 0 && ((float)alphaPixels) / totalPixels > 0.01f)
|
||||
yield return rect;
|
||||
}
|
||||
else
|
||||
yield return rect;
|
||||
x += size.x;
|
||||
}
|
||||
isAlt = !isAlt;
|
||||
x = offset.x;
|
||||
if (isAlt)
|
||||
x += size.x / 2;
|
||||
y -= size.y / 2;
|
||||
}
|
||||
}
|
||||
|
||||
public void DoIsometricGridSlicing(Vector2 size, Vector2 offset, int alignment, Vector2 pivot, AutoSlicingMethod slicingMethod, bool keepEmptyRects = false, bool isAlternate = false)
|
||||
{
|
||||
var frames = GetIsometricRects(size, offset, isAlternate, keepEmptyRects);
|
||||
|
||||
List<Vector2[]> outlines = new List<Vector2[]>(4);
|
||||
outlines.Add(new[] { new Vector2(0.0f, -size.y / 2)
|
||||
, new Vector2(size.x / 2, 0.0f)
|
||||
, new Vector2(0.0f, size.y / 2)
|
||||
, new Vector2(-size.x / 2, 0.0f)});
|
||||
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Isometric Grid Slicing");
|
||||
if (slicingMethod == AutoSlicingMethod.DeleteAll)
|
||||
m_RectsCache.Clear();
|
||||
|
||||
int index = 0;
|
||||
var spriteRects = m_RectsCache.GetSpriteRects();
|
||||
int originalCount = spriteRects.Count;
|
||||
foreach (var frame in frames)
|
||||
{
|
||||
var spriteIndex = AddSprite(frame, alignment, pivot, slicingMethod, originalCount, ref index);
|
||||
var outlineRect = new OutlineSpriteRect(spriteRects[spriteIndex]);
|
||||
outlineRect.outlines = outlines;
|
||||
spriteRects[spriteIndex] = outlineRect;
|
||||
}
|
||||
|
||||
selected = null;
|
||||
spriteEditor.SetDataModified();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
public void ScaleSpriteRect(Rect r)
|
||||
{
|
||||
if (selected != null)
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Scale sprite");
|
||||
selected.rect = ClampSpriteRect(r, textureActualWidth, textureActualHeight);
|
||||
selected.border = ClampSpriteBorderToRect(selected.border, selected.rect);
|
||||
spriteEditor.SetDataModified();
|
||||
}
|
||||
}
|
||||
|
||||
public void TrimAlpha()
|
||||
{
|
||||
var texture = GetTextureToSlice();
|
||||
if (texture == null)
|
||||
return;
|
||||
|
||||
Rect rect = selected.rect;
|
||||
|
||||
int xMin = (int)rect.xMax;
|
||||
int xMax = (int)rect.xMin;
|
||||
int yMin = (int)rect.yMax;
|
||||
int yMax = (int)rect.yMin;
|
||||
|
||||
for (int y = (int)rect.yMin; y < (int)rect.yMax; y++)
|
||||
{
|
||||
for (int x = (int)rect.xMin; x < (int)rect.xMax; x++)
|
||||
{
|
||||
if (PixelHasAlpha(x, y, texture))
|
||||
{
|
||||
xMin = Mathf.Min(xMin, x);
|
||||
xMax = Mathf.Max(xMax, x);
|
||||
yMin = Mathf.Min(yMin, y);
|
||||
yMax = Mathf.Max(yMax, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Case 582309: Return an empty rectangle if no pixel has an alpha
|
||||
if (xMin > xMax || yMin > yMax)
|
||||
rect = new Rect(0, 0, 0, 0);
|
||||
else
|
||||
rect = new Rect(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
|
||||
|
||||
if (rect.width <= 0 && rect.height <= 0)
|
||||
{
|
||||
m_RectsCache.Remove(selected);
|
||||
spriteEditor.SetDataModified();
|
||||
selected = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
rect = ClampSpriteRect(rect, texture.width, texture.height);
|
||||
if (selected.rect != rect)
|
||||
spriteEditor.SetDataModified();
|
||||
|
||||
selected.rect = rect;
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
public void DuplicateSprite()
|
||||
{
|
||||
if (selected != null)
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Duplicate sprite");
|
||||
var index = 0;
|
||||
var createdIndex = -1;
|
||||
while (createdIndex == -1)
|
||||
{
|
||||
createdIndex = AddSprite(selected.rect, (int)selected.alignment, selected.pivot, GenerateSpriteNameWithIndex(index++), selected.border);
|
||||
}
|
||||
selected = m_RectsCache.spriteRects[createdIndex];
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateSprite(Rect rect)
|
||||
{
|
||||
rect = ClampSpriteRect(rect, textureActualWidth, textureActualHeight);
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Create sprite");
|
||||
var index = 0;
|
||||
var createdIndex = -1;
|
||||
while (createdIndex == -1)
|
||||
{
|
||||
createdIndex = AddSprite(rect, 0, Vector2.zero, GenerateSpriteNameWithIndex(index++), Vector4.zero);
|
||||
}
|
||||
selected = m_RectsCache.spriteRects[createdIndex];
|
||||
}
|
||||
|
||||
public void DeleteSprite()
|
||||
{
|
||||
if (selected != null)
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Delete sprite");
|
||||
m_RectsCache.Remove(selected);
|
||||
selected = null;
|
||||
spriteEditor.SetDataModified();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,524 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using UnityEngine;
|
||||
using UnityEditorInternal;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal class SpriteRectModel : ScriptableObject, ISerializationCallbackReceiver
|
||||
{
|
||||
[SerializeField]
|
||||
private List<SpriteRect> m_SpriteRects;
|
||||
private HashSet<string> m_Names;
|
||||
private HashSet<long> m_InternalIds;
|
||||
|
||||
private IReadOnlyList<SpriteRect> m_SpriteReadOnlyList;
|
||||
public IReadOnlyList<SpriteRect> spriteRects
|
||||
{
|
||||
get { return m_SpriteReadOnlyList; }
|
||||
}
|
||||
|
||||
private SpriteRectModel()
|
||||
{
|
||||
m_Names = new HashSet<string>();
|
||||
m_InternalIds = new HashSet<long>();
|
||||
}
|
||||
|
||||
public void SetSpriteRects(List<SpriteRect> newSpriteRects)
|
||||
{
|
||||
m_SpriteRects = newSpriteRects;
|
||||
foreach (var spriteRect in m_SpriteRects)
|
||||
{
|
||||
m_Names.Add(spriteRect.name);
|
||||
m_InternalIds.Add(spriteRect.internalID);
|
||||
}
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
}
|
||||
|
||||
public int FindIndex(Predicate<SpriteRect> match)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var spriteRect in m_SpriteRects)
|
||||
{
|
||||
if (match.Invoke(spriteRect))
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_SpriteRects = new List<SpriteRect>();
|
||||
m_InternalIds.Clear();
|
||||
m_Names.Clear();
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
}
|
||||
|
||||
public bool Add(SpriteRect spriteRect)
|
||||
{
|
||||
if (m_Names.Contains(spriteRect.name))
|
||||
return false;
|
||||
if (spriteRect.internalID != 0 && m_InternalIds.Contains(spriteRect.internalID))
|
||||
return false;
|
||||
m_Names.Add(spriteRect.name);
|
||||
if (spriteRect.internalID != 0)
|
||||
m_InternalIds.Add(spriteRect.internalID);
|
||||
m_SpriteRects.Add(spriteRect);
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Remove(SpriteRect spriteRect)
|
||||
{
|
||||
m_Names.Remove(spriteRect.name);
|
||||
if (spriteRect.internalID != 0)
|
||||
m_InternalIds.Remove(spriteRect.internalID);
|
||||
m_SpriteRects.Remove(spriteRect);
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
}
|
||||
|
||||
public bool HasName(string rectName)
|
||||
{
|
||||
return m_Names.Contains(rectName);
|
||||
}
|
||||
|
||||
public bool HasInternalID(long internalID)
|
||||
{
|
||||
return m_InternalIds.Contains(internalID);
|
||||
}
|
||||
|
||||
public List<SpriteRect> GetSpriteRects()
|
||||
{
|
||||
return m_SpriteRects;
|
||||
}
|
||||
|
||||
public void Rename(string oldName, string newName)
|
||||
{
|
||||
m_Names.Remove(oldName);
|
||||
m_Names.Add(newName);
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
}
|
||||
|
||||
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
||||
{}
|
||||
|
||||
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
||||
{
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
m_Names.Clear();
|
||||
m_InternalIds.Clear();
|
||||
foreach (var sprite in m_SpriteReadOnlyList)
|
||||
{
|
||||
m_Names.Add(sprite.name);
|
||||
m_InternalIds.Add(sprite.internalID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class OutlineSpriteRect : SpriteRect
|
||||
{
|
||||
public List<Vector2[]> outlines;
|
||||
|
||||
public OutlineSpriteRect(SpriteRect rect)
|
||||
{
|
||||
this.name = rect.name;
|
||||
this.originalName = rect.originalName;
|
||||
this.pivot = rect.pivot;
|
||||
this.alignment = rect.alignment;
|
||||
this.border = rect.border;
|
||||
this.rect = rect.rect;
|
||||
this.spriteID = rect.spriteID;
|
||||
this.internalID = rect.internalID;
|
||||
outlines = new List<Vector2[]>();
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract partial class SpriteFrameModuleBase : SpriteEditorModuleBase
|
||||
{
|
||||
protected static UnityType spriteType = UnityType.FindTypeByName("Sprite");
|
||||
|
||||
protected SpriteRectModel m_RectsCache;
|
||||
protected ITextureDataProvider m_TextureDataProvider;
|
||||
protected ISpriteEditorDataProvider m_SpriteDataProvider;
|
||||
string m_ModuleName;
|
||||
|
||||
internal enum PivotUnitMode
|
||||
{
|
||||
Normalized,
|
||||
Pixels
|
||||
}
|
||||
|
||||
private PivotUnitMode m_PivotUnitMode = PivotUnitMode.Normalized;
|
||||
|
||||
protected SpriteFrameModuleBase(string name, ISpriteEditor sw, IEventSystem es, IUndoSystem us, IAssetDatabase ad)
|
||||
{
|
||||
spriteEditor = sw;
|
||||
eventSystem = es;
|
||||
undoSystem = us;
|
||||
assetDatabase = ad;
|
||||
m_ModuleName = name;
|
||||
}
|
||||
|
||||
// implements ISpriteEditorModule
|
||||
|
||||
public override void OnModuleActivate()
|
||||
{
|
||||
spriteImportMode = SpriteFrameModule.GetSpriteImportMode(spriteEditor.GetDataProvider<ISpriteEditorDataProvider>());
|
||||
m_TextureDataProvider = spriteEditor.GetDataProvider<ITextureDataProvider>();
|
||||
m_SpriteDataProvider = spriteEditor.GetDataProvider<ISpriteEditorDataProvider>();
|
||||
int width, height;
|
||||
m_TextureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||||
textureActualWidth = width;
|
||||
textureActualHeight = height;
|
||||
m_RectsCache = ScriptableObject.CreateInstance<SpriteRectModel>();
|
||||
m_RectsCache.hideFlags = HideFlags.HideAndDontSave;
|
||||
var spriteList = m_SpriteDataProvider.GetSpriteRects().ToList();
|
||||
m_RectsCache.SetSpriteRects(spriteList);
|
||||
spriteEditor.spriteRects = spriteList;
|
||||
if (spriteEditor.selectedSpriteRect != null)
|
||||
spriteEditor.selectedSpriteRect = m_RectsCache.spriteRects.FirstOrDefault(x => x.spriteID == spriteEditor.selectedSpriteRect.spriteID);
|
||||
AddMainUI(spriteEditor.GetMainVisualContainer());
|
||||
undoSystem.RegisterUndoCallback(UndoCallback);
|
||||
}
|
||||
|
||||
public override void OnModuleDeactivate()
|
||||
{
|
||||
if (m_RectsCache != null)
|
||||
{
|
||||
undoSystem.ClearUndo(m_RectsCache);
|
||||
ScriptableObject.DestroyImmediate(m_RectsCache);
|
||||
m_RectsCache = null;
|
||||
}
|
||||
undoSystem.UnregisterUndoCallback(UndoCallback);
|
||||
RemoveMainUI(spriteEditor.GetMainVisualContainer());
|
||||
}
|
||||
|
||||
public override bool ApplyRevert(bool apply)
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
if (containsMultipleSprites)
|
||||
{
|
||||
var oldNames = new List<string>();
|
||||
var newNames = new List<string>();
|
||||
var ids = new List<long>();
|
||||
var names = new List<string>();
|
||||
|
||||
foreach (var spriteRect in m_RectsCache.spriteRects)
|
||||
{
|
||||
if (string.IsNullOrEmpty(spriteRect.name))
|
||||
spriteRect.name = "Empty";
|
||||
|
||||
if (!string.IsNullOrEmpty(spriteRect.originalName))
|
||||
{
|
||||
oldNames.Add(spriteRect.originalName);
|
||||
newNames.Add(spriteRect.name);
|
||||
}
|
||||
|
||||
if (spriteRect.m_RegisterInternalID)
|
||||
{
|
||||
ids.Add(spriteRect.internalID);
|
||||
names.Add(spriteRect.name);
|
||||
}
|
||||
spriteRect.m_RegisterInternalID = false;
|
||||
}
|
||||
var so = new SerializedObject(m_SpriteDataProvider.targetObject);
|
||||
if (so.isValid && ids.Count > 0)
|
||||
{
|
||||
ImportSettingInternalID.RegisterInternalID(so, spriteType, ids, names);
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
AssetImporter assetImporter = m_SpriteDataProvider.targetObject as AssetImporter;
|
||||
if (oldNames.Count > 0 && assetImporter != null)
|
||||
{
|
||||
assetImporter.RenameSubAssets(spriteType.persistentTypeID, oldNames.ToArray(), newNames.ToArray());
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
}
|
||||
var array = m_RectsCache != null ? m_RectsCache.spriteRects.ToArray() : null;
|
||||
m_SpriteDataProvider.SetSpriteRects(array);
|
||||
|
||||
var outlineDataProvider = m_SpriteDataProvider.GetDataProvider<ISpriteOutlineDataProvider>();
|
||||
var physicsDataProvider = m_SpriteDataProvider.GetDataProvider<ISpritePhysicsOutlineDataProvider>();
|
||||
foreach (var rect in array)
|
||||
{
|
||||
if (rect is OutlineSpriteRect outlineRect)
|
||||
{
|
||||
if (outlineRect.outlines.Count > 0)
|
||||
{
|
||||
outlineDataProvider.SetOutlines(outlineRect.spriteID, outlineRect.outlines);
|
||||
physicsDataProvider.SetOutlines(outlineRect.spriteID, outlineRect.outlines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_RectsCache != null)
|
||||
undoSystem.ClearUndo(m_RectsCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_RectsCache != null)
|
||||
{
|
||||
undoSystem.ClearUndo(m_RectsCache);
|
||||
var spriteList = m_SpriteDataProvider.GetSpriteRects().ToList();
|
||||
m_RectsCache.SetSpriteRects(spriteList);
|
||||
spriteEditor.spriteRects = spriteList;
|
||||
if (spriteEditor.selectedSpriteRect != null)
|
||||
spriteEditor.selectedSpriteRect = m_RectsCache.spriteRects.FirstOrDefault(x => x.spriteID == spriteEditor.selectedSpriteRect.spriteID);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string moduleName
|
||||
{
|
||||
get { return m_ModuleName; }
|
||||
}
|
||||
|
||||
// injected interfaces
|
||||
protected IEventSystem eventSystem
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected IUndoSystem undoSystem
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected IAssetDatabase assetDatabase
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected SpriteRect selected
|
||||
{
|
||||
get { return spriteEditor.selectedSpriteRect; }
|
||||
set { spriteEditor.selectedSpriteRect = value; }
|
||||
}
|
||||
|
||||
protected SpriteImportMode spriteImportMode
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
protected string spriteAssetPath
|
||||
{
|
||||
get { return assetDatabase.GetAssetPath(m_TextureDataProvider.texture); }
|
||||
}
|
||||
|
||||
public bool hasSelected
|
||||
{
|
||||
get { return spriteEditor.selectedSpriteRect != null; }
|
||||
}
|
||||
|
||||
public SpriteAlignment selectedSpriteAlignment
|
||||
{
|
||||
get { return selected.alignment; }
|
||||
}
|
||||
|
||||
public Vector2 selectedSpritePivot
|
||||
{
|
||||
get { return selected.pivot; }
|
||||
}
|
||||
|
||||
private Vector2 selectedSpritePivotInCurUnitMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_PivotUnitMode == PivotUnitMode.Pixels
|
||||
? ConvertFromNormalizedToRectSpace(selectedSpritePivot, selectedSpriteRect)
|
||||
: selectedSpritePivot;
|
||||
}
|
||||
}
|
||||
|
||||
public int CurrentSelectedSpriteIndex()
|
||||
{
|
||||
if (m_RectsCache != null && selected != null)
|
||||
return m_RectsCache.FindIndex(x => x.spriteID == selected.spriteID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Vector4 selectedSpriteBorder
|
||||
{
|
||||
get { return ClampSpriteBorderToRect(selected.border, selected.rect); }
|
||||
set
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Change Sprite Border");
|
||||
spriteEditor.SetDataModified();
|
||||
selected.border = ClampSpriteBorderToRect(value, selected.rect);
|
||||
}
|
||||
}
|
||||
|
||||
public Rect selectedSpriteRect
|
||||
{
|
||||
get { return selected.rect; }
|
||||
set
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Change Sprite rect");
|
||||
spriteEditor.SetDataModified();
|
||||
selected.rect = ClampSpriteRect(value, textureActualWidth, textureActualHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public string selectedSpriteName
|
||||
{
|
||||
get { return selected.name; }
|
||||
set
|
||||
{
|
||||
if (selected.name == value)
|
||||
return;
|
||||
if (m_RectsCache.HasName(value))
|
||||
return;
|
||||
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Change Sprite Name");
|
||||
spriteEditor.SetDataModified();
|
||||
|
||||
string oldName = selected.name;
|
||||
string newName = InternalEditorUtility.RemoveInvalidCharsFromFileName(value, true);
|
||||
|
||||
// These can only be changed in sprite multiple mode
|
||||
if (string.IsNullOrEmpty(selected.originalName) && (newName != oldName))
|
||||
selected.originalName = oldName;
|
||||
|
||||
// Is the name empty?
|
||||
if (string.IsNullOrEmpty(newName))
|
||||
newName = oldName;
|
||||
|
||||
m_RectsCache.Rename(oldName, newName);
|
||||
selected.name = newName;
|
||||
}
|
||||
}
|
||||
|
||||
public int spriteCount
|
||||
{
|
||||
get { return m_RectsCache.spriteRects.Count; }
|
||||
}
|
||||
|
||||
public Vector4 GetSpriteBorderAt(int i)
|
||||
{
|
||||
return m_RectsCache.spriteRects[i].border;
|
||||
}
|
||||
|
||||
public Rect GetSpriteRectAt(int i)
|
||||
{
|
||||
return m_RectsCache.spriteRects[i].rect;
|
||||
}
|
||||
|
||||
public int textureActualWidth { get; private set; }
|
||||
public int textureActualHeight { get; private set; }
|
||||
|
||||
public void SetSpritePivotAndAlignment(Vector2 pivot, SpriteAlignment alignment)
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Change Sprite Pivot");
|
||||
spriteEditor.SetDataModified();
|
||||
selected.alignment = alignment;
|
||||
selected.pivot = SpriteEditorUtility.GetPivotValue(alignment, pivot);
|
||||
}
|
||||
|
||||
public bool containsMultipleSprites
|
||||
{
|
||||
get { return spriteImportMode == SpriteImportMode.Multiple; }
|
||||
}
|
||||
|
||||
protected void SnapPivotToSnapPoints(Vector2 pivot, out Vector2 outPivot, out SpriteAlignment outAlignment)
|
||||
{
|
||||
Rect rect = selectedSpriteRect;
|
||||
|
||||
// Convert from normalized space to texture space
|
||||
Vector2 texturePos = new Vector2(rect.xMin + rect.width * pivot.x, rect.yMin + rect.height * pivot.y);
|
||||
|
||||
Vector2[] snapPoints = GetSnapPointsArray(rect);
|
||||
|
||||
// Snapping is now a firm action, it will always snap to one of the snapping points.
|
||||
SpriteAlignment snappedAlignment = SpriteAlignment.Custom;
|
||||
float nearestDistance = float.MaxValue;
|
||||
for (int alignment = 0; alignment < snapPoints.Length; alignment++)
|
||||
{
|
||||
float distance = (texturePos - snapPoints[alignment]).magnitude * m_Zoom;
|
||||
if (distance < nearestDistance)
|
||||
{
|
||||
snappedAlignment = (SpriteAlignment)alignment;
|
||||
nearestDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
outAlignment = snappedAlignment;
|
||||
outPivot = ConvertFromTextureToNormalizedSpace(snapPoints[(int)snappedAlignment], rect);
|
||||
}
|
||||
|
||||
protected void SnapPivotToPixels(Vector2 pivot, out Vector2 outPivot, out SpriteAlignment outAlignment)
|
||||
{
|
||||
outAlignment = SpriteAlignment.Custom;
|
||||
|
||||
Rect rect = selectedSpriteRect;
|
||||
float unitsPerPixelX = 1.0f / rect.width;
|
||||
float unitsPerPixelY = 1.0f / rect.height;
|
||||
outPivot.x = Mathf.Round(pivot.x / unitsPerPixelX) * unitsPerPixelX;
|
||||
outPivot.y = Mathf.Round(pivot.y / unitsPerPixelY) * unitsPerPixelY;
|
||||
}
|
||||
|
||||
private void UndoCallback()
|
||||
{
|
||||
UIUndoCallback();
|
||||
}
|
||||
|
||||
protected static Rect ClampSpriteRect(Rect rect, float maxX, float maxY)
|
||||
{
|
||||
// Clamp rect to width height
|
||||
rect = FlipNegativeRect(rect);
|
||||
Rect newRect = new Rect();
|
||||
|
||||
newRect.xMin = Mathf.Clamp(rect.xMin, 0, maxX - 1);
|
||||
newRect.yMin = Mathf.Clamp(rect.yMin, 0, maxY - 1);
|
||||
newRect.xMax = Mathf.Clamp(rect.xMax, 1, maxX);
|
||||
newRect.yMax = Mathf.Clamp(rect.yMax, 1, maxY);
|
||||
|
||||
// Prevent width and height to be 0 value after clamping.
|
||||
if (Mathf.RoundToInt(newRect.width) == 0)
|
||||
newRect.width = 1;
|
||||
if (Mathf.RoundToInt(newRect.height) == 0)
|
||||
newRect.height = 1;
|
||||
|
||||
return SpriteEditorUtility.RoundedRect(newRect);
|
||||
}
|
||||
|
||||
protected static Rect FlipNegativeRect(Rect rect)
|
||||
{
|
||||
Rect newRect = new Rect();
|
||||
|
||||
newRect.xMin = Mathf.Min(rect.xMin, rect.xMax);
|
||||
newRect.yMin = Mathf.Min(rect.yMin, rect.yMax);
|
||||
newRect.xMax = Mathf.Max(rect.xMin, rect.xMax);
|
||||
newRect.yMax = Mathf.Max(rect.yMin, rect.yMax);
|
||||
|
||||
return newRect;
|
||||
}
|
||||
|
||||
protected static Vector4 ClampSpriteBorderToRect(Vector4 border, Rect rect)
|
||||
{
|
||||
Rect flipRect = FlipNegativeRect(rect);
|
||||
float w = flipRect.width;
|
||||
float h = flipRect.height;
|
||||
|
||||
Vector4 newBorder = new Vector4();
|
||||
|
||||
// Make sure borders are within the width/height and left < right and top < bottom
|
||||
newBorder.x = Mathf.RoundToInt(Mathf.Clamp(border.x, 0, Mathf.Min(Mathf.Abs(w - border.z), w))); // Left
|
||||
newBorder.z = Mathf.RoundToInt(Mathf.Clamp(border.z, 0, Mathf.Min(Mathf.Abs(w - newBorder.x), w))); // Right
|
||||
|
||||
newBorder.y = Mathf.RoundToInt(Mathf.Clamp(border.y, 0, Mathf.Min(Mathf.Abs(h - border.w), h))); // Bottom
|
||||
newBorder.w = Mathf.RoundToInt(Mathf.Clamp(border.w, 0, Mathf.Min(Mathf.Abs(h - newBorder.y), h))); // Top
|
||||
|
||||
return newBorder;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,744 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal abstract partial class SpriteFrameModuleBase : SpriteEditorModuleBase
|
||||
{
|
||||
protected enum GizmoMode
|
||||
{
|
||||
BorderEditing,
|
||||
RectEditing
|
||||
}
|
||||
|
||||
protected class Styles
|
||||
{
|
||||
public readonly GUIStyle dragdot = "U2D.dragDot";
|
||||
public readonly GUIStyle dragdotactive = "U2D.dragDotActive";
|
||||
public readonly GUIStyle createRect = "U2D.createRect";
|
||||
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 Styles()
|
||||
{
|
||||
toolbar = new GUIStyle(EditorStyles.inspectorBig);
|
||||
toolbar.margin.top = 0;
|
||||
toolbar.margin.bottom = 0;
|
||||
createRect.border = new RectOffset(3, 3, 3, 3);
|
||||
|
||||
dragBorderdot.fixedHeight = 5f;
|
||||
dragBorderdot.fixedWidth = 5f;
|
||||
dragBorderdot.normal.background = EditorGUIUtility.whiteTexture;
|
||||
|
||||
dragBorderDotActive.fixedHeight = dragBorderdot.fixedHeight;
|
||||
dragBorderDotActive.fixedWidth = dragBorderdot.fixedWidth;
|
||||
dragBorderDotActive.normal.background = EditorGUIUtility.whiteTexture;
|
||||
}
|
||||
}
|
||||
|
||||
private static Styles s_Styles;
|
||||
|
||||
protected static Styles styles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Styles == null)
|
||||
s_Styles = new Styles();
|
||||
return s_Styles;
|
||||
}
|
||||
}
|
||||
|
||||
private const float kInspectorWidth = 330f;
|
||||
private const float kInspectorHeight = 170;
|
||||
private const float kPivotFieldPrecision = 0.0001f;
|
||||
private float m_Zoom = 1.0f;
|
||||
private GizmoMode m_GizmoMode;
|
||||
|
||||
private VisualElement m_NameElement;
|
||||
private TextField m_NameField;
|
||||
private VisualElement m_PositionElement;
|
||||
private IntegerField m_PositionFieldX;
|
||||
private IntegerField m_PositionFieldY;
|
||||
private IntegerField m_PositionFieldW;
|
||||
private IntegerField m_PositionFieldH;
|
||||
private IntegerField m_BorderFieldL;
|
||||
private IntegerField m_BorderFieldT;
|
||||
private IntegerField m_BorderFieldR;
|
||||
private IntegerField m_BorderFieldB;
|
||||
private EnumField m_PivotField;
|
||||
private EnumField m_PivotUnitModeField;
|
||||
private VisualElement m_CustomPivotElement;
|
||||
private FloatField m_CustomPivotFieldX;
|
||||
private FloatField m_CustomPivotFieldY;
|
||||
private VisualElement m_SelectedFrameInspector;
|
||||
|
||||
private bool ShouldShowRectScaling()
|
||||
{
|
||||
return hasSelected && m_GizmoMode == GizmoMode.RectEditing;
|
||||
}
|
||||
|
||||
private static Rect inspectorRect
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Rect(
|
||||
0, 0,
|
||||
kInspectorWidth,
|
||||
kInspectorHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveMainUI(VisualElement mainView)
|
||||
{
|
||||
if (mainView.Contains(m_SelectedFrameInspector))
|
||||
mainView.Remove(m_SelectedFrameInspector);
|
||||
mainView.UnregisterCallback<SpriteSelectionChangeEvent>(SelectionChange);
|
||||
}
|
||||
|
||||
protected void UpdatePositionField(FocusOutEvent evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
m_PositionFieldX.SetValueWithoutNotify((int)selectedSpriteRect.x);
|
||||
m_PositionFieldY.SetValueWithoutNotify((int)selectedSpriteRect.y);
|
||||
m_PositionFieldW.SetValueWithoutNotify((int)selectedSpriteRect.width);
|
||||
m_PositionFieldH.SetValueWithoutNotify((int)selectedSpriteRect.height);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBorderField(FocusOutEvent evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
m_BorderFieldL.SetValueWithoutNotify((int)selectedSpriteBorder.x);
|
||||
m_BorderFieldB.SetValueWithoutNotify((int)selectedSpriteBorder.y);
|
||||
m_BorderFieldR.SetValueWithoutNotify((int)selectedSpriteBorder.z);
|
||||
m_BorderFieldT.SetValueWithoutNotify((int)selectedSpriteBorder.w);
|
||||
}
|
||||
}
|
||||
|
||||
void SetupIntegerField(IntegerField field, EventCallback<FocusOutEvent> onFocusOutEvent, EventCallback<ChangeEvent<int>> onChangeEvent)
|
||||
{
|
||||
field.RegisterCallback(onFocusOutEvent);
|
||||
field.RegisterValueChangedCallback(onChangeEvent);
|
||||
}
|
||||
|
||||
void SetDragFieldLimit(IntegerField field, int value)
|
||||
{
|
||||
// The only way to know if value change is due to dragger or text input
|
||||
var t = field.Q("unity-text-input");
|
||||
if (!t.focusController.IsFocused(t))
|
||||
{
|
||||
// Value changed due to drag. We set back the field so to show the drag limit
|
||||
field.SetValueWithoutNotify(value);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPositionIntXChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var rect = selectedSpriteRect;
|
||||
rect.x = evt.newValue;
|
||||
selectedSpriteRect = rect;
|
||||
SetDragFieldLimit(m_PositionFieldX, (int)selectedSpriteRect.x);
|
||||
m_PositionFieldW.SetValueWithoutNotify((int)selectedSpriteRect.width);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPositionIntYChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var rect = selectedSpriteRect;
|
||||
rect.y = evt.newValue;
|
||||
selectedSpriteRect = rect;
|
||||
SetDragFieldLimit(m_PositionFieldY, (int)selectedSpriteRect.y);
|
||||
m_PositionFieldH.SetValueWithoutNotify((int)selectedSpriteRect.height);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPositionIntWChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var rect = selectedSpriteRect;
|
||||
rect.width = evt.newValue;
|
||||
selectedSpriteRect = rect;
|
||||
SetDragFieldLimit(m_PositionFieldW, (int)selectedSpriteRect.width);
|
||||
m_PositionFieldX.SetValueWithoutNotify((int)selectedSpriteRect.x);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPositionIntHChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var rect = selectedSpriteRect;
|
||||
rect.height = evt.newValue;
|
||||
selectedSpriteRect = rect;
|
||||
SetDragFieldLimit(m_PositionFieldH, (int)selectedSpriteRect.height);
|
||||
m_PositionFieldY.SetValueWithoutNotify((int)selectedSpriteRect.y);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBorderIntLChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var border = selectedSpriteBorder;
|
||||
border.x = evt.newValue;
|
||||
selectedSpriteBorder = border;
|
||||
SetDragFieldLimit(m_BorderFieldL, (int)selectedSpriteBorder.x);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBorderIntBChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var border = selectedSpriteBorder;
|
||||
border.y = evt.newValue;
|
||||
selectedSpriteBorder = border;
|
||||
SetDragFieldLimit(m_BorderFieldB, (int)selectedSpriteBorder.y);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBorderIntRChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var border = selectedSpriteBorder;
|
||||
border.z = (evt.newValue + border.x) <= selectedSpriteRect.width ? evt.newValue : selectedSpriteRect.width - border.x;
|
||||
selectedSpriteBorder = border;
|
||||
SetDragFieldLimit(m_BorderFieldR, (int)selectedSpriteBorder.z);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBorderIntTChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var border = selectedSpriteBorder;
|
||||
border.w = (evt.newValue + border.y) <= selectedSpriteRect.height ? evt.newValue : selectedSpriteRect.height - border.y;
|
||||
selectedSpriteBorder = border;
|
||||
SetDragFieldLimit(m_BorderFieldT, (int)selectedSpriteBorder.w);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMainUI(VisualElement mainView)
|
||||
{
|
||||
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Packages/com.unity.2d.sprite/Editor/UI/SpriteEditor/SpriteFrameModuleInspector.uxml") as VisualTreeAsset;
|
||||
m_SelectedFrameInspector = visualTree.CloneTree().Q("spriteFrameModuleInspector");
|
||||
|
||||
m_NameElement = m_SelectedFrameInspector.Q("name");
|
||||
m_NameField = m_SelectedFrameInspector.Q<TextField>("spriteName");
|
||||
m_NameField.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
selectedSpriteName = evt.newValue;
|
||||
}
|
||||
});
|
||||
|
||||
m_NameField.RegisterCallback<FocusOutEvent>((focus) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
m_NameField.SetValueWithoutNotify(selectedSpriteName);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
m_PositionElement = m_SelectedFrameInspector.Q("position");
|
||||
m_PositionFieldX = m_PositionElement.Q<IntegerField>("positionX");
|
||||
SetupIntegerField(m_PositionFieldX, UpdatePositionField, OnPositionIntXChange);
|
||||
|
||||
m_PositionFieldY = m_PositionElement.Q<IntegerField>("positionY");
|
||||
SetupIntegerField(m_PositionFieldY, UpdatePositionField, OnPositionIntYChange);
|
||||
|
||||
m_PositionFieldW = m_PositionElement.Q<IntegerField>("positionW");
|
||||
SetupIntegerField(m_PositionFieldW, UpdatePositionField, OnPositionIntWChange);
|
||||
|
||||
m_PositionFieldH = m_PositionElement.Q<IntegerField>("positionH");
|
||||
SetupIntegerField(m_PositionFieldH, UpdatePositionField, OnPositionIntHChange);
|
||||
|
||||
var borderElement = m_SelectedFrameInspector.Q("border");
|
||||
m_BorderFieldL = borderElement.Q<IntegerField>("borderL");
|
||||
SetupIntegerField(m_BorderFieldL, UpdateBorderField, OnBorderIntLChange);
|
||||
|
||||
m_BorderFieldT = borderElement.Q<IntegerField>("borderT");
|
||||
SetupIntegerField(m_BorderFieldT, UpdateBorderField, OnBorderIntTChange);
|
||||
|
||||
m_BorderFieldR = borderElement.Q<IntegerField>("borderR");
|
||||
SetupIntegerField(m_BorderFieldR, UpdateBorderField, OnBorderIntRChange);
|
||||
|
||||
m_BorderFieldB = borderElement.Q<IntegerField>("borderB");
|
||||
SetupIntegerField(m_BorderFieldB, UpdateBorderField, OnBorderIntBChange);
|
||||
|
||||
m_PivotField = m_SelectedFrameInspector.Q<EnumField>("pivotField");
|
||||
m_PivotField.Init(SpriteAlignment.Center);
|
||||
m_PivotField.label = L10n.Tr("Pivot");
|
||||
m_PivotField.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
SpriteAlignment alignment = (SpriteAlignment)evt.newValue;
|
||||
SetSpritePivotAndAlignment(selectedSpritePivot, alignment);
|
||||
m_CustomPivotElement.SetEnabled(selectedSpriteAlignment == SpriteAlignment.Custom);
|
||||
Vector2 pivot = selectedSpritePivotInCurUnitMode;
|
||||
m_CustomPivotFieldX.SetValueWithoutNotify(pivot.x);
|
||||
m_CustomPivotFieldY.SetValueWithoutNotify(pivot.y);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
m_PivotUnitModeField = m_SelectedFrameInspector.Q<EnumField>("pivotUnitModeField");
|
||||
m_PivotUnitModeField.Init(PivotUnitMode.Normalized);
|
||||
m_PivotUnitModeField.label = L10n.Tr("Pivot Unit Mode");
|
||||
m_PivotUnitModeField.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
m_PivotUnitMode = (PivotUnitMode)evt.newValue;
|
||||
|
||||
Vector2 pivot = selectedSpritePivotInCurUnitMode;
|
||||
m_CustomPivotFieldX.SetValueWithoutNotify(pivot.x);
|
||||
m_CustomPivotFieldY.SetValueWithoutNotify(pivot.y);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
m_CustomPivotElement = m_SelectedFrameInspector.Q("customPivot");
|
||||
m_CustomPivotFieldX = m_CustomPivotElement.Q<FloatField>("customPivotX");
|
||||
m_CustomPivotFieldX.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
float newValue = (float)evt.newValue;
|
||||
float pivotX = m_PivotUnitMode == PivotUnitMode.Pixels
|
||||
? ConvertFromRectToNormalizedSpace(new Vector2(newValue, 0.0f), selectedSpriteRect).x
|
||||
: newValue;
|
||||
|
||||
var pivot = selectedSpritePivot;
|
||||
pivot.x = pivotX;
|
||||
SetSpritePivotAndAlignment(pivot, selectedSpriteAlignment);
|
||||
}
|
||||
});
|
||||
|
||||
m_CustomPivotFieldY = m_CustomPivotElement.Q<FloatField>("customPivotY");
|
||||
m_CustomPivotFieldY.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
float newValue = (float)evt.newValue;
|
||||
float pivotY = m_PivotUnitMode == PivotUnitMode.Pixels
|
||||
? ConvertFromRectToNormalizedSpace(new Vector2(0.0f, newValue), selectedSpriteRect).y
|
||||
: newValue;
|
||||
|
||||
var pivot = selectedSpritePivot;
|
||||
pivot.y = pivotY;
|
||||
SetSpritePivotAndAlignment(pivot, selectedSpriteAlignment);
|
||||
}
|
||||
});
|
||||
|
||||
//// Force an update of all the fields.
|
||||
PopulateSpriteFrameInspectorField();
|
||||
|
||||
mainView.RegisterCallback<SpriteSelectionChangeEvent>(SelectionChange);
|
||||
|
||||
// Stop mouse events from reaching the main view.
|
||||
m_SelectedFrameInspector.pickingMode = PickingMode.Ignore;
|
||||
m_SelectedFrameInspector.RegisterCallback<MouseDownEvent>((e) => { e.StopPropagation(); });
|
||||
m_SelectedFrameInspector.RegisterCallback<MouseUpEvent>((e) => { e.StopPropagation(); });
|
||||
m_SelectedFrameInspector.AddToClassList("moduleWindow");
|
||||
m_SelectedFrameInspector.AddToClassList("bottomRightFloating");
|
||||
mainView.Add(m_SelectedFrameInspector);
|
||||
}
|
||||
|
||||
private void SelectionChange(SpriteSelectionChangeEvent evt)
|
||||
{
|
||||
m_SelectedFrameInspector.style.display = hasSelected ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
|
||||
private void UIUndoCallback()
|
||||
{
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
|
||||
protected void PopulateSpriteFrameInspectorField()
|
||||
{
|
||||
m_SelectedFrameInspector.style.display = hasSelected ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (!hasSelected)
|
||||
return;
|
||||
m_NameElement.SetEnabled(containsMultipleSprites);
|
||||
m_NameField.SetValueWithoutNotify(selectedSpriteName);
|
||||
m_PositionElement.SetEnabled(containsMultipleSprites);
|
||||
var spriteRect = selectedSpriteRect;
|
||||
m_PositionFieldX.SetValueWithoutNotify(Mathf.RoundToInt(spriteRect.x));
|
||||
m_PositionFieldY.SetValueWithoutNotify(Mathf.RoundToInt(spriteRect.y));
|
||||
m_PositionFieldW.SetValueWithoutNotify(Mathf.RoundToInt(spriteRect.width));
|
||||
m_PositionFieldH.SetValueWithoutNotify(Mathf.RoundToInt(spriteRect.height));
|
||||
var spriteBorder = selectedSpriteBorder;
|
||||
m_BorderFieldL.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.x));
|
||||
m_BorderFieldT.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.w));
|
||||
m_BorderFieldR.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.z));
|
||||
m_BorderFieldB.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.y));
|
||||
m_PivotField.SetValueWithoutNotify(selectedSpriteAlignment);
|
||||
m_PivotUnitModeField.SetValueWithoutNotify(m_PivotUnitMode);
|
||||
Vector2 pivot = selectedSpritePivotInCurUnitMode;
|
||||
m_CustomPivotFieldX.SetValueWithoutNotify(pivot.x);
|
||||
m_CustomPivotFieldY.SetValueWithoutNotify(pivot.y);
|
||||
|
||||
m_CustomPivotElement.SetEnabled(hasSelected && selectedSpriteAlignment == SpriteAlignment.Custom);
|
||||
}
|
||||
|
||||
private static Vector2 ApplySpriteAlignmentToPivot(Vector2 pivot, Rect rect, SpriteAlignment alignment)
|
||||
{
|
||||
if (alignment != SpriteAlignment.Custom)
|
||||
{
|
||||
Vector2[] snapPoints = GetSnapPointsArray(rect);
|
||||
Vector2 texturePos = snapPoints[(int)alignment];
|
||||
return ConvertFromTextureToNormalizedSpace(texturePos, rect);
|
||||
}
|
||||
return pivot;
|
||||
}
|
||||
|
||||
private static Vector2 ConvertFromTextureToNormalizedSpace(Vector2 texturePos, Rect rect)
|
||||
{
|
||||
return new Vector2((texturePos.x - rect.xMin) / rect.width, (texturePos.y - rect.yMin) / rect.height);
|
||||
}
|
||||
|
||||
private static Vector2 ConvertFromNormalizedToRectSpace(Vector2 normalizedPos, Rect rect)
|
||||
{
|
||||
Vector2 rectPos = new Vector2(rect.width * normalizedPos.x, rect.height * normalizedPos.y);
|
||||
|
||||
// This is to combat the lack of precision formating on the UI controls.
|
||||
rectPos.x = Mathf.Round(rectPos.x / kPivotFieldPrecision) * kPivotFieldPrecision;
|
||||
rectPos.y = Mathf.Round(rectPos.y / kPivotFieldPrecision) * kPivotFieldPrecision;
|
||||
|
||||
return rectPos;
|
||||
}
|
||||
|
||||
private static Vector2 ConvertFromRectToNormalizedSpace(Vector2 rectPos, Rect rect)
|
||||
{
|
||||
return new Vector2(rectPos.x / rect.width, rectPos.y / rect.height);
|
||||
}
|
||||
|
||||
private static Vector2[] GetSnapPointsArray(Rect rect)
|
||||
{
|
||||
Vector2[] snapPoints = new Vector2[9];
|
||||
snapPoints[(int)SpriteAlignment.TopLeft] = new Vector2(rect.xMin, rect.yMax);
|
||||
snapPoints[(int)SpriteAlignment.TopCenter] = new Vector2(rect.center.x, rect.yMax);
|
||||
snapPoints[(int)SpriteAlignment.TopRight] = new Vector2(rect.xMax, rect.yMax);
|
||||
snapPoints[(int)SpriteAlignment.LeftCenter] = new Vector2(rect.xMin, rect.center.y);
|
||||
snapPoints[(int)SpriteAlignment.Center] = new Vector2(rect.center.x, rect.center.y);
|
||||
snapPoints[(int)SpriteAlignment.RightCenter] = new Vector2(rect.xMax, rect.center.y);
|
||||
snapPoints[(int)SpriteAlignment.BottomLeft] = new Vector2(rect.xMin, rect.yMin);
|
||||
snapPoints[(int)SpriteAlignment.BottomCenter] = new Vector2(rect.center.x, rect.yMin);
|
||||
snapPoints[(int)SpriteAlignment.BottomRight] = new Vector2(rect.xMax, rect.yMin);
|
||||
return snapPoints;
|
||||
}
|
||||
|
||||
protected void Repaint()
|
||||
{
|
||||
spriteEditor.RequestRepaint();
|
||||
}
|
||||
|
||||
protected void HandleGizmoMode()
|
||||
{
|
||||
GizmoMode oldGizmoMode = m_GizmoMode;
|
||||
var evt = eventSystem.current;
|
||||
if (evt.control)
|
||||
m_GizmoMode = GizmoMode.BorderEditing;
|
||||
else
|
||||
m_GizmoMode = GizmoMode.RectEditing;
|
||||
|
||||
if (oldGizmoMode != m_GizmoMode && (evt.type == EventType.KeyDown || evt.type == EventType.KeyUp) && (evt.keyCode == KeyCode.LeftControl || evt.keyCode == KeyCode.RightControl || evt.keyCode == KeyCode.LeftAlt || evt.keyCode == KeyCode.RightAlt))
|
||||
Repaint();
|
||||
}
|
||||
|
||||
protected bool MouseOnTopOfInspector()
|
||||
{
|
||||
if (hasSelected == false)
|
||||
return false;
|
||||
|
||||
var point = GUIClip.Unclip(eventSystem.current.mousePosition);
|
||||
point = m_SelectedFrameInspector.parent.LocalToWorld(point);
|
||||
|
||||
var selectedElement = m_SelectedFrameInspector.panel.Pick(point);
|
||||
if (selectedElement != null
|
||||
&& selectedElement.pickingMode != PickingMode.Ignore
|
||||
&& selectedElement.FindCommonAncestor(m_SelectedFrameInspector) == m_SelectedFrameInspector)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void HandlePivotHandle()
|
||||
{
|
||||
if (!hasSelected)
|
||||
return;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
SpriteAlignment alignment = selectedSpriteAlignment;
|
||||
Vector2 pivot = selectedSpritePivot;
|
||||
Rect rect = selectedSpriteRect;
|
||||
pivot = ApplySpriteAlignmentToPivot(pivot, rect, alignment);
|
||||
Vector2 pivotHandlePosition = SpriteEditorHandles.PivotSlider(rect, pivot, styles.pivotdot, styles.pivotdotactive);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
// Pivot snapping only happen when ctrl is press. Same as scene view snapping move
|
||||
if (eventSystem.current.control)
|
||||
SnapPivotToSnapPoints(pivotHandlePosition, out pivot, out alignment);
|
||||
else if (m_PivotUnitMode == PivotUnitMode.Pixels)
|
||||
SnapPivotToPixels(pivotHandlePosition, out pivot, out alignment);
|
||||
else
|
||||
{
|
||||
pivot = pivotHandlePosition;
|
||||
alignment = SpriteAlignment.Custom;
|
||||
}
|
||||
SetSpritePivotAndAlignment(pivot, alignment);
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleBorderSidePointScalingSliders()
|
||||
{
|
||||
if (!hasSelected)
|
||||
return;
|
||||
|
||||
GUIStyle dragDot = styles.dragBorderdot;
|
||||
GUIStyle dragDotActive = styles.dragBorderDotActive;
|
||||
var color = new Color(0f, 1f, 0f);
|
||||
|
||||
Rect rect = selectedSpriteRect;
|
||||
Vector4 border = selectedSpriteBorder;
|
||||
|
||||
float left = rect.xMin + border.x;
|
||||
float right = rect.xMax - border.z;
|
||||
float top = rect.yMax - border.w;
|
||||
float bottom = rect.yMin + border.y;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
float horizontal = bottom - (bottom - top) / 2;
|
||||
float vertical = left - (left - right) / 2;
|
||||
|
||||
float center = horizontal;
|
||||
HandleBorderPointSlider(ref left, ref center, MouseCursor.ResizeHorizontal, false, dragDot, dragDotActive, color);
|
||||
|
||||
center = horizontal;
|
||||
HandleBorderPointSlider(ref right, ref center, MouseCursor.ResizeHorizontal, false, dragDot, dragDotActive, color);
|
||||
|
||||
center = vertical;
|
||||
HandleBorderPointSlider(ref center, ref top, MouseCursor.ResizeVertical, false, dragDot, dragDotActive, color);
|
||||
|
||||
center = vertical;
|
||||
HandleBorderPointSlider(ref center, ref bottom, MouseCursor.ResizeVertical, false, dragDot, dragDotActive, color);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
border.x = left - rect.xMin;
|
||||
border.z = rect.xMax - right;
|
||||
border.w = rect.yMax - top;
|
||||
border.y = bottom - rect.yMin;
|
||||
selectedSpriteBorder = border;
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleBorderCornerScalingHandles()
|
||||
{
|
||||
if (!hasSelected)
|
||||
return;
|
||||
|
||||
GUIStyle dragDot = styles.dragBorderdot;
|
||||
GUIStyle dragDotActive = styles.dragBorderDotActive;
|
||||
var color = new Color(0f, 1f, 0f);
|
||||
|
||||
Rect rect = selectedSpriteRect;
|
||||
Vector4 border = selectedSpriteBorder;
|
||||
|
||||
float left = rect.xMin + border.x;
|
||||
float right = rect.xMax - border.z;
|
||||
float top = rect.yMax - border.w;
|
||||
float bottom = rect.yMin + border.y;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
// Handle corner points, but hide them if border values are below 1
|
||||
HandleBorderPointSlider(ref left, ref top, MouseCursor.ResizeUpLeft, border.x < 1 && border.w < 1, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref right, ref top, MouseCursor.ResizeUpRight, border.z < 1 && border.w < 1, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref left, ref bottom, MouseCursor.ResizeUpRight, border.x < 1 && border.y < 1, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref right, ref bottom, MouseCursor.ResizeUpLeft, border.z < 1 && border.y < 1, dragDot, dragDotActive, color);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
border.x = left - rect.xMin;
|
||||
border.z = rect.xMax - right;
|
||||
border.w = rect.yMax - top;
|
||||
border.y = bottom - rect.yMin;
|
||||
selectedSpriteBorder = border;
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleBorderSideScalingHandles()
|
||||
{
|
||||
if (hasSelected == false)
|
||||
return;
|
||||
|
||||
Rect rect = new Rect(selectedSpriteRect);
|
||||
Vector4 border = selectedSpriteBorder;
|
||||
|
||||
float left = rect.xMin + border.x;
|
||||
float right = rect.xMax - border.z;
|
||||
float top = rect.yMax - border.w;
|
||||
float bottom = rect.yMin + border.y;
|
||||
|
||||
Vector2 screenRectTopLeft = Handles.matrix.MultiplyPoint(new Vector3(rect.xMin, rect.yMin));
|
||||
Vector2 screenRectBottomRight = Handles.matrix.MultiplyPoint(new Vector3(rect.xMax, rect.yMax));
|
||||
|
||||
float screenRectWidth = Mathf.Abs(screenRectBottomRight.x - screenRectTopLeft.x);
|
||||
float screenRectHeight = Mathf.Abs(screenRectBottomRight.y - screenRectTopLeft.y);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
left = HandleBorderScaleSlider(left, rect.yMax, screenRectWidth, screenRectHeight, true);
|
||||
right = HandleBorderScaleSlider(right, rect.yMax, screenRectWidth, screenRectHeight, true);
|
||||
|
||||
top = HandleBorderScaleSlider(rect.xMin, top, screenRectWidth, screenRectHeight, false);
|
||||
bottom = HandleBorderScaleSlider(rect.xMin, bottom, screenRectWidth, screenRectHeight, false);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
border.x = left - rect.xMin;
|
||||
border.z = rect.xMax - right;
|
||||
border.w = rect.yMax - top;
|
||||
border.y = bottom - rect.yMin;
|
||||
|
||||
selectedSpriteBorder = border;
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleBorderPointSlider(ref float x, ref float y, MouseCursor mouseCursor, bool isHidden, GUIStyle dragDot, GUIStyle dragDotActive, Color color)
|
||||
{
|
||||
var originalColor = GUI.color;
|
||||
|
||||
if (isHidden)
|
||||
GUI.color = new Color(0, 0, 0, 0);
|
||||
else
|
||||
GUI.color = color;
|
||||
|
||||
Vector2 point = SpriteEditorHandles.PointSlider(new Vector2(x, y), mouseCursor, dragDot, dragDotActive);
|
||||
x = point.x;
|
||||
y = point.y;
|
||||
|
||||
GUI.color = originalColor;
|
||||
}
|
||||
|
||||
protected float HandleBorderScaleSlider(float x, float y, float width, float height, bool isHorizontal)
|
||||
{
|
||||
float handleSize = styles.dragBorderdot.fixedWidth;
|
||||
Vector2 point = Handles.matrix.MultiplyPoint(new Vector2(x, y));
|
||||
float result;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
if (isHorizontal)
|
||||
{
|
||||
Rect newRect = new Rect(point.x - handleSize * .5f, point.y, handleSize, height);
|
||||
result = SpriteEditorHandles.ScaleSlider(point, MouseCursor.ResizeHorizontal, newRect).x;
|
||||
}
|
||||
else
|
||||
{
|
||||
Rect newRect = new Rect(point.x, point.y - handleSize * .5f, width, handleSize);
|
||||
result = SpriteEditorHandles.ScaleSlider(point, MouseCursor.ResizeVertical, newRect).y;
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
return result;
|
||||
|
||||
return isHorizontal ? x : y;
|
||||
}
|
||||
|
||||
protected void DrawSpriteRectGizmos()
|
||||
{
|
||||
if (eventSystem.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
SpriteEditorUtility.BeginLines(new Color(0f, 1f, 0f, 0.7f));
|
||||
var selectedGUID = selected != null ? selected.spriteID : new GUID();
|
||||
for (int i = 0; i < spriteCount; i++)
|
||||
{
|
||||
Vector4 border = GetSpriteBorderAt(i);
|
||||
if (m_GizmoMode != GizmoMode.BorderEditing && (m_RectsCache != null && m_RectsCache.spriteRects[i].spriteID != selectedGUID))
|
||||
{
|
||||
// border does not contain negative values
|
||||
if (border.sqrMagnitude < Mathf.Epsilon * 8)
|
||||
continue;
|
||||
}
|
||||
|
||||
var rect = GetSpriteRectAt(i);
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin + border.x, rect.yMin), new Vector3(rect.xMin + border.x, rect.yMax));
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMax - border.z, rect.yMin), new Vector3(rect.xMax - border.z, rect.yMax));
|
||||
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin, rect.yMin + border.y), new Vector3(rect.xMax, rect.yMin + border.y));
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin, rect.yMax - border.w), new Vector3(rect.xMax, rect.yMax - border.w));
|
||||
}
|
||||
SpriteEditorUtility.EndLines();
|
||||
|
||||
if (ShouldShowRectScaling())
|
||||
{
|
||||
Rect r = selectedSpriteRect;
|
||||
SpriteEditorUtility.BeginLines(new Color(0f, 0.1f, 0.3f, 0.25f));
|
||||
SpriteEditorUtility.DrawBox(new Rect(r.xMin + 1f / m_Zoom, r.yMin + 1f / m_Zoom, r.width, r.height));
|
||||
SpriteEditorUtility.EndLines();
|
||||
SpriteEditorUtility.BeginLines(new Color(0.25f, 0.5f, 1f, 0.75f));
|
||||
SpriteEditorUtility.DrawBox(r);
|
||||
SpriteEditorUtility.EndLines();
|
||||
}
|
||||
}
|
||||
|
||||
protected void DrawRectGizmos(IEnumerable<Rect> rects, Color color)
|
||||
{
|
||||
if (eventSystem.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
SpriteEditorUtility.BeginLines(color);
|
||||
foreach (var rect in rects)
|
||||
{
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin, rect.yMin), new Vector3(rect.xMin, rect.yMax));
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMax, rect.yMin), new Vector3(rect.xMax, rect.yMax));
|
||||
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin, rect.yMin), new Vector3(rect.xMax, rect.yMin));
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin, rect.yMax), new Vector3(rect.xMax, rect.yMax));
|
||||
}
|
||||
SpriteEditorUtility.EndLines();
|
||||
}
|
||||
|
||||
// implements ISpriteEditorModule
|
||||
|
||||
public override void DoMainGUI()
|
||||
{
|
||||
m_Zoom = Handles.matrix.GetColumn(0).magnitude;
|
||||
}
|
||||
|
||||
public override void DoPostGUI()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,220 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal partial class SpriteFrameModule : SpriteFrameModuleBase
|
||||
{
|
||||
private static class SpriteFrameModuleStyles
|
||||
{
|
||||
public static readonly GUIContent sliceButtonLabel = EditorGUIUtility.TrTextContent("Slice");
|
||||
public static readonly GUIContent trimButtonLabel = EditorGUIUtility.TrTextContent("Trim", "Trims selected rectangle (T)");
|
||||
}
|
||||
|
||||
// overrides for SpriteFrameModuleBase
|
||||
public override void DoMainGUI()
|
||||
{
|
||||
base.DoMainGUI();
|
||||
DrawSpriteRectGizmos();
|
||||
DrawPotentialSpriteRectGizmos();
|
||||
|
||||
HandleGizmoMode();
|
||||
|
||||
if (containsMultipleSprites)
|
||||
HandleRectCornerScalingHandles();
|
||||
|
||||
HandleBorderCornerScalingHandles();
|
||||
HandleBorderSidePointScalingSliders();
|
||||
|
||||
if (containsMultipleSprites)
|
||||
HandleRectSideScalingHandles();
|
||||
|
||||
HandleBorderSideScalingHandles();
|
||||
HandlePivotHandle();
|
||||
|
||||
if (containsMultipleSprites)
|
||||
HandleDragging();
|
||||
|
||||
spriteEditor.HandleSpriteSelection();
|
||||
|
||||
if (containsMultipleSprites)
|
||||
{
|
||||
HandleCreate();
|
||||
HandleDelete();
|
||||
HandleDuplicate();
|
||||
}
|
||||
spriteEditor.spriteRects = m_RectsCache.GetSpriteRects();
|
||||
}
|
||||
|
||||
private void DrawPotentialSpriteRectGizmos()
|
||||
{
|
||||
if (m_PotentialRects != null && m_PotentialRects.Count > 0)
|
||||
DrawRectGizmos(m_PotentialRects, Color.red);
|
||||
}
|
||||
|
||||
public override void DoToolbarGUI(Rect toolbarRect)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(!containsMultipleSprites || spriteEditor.editingDisabled || m_TextureDataProvider.GetReadableTexture2D() == null))
|
||||
{
|
||||
GUIStyle skin = EditorStyles.toolbarPopup;
|
||||
|
||||
Rect drawArea = toolbarRect;
|
||||
|
||||
drawArea.width = skin.CalcSize(SpriteFrameModuleStyles.sliceButtonLabel).x;
|
||||
SpriteUtilityWindow.DrawToolBarWidget(ref drawArea, ref toolbarRect, (adjustedDrawArea) =>
|
||||
{
|
||||
if (GUI.Button(adjustedDrawArea, SpriteFrameModuleStyles.sliceButtonLabel, skin))
|
||||
{
|
||||
if (SpriteEditorMenu.ShowAtPosition(adjustedDrawArea, this, m_TextureDataProvider))
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
});
|
||||
|
||||
using (new EditorGUI.DisabledScope(!hasSelected))
|
||||
{
|
||||
drawArea.x += drawArea.width;
|
||||
drawArea.width = skin.CalcSize(SpriteFrameModuleStyles.trimButtonLabel).x;
|
||||
SpriteUtilityWindow.DrawToolBarWidget(ref drawArea, ref toolbarRect, (adjustedDrawArea) =>
|
||||
{
|
||||
if (GUI.Button(adjustedDrawArea, SpriteFrameModuleStyles.trimButtonLabel, EditorStyles.toolbarButton))
|
||||
{
|
||||
TrimAlpha();
|
||||
Repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleRectCornerScalingHandles()
|
||||
{
|
||||
if (!hasSelected)
|
||||
return;
|
||||
|
||||
GUIStyle dragDot = styles.dragdot;
|
||||
GUIStyle dragDotActive = styles.dragdotactive;
|
||||
var color = Color.white;
|
||||
|
||||
Rect rect = new Rect(selectedSpriteRect);
|
||||
|
||||
float left = rect.xMin;
|
||||
float right = rect.xMax;
|
||||
float top = rect.yMax;
|
||||
float bottom = rect.yMin;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
HandleBorderPointSlider(ref left, ref top, MouseCursor.ResizeUpLeft, false, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref right, ref top, MouseCursor.ResizeUpRight, false, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref left, ref bottom, MouseCursor.ResizeUpRight, false, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref right, ref bottom, MouseCursor.ResizeUpLeft, false, dragDot, dragDotActive, color);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
rect.xMin = left;
|
||||
rect.xMax = right;
|
||||
rect.yMax = top;
|
||||
rect.yMin = bottom;
|
||||
ScaleSpriteRect(rect);
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleRectSideScalingHandles()
|
||||
{
|
||||
if (!hasSelected)
|
||||
return;
|
||||
|
||||
Rect rect = new Rect(selectedSpriteRect);
|
||||
|
||||
float left = rect.xMin;
|
||||
float right = rect.xMax;
|
||||
float top = rect.yMax;
|
||||
float bottom = rect.yMin;
|
||||
|
||||
Vector2 screenRectTopLeft = Handles.matrix.MultiplyPoint(new Vector3(rect.xMin, rect.yMin));
|
||||
Vector2 screenRectBottomRight = Handles.matrix.MultiplyPoint(new Vector3(rect.xMax, rect.yMax));
|
||||
|
||||
float screenRectWidth = Mathf.Abs(screenRectBottomRight.x - screenRectTopLeft.x);
|
||||
float screenRectHeight = Mathf.Abs(screenRectBottomRight.y - screenRectTopLeft.y);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
left = HandleBorderScaleSlider(left, rect.yMax, screenRectWidth, screenRectHeight, true);
|
||||
right = HandleBorderScaleSlider(right, rect.yMax, screenRectWidth, screenRectHeight, true);
|
||||
|
||||
top = HandleBorderScaleSlider(rect.xMin, top, screenRectWidth, screenRectHeight, false);
|
||||
bottom = HandleBorderScaleSlider(rect.xMin, bottom, screenRectWidth, screenRectHeight, false);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
rect.xMin = left;
|
||||
rect.xMax = right;
|
||||
rect.yMax = top;
|
||||
rect.yMin = bottom;
|
||||
|
||||
ScaleSpriteRect(rect);
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDragging()
|
||||
{
|
||||
if (hasSelected && !MouseOnTopOfInspector())
|
||||
{
|
||||
Rect textureBounds = new Rect(0, 0, textureActualWidth, textureActualHeight);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
Rect oldRect = selectedSpriteRect;
|
||||
Rect newRect = SpriteEditorUtility.ClampedRect(SpriteEditorUtility.RoundedRect(SpriteEditorHandles.SliderRect(oldRect)), textureBounds, true);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
selectedSpriteRect = newRect;
|
||||
UpdatePositionField(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCreate()
|
||||
{
|
||||
if (!MouseOnTopOfInspector() && !eventSystem.current.alt)
|
||||
{
|
||||
// Create new rects via dragging in empty space
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Rect newRect = SpriteEditorHandles.RectCreator(textureActualWidth, textureActualHeight, styles.createRect);
|
||||
if (EditorGUI.EndChangeCheck() && newRect.width > 0f && newRect.height > 0f)
|
||||
{
|
||||
CreateSprite(newRect);
|
||||
GUIUtility.keyboardControl = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDuplicate()
|
||||
{
|
||||
IEvent evt = eventSystem.current;
|
||||
if ((evt.type == EventType.ValidateCommand || evt.type == EventType.ExecuteCommand)
|
||||
&& evt.commandName == EventCommandNames.Duplicate)
|
||||
{
|
||||
if (evt.type == EventType.ExecuteCommand)
|
||||
DuplicateSprite();
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDelete()
|
||||
{
|
||||
IEvent evt = eventSystem.current;
|
||||
|
||||
if ((evt.type == EventType.ValidateCommand || evt.type == EventType.ExecuteCommand)
|
||||
&& (evt.commandName == EventCommandNames.SoftDelete || evt.commandName == EventCommandNames.Delete))
|
||||
{
|
||||
if (evt.type == EventType.ExecuteCommand && hasSelected)
|
||||
DeleteSprite();
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
[RequireSpriteDataProvider(typeof(ISpriteOutlineDataProvider), typeof(ITextureDataProvider))]
|
||||
internal partial class SpritePolygonModeModule : SpriteFrameModuleBase
|
||||
{
|
||||
List<List<Vector2[]>> m_Outline;
|
||||
|
||||
public SpritePolygonModeModule(ISpriteEditor sw, IEventSystem es, IUndoSystem us, IAssetDatabase ad) :
|
||||
base("Sprite Polygon Mode Editor", sw, es, us, ad)
|
||||
{}
|
||||
|
||||
// ISpriteEditorModule implemenation
|
||||
public override void OnModuleActivate()
|
||||
{
|
||||
base.OnModuleActivate();
|
||||
AddMainUI(spriteEditor.GetMainVisualContainer());
|
||||
m_Outline = new List<List<Vector2[]>>();
|
||||
|
||||
for (int i = 0; i < m_RectsCache.spriteRects.Count; ++i)
|
||||
{
|
||||
var rect = m_RectsCache.spriteRects[i];
|
||||
m_Outline.Add(spriteEditor.GetDataProvider<ISpriteOutlineDataProvider>().GetOutlines(rect.spriteID));
|
||||
}
|
||||
|
||||
showChangeShapeWindow = polygonSprite;
|
||||
if (polygonSprite)
|
||||
DeterminePolygonSides();
|
||||
}
|
||||
|
||||
public override bool CanBeActivated()
|
||||
{
|
||||
return SpriteFrameModule.GetSpriteImportMode(spriteEditor.GetDataProvider<ISpriteEditorDataProvider>()) == SpriteImportMode.Polygon;
|
||||
}
|
||||
|
||||
private bool polygonSprite
|
||||
{
|
||||
get { return spriteImportMode == SpriteImportMode.Polygon; }
|
||||
}
|
||||
|
||||
private void DeterminePolygonSides()
|
||||
{
|
||||
if (polygonSprite && m_RectsCache.spriteRects.Count == 1 && m_Outline.Count == 1 && m_Outline[0].Count == 1)
|
||||
{
|
||||
polygonSides = m_Outline[0][0].Length;
|
||||
}
|
||||
else
|
||||
// If for reasons we cannot determine the sides of the polygon, fall back to 0 (Square)
|
||||
polygonSides = 0;
|
||||
ViewUpdateSideCountField();
|
||||
}
|
||||
|
||||
public int GetPolygonSideCount()
|
||||
{
|
||||
DeterminePolygonSides();
|
||||
return polygonSides;
|
||||
}
|
||||
|
||||
public int polygonSides
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public List<Vector2[]> GetSpriteOutlineAt(int i)
|
||||
{
|
||||
return m_Outline[i];
|
||||
}
|
||||
|
||||
public void GeneratePolygonOutline()
|
||||
{
|
||||
for (int i = 0; i < m_RectsCache.spriteRects.Count; i++)
|
||||
{
|
||||
SpriteRect currentRect = m_RectsCache.spriteRects[i];
|
||||
|
||||
var result = UnityEditor.Sprites.SpriteUtility.GeneratePolygonOutlineVerticesOfSize(polygonSides, (int)currentRect.rect.width, (int)currentRect.rect.height);
|
||||
|
||||
m_Outline.Clear();
|
||||
var newOutlineList = new List<Vector2[]>();
|
||||
newOutlineList.Add(result);
|
||||
m_Outline.Add(newOutlineList);
|
||||
|
||||
spriteEditor.SetDataModified();
|
||||
}
|
||||
Repaint();
|
||||
}
|
||||
|
||||
public override bool ApplyRevert(bool apply)
|
||||
{
|
||||
var outlineProvider = spriteEditor.GetDataProvider<ISpriteOutlineDataProvider>();
|
||||
if (apply)
|
||||
{
|
||||
for (int i = 0; i < m_RectsCache.spriteRects.Count && i < m_Outline.Count; ++i)
|
||||
outlineProvider.SetOutlines(m_RectsCache.spriteRects[i].spriteID, m_Outline[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Outline.Clear();
|
||||
for (int i = 0; i < m_RectsCache.spriteRects.Count; ++i)
|
||||
m_Outline.Add(outlineProvider.GetOutlines(m_RectsCache.spriteRects[i].spriteID));
|
||||
DeterminePolygonSides();
|
||||
ViewUpdateSideCountField();
|
||||
}
|
||||
return base.ApplyRevert(apply);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
using UIElementButton = UnityEngine.UIElements.Button;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal partial class SpritePolygonModeModule : SpriteFrameModuleBase
|
||||
{
|
||||
private static class SpritePolygonModeStyles
|
||||
{
|
||||
public static readonly GUIContent changeShapeLabel = EditorGUIUtility.TrTextContent("Change Shape");
|
||||
}
|
||||
|
||||
private VisualElement m_PolygonShapeView;
|
||||
private UIElementButton m_ChangeButton;
|
||||
private VisualElement m_WarningMessage;
|
||||
|
||||
// overrides for SpriteFrameModuleViewBase
|
||||
private void AddMainUI(VisualElement element)
|
||||
{
|
||||
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Packages/com.unity.2d.sprite/Editor/UI/SpriteEditor/PolygonChangeShapeWindow.uxml") as VisualTreeAsset;
|
||||
m_PolygonShapeView = visualTree.CloneTree().Q<VisualElement>("polygonShapeWindow");
|
||||
m_PolygonShapeView.RegisterCallback<MouseDownEvent>((e) => { e.StopPropagation(); });
|
||||
m_PolygonShapeView.RegisterCallback<MouseUpEvent>((e) => { e.StopPropagation(); });
|
||||
SetupPolygonChangeShapeWindowElements(m_PolygonShapeView);
|
||||
element.Add(m_PolygonShapeView);
|
||||
}
|
||||
|
||||
public override void DoMainGUI()
|
||||
{
|
||||
base.DoMainGUI();
|
||||
DrawGizmos();
|
||||
|
||||
HandleGizmoMode();
|
||||
|
||||
HandleBorderCornerScalingHandles();
|
||||
HandleBorderSidePointScalingSliders();
|
||||
|
||||
HandleBorderSideScalingHandles();
|
||||
HandlePivotHandle();
|
||||
|
||||
if (!MouseOnTopOfInspector())
|
||||
spriteEditor.HandleSpriteSelection();
|
||||
}
|
||||
|
||||
public override void DoToolbarGUI(Rect toolbarRect)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(spriteEditor.editingDisabled))
|
||||
{
|
||||
GUIStyle skin = EditorStyles.toolbarPopup;
|
||||
Rect drawArea = toolbarRect;
|
||||
drawArea.width = skin.CalcSize(SpritePolygonModeStyles.changeShapeLabel).x;
|
||||
SpriteUtilityWindow.DrawToolBarWidget(ref drawArea, ref toolbarRect, (adjustedDrawArea) =>
|
||||
{
|
||||
showChangeShapeWindow = GUI.Toggle(adjustedDrawArea, showChangeShapeWindow, SpritePolygonModeStyles.changeShapeLabel, EditorStyles.toolbarButton);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGizmos()
|
||||
{
|
||||
if (eventSystem.current.type != EventType.Repaint)
|
||||
return;
|
||||
for (int i = 0; i < spriteCount; i++)
|
||||
{
|
||||
List<Vector2[]> outline = GetSpriteOutlineAt(i);
|
||||
Vector2 offset = GetSpriteRectAt(i).size * 0.5f;
|
||||
if (outline.Count > 0)
|
||||
{
|
||||
SpriteEditorUtility.BeginLines(new Color(0.75f, 0.75f, 0.75f, 0.75f));
|
||||
for (int j = 0; j < outline.Count; ++j)
|
||||
{
|
||||
for (int k = 0, last = outline[j].Length - 1; k < outline[j].Length; last = k, ++k)
|
||||
SpriteEditorUtility.DrawLine(outline[j][last] + offset, outline[j][k] + offset);
|
||||
}
|
||||
SpriteEditorUtility.EndLines();
|
||||
}
|
||||
}
|
||||
DrawSpriteRectGizmos();
|
||||
}
|
||||
|
||||
private void ViewUpdateSideCountField()
|
||||
{
|
||||
var sidesField = m_PolygonShapeView.Q<IntegerField>("labelIntegerField");
|
||||
sidesField.value = polygonSides;
|
||||
}
|
||||
|
||||
private void SetupPolygonChangeShapeWindowElements(VisualElement moduleView)
|
||||
{
|
||||
var sidesField = moduleView.Q<IntegerField>("labelIntegerField");
|
||||
sidesField.SetValueWithoutNotify(polygonSides);
|
||||
sidesField.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
polygonSides = (int)evt.newValue;
|
||||
ShowHideWarningMessage();
|
||||
});
|
||||
m_ChangeButton = moduleView.Q<UIElementButton>("changeButton");
|
||||
m_ChangeButton.RegisterCallback<MouseUpEvent>((e) =>
|
||||
{
|
||||
if (isSidesValid)
|
||||
{
|
||||
GeneratePolygonOutline();
|
||||
showChangeShapeWindow = false;
|
||||
}
|
||||
});
|
||||
m_WarningMessage = moduleView.Q("warning");
|
||||
ShowHideWarningMessage();
|
||||
}
|
||||
|
||||
void ShowHideWarningMessage()
|
||||
{
|
||||
m_WarningMessage.style.display = !isSidesValid ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
m_WarningMessage.style.position = m_WarningMessage.style.display == DisplayStyle.Flex ? Position.Relative : Position.Absolute;
|
||||
|
||||
m_ChangeButton.style.display = isSidesValid ? DisplayStyle.Flex : DisplayStyle.None;;
|
||||
m_ChangeButton.style.position = m_ChangeButton.style.display == DisplayStyle.Flex ? Position.Relative : Position.Absolute;
|
||||
}
|
||||
|
||||
private bool isSidesValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return polygonSides == 0 || (polygonSides >= 3 && polygonSides <= 128);
|
||||
}
|
||||
}
|
||||
|
||||
public bool showChangeShapeWindow
|
||||
{
|
||||
get { return m_PolygonShapeView.style.display == DisplayStyle.Flex; }
|
||||
set
|
||||
{
|
||||
var displayValue = value ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (m_PolygonShapeView.style.display == displayValue)
|
||||
return;
|
||||
m_PolygonShapeView.style.display = displayValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,915 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
// We need this so that undo/redo works
|
||||
[Serializable]
|
||||
internal class SpriteOutline
|
||||
{
|
||||
[SerializeField]
|
||||
public List<Vector2> m_Path = new List<Vector2>();
|
||||
|
||||
public void Add(Vector2 point)
|
||||
{
|
||||
m_Path.Add(point);
|
||||
}
|
||||
|
||||
public void Insert(int index, Vector2 point)
|
||||
{
|
||||
m_Path.Insert(index, point);
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
m_Path.RemoveAt(index);
|
||||
}
|
||||
|
||||
public Vector2 this[int index]
|
||||
{
|
||||
get { return m_Path[index]; }
|
||||
set { m_Path[index] = value; }
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_Path.Count; }
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable<Vector2> addRange)
|
||||
{
|
||||
m_Path.AddRange(addRange);
|
||||
}
|
||||
}
|
||||
|
||||
// Collection of outlines for a single Sprite
|
||||
[Serializable]
|
||||
internal class SpriteOutlineList
|
||||
{
|
||||
[SerializeField]
|
||||
List<SpriteOutline> m_SpriteOutlines = new List<SpriteOutline>();
|
||||
[SerializeField]
|
||||
float m_TessellationDetail = 0;
|
||||
|
||||
public List<SpriteOutline> spriteOutlines { get { return m_SpriteOutlines; } set { m_SpriteOutlines = value; } }
|
||||
public GUID spriteID { get; private set; }
|
||||
|
||||
public float tessellationDetail
|
||||
{
|
||||
get { return m_TessellationDetail; }
|
||||
set
|
||||
{
|
||||
m_TessellationDetail = value;
|
||||
m_TessellationDetail = Mathf.Min(1, m_TessellationDetail);
|
||||
m_TessellationDetail = Mathf.Max(0, m_TessellationDetail);
|
||||
}
|
||||
}
|
||||
|
||||
public SpriteOutlineList(GUID guid)
|
||||
{
|
||||
this.spriteID = guid;
|
||||
m_SpriteOutlines = new List<SpriteOutline>();
|
||||
}
|
||||
|
||||
public SpriteOutlineList(GUID guid, List<Vector2[]> list)
|
||||
{
|
||||
this.spriteID = guid;
|
||||
|
||||
m_SpriteOutlines = new List<SpriteOutline>(list.Count);
|
||||
for (int i = 0; i < list.Count; ++i)
|
||||
{
|
||||
var newList = new SpriteOutline();
|
||||
newList.m_Path.AddRange(list[i]);
|
||||
m_SpriteOutlines.Add(newList);
|
||||
}
|
||||
}
|
||||
|
||||
public SpriteOutlineList(GUID guid, List<SpriteOutline> list)
|
||||
{
|
||||
this.spriteID = guid;
|
||||
|
||||
m_SpriteOutlines = list;
|
||||
}
|
||||
|
||||
public List<Vector2[]> ToListVector()
|
||||
{
|
||||
var value = new List<Vector2[]>(m_SpriteOutlines.Count);
|
||||
foreach (var s in m_SpriteOutlines)
|
||||
{
|
||||
value.Add(s.m_Path.ToArray());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public List<Vector2[]> ToListVectorCapped(Rect rect)
|
||||
{
|
||||
var value = ToListVector();
|
||||
rect.center = Vector2.zero;
|
||||
foreach (var path in value)
|
||||
{
|
||||
for (int i = 0; i < path.Length; ++i)
|
||||
{
|
||||
var point = path[i];
|
||||
path[i] = SpriteOutlineModule.CapPointToRect(point, rect);;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public SpriteOutline this[int index]
|
||||
{
|
||||
get { return IsValidIndex(index) ? m_SpriteOutlines[index] : null; }
|
||||
set
|
||||
{
|
||||
if (IsValidIndex(index))
|
||||
m_SpriteOutlines[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator List<SpriteOutline>(SpriteOutlineList list)
|
||||
{
|
||||
return list != null ? list.m_SpriteOutlines : null;
|
||||
}
|
||||
|
||||
public int Count { get { return m_SpriteOutlines.Count; } }
|
||||
|
||||
bool IsValidIndex(int index)
|
||||
{
|
||||
return index >= 0 && index < Count;
|
||||
}
|
||||
}
|
||||
|
||||
// Collection of Sprites' outlines
|
||||
internal class SpriteOutlineModel : ScriptableObject
|
||||
{
|
||||
[SerializeField]
|
||||
List<SpriteOutlineList> m_SpriteOutlineList = new List<SpriteOutlineList>();
|
||||
|
||||
private SpriteOutlineModel()
|
||||
{}
|
||||
|
||||
public SpriteOutlineList this[int index]
|
||||
{
|
||||
get { return IsValidIndex(index) ? m_SpriteOutlineList[index] : null; }
|
||||
set
|
||||
{
|
||||
if (IsValidIndex(index))
|
||||
m_SpriteOutlineList[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public SpriteOutlineList this[GUID guid]
|
||||
{
|
||||
get { return m_SpriteOutlineList.FirstOrDefault(x => x.spriteID == guid); }
|
||||
set
|
||||
{
|
||||
var index = m_SpriteOutlineList.FindIndex(x => x.spriteID == guid);
|
||||
if (index != -1)
|
||||
m_SpriteOutlineList[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddListVector2(GUID guid, List<Vector2[]> outline)
|
||||
{
|
||||
m_SpriteOutlineList.Add(new SpriteOutlineList(guid, outline));
|
||||
}
|
||||
|
||||
public int Count { get { return m_SpriteOutlineList.Count; } }
|
||||
|
||||
bool IsValidIndex(int index)
|
||||
{
|
||||
return index >= 0 && index < Count;
|
||||
}
|
||||
}
|
||||
|
||||
[RequireSpriteDataProvider(typeof(ISpriteOutlineDataProvider), typeof(ITextureDataProvider))]
|
||||
internal class SpriteOutlineModule : SpriteEditorModuleBase
|
||||
{
|
||||
class Styles
|
||||
{
|
||||
public GUIContent generateOutlineLabel = EditorGUIUtility.TrTextContent("Generate", "Generate new outline based on mesh detail value.");
|
||||
public GUIContent outlineTolerance = EditorGUIUtility.TrTextContent("Outline Tolerance", "Sets how tight the outline should be from the sprite.");
|
||||
public GUIContent snapButtonLabel = EditorGUIUtility.TrTextContent("Snap", "Snap points to nearest pixel");
|
||||
|
||||
public GUIContent copyButtonLabel = EditorGUIUtility.TrTextContent("Copy", "Copy outline from Sprite");
|
||||
public GUIContent pasteButtonLabel = EditorGUIUtility.TrTextContent("Paste", "Paste outline to Sprite");
|
||||
public GUIContent pasteAllButtonLabel = EditorGUIUtility.TrTextContent("Paste All", "Paste outline to all Sprites");
|
||||
|
||||
public GUIContent generatingOutlineDialogTitle = EditorGUIUtility.TrTextContent("Outline");
|
||||
public GUIContent generatingOutlineDialogContent = EditorGUIUtility.TrTextContent("Generating outline {0}/{1}");
|
||||
public Color spriteBorderColor = new Color(0.25f, 0.5f, 1f, 0.75f);
|
||||
}
|
||||
|
||||
protected SpriteRect m_Selected;
|
||||
|
||||
private const float k_HandleSize = 5f;
|
||||
private readonly string k_DeleteCommandName = EventCommandNames.Delete;
|
||||
private readonly string k_SoftDeleteCommandName = EventCommandNames.SoftDelete;
|
||||
|
||||
private ShapeEditor[] m_ShapeEditors;
|
||||
private bool m_RequestRepaint;
|
||||
private Matrix4x4 m_HandleMatrix;
|
||||
private Vector2 m_MousePosition;
|
||||
private bool m_Snap = true;
|
||||
private ShapeEditorRectSelectionTool m_ShapeSelectionUI;
|
||||
private bool m_WasRectSelecting = false;
|
||||
private Rect? m_SelectionRect;
|
||||
private ITexture2D m_OutlineTexture;
|
||||
private Styles m_Styles;
|
||||
protected SpriteOutlineModel m_Outline;
|
||||
private SpriteOutlineList m_CopyOutline = null;
|
||||
protected ITextureDataProvider m_TextureDataProvider;
|
||||
|
||||
public SpriteOutlineModule(ISpriteEditor sem, IEventSystem es, IUndoSystem us, IAssetDatabase ad, IGUIUtility gu, IShapeEditorFactory sef, ITexture2D outlineTexture)
|
||||
{
|
||||
spriteEditorWindow = sem;
|
||||
undoSystem = us;
|
||||
eventSystem = es;
|
||||
assetDatabase = ad;
|
||||
guiUtility = gu;
|
||||
shapeEditorFactory = sef;
|
||||
m_OutlineTexture = outlineTexture;
|
||||
|
||||
m_ShapeSelectionUI = new ShapeEditorRectSelectionTool(gu);
|
||||
|
||||
m_ShapeSelectionUI.RectSelect += RectSelect;
|
||||
m_ShapeSelectionUI.ClearSelection += ClearSelection;
|
||||
}
|
||||
|
||||
public override string moduleName
|
||||
{
|
||||
get { return "Custom Outline"; }
|
||||
}
|
||||
|
||||
public override bool ApplyRevert(bool apply)
|
||||
{
|
||||
if (m_Outline != null)
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
var outlineDataProvider = spriteEditorWindow.GetDataProvider<ISpriteOutlineDataProvider>();
|
||||
for (int i = 0; i < m_Outline.Count; ++i)
|
||||
{
|
||||
outlineDataProvider.SetOutlines(m_Outline[i].spriteID, m_Outline[i].ToListVector());
|
||||
outlineDataProvider.SetTessellationDetail(m_Outline[i].spriteID, m_Outline[i].tessellationDetail);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptableObject.DestroyImmediate(m_Outline);
|
||||
m_Outline = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Styles styles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Styles == null)
|
||||
m_Styles = new Styles();
|
||||
return m_Styles;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual List<SpriteOutline> selectedShapeOutline
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Outline[m_Selected.spriteID].spriteOutlines;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_Outline[m_Selected.spriteID].spriteOutlines = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool shapeEditorDirty
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private bool editingDisabled
|
||||
{
|
||||
get { return spriteEditorWindow.editingDisabled; }
|
||||
}
|
||||
|
||||
private ISpriteEditor spriteEditorWindow
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private IUndoSystem undoSystem
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private IEventSystem eventSystem
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private IAssetDatabase assetDatabase
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private IGUIUtility guiUtility
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private IShapeEditorFactory shapeEditorFactory
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private void RectSelect(Rect r, ShapeEditor.SelectionType selectionType)
|
||||
{
|
||||
var localRect = EditorGUIExt.FromToRect(ScreenToLocal(r.min), ScreenToLocal(r.max));
|
||||
m_SelectionRect = localRect;
|
||||
}
|
||||
|
||||
private void ClearSelection()
|
||||
{
|
||||
m_RequestRepaint = true;
|
||||
}
|
||||
|
||||
protected virtual void LoadOutline()
|
||||
{
|
||||
m_Outline = ScriptableObject.CreateInstance<SpriteOutlineModel>();
|
||||
m_Outline.hideFlags = HideFlags.HideAndDontSave;
|
||||
var spriteDataProvider = spriteEditorWindow.GetDataProvider<ISpriteEditorDataProvider>();
|
||||
var outlineDataProvider = spriteEditorWindow.GetDataProvider<ISpriteOutlineDataProvider>();
|
||||
foreach (var rect in spriteDataProvider.GetSpriteRects())
|
||||
{
|
||||
var outlines = outlineDataProvider.GetOutlines(rect.spriteID);
|
||||
m_Outline.AddListVector2(rect.spriteID, outlines);
|
||||
m_Outline[m_Outline.Count - 1].tessellationDetail = outlineDataProvider.GetTessellationDetail(rect.spriteID);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnModuleActivate()
|
||||
{
|
||||
m_TextureDataProvider = spriteEditorWindow.GetDataProvider<ITextureDataProvider>();
|
||||
LoadOutline();
|
||||
GenerateOutlineIfNotExist();
|
||||
undoSystem.RegisterUndoCallback(UndoRedoPerformed);
|
||||
shapeEditorDirty = true;
|
||||
SetupShapeEditor();
|
||||
spriteEditorWindow.enableMouseMoveEvent = true;
|
||||
}
|
||||
|
||||
void GenerateOutlineIfNotExist()
|
||||
{
|
||||
var rectCache = spriteEditorWindow.GetDataProvider<ISpriteEditorDataProvider>().GetSpriteRects();
|
||||
if (rectCache != null)
|
||||
{
|
||||
bool needApply = false;
|
||||
for (int i = 0; i < rectCache.Length; ++i)
|
||||
{
|
||||
var rect = rectCache[i];
|
||||
if (!HasShapeOutline(rect))
|
||||
{
|
||||
EditorUtility.DisplayProgressBar(styles.generatingOutlineDialogTitle.text,
|
||||
string.Format(styles.generatingOutlineDialogContent.text, i + 1 , rectCache.Length),
|
||||
(float)(i) / rectCache.Length);
|
||||
|
||||
SetupShapeEditorOutline(rect);
|
||||
needApply = true;
|
||||
}
|
||||
}
|
||||
if (needApply)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
spriteEditorWindow.ApplyOrRevertModification(true);
|
||||
LoadOutline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnModuleDeactivate()
|
||||
{
|
||||
undoSystem.UnregisterUndoCallback(UndoRedoPerformed);
|
||||
CleanupShapeEditors();
|
||||
m_Selected = null;
|
||||
spriteEditorWindow.enableMouseMoveEvent = false;
|
||||
if (m_Outline != null)
|
||||
{
|
||||
undoSystem.ClearUndo(m_Outline);
|
||||
ScriptableObject.DestroyImmediate(m_Outline);
|
||||
m_Outline = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void DoMainGUI()
|
||||
{
|
||||
IEvent evt = eventSystem.current;
|
||||
|
||||
m_RequestRepaint = false;
|
||||
m_HandleMatrix = Handles.matrix;
|
||||
|
||||
m_MousePosition = Handles.inverseMatrix.MultiplyPoint(eventSystem.current.mousePosition);
|
||||
if (m_Selected == null || !m_Selected.rect.Contains(m_MousePosition) && !IsMouseOverOutlinePoints() && evt.shift == false)
|
||||
spriteEditorWindow.HandleSpriteSelection();
|
||||
|
||||
HandleCreateNewOutline();
|
||||
|
||||
m_WasRectSelecting = m_ShapeSelectionUI.isSelecting;
|
||||
|
||||
UpdateShapeEditors();
|
||||
|
||||
m_ShapeSelectionUI.OnGUI();
|
||||
|
||||
DrawGizmos();
|
||||
|
||||
if (m_RequestRepaint || evt.type == EventType.MouseMove)
|
||||
spriteEditorWindow.RequestRepaint();
|
||||
}
|
||||
|
||||
public override void DoToolbarGUI(Rect drawArea)
|
||||
{
|
||||
var style = styles;
|
||||
|
||||
Rect snapDrawArea = new Rect(drawArea.x, drawArea.y, EditorStyles.toolbarButton.CalcSize(style.snapButtonLabel).x, drawArea.height);
|
||||
m_Snap = GUI.Toggle(snapDrawArea, m_Snap, style.snapButtonLabel, EditorStyles.toolbarButton);
|
||||
|
||||
using (new EditorGUI.DisabledScope(editingDisabled || m_Selected == null))
|
||||
{
|
||||
float totalWidth = drawArea.width - snapDrawArea.width;
|
||||
drawArea.x = snapDrawArea.xMax;
|
||||
drawArea.width = EditorStyles.toolbarButton.CalcSize(style.outlineTolerance).x;
|
||||
totalWidth -= drawArea.width;
|
||||
if (totalWidth < 0)
|
||||
drawArea.width += totalWidth;
|
||||
if (drawArea.width > 0)
|
||||
GUI.Label(drawArea, style.outlineTolerance, EditorStyles.miniLabel);
|
||||
drawArea.x += drawArea.width;
|
||||
|
||||
drawArea.width = 100;
|
||||
totalWidth -= drawArea.width;
|
||||
if (totalWidth < 0)
|
||||
drawArea.width += totalWidth;
|
||||
|
||||
if (drawArea.width > 0)
|
||||
{
|
||||
float tesselationValue = m_Selected != null ? m_Outline[m_Selected.spriteID].tessellationDetail : 0;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
float oldFieldWidth = EditorGUIUtility.fieldWidth;
|
||||
float oldLabelWidth = EditorGUIUtility.labelWidth;
|
||||
EditorGUIUtility.fieldWidth = 35;
|
||||
EditorGUIUtility.labelWidth = 1;
|
||||
tesselationValue = EditorGUI.Slider(drawArea, Mathf.Clamp01(tesselationValue), 0, 1);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
RecordUndo();
|
||||
m_Outline[m_Selected.spriteID].tessellationDetail = tesselationValue;
|
||||
}
|
||||
EditorGUIUtility.fieldWidth = oldFieldWidth;
|
||||
EditorGUIUtility.labelWidth = oldLabelWidth;
|
||||
}
|
||||
|
||||
drawArea.x += drawArea.width + 2;
|
||||
drawArea.width = EditorStyles.toolbarButton.CalcSize(style.generateOutlineLabel).x;
|
||||
totalWidth -= drawArea.width;
|
||||
if (totalWidth < 0)
|
||||
drawArea.width += totalWidth;
|
||||
|
||||
if (drawArea.width > 0 && GUI.Button(drawArea, style.generateOutlineLabel, EditorStyles.toolbarButton))
|
||||
{
|
||||
RecordUndo();
|
||||
selectedShapeOutline.Clear();
|
||||
SetupShapeEditorOutline(m_Selected);
|
||||
spriteEditorWindow.SetDataModified();
|
||||
shapeEditorDirty = true;
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledScope(m_Selected == null || !HasShapeOutline(m_Selected)))
|
||||
{
|
||||
drawArea.x += drawArea.width + 2;
|
||||
drawArea.width = EditorStyles.toolbarButton.CalcSize(style.copyButtonLabel).x;
|
||||
totalWidth -= drawArea.width;
|
||||
if (totalWidth < 0)
|
||||
drawArea.width += totalWidth;
|
||||
|
||||
if (drawArea.width > 0 && GUI.Button(drawArea, style.copyButtonLabel, EditorStyles.toolbarButton))
|
||||
{
|
||||
Copy();
|
||||
}
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledScope(m_Selected == null || m_CopyOutline == null))
|
||||
{
|
||||
drawArea.x += drawArea.width;
|
||||
drawArea.width = EditorStyles.toolbarButton.CalcSize(style.pasteButtonLabel).x;
|
||||
totalWidth -= drawArea.width;
|
||||
if (totalWidth < 0)
|
||||
drawArea.width += totalWidth;
|
||||
|
||||
if (drawArea.width > 0 && GUI.Button(drawArea, style.pasteButtonLabel, EditorStyles.toolbarButton))
|
||||
{
|
||||
Paste();
|
||||
}
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledScope(m_CopyOutline == null))
|
||||
{
|
||||
drawArea.x += drawArea.width;
|
||||
drawArea.width = EditorStyles.toolbarButton.CalcSize(style.pasteAllButtonLabel).x;
|
||||
totalWidth -= drawArea.width;
|
||||
if (totalWidth < 0)
|
||||
drawArea.width += totalWidth;
|
||||
|
||||
if (drawArea.width > 0 && GUI.Button(drawArea, style.pasteAllButtonLabel, EditorStyles.toolbarButton))
|
||||
{
|
||||
PasteAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DoPostGUI()
|
||||
{}
|
||||
|
||||
public override bool CanBeActivated()
|
||||
{
|
||||
return SpriteFrameModule.GetSpriteImportMode(spriteEditorWindow.GetDataProvider<ISpriteEditorDataProvider>()) != SpriteImportMode.None;
|
||||
}
|
||||
|
||||
private void RecordUndo()
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_Outline, "Outline changed");
|
||||
}
|
||||
|
||||
public void CreateNewOutline(Rect rectOutline)
|
||||
{
|
||||
Rect rect = m_Selected.rect;
|
||||
if (rect.Contains(rectOutline.min) && rect.Contains(rectOutline.max))
|
||||
{
|
||||
RecordUndo();
|
||||
SpriteOutline so = new SpriteOutline();
|
||||
Vector2 outlineOffset = new Vector2(0.5f * rect.width + rect.x, 0.5f * rect.height + rect.y);
|
||||
Rect selectionRect = new Rect(rectOutline);
|
||||
selectionRect.min = SnapPoint(rectOutline.min);
|
||||
selectionRect.max = SnapPoint(rectOutline.max);
|
||||
so.Add(CapPointToRect(new Vector2(selectionRect.xMin, selectionRect.yMin), rect) - outlineOffset);
|
||||
so.Add(CapPointToRect(new Vector2(selectionRect.xMin, selectionRect.yMax), rect) - outlineOffset);
|
||||
so.Add(CapPointToRect(new Vector2(selectionRect.xMax, selectionRect.yMax), rect) - outlineOffset);
|
||||
so.Add(CapPointToRect(new Vector2(selectionRect.xMax, selectionRect.yMin), rect) - outlineOffset);
|
||||
selectedShapeOutline.Add(so);
|
||||
spriteEditorWindow.SetDataModified();
|
||||
shapeEditorDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCreateNewOutline()
|
||||
{
|
||||
if (m_WasRectSelecting && m_ShapeSelectionUI.isSelecting == false && m_SelectionRect != null && m_Selected != null)
|
||||
{
|
||||
bool createNewOutline = true;
|
||||
foreach (var se in m_ShapeEditors)
|
||||
{
|
||||
if (se.selectedPoints.Count != 0)
|
||||
{
|
||||
createNewOutline = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (createNewOutline)
|
||||
CreateNewOutline(m_SelectionRect.Value);
|
||||
}
|
||||
m_SelectionRect = null;
|
||||
}
|
||||
|
||||
public void UpdateShapeEditors()
|
||||
{
|
||||
SetupShapeEditor();
|
||||
|
||||
if (m_Selected != null)
|
||||
{
|
||||
IEvent currentEvent = eventSystem.current;
|
||||
var wantsDelete = currentEvent.type == EventType.ExecuteCommand && (currentEvent.commandName == k_SoftDeleteCommandName || currentEvent.commandName == k_DeleteCommandName);
|
||||
|
||||
for (int i = 0; i < m_ShapeEditors.Length; ++i)
|
||||
{
|
||||
if (m_ShapeEditors[i].GetPointsCount() == 0)
|
||||
continue;
|
||||
|
||||
m_ShapeEditors[i].inEditMode = true;
|
||||
m_ShapeEditors[i].OnGUI();
|
||||
if (shapeEditorDirty)
|
||||
break;
|
||||
}
|
||||
|
||||
if (wantsDelete)
|
||||
{
|
||||
// remove outline which have lesser than 3 points
|
||||
for (int i = selectedShapeOutline.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (selectedShapeOutline[i].Count < 3)
|
||||
{
|
||||
selectedShapeOutline.RemoveAt(i);
|
||||
shapeEditorDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsMouseOverOutlinePoints()
|
||||
{
|
||||
if (m_Selected == null)
|
||||
return false;
|
||||
Vector2 outlineOffset = new Vector2(0.5f * m_Selected.rect.width + m_Selected.rect.x, 0.5f * m_Selected.rect.height + m_Selected.rect.y);
|
||||
float handleSize = GetHandleSize();
|
||||
Rect r = new Rect(0, 0, handleSize * 2, handleSize * 2);
|
||||
for (int i = 0; i < selectedShapeOutline.Count; ++i)
|
||||
{
|
||||
var outline = selectedShapeOutline[i];
|
||||
for (int j = 0; j < outline.Count; ++j)
|
||||
{
|
||||
r.center = outline[j] + outlineOffset;
|
||||
if (r.Contains(m_MousePosition))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private float GetHandleSize()
|
||||
{
|
||||
return k_HandleSize / m_HandleMatrix.m00;
|
||||
}
|
||||
|
||||
private void CleanupShapeEditors()
|
||||
{
|
||||
if (m_ShapeEditors != null)
|
||||
{
|
||||
for (int i = 0; i < m_ShapeEditors.Length; ++i)
|
||||
{
|
||||
for (int j = 0; j < m_ShapeEditors.Length; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
m_ShapeEditors[j].UnregisterFromShapeEditor(m_ShapeEditors[i]);
|
||||
}
|
||||
m_ShapeEditors[i].OnDisable();
|
||||
}
|
||||
}
|
||||
m_ShapeEditors = null;
|
||||
}
|
||||
|
||||
public void SetupShapeEditor()
|
||||
{
|
||||
if (shapeEditorDirty || m_Selected != spriteEditorWindow.selectedSpriteRect)
|
||||
{
|
||||
m_Selected = spriteEditorWindow.selectedSpriteRect;
|
||||
CleanupShapeEditors();
|
||||
|
||||
if (m_Selected != null)
|
||||
{
|
||||
if (!HasShapeOutline(m_Selected))
|
||||
SetupShapeEditorOutline(m_Selected);
|
||||
m_ShapeEditors = new ShapeEditor[selectedShapeOutline.Count];
|
||||
|
||||
for (int i = 0; i < selectedShapeOutline.Count; ++i)
|
||||
{
|
||||
int outlineIndex = i;
|
||||
m_ShapeEditors[i] = shapeEditorFactory.CreateShapeEditor();
|
||||
m_ShapeEditors[i].SetRectSelectionTool(m_ShapeSelectionUI);
|
||||
m_ShapeEditors[i].LocalToWorldMatrix = () => m_HandleMatrix;
|
||||
m_ShapeEditors[i].LocalToScreen = (point) => Handles.matrix.MultiplyPoint(point);
|
||||
m_ShapeEditors[i].ScreenToLocal = ScreenToLocal;
|
||||
m_ShapeEditors[i].RecordUndo = RecordUndo;
|
||||
m_ShapeEditors[i].GetHandleSize = GetHandleSize;
|
||||
m_ShapeEditors[i].lineTexture = m_OutlineTexture;
|
||||
m_ShapeEditors[i].Snap = SnapPoint;
|
||||
m_ShapeEditors[i].GetPointPosition = (index) => GetPointPosition(outlineIndex, index);
|
||||
m_ShapeEditors[i].SetPointPosition = (index, position) => SetPointPosition(outlineIndex, index, position);
|
||||
m_ShapeEditors[i].InsertPointAt = (index, position) => InsertPointAt(outlineIndex, index, position);
|
||||
m_ShapeEditors[i].RemovePointAt = (index) => RemovePointAt(outlineIndex, index);
|
||||
m_ShapeEditors[i].GetPointsCount = () => GetPointsCount(outlineIndex);
|
||||
}
|
||||
for (int i = 0; i < selectedShapeOutline.Count; ++i)
|
||||
{
|
||||
for (int j = 0; j < selectedShapeOutline.Count; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
m_ShapeEditors[j].RegisterToShapeEditor(m_ShapeEditors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ShapeEditors = new ShapeEditor[0];
|
||||
}
|
||||
}
|
||||
shapeEditorDirty = false;
|
||||
}
|
||||
|
||||
protected virtual bool HasShapeOutline(SpriteRect spriteRect)
|
||||
{
|
||||
var outline = m_Outline[spriteRect.spriteID] != null ? m_Outline[spriteRect.spriteID].spriteOutlines : null;
|
||||
return outline != null;
|
||||
}
|
||||
|
||||
protected virtual void SetupShapeEditorOutline(SpriteRect spriteRect)
|
||||
{
|
||||
var outline = m_Outline[spriteRect.spriteID];
|
||||
var outlines = GenerateSpriteRectOutline(spriteRect.rect,
|
||||
Math.Abs(outline.tessellationDetail - (-1f)) < Mathf.Epsilon ? 0 : outline.tessellationDetail,
|
||||
0, m_TextureDataProvider);
|
||||
if (outlines.Count == 0)
|
||||
{
|
||||
Vector2 halfSize = spriteRect.rect.size * 0.5f;
|
||||
outlines = new List<SpriteOutline>()
|
||||
{
|
||||
new SpriteOutline()
|
||||
{
|
||||
m_Path = new List<Vector2>()
|
||||
{
|
||||
new Vector2(-halfSize.x, -halfSize.y),
|
||||
new Vector2(-halfSize.x, halfSize.y),
|
||||
new Vector2(halfSize.x, halfSize.y),
|
||||
new Vector2(halfSize.x, -halfSize.y),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
m_Outline[spriteRect.spriteID].spriteOutlines = outlines;
|
||||
}
|
||||
|
||||
public Vector3 SnapPoint(Vector3 position)
|
||||
{
|
||||
if (m_Snap)
|
||||
{
|
||||
position.x = Mathf.RoundToInt(position.x);
|
||||
position.y = Mathf.RoundToInt(position.y);
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
public Vector3 GetPointPosition(int outlineIndex, int pointIndex)
|
||||
{
|
||||
if (outlineIndex >= 0 && outlineIndex < selectedShapeOutline.Count)
|
||||
{
|
||||
var outline = selectedShapeOutline[outlineIndex];
|
||||
if (pointIndex >= 0 && pointIndex < outline.Count)
|
||||
{
|
||||
return ConvertSpriteRectSpaceToTextureSpace(outline[pointIndex]);
|
||||
}
|
||||
}
|
||||
return new Vector3(float.NaN, float.NaN, float.NaN);
|
||||
}
|
||||
|
||||
public void SetPointPosition(int outlineIndex, int pointIndex, Vector3 position)
|
||||
{
|
||||
selectedShapeOutline[outlineIndex][pointIndex] = ConvertTextureSpaceToSpriteRectSpace(CapPointToRect(position, m_Selected.rect));
|
||||
spriteEditorWindow.SetDataModified();
|
||||
}
|
||||
|
||||
public void InsertPointAt(int outlineIndex, int pointIndex, Vector3 position)
|
||||
{
|
||||
selectedShapeOutline[outlineIndex].Insert(pointIndex, ConvertTextureSpaceToSpriteRectSpace(CapPointToRect(position, m_Selected.rect)));
|
||||
spriteEditorWindow.SetDataModified();
|
||||
}
|
||||
|
||||
public void RemovePointAt(int outlineIndex, int i)
|
||||
{
|
||||
selectedShapeOutline[outlineIndex].RemoveAt(i);
|
||||
spriteEditorWindow.SetDataModified();
|
||||
}
|
||||
|
||||
public int GetPointsCount(int outlineIndex)
|
||||
{
|
||||
return selectedShapeOutline[outlineIndex].Count;
|
||||
}
|
||||
|
||||
private Vector2 ConvertSpriteRectSpaceToTextureSpace(Vector2 value)
|
||||
{
|
||||
Vector2 outlineOffset = new Vector2(0.5f * m_Selected.rect.width + m_Selected.rect.x, 0.5f * m_Selected.rect.height + m_Selected.rect.y);
|
||||
value += outlineOffset;
|
||||
return value;
|
||||
}
|
||||
|
||||
private Vector2 ConvertTextureSpaceToSpriteRectSpace(Vector2 value)
|
||||
{
|
||||
Vector2 outlineOffset = new Vector2(0.5f * m_Selected.rect.width + m_Selected.rect.x, 0.5f * m_Selected.rect.height + m_Selected.rect.y);
|
||||
value -= outlineOffset;
|
||||
return value;
|
||||
}
|
||||
|
||||
private Vector3 ScreenToLocal(Vector2 point)
|
||||
{
|
||||
return Handles.inverseMatrix.MultiplyPoint(point);
|
||||
}
|
||||
|
||||
private void UndoRedoPerformed()
|
||||
{
|
||||
shapeEditorDirty = true;
|
||||
}
|
||||
|
||||
private void DrawGizmos()
|
||||
{
|
||||
if (eventSystem.current.type == EventType.Repaint)
|
||||
{
|
||||
var selected = spriteEditorWindow.selectedSpriteRect;
|
||||
if (selected != null)
|
||||
{
|
||||
SpriteEditorUtility.BeginLines(styles.spriteBorderColor);
|
||||
SpriteEditorUtility.DrawBox(selected.rect);
|
||||
SpriteEditorUtility.EndLines();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static List<SpriteOutline> GenerateSpriteRectOutline(Rect rect, float detail, byte alphaTolerance, ITextureDataProvider textureProvider)
|
||||
{
|
||||
List<SpriteOutline> outline = new List<SpriteOutline>();
|
||||
var texture = textureProvider.GetReadableTexture2D();
|
||||
if (texture != null)
|
||||
{
|
||||
Vector2[][] paths;
|
||||
|
||||
// we might have a texture that is capped because of max size or NPOT.
|
||||
// in that case, we need to convert values from capped space to actual texture space and back.
|
||||
int actualWidth = 0, actualHeight = 0;
|
||||
int cappedWidth, cappedHeight;
|
||||
textureProvider.GetTextureActualWidthAndHeight(out actualWidth, out actualHeight);
|
||||
cappedWidth = texture.width;
|
||||
cappedHeight = texture.height;
|
||||
|
||||
Vector2 scale = new Vector2(cappedWidth / (float)actualWidth, cappedHeight / (float)actualHeight);
|
||||
Rect spriteRect = rect;
|
||||
spriteRect.xMin *= scale.x;
|
||||
spriteRect.xMax *= scale.x;
|
||||
spriteRect.yMin *= scale.y;
|
||||
spriteRect.yMax *= scale.y;
|
||||
|
||||
UnityEditor.Sprites.SpriteUtility.GenerateOutline(texture, spriteRect, detail, alphaTolerance, true, out paths);
|
||||
|
||||
Rect capRect = new Rect();
|
||||
capRect.size = rect.size;
|
||||
capRect.center = Vector2.zero;
|
||||
for (int j = 0; j < paths.Length; ++j)
|
||||
{
|
||||
SpriteOutline points = new SpriteOutline();
|
||||
foreach (Vector2 v in paths[j])
|
||||
points.Add(CapPointToRect(new Vector2(v.x / scale.x, v.y / scale.y), capRect));
|
||||
|
||||
outline.Add(points);
|
||||
}
|
||||
}
|
||||
return outline;
|
||||
}
|
||||
|
||||
public void Copy()
|
||||
{
|
||||
if (m_Selected == null || !HasShapeOutline(m_Selected))
|
||||
return;
|
||||
|
||||
m_CopyOutline = new SpriteOutlineList(m_Selected.spriteID, m_Outline[m_Selected.spriteID].ToListVectorCapped(m_Selected.rect));
|
||||
}
|
||||
|
||||
public void Paste()
|
||||
{
|
||||
if (m_Selected == null || m_CopyOutline == null)
|
||||
return;
|
||||
|
||||
RecordUndo();
|
||||
m_Outline[m_Selected.spriteID] = new SpriteOutlineList(m_Selected.spriteID, m_CopyOutline.ToListVectorCapped(m_Selected.rect));
|
||||
spriteEditorWindow.SetDataModified();
|
||||
shapeEditorDirty = true;
|
||||
}
|
||||
|
||||
public void PasteAll()
|
||||
{
|
||||
if (m_CopyOutline == null)
|
||||
return;
|
||||
|
||||
RecordUndo();
|
||||
var rectCache = spriteEditorWindow.GetDataProvider<ISpriteEditorDataProvider>().GetSpriteRects();
|
||||
if (rectCache != null)
|
||||
{
|
||||
foreach (var spriteRect in rectCache)
|
||||
{
|
||||
var outlines = m_CopyOutline.ToListVectorCapped(spriteRect.rect);
|
||||
m_Outline[spriteRect.spriteID] = new SpriteOutlineList(spriteRect.spriteID, outlines);
|
||||
}
|
||||
}
|
||||
spriteEditorWindow.SetDataModified();
|
||||
shapeEditorDirty = true;
|
||||
}
|
||||
|
||||
internal static Vector2 CapPointToRect(Vector2 so, Rect r)
|
||||
{
|
||||
so.x = Mathf.Min(r.xMax, so.x);
|
||||
so.x = Mathf.Max(r.xMin, so.x);
|
||||
so.y = Mathf.Min(r.yMax, so.y);
|
||||
so.y = Mathf.Max(r.yMin, so.y);
|
||||
return so;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
[RequireSpriteDataProvider(typeof(ISpritePhysicsOutlineDataProvider), typeof(ITextureDataProvider))]
|
||||
internal class SpritePhysicsShapeModule : SpriteOutlineModule
|
||||
{
|
||||
private readonly float kDefaultPhysicsTessellationDetail = 0.25f;
|
||||
private readonly byte kDefaultPhysicsAlphaTolerance = 200;
|
||||
|
||||
public SpritePhysicsShapeModule(ISpriteEditor sem, IEventSystem ege, IUndoSystem us, IAssetDatabase ad, IGUIUtility gu, IShapeEditorFactory sef, ITexture2D outlineTexture)
|
||||
: base(sem, ege, us, ad, gu, sef, outlineTexture)
|
||||
{
|
||||
spriteEditorWindow = sem;
|
||||
}
|
||||
|
||||
public override string moduleName
|
||||
{
|
||||
get { return "Custom Physics Shape"; }
|
||||
}
|
||||
|
||||
private ISpriteEditor spriteEditorWindow
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public override bool ApplyRevert(bool apply)
|
||||
{
|
||||
if (m_Outline != null)
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
var dp = spriteEditorWindow.GetDataProvider<ISpritePhysicsOutlineDataProvider>();
|
||||
for (int i = 0; i < m_Outline.Count; ++i)
|
||||
{
|
||||
dp.SetOutlines(m_Outline[i].spriteID, m_Outline[i].ToListVector());
|
||||
dp.SetTessellationDetail(m_Outline[i].spriteID, m_Outline[i].tessellationDetail);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptableObject.DestroyImmediate(m_Outline);
|
||||
m_Outline = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void LoadOutline()
|
||||
{
|
||||
m_Outline = ScriptableObject.CreateInstance<SpriteOutlineModel>();
|
||||
m_Outline.hideFlags = HideFlags.HideAndDontSave;
|
||||
var spriteDataProvider = spriteEditorWindow.GetDataProvider<ISpriteEditorDataProvider>();
|
||||
var outlineDataProvider = spriteEditorWindow.GetDataProvider<ISpritePhysicsOutlineDataProvider>();
|
||||
foreach (var rect in spriteDataProvider.GetSpriteRects())
|
||||
{
|
||||
var outlines = outlineDataProvider.GetOutlines(rect.spriteID);
|
||||
m_Outline.AddListVector2(rect.spriteID, outlines);
|
||||
m_Outline[m_Outline.Count - 1].tessellationDetail = outlineDataProvider.GetTessellationDetail(rect.spriteID);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SetupShapeEditorOutline(SpriteRect spriteRect)
|
||||
{
|
||||
var physicsShape = m_Outline[spriteRect.spriteID];
|
||||
var physicsShapes = GenerateSpriteRectOutline(spriteRect.rect,
|
||||
Math.Abs(physicsShape.tessellationDetail - (-1f)) < Mathf.Epsilon ? kDefaultPhysicsTessellationDetail : physicsShape.tessellationDetail,
|
||||
kDefaultPhysicsAlphaTolerance, m_TextureDataProvider);
|
||||
m_Outline[spriteRect.spriteID].spriteOutlines = physicsShapes;
|
||||
spriteEditorWindow.SetDataModified();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,253 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.U2D.Sprites;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor._2D.Sprite.Editor
|
||||
{
|
||||
[RequireSpriteDataProvider(typeof(ISecondaryTextureDataProvider), typeof(ITextureDataProvider))]
|
||||
internal class SpriteSecondaryTexturesModule : SpriteEditorModuleBase
|
||||
{
|
||||
private static class Styles
|
||||
{
|
||||
public static readonly string invalidEntriesWarning = L10n.Tr("Invalid secondary Texture entries (without names or Textures) have been removed.");
|
||||
public static readonly string nameUniquenessWarning = L10n.Tr("Every secondary Texture attached to the Sprite must have a unique name.");
|
||||
public static readonly string builtInNameCollisionWarning = L10n.Tr("The names _MainTex and _AlphaTex are reserved for internal use.");
|
||||
public static readonly GUIContent panelTitle = EditorGUIUtility.TrTextContent("Secondary Textures");
|
||||
public static readonly GUIContent name = EditorGUIUtility.TrTextContent("Name");
|
||||
public static readonly GUIContent texture = EditorGUIUtility.TrTextContent("Texture");
|
||||
public const float textFieldDropDownWidth = 18.0f;
|
||||
}
|
||||
|
||||
|
||||
ReorderableList m_ReorderableList;
|
||||
Vector2 m_ReorderableListScrollPosition;
|
||||
string[] m_SuggestedNames;
|
||||
private IMGUIContainer m_SecondaryTextureInspectorContainer;
|
||||
internal List<SecondarySpriteTexture> secondaryTextureList { get; private set; }
|
||||
|
||||
public override string moduleName
|
||||
{
|
||||
get { return Styles.panelTitle.text; }
|
||||
}
|
||||
|
||||
public override bool ApplyRevert(bool apply)
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
var secondaryTextureDataProvider = spriteEditor.GetDataProvider<ISecondaryTextureDataProvider>();
|
||||
|
||||
|
||||
// Remove invalid entries.
|
||||
var validEntries = secondaryTextureList.FindAll(x => (x.name != null && x.name != "" && x.texture != null));
|
||||
if (validEntries.Count < secondaryTextureList.Count)
|
||||
Debug.Log(Styles.invalidEntriesWarning);
|
||||
|
||||
secondaryTextureDataProvider.textures = validEntries.ToArray();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CanBeActivated()
|
||||
{
|
||||
var dataProvider = spriteEditor.GetDataProvider<ISpriteEditorDataProvider>();
|
||||
return dataProvider != null && dataProvider.spriteImportMode != SpriteImportMode.None;
|
||||
}
|
||||
|
||||
public override void DoPostGUI()
|
||||
{
|
||||
}
|
||||
|
||||
public void SecondaryTextureReorderableListUI()
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(spriteEditor.editingDisabled))
|
||||
{
|
||||
var windowDimension = spriteEditor.windowDimension;
|
||||
|
||||
GUILayout.BeginArea(new Rect(0, 0, 290, 290), Styles.panelTitle, GUI.skin.window);
|
||||
m_ReorderableListScrollPosition = GUILayout.BeginScrollView(m_ReorderableListScrollPosition);
|
||||
m_ReorderableList.DoLayoutList();
|
||||
GUILayout.EndScrollView();
|
||||
GUILayout.EndArea();
|
||||
|
||||
// Deselect the list item if left click outside the panel area.
|
||||
UnityEngine.Event e = UnityEngine.Event.current;
|
||||
if (e.type == EventType.MouseDown && e.button == 0)
|
||||
{
|
||||
m_ReorderableList.index = -1;
|
||||
OnSelectCallback(m_ReorderableList);
|
||||
spriteEditor.RequestRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DoMainGUI()
|
||||
{
|
||||
}
|
||||
|
||||
public override void DoToolbarGUI(Rect drawArea)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnModuleActivate()
|
||||
{
|
||||
var secondaryTextureDataProvider = spriteEditor.GetDataProvider<ISecondaryTextureDataProvider>();
|
||||
secondaryTextureList = secondaryTextureDataProvider.textures == null ? new List<SecondarySpriteTexture>() : secondaryTextureDataProvider.textures.ToList();
|
||||
|
||||
m_ReorderableListScrollPosition = Vector2.zero;
|
||||
m_ReorderableList = new ReorderableList(secondaryTextureList, typeof(SecondarySpriteTexture), false, false, true, true);
|
||||
m_ReorderableList.drawElementCallback = DrawSpriteSecondaryTextureElement;
|
||||
m_ReorderableList.onAddCallback = AddSpriteSecondaryTextureElement;
|
||||
m_ReorderableList.onRemoveCallback = RemoveSpriteSecondaryTextureElement;
|
||||
m_ReorderableList.onCanAddCallback = CanAddSpriteSecondaryTextureElement;
|
||||
m_ReorderableList.elementHeightCallback = (int index) => (EditorGUIUtility.singleLineHeight * 3) + 5;
|
||||
m_ReorderableList.onSelectCallback = OnSelectCallback;
|
||||
|
||||
spriteEditor.selectedSpriteRect = null;
|
||||
|
||||
string suggestedNamesPrefs = EditorPrefs.GetString("SecondarySpriteTexturePropertyNames");
|
||||
if (!string.IsNullOrEmpty(suggestedNamesPrefs))
|
||||
{
|
||||
m_SuggestedNames = suggestedNamesPrefs.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
for (int i = 0; i < m_SuggestedNames.Length; ++i)
|
||||
m_SuggestedNames[i] = m_SuggestedNames[i].Trim();
|
||||
|
||||
Array.Sort(m_SuggestedNames);
|
||||
}
|
||||
else
|
||||
m_SuggestedNames = null;
|
||||
|
||||
m_SecondaryTextureInspectorContainer = new IMGUIContainer(SecondaryTextureReorderableListUI)
|
||||
{
|
||||
style =
|
||||
{
|
||||
flexGrow = 0,
|
||||
flexBasis = 1,
|
||||
flexShrink = 0,
|
||||
minWidth = 290,
|
||||
minHeight = 290,
|
||||
bottom = 24,
|
||||
right = 24,
|
||||
position = new StyleEnum<Position>(Position.Absolute)
|
||||
},
|
||||
name = "SecondaryTextureInspector"
|
||||
};
|
||||
spriteEditor.GetMainVisualContainer().Add(m_SecondaryTextureInspectorContainer);
|
||||
}
|
||||
|
||||
void OnSelectCallback(ReorderableList list)
|
||||
{
|
||||
// Preview the current selected secondary texture.
|
||||
Texture2D previewTexture = null;
|
||||
int width = 0, height = 0;
|
||||
|
||||
var textureDataProvider = spriteEditor.GetDataProvider<ITextureDataProvider>();
|
||||
if (textureDataProvider != null)
|
||||
{
|
||||
previewTexture = textureDataProvider.previewTexture;
|
||||
textureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||||
}
|
||||
|
||||
if (m_ReorderableList.index >= 0 && m_ReorderableList.index < secondaryTextureList.Count)
|
||||
previewTexture = secondaryTextureList[m_ReorderableList.index].texture;
|
||||
|
||||
if (previewTexture != null)
|
||||
spriteEditor.SetPreviewTexture(previewTexture, width, height);
|
||||
}
|
||||
|
||||
public override void OnModuleDeactivate()
|
||||
{
|
||||
DisplayMainTexture();
|
||||
if (spriteEditor.GetMainVisualContainer().Contains(m_SecondaryTextureInspectorContainer))
|
||||
spriteEditor.GetMainVisualContainer().Remove(m_SecondaryTextureInspectorContainer);
|
||||
}
|
||||
|
||||
void DrawSpriteSecondaryTextureElement(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
bool dataModified = false;
|
||||
float oldLabelWidth = EditorGUIUtility.labelWidth;
|
||||
EditorGUIUtility.labelWidth = 70.0f;
|
||||
SecondarySpriteTexture secondaryTexture = secondaryTextureList[index];
|
||||
|
||||
// "Name" text field
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var r = new Rect(rect.x, rect.y + 5, rect.width - Styles.textFieldDropDownWidth, EditorGUIUtility.singleLineHeight);
|
||||
string newName = EditorGUI.TextField(r, Styles.name, secondaryTexture.name);
|
||||
dataModified = EditorGUI.EndChangeCheck();
|
||||
|
||||
// Suggested names
|
||||
if (m_SuggestedNames != null)
|
||||
{
|
||||
var popupRect = new Rect(r.x + r.width, r.y, Styles.textFieldDropDownWidth, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int selected = EditorGUI.Popup(popupRect, -1, m_SuggestedNames, EditorStyles.textFieldDropDown);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
newName = m_SuggestedNames[selected];
|
||||
dataModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (dataModified)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(newName) && newName != secondaryTexture.name && secondaryTextureList.Exists(x => x.name == newName))
|
||||
Debug.LogWarning(Styles.nameUniquenessWarning);
|
||||
else if (newName == "_MainTex" || newName == "_AlphaTex")
|
||||
Debug.LogWarning(Styles.builtInNameCollisionWarning);
|
||||
else
|
||||
secondaryTexture.name = newName;
|
||||
}
|
||||
|
||||
// "Texture" object field
|
||||
EditorGUI.BeginChangeCheck();
|
||||
r.width = rect.width;
|
||||
r.y += EditorGUIUtility.singleLineHeight;
|
||||
secondaryTexture.texture = EditorGUI.ObjectField(r, Styles.texture, secondaryTexture.texture, typeof(Texture2D), false) as Texture2D;
|
||||
dataModified = dataModified || EditorGUI.EndChangeCheck();
|
||||
|
||||
if (dataModified)
|
||||
{
|
||||
secondaryTextureList[index] = secondaryTexture;
|
||||
spriteEditor.SetDataModified();
|
||||
}
|
||||
|
||||
EditorGUIUtility.labelWidth = oldLabelWidth;
|
||||
}
|
||||
|
||||
void AddSpriteSecondaryTextureElement(ReorderableList list)
|
||||
{
|
||||
m_ReorderableListScrollPosition += new Vector2(0.0f, list.elementHeightCallback(0));
|
||||
secondaryTextureList.Add(new SecondarySpriteTexture());
|
||||
spriteEditor.SetDataModified();
|
||||
}
|
||||
|
||||
void RemoveSpriteSecondaryTextureElement(ReorderableList list)
|
||||
{
|
||||
DisplayMainTexture();
|
||||
secondaryTextureList.RemoveAt(list.index);
|
||||
spriteEditor.SetDataModified();
|
||||
}
|
||||
|
||||
bool CanAddSpriteSecondaryTextureElement(ReorderableList list)
|
||||
{
|
||||
return list.count < 8;
|
||||
}
|
||||
|
||||
void DisplayMainTexture()
|
||||
{
|
||||
ITextureDataProvider textureDataProvider = spriteEditor.GetDataProvider<ITextureDataProvider>();
|
||||
if (textureDataProvider != null && textureDataProvider.previewTexture != null)
|
||||
{
|
||||
Texture2D mainTexture = textureDataProvider.previewTexture;
|
||||
int width = 0, height = 0;
|
||||
textureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||||
spriteEditor.SetPreviewTexture(mainTexture, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,339 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal class TextureImporterDataProviderFactory :
|
||||
ISpriteDataProviderFactory<Texture2D>,
|
||||
ISpriteDataProviderFactory<Sprite>,
|
||||
ISpriteDataProviderFactory<TextureImporter>,
|
||||
ISpriteDataProviderFactory<GameObject>
|
||||
{
|
||||
public ISpriteEditorDataProvider CreateDataProvider(Texture2D obj)
|
||||
{
|
||||
var ti = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(obj)) as TextureImporter;
|
||||
if (ti != null)
|
||||
return new TextureImporterDataProvider(ti);
|
||||
return null;
|
||||
}
|
||||
|
||||
public ISpriteEditorDataProvider CreateDataProvider(Sprite obj)
|
||||
{
|
||||
var ti = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(obj)) as TextureImporter;
|
||||
if (ti != null)
|
||||
return new TextureImporterDataProvider(ti);
|
||||
return null;
|
||||
}
|
||||
|
||||
public ISpriteEditorDataProvider CreateDataProvider(GameObject obj)
|
||||
{
|
||||
var spriteRenderer = obj.GetComponent<SpriteRenderer>();
|
||||
if (spriteRenderer != null && spriteRenderer.sprite != null)
|
||||
{
|
||||
var ti = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(spriteRenderer.sprite)) as TextureImporter;
|
||||
if (ti != null)
|
||||
return new TextureImporterDataProvider(ti);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ISpriteEditorDataProvider CreateDataProvider(TextureImporter obj)
|
||||
{
|
||||
return new TextureImporterDataProvider(obj);
|
||||
}
|
||||
|
||||
[SpriteEditorAssetPathProviderAttribute]
|
||||
static string GetAssetPathFromSpriteRenderer(UnityEngine.Object obj)
|
||||
{
|
||||
var go = obj as GameObject;
|
||||
if (go != null)
|
||||
{
|
||||
var spriteRenderer = go.GetComponent<SpriteRenderer>();
|
||||
if (spriteRenderer != null && spriteRenderer.sprite != null)
|
||||
return AssetDatabase.GetAssetPath(spriteRenderer.sprite);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
[SpriteObjectProvider]
|
||||
static Sprite GetSpriteObjectFromSpriteRenderer(UnityEngine.Object obj)
|
||||
{
|
||||
var go = obj as GameObject;
|
||||
if (go != null)
|
||||
{
|
||||
var spriteRenderer = go.GetComponent<SpriteRenderer>();
|
||||
if (spriteRenderer != null)
|
||||
return spriteRenderer.sprite;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal class TextureImporterDataProvider : ISpriteEditorDataProvider
|
||||
{
|
||||
TextureImporter m_TextureImporter;
|
||||
List<SpriteDataExt> m_SpritesMultiple;
|
||||
SpriteDataExt m_SpriteSingle;
|
||||
SpriteImportMode m_SpriteImportMode = SpriteImportMode.None;
|
||||
SecondarySpriteTexture[] m_SecondaryTextureDataTransfer;
|
||||
SerializedObject m_CachedSerializedObject;
|
||||
|
||||
internal TextureImporterDataProvider(TextureImporter importer)
|
||||
{
|
||||
m_TextureImporter = importer;
|
||||
if (m_TextureImporter != null)
|
||||
{
|
||||
m_SpriteImportMode = m_TextureImporter.spriteImportMode;
|
||||
m_CachedSerializedObject = new SerializedObject(m_TextureImporter);
|
||||
}
|
||||
}
|
||||
|
||||
float ISpriteEditorDataProvider.pixelsPerUnit
|
||||
{
|
||||
get { return m_TextureImporter.spritePixelsPerUnit; }
|
||||
}
|
||||
|
||||
UnityEngine.Object ISpriteEditorDataProvider.targetObject
|
||||
{
|
||||
get { return m_TextureImporter; }
|
||||
}
|
||||
|
||||
public SpriteImportMode spriteImportMode
|
||||
{
|
||||
get { return m_SpriteImportMode; }
|
||||
}
|
||||
|
||||
SpriteRect[] ISpriteEditorDataProvider.GetSpriteRects()
|
||||
{
|
||||
return spriteImportMode == SpriteImportMode.Multiple ? m_SpritesMultiple.Select(x => new SpriteDataExt(x) as SpriteRect).ToArray() : new[] { new SpriteDataExt(m_SpriteSingle) };
|
||||
}
|
||||
|
||||
public SerializedObject GetSerializedObject()
|
||||
{
|
||||
m_CachedSerializedObject?.UpdateIfRequiredOrScript();
|
||||
return m_CachedSerializedObject;
|
||||
}
|
||||
|
||||
public string assetPath
|
||||
{
|
||||
get { return m_TextureImporter.assetPath; }
|
||||
}
|
||||
|
||||
public void GetWidthAndHeight(ref int width, ref int height)
|
||||
{
|
||||
m_TextureImporter.GetWidthAndHeight(ref width, ref height);
|
||||
}
|
||||
|
||||
void ISpriteEditorDataProvider.SetSpriteRects(SpriteRect[] spriteRects)
|
||||
{
|
||||
if (spriteImportMode != SpriteImportMode.Multiple && spriteImportMode != SpriteImportMode.None && spriteRects.Length == 1)
|
||||
{
|
||||
m_SpriteSingle.CopyFromSpriteRect(spriteRects[0]);
|
||||
}
|
||||
else if (spriteImportMode == SpriteImportMode.Multiple)
|
||||
{
|
||||
Dictionary<GUID, SpriteRect> newSprites = new Dictionary<GUID, SpriteRect>();
|
||||
foreach (var newSprite in spriteRects)
|
||||
{
|
||||
newSprites.Add(newSprite.spriteID, newSprite);
|
||||
}
|
||||
|
||||
for (int i = m_SpritesMultiple.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var spriteID = m_SpritesMultiple[i].spriteID;
|
||||
if (newSprites.TryGetValue(spriteID, out SpriteRect smd))
|
||||
{
|
||||
m_SpritesMultiple[i].CopyFromSpriteRect(smd);
|
||||
newSprites.Remove(spriteID);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SpritesMultiple.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
// Add new ones
|
||||
foreach (var newSprite in newSprites.Values)
|
||||
{
|
||||
m_SpritesMultiple.Add(new SpriteDataExt(newSprite));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal SpriteRect GetSpriteData(GUID guid)
|
||||
{
|
||||
return spriteImportMode == SpriteImportMode.Multiple ? m_SpritesMultiple.FirstOrDefault(x => x.spriteID == guid) : m_SpriteSingle;
|
||||
}
|
||||
|
||||
internal int GetSpriteDataIndex(GUID guid)
|
||||
{
|
||||
switch (spriteImportMode)
|
||||
{
|
||||
case SpriteImportMode.Single:
|
||||
case SpriteImportMode.Polygon:
|
||||
return 0;
|
||||
case SpriteImportMode.Multiple:
|
||||
{
|
||||
return m_SpritesMultiple.FindIndex(x => x.spriteID == guid);
|
||||
}
|
||||
default:
|
||||
throw new InvalidOperationException(string.Format("Sprite with GUID {0} not found", guid));
|
||||
}
|
||||
}
|
||||
|
||||
void ISpriteEditorDataProvider.Apply()
|
||||
{
|
||||
var so = GetSerializedObject();
|
||||
m_SpriteSingle.Apply(so);
|
||||
var spriteSheetSO = so.FindProperty("m_SpriteSheet.m_Sprites");
|
||||
Dictionary<GUID, SpriteDataExt> newSprites = new Dictionary<GUID, SpriteDataExt>();
|
||||
foreach (var newSprite in m_SpritesMultiple)
|
||||
{
|
||||
newSprites.Add(newSprite.spriteID, newSprite);
|
||||
}
|
||||
|
||||
if (spriteSheetSO.arraySize > 0)
|
||||
{
|
||||
var validateCurrent = spriteSheetSO.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < spriteSheetSO.arraySize; ++i)
|
||||
{
|
||||
var guid = SpriteRect.GetSpriteIDFromSerializedProperty(validateCurrent);
|
||||
// find the GUID in our sprite list and apply to it;
|
||||
if (newSprites.TryGetValue(guid, out SpriteDataExt smd))
|
||||
{
|
||||
smd.Apply(validateCurrent);
|
||||
newSprites.Remove(guid);
|
||||
validateCurrent.Next(false);
|
||||
}
|
||||
else// we can't find it, it is already deleted
|
||||
{
|
||||
spriteSheetSO.DeleteArrayElementAtIndex(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new ones
|
||||
int newCount = newSprites.Count();
|
||||
if (newCount > 0)
|
||||
{
|
||||
var idx = spriteSheetSO.arraySize;
|
||||
spriteSheetSO.arraySize += newCount;
|
||||
var addCurrent = spriteSheetSO.GetArrayElementAtIndex(idx);
|
||||
foreach (var newSprite in newSprites.Values)
|
||||
{
|
||||
newSprite.Apply(addCurrent);
|
||||
addCurrent.Next(false);
|
||||
}
|
||||
}
|
||||
|
||||
SpriteSecondaryTextureDataTransfer.Apply(so, m_SecondaryTextureDataTransfer);
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
void ISpriteEditorDataProvider.InitSpriteEditorDataProvider()
|
||||
{
|
||||
var so = GetSerializedObject();
|
||||
var spriteSheetSO = so.FindProperty("m_SpriteSheet.m_Sprites");
|
||||
m_SpritesMultiple = new List<SpriteDataExt>();
|
||||
m_SpriteSingle = new SpriteDataExt(so);
|
||||
|
||||
if (spriteSheetSO.arraySize > 0)
|
||||
{
|
||||
var sp = spriteSheetSO.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < spriteSheetSO.arraySize; ++i)
|
||||
{
|
||||
var data = new SpriteDataExt(sp);
|
||||
m_SpritesMultiple.Add(data);
|
||||
sp.Next(false);
|
||||
}
|
||||
}
|
||||
m_SecondaryTextureDataTransfer = SpriteSecondaryTextureDataTransfer.Load(so);
|
||||
}
|
||||
|
||||
T ISpriteEditorDataProvider.GetDataProvider<T>()
|
||||
{
|
||||
if (typeof(T) == typeof(ISpriteBoneDataProvider))
|
||||
{
|
||||
return new SpriteBoneDataTransfer(this) as T;
|
||||
}
|
||||
if (typeof(T) == typeof(ISpriteMeshDataProvider))
|
||||
{
|
||||
return new SpriteMeshDataTransfer(this) as T;
|
||||
}
|
||||
if (typeof(T) == typeof(ISpriteOutlineDataProvider))
|
||||
{
|
||||
return new SpriteOutlineDataTransfer(this) as T;
|
||||
}
|
||||
if (typeof(T) == typeof(ISpritePhysicsOutlineDataProvider))
|
||||
{
|
||||
return new SpritePhysicsOutlineDataTransfer(this) as T;
|
||||
}
|
||||
if (typeof(T) == typeof(ITextureDataProvider))
|
||||
{
|
||||
return new SpriteTextureDataTransfer(this) as T;
|
||||
}
|
||||
if (typeof(T) == typeof(ISecondaryTextureDataProvider))
|
||||
{
|
||||
return new SpriteSecondaryTextureDataTransfer(this) as T;
|
||||
}
|
||||
else
|
||||
return this as T;
|
||||
}
|
||||
|
||||
bool ISpriteEditorDataProvider.HasDataProvider(Type type)
|
||||
{
|
||||
if (type == typeof(ISpriteBoneDataProvider) ||
|
||||
type == typeof(ISpriteMeshDataProvider) ||
|
||||
type == typeof(ISpriteOutlineDataProvider) ||
|
||||
type == typeof(ISpritePhysicsOutlineDataProvider) ||
|
||||
type == typeof(ITextureDataProvider) ||
|
||||
type == typeof(ISecondaryTextureDataProvider))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return type.IsAssignableFrom(GetType());
|
||||
}
|
||||
|
||||
public override bool Equals(object a)
|
||||
{
|
||||
return this == (a as TextureImporterDataProvider);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool operator!=(TextureImporterDataProvider t1, TextureImporterDataProvider t2)
|
||||
{
|
||||
return !(t1 == t2);
|
||||
}
|
||||
|
||||
public static bool operator==(TextureImporterDataProvider t1, TextureImporterDataProvider t2)
|
||||
{
|
||||
if (ReferenceEquals(null, t1) && (!ReferenceEquals(null, t2) && t2.m_TextureImporter == null) ||
|
||||
ReferenceEquals(null, t2) && (!ReferenceEquals(null, t1) && t1.m_TextureImporter == null))
|
||||
return true;
|
||||
|
||||
if (!ReferenceEquals(null, t1) && !ReferenceEquals(null, t2))
|
||||
{
|
||||
if (t1.m_TextureImporter == null && t2.m_TextureImporter == null ||
|
||||
t1.m_TextureImporter == t2.m_TextureImporter)
|
||||
return true;
|
||||
}
|
||||
if (ReferenceEquals(t1, t2))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public SecondarySpriteTexture[] secdonaryTextures
|
||||
{
|
||||
get { return m_SecondaryTextureDataTransfer; }
|
||||
set { m_SecondaryTextureDataTransfer = value; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,482 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.U2D;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal class SpriteDataProviderBase
|
||||
{
|
||||
public SpriteDataProviderBase(TextureImporterDataProvider dp)
|
||||
{
|
||||
dataProvider = dp;
|
||||
}
|
||||
|
||||
protected TextureImporterDataProvider dataProvider { get; private set; }
|
||||
}
|
||||
|
||||
internal class SpriteBoneDataTransfer : SpriteDataProviderBase, ISpriteBoneDataProvider
|
||||
{
|
||||
public SpriteBoneDataTransfer(TextureImporterDataProvider dp) : base(dp)
|
||||
{}
|
||||
|
||||
public List<SpriteBone> GetBones(GUID guid)
|
||||
{
|
||||
var index = dataProvider.GetSpriteDataIndex(guid);
|
||||
return Load(dataProvider.GetSerializedObject(), dataProvider.spriteImportMode, index);
|
||||
}
|
||||
|
||||
public void SetBones(GUID guid, List<SpriteBone> bones)
|
||||
{
|
||||
((SpriteDataExt)dataProvider.GetSpriteData(guid)).spriteBone = bones;
|
||||
}
|
||||
|
||||
private static List<SpriteBone> Load(SerializedObject importer, SpriteImportMode mode, int index)
|
||||
{
|
||||
var sp = mode == SpriteImportMode.Multiple ?
|
||||
importer.FindProperty("m_SpriteSheet.m_Sprites").GetArrayElementAtIndex(index).FindPropertyRelative("m_Bones") :
|
||||
importer.FindProperty("m_SpriteSheet.m_Bones");
|
||||
|
||||
var spriteBone = new List<SpriteBone>(sp.arraySize);
|
||||
if (sp.arraySize > 0)
|
||||
{
|
||||
var boneSO = sp.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < sp.arraySize; ++i, boneSO.Next(false))
|
||||
{
|
||||
var sb = new SpriteBone();
|
||||
sb.length = boneSO.FindPropertyRelative("length").floatValue;
|
||||
sb.position = boneSO.FindPropertyRelative("position").vector3Value;
|
||||
sb.rotation = boneSO.FindPropertyRelative("rotation").quaternionValue;
|
||||
sb.parentId = boneSO.FindPropertyRelative("parentId").intValue;
|
||||
sb.name = boneSO.FindPropertyRelative("name").stringValue;
|
||||
sb.guid = boneSO.FindPropertyRelative("guid").stringValue;
|
||||
sb.color = boneSO.FindPropertyRelative("color").colorValue;
|
||||
spriteBone.Add(sb);
|
||||
}
|
||||
}
|
||||
return spriteBone;
|
||||
}
|
||||
|
||||
public static void Apply(SerializedProperty rectSP, List<SpriteBone> spriteBone)
|
||||
{
|
||||
var sp = rectSP.FindPropertyRelative("m_Bones");
|
||||
sp.arraySize = spriteBone.Count;
|
||||
if (spriteBone.Count > 0)
|
||||
{
|
||||
var boneSO = sp.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < sp.arraySize; ++i, boneSO.Next(false))
|
||||
{
|
||||
var sb = spriteBone[i];
|
||||
boneSO.FindPropertyRelative("length").floatValue = sb.length;
|
||||
boneSO.FindPropertyRelative("position").vector3Value = sb.position;
|
||||
boneSO.FindPropertyRelative("rotation").quaternionValue = sb.rotation;
|
||||
boneSO.FindPropertyRelative("parentId").intValue = sb.parentId;
|
||||
boneSO.FindPropertyRelative("name").stringValue = sb.name;
|
||||
boneSO.FindPropertyRelative("guid").stringValue = sb.guid;
|
||||
boneSO.FindPropertyRelative("color").colorValue = sb.color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SpriteOutlineDataTransfer : SpriteDataProviderBase, ISpriteOutlineDataProvider
|
||||
{
|
||||
public SpriteOutlineDataTransfer(TextureImporterDataProvider dp) : base(dp)
|
||||
{}
|
||||
|
||||
public List<Vector2[]> GetOutlines(GUID guid)
|
||||
{
|
||||
var index = dataProvider.GetSpriteDataIndex(guid);
|
||||
return Load(dataProvider.GetSerializedObject(), dataProvider.spriteImportMode, index);
|
||||
}
|
||||
|
||||
public void SetOutlines(GUID guid, List<Vector2[]> data)
|
||||
{
|
||||
((SpriteDataExt)dataProvider.GetSpriteData(guid)).spriteOutline = data;
|
||||
}
|
||||
|
||||
public float GetTessellationDetail(GUID guid)
|
||||
{
|
||||
return ((SpriteDataExt)dataProvider.GetSpriteData(guid)).tessellationDetail;
|
||||
}
|
||||
|
||||
public void SetTessellationDetail(GUID guid, float value)
|
||||
{
|
||||
((SpriteDataExt)dataProvider.GetSpriteData(guid)).tessellationDetail = value;
|
||||
}
|
||||
|
||||
private static List<Vector2[]> Load(SerializedObject importer, SpriteImportMode mode, int index)
|
||||
{
|
||||
var outlineSP = mode == SpriteImportMode.Multiple ?
|
||||
importer.FindProperty("m_SpriteSheet.m_Sprites").GetArrayElementAtIndex(index).FindPropertyRelative("m_Outline") :
|
||||
importer.FindProperty("m_SpriteSheet.m_Outline");
|
||||
|
||||
var outline = new List<Vector2[]>();
|
||||
if (outlineSP.arraySize > 0)
|
||||
{
|
||||
var outlinePathSP = outlineSP.GetArrayElementAtIndex(0);
|
||||
for (int j = 0; j < outlineSP.arraySize; ++j, outlinePathSP.Next(false))
|
||||
{
|
||||
var o = new Vector2[outlinePathSP.arraySize];
|
||||
if (o.Length > 0)
|
||||
{
|
||||
var psp = outlinePathSP.GetArrayElementAtIndex(0);
|
||||
for (int k = 0; k < outlinePathSP.arraySize; ++k, psp.Next(false))
|
||||
{
|
||||
o[k] = psp.vector2Value;
|
||||
}
|
||||
}
|
||||
outline.Add(o);
|
||||
}
|
||||
}
|
||||
return outline;
|
||||
}
|
||||
|
||||
public static void Apply(SerializedProperty rectSP, List<Vector2[]> outline)
|
||||
{
|
||||
var outlineSP = rectSP.FindPropertyRelative("m_Outline");
|
||||
outlineSP.arraySize = outline.Count;
|
||||
if (outline.Count > 0)
|
||||
{
|
||||
var outlinePathSP = outlineSP.GetArrayElementAtIndex(0);
|
||||
for (int j = 0; j < outline.Count; ++j, outlinePathSP.Next(false))
|
||||
{
|
||||
var o = outline[j];
|
||||
outlinePathSP.arraySize = o.Length;
|
||||
if (o.Length > 0)
|
||||
{
|
||||
var psp = outlinePathSP.GetArrayElementAtIndex(0);
|
||||
for (int k = 0; k < o.Length; ++k, psp.Next(false))
|
||||
{
|
||||
psp.vector2Value = o[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SpriteMeshDataTransfer : SpriteDataProviderBase, ISpriteMeshDataProvider
|
||||
{
|
||||
public SpriteMeshDataTransfer(TextureImporterDataProvider dp) : base(dp)
|
||||
{}
|
||||
|
||||
public Vertex2DMetaData[] GetVertices(GUID guid)
|
||||
{
|
||||
var index = dataProvider.GetSpriteDataIndex(guid);
|
||||
return LoadVertex2DMetaData(dataProvider.GetSerializedObject(), dataProvider.spriteImportMode, index);
|
||||
}
|
||||
|
||||
public void SetVertices(GUID guid, Vertex2DMetaData[] data)
|
||||
{
|
||||
((SpriteDataExt)dataProvider.GetSpriteData(guid)).vertices = new List<Vertex2DMetaData>(data);
|
||||
}
|
||||
|
||||
public int[] GetIndices(GUID guid)
|
||||
{
|
||||
var index = dataProvider.GetSpriteDataIndex(guid);
|
||||
return LoadIndices(dataProvider.GetSerializedObject(), dataProvider.spriteImportMode, index);
|
||||
}
|
||||
|
||||
public void SetIndices(GUID guid, int[] indices)
|
||||
{
|
||||
((SpriteDataExt)dataProvider.GetSpriteData(guid)).indices = new List<int>(indices);
|
||||
}
|
||||
|
||||
public Vector2Int[] GetEdges(GUID guid)
|
||||
{
|
||||
var index = dataProvider.GetSpriteDataIndex(guid);
|
||||
return LoadEdges(dataProvider.GetSerializedObject(), dataProvider.spriteImportMode, index);
|
||||
}
|
||||
|
||||
public void SetEdges(GUID guid, Vector2Int[] edges)
|
||||
{
|
||||
((SpriteDataExt)dataProvider.GetSpriteData(guid)).edges = new List<Vector2Int>(edges);
|
||||
}
|
||||
|
||||
private Vertex2DMetaData[] LoadVertex2DMetaData(SerializedObject importer, SpriteImportMode mode, int index)
|
||||
{
|
||||
var so = mode == SpriteImportMode.Multiple ?
|
||||
importer.FindProperty("m_SpriteSheet.m_Sprites").GetArrayElementAtIndex(index) :
|
||||
importer.FindProperty("m_SpriteSheet");
|
||||
|
||||
var verticesSP = so.FindPropertyRelative("m_Vertices");
|
||||
var vertices = new Vertex2DMetaData[verticesSP.arraySize];
|
||||
if (verticesSP.arraySize > 0)
|
||||
{
|
||||
var weightsSP = so.FindPropertyRelative("m_Weights");
|
||||
var vsp = verticesSP.GetArrayElementAtIndex(0);
|
||||
var wsp = weightsSP.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < verticesSP.arraySize; ++i, vsp.Next(false), wsp.Next(false))
|
||||
{
|
||||
vertices[i] = new Vertex2DMetaData
|
||||
{
|
||||
position = vsp.vector2Value,
|
||||
boneWeight = new BoneWeight
|
||||
{
|
||||
weight0 = wsp.FindPropertyRelative("weight[0]").floatValue,
|
||||
weight1 = wsp.FindPropertyRelative("weight[1]").floatValue,
|
||||
weight2 = wsp.FindPropertyRelative("weight[2]").floatValue,
|
||||
weight3 = wsp.FindPropertyRelative("weight[3]").floatValue,
|
||||
boneIndex0 = wsp.FindPropertyRelative("boneIndex[0]").intValue,
|
||||
boneIndex1 = wsp.FindPropertyRelative("boneIndex[1]").intValue,
|
||||
boneIndex2 = wsp.FindPropertyRelative("boneIndex[2]").intValue,
|
||||
boneIndex3 = wsp.FindPropertyRelative("boneIndex[3]").intValue
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
private int[] LoadIndices(SerializedObject importer, SpriteImportMode mode, int index)
|
||||
{
|
||||
var so = mode == SpriteImportMode.Multiple ?
|
||||
importer.FindProperty("m_SpriteSheet.m_Sprites").GetArrayElementAtIndex(index) :
|
||||
importer.FindProperty("m_SpriteSheet");
|
||||
|
||||
var indicesSP = so.FindPropertyRelative("m_Indices");
|
||||
var indices = new int[indicesSP.arraySize];
|
||||
if (indices.Length > 0)
|
||||
{
|
||||
var isp = indicesSP.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < indicesSP.arraySize; ++i, isp.Next(false))
|
||||
{
|
||||
indices[i] = isp.intValue;
|
||||
}
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
private Vector2Int[] LoadEdges(SerializedObject importer, SpriteImportMode mode, int index)
|
||||
{
|
||||
var so = mode == SpriteImportMode.Multiple ?
|
||||
importer.FindProperty("m_SpriteSheet.m_Sprites").GetArrayElementAtIndex(index) :
|
||||
importer.FindProperty("m_SpriteSheet");
|
||||
|
||||
var edgesSP = so.FindPropertyRelative("m_Edges");
|
||||
var edges = new Vector2Int[edgesSP.arraySize];
|
||||
if (edges.Length > 0)
|
||||
{
|
||||
var esp = edgesSP.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < edgesSP.arraySize; ++i, esp.Next(false))
|
||||
{
|
||||
edges[i] = esp.vector2IntValue;
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
public static void Apply(SerializedProperty rectSP, List<Vertex2DMetaData> vertices, List<int> indices, List<Vector2Int> edges)
|
||||
{
|
||||
var verticesSP = rectSP.FindPropertyRelative("m_Vertices");
|
||||
var weightsSP = rectSP.FindPropertyRelative("m_Weights");
|
||||
var indicesSP = rectSP.FindPropertyRelative("m_Indices");
|
||||
var edgesSP = rectSP.FindPropertyRelative("m_Edges");
|
||||
|
||||
verticesSP.arraySize = vertices.Count;
|
||||
weightsSP.arraySize = vertices.Count;
|
||||
if (vertices.Count > 0)
|
||||
{
|
||||
var vsp = verticesSP.GetArrayElementAtIndex(0);
|
||||
var wsp = weightsSP.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < vertices.Count; ++i, vsp.Next(false), wsp.Next(false))
|
||||
{
|
||||
vsp.vector2Value = vertices[i].position;
|
||||
wsp.FindPropertyRelative("weight[0]").floatValue = vertices[i].boneWeight.weight0;
|
||||
wsp.FindPropertyRelative("weight[1]").floatValue = vertices[i].boneWeight.weight1;
|
||||
wsp.FindPropertyRelative("weight[2]").floatValue = vertices[i].boneWeight.weight2;
|
||||
wsp.FindPropertyRelative("weight[3]").floatValue = vertices[i].boneWeight.weight3;
|
||||
wsp.FindPropertyRelative("boneIndex[0]").intValue = vertices[i].boneWeight.boneIndex0;
|
||||
wsp.FindPropertyRelative("boneIndex[1]").intValue = vertices[i].boneWeight.boneIndex1;
|
||||
wsp.FindPropertyRelative("boneIndex[2]").intValue = vertices[i].boneWeight.boneIndex2;
|
||||
wsp.FindPropertyRelative("boneIndex[3]").intValue = vertices[i].boneWeight.boneIndex3;
|
||||
}
|
||||
}
|
||||
|
||||
indicesSP.arraySize = indices.Count;
|
||||
if (indices.Count > 0)
|
||||
{
|
||||
var isp = indicesSP.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < indices.Count; ++i, isp.Next(false))
|
||||
{
|
||||
isp.intValue = indices[i];
|
||||
}
|
||||
}
|
||||
|
||||
edgesSP.arraySize = edges.Count;
|
||||
if (edges.Count > 0)
|
||||
{
|
||||
var esp = edgesSP.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < edges.Count; ++i, esp.Next(false))
|
||||
{
|
||||
esp.vector2IntValue = edges[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SpritePhysicsOutlineDataTransfer : SpriteDataProviderBase, ISpritePhysicsOutlineDataProvider
|
||||
{
|
||||
public SpritePhysicsOutlineDataTransfer(TextureImporterDataProvider dp) : base(dp)
|
||||
{}
|
||||
|
||||
public List<Vector2[]> GetOutlines(GUID guid)
|
||||
{
|
||||
var index = dataProvider.GetSpriteDataIndex(guid);
|
||||
return Load(dataProvider.GetSerializedObject(), dataProvider.spriteImportMode, index);
|
||||
}
|
||||
|
||||
public void SetOutlines(GUID guid, List<Vector2[]> data)
|
||||
{
|
||||
((SpriteDataExt)dataProvider.GetSpriteData(guid)).spritePhysicsOutline = data;
|
||||
}
|
||||
|
||||
public float GetTessellationDetail(GUID guid)
|
||||
{
|
||||
return ((SpriteDataExt)dataProvider.GetSpriteData(guid)).tessellationDetail;
|
||||
}
|
||||
|
||||
public void SetTessellationDetail(GUID guid, float value)
|
||||
{
|
||||
((SpriteDataExt)dataProvider.GetSpriteData(guid)).tessellationDetail = value;
|
||||
}
|
||||
|
||||
private static List<Vector2[]> Load(SerializedObject importer, SpriteImportMode mode, int index)
|
||||
{
|
||||
var outlineSP = mode == SpriteImportMode.Multiple ?
|
||||
importer.FindProperty("m_SpriteSheet.m_Sprites").GetArrayElementAtIndex(index).FindPropertyRelative("m_PhysicsShape") :
|
||||
importer.FindProperty("m_SpriteSheet.m_PhysicsShape");
|
||||
|
||||
var outline = new List<Vector2[]>();
|
||||
if (outlineSP.arraySize > 0)
|
||||
{
|
||||
var outlinePathSP = outlineSP.GetArrayElementAtIndex(0);
|
||||
for (int j = 0; j < outlineSP.arraySize; ++j, outlinePathSP.Next(false))
|
||||
{
|
||||
var o = new Vector2[outlinePathSP.arraySize];
|
||||
if (o.Length > 0)
|
||||
{
|
||||
var psp = outlinePathSP.GetArrayElementAtIndex(0);
|
||||
for (int k = 0; k < outlinePathSP.arraySize; ++k, psp.Next(false))
|
||||
{
|
||||
o[k] = psp.vector2Value;
|
||||
}
|
||||
}
|
||||
outline.Add(o);
|
||||
}
|
||||
}
|
||||
return outline;
|
||||
}
|
||||
|
||||
public static void Apply(SerializedProperty rectSP, List<Vector2[]> value)
|
||||
{
|
||||
var outlineSP = rectSP.FindPropertyRelative("m_PhysicsShape");
|
||||
outlineSP.arraySize = value.Count;
|
||||
if (value.Count > 0)
|
||||
{
|
||||
var outlinePathSP = outlineSP.GetArrayElementAtIndex(0);
|
||||
for (int j = 0; j < value.Count; ++j, outlinePathSP.Next(false))
|
||||
{
|
||||
var o = value[j];
|
||||
outlinePathSP.arraySize = o.Length;
|
||||
if (o.Length > 0)
|
||||
{
|
||||
var psp = outlinePathSP.GetArrayElementAtIndex(0);
|
||||
for (int k = 0; k < o.Length; ++k, psp.Next(false))
|
||||
{
|
||||
psp.vector2Value = o[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SpriteSecondaryTextureDataTransfer : SpriteDataProviderBase, ISecondaryTextureDataProvider
|
||||
{
|
||||
public SpriteSecondaryTextureDataTransfer(TextureImporterDataProvider dp) : base(dp)
|
||||
{}
|
||||
|
||||
public SecondarySpriteTexture[] textures
|
||||
{
|
||||
get { return dataProvider.secdonaryTextures; }
|
||||
set { dataProvider.secdonaryTextures = value; }
|
||||
}
|
||||
|
||||
public static SecondarySpriteTexture[] Load(SerializedObject so)
|
||||
{
|
||||
var secondaryTextures = so.FindProperty("m_SpriteSheet.m_SecondaryTextures");
|
||||
var returnValue = new SecondarySpriteTexture[secondaryTextures.arraySize];
|
||||
if (secondaryTextures.arraySize > 0)
|
||||
{
|
||||
var sp = secondaryTextures.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < returnValue.Length; ++i, sp.Next(false))
|
||||
{
|
||||
returnValue[i].name = sp.FindPropertyRelative("name").stringValue;
|
||||
returnValue[i].texture = sp.FindPropertyRelative("texture").objectReferenceValue as Texture2D;
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public static void Apply(SerializedObject so, SecondarySpriteTexture[] values)
|
||||
{
|
||||
var secondaryTextures = so.FindProperty("m_SpriteSheet.m_SecondaryTextures");
|
||||
secondaryTextures.arraySize = values.Length;
|
||||
if (values.Length > 0)
|
||||
{
|
||||
var e = secondaryTextures.GetArrayElementAtIndex(0);
|
||||
for (int i = 0; i < values.Length; ++i, e.Next(false))
|
||||
{
|
||||
e.FindPropertyRelative("name").stringValue = values[i].name;
|
||||
e.FindPropertyRelative("texture").objectReferenceValue = values[i].texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SpriteTextureDataTransfer : SpriteDataProviderBase, ITextureDataProvider
|
||||
{
|
||||
public SpriteTextureDataTransfer(TextureImporterDataProvider dp) : base(dp)
|
||||
{}
|
||||
|
||||
Texture2D m_ReadableTexture;
|
||||
Texture2D m_OriginalTexture;
|
||||
|
||||
public Texture2D texture
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_OriginalTexture == null)
|
||||
m_OriginalTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(dataProvider.assetPath);
|
||||
return m_OriginalTexture;
|
||||
}
|
||||
}
|
||||
|
||||
public Texture2D previewTexture
|
||||
{
|
||||
get { return texture; }
|
||||
}
|
||||
|
||||
public void GetTextureActualWidthAndHeight(out int width, out int height)
|
||||
{
|
||||
width = height = 0;
|
||||
dataProvider.GetWidthAndHeight(ref width, ref height);
|
||||
}
|
||||
|
||||
public Texture2D GetReadableTexture2D()
|
||||
{
|
||||
if (m_ReadableTexture == null)
|
||||
{
|
||||
m_ReadableTexture = UnityEditor.SpriteUtility.CreateTemporaryDuplicate(texture, texture.width, texture.height);
|
||||
if (m_ReadableTexture != null)
|
||||
m_ReadableTexture.filterMode = texture.filterMode;
|
||||
}
|
||||
return m_ReadableTexture;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
<UXML xmlns:ui= "UnityEngine.Experimental.UIElements" xmlns:uie= "UnityEditor.Experimental.UIElements">
|
||||
<ui:VisualElement name = "polygonShapeWindow" class = "moduleWindow topLeft">
|
||||
<ui:Box name = "polygonShapeWindowFrame">
|
||||
<uie:IntegerField name = "labelIntegerField" class = "labelIntegerField" label = "Sides" value = "0"/>
|
||||
<ui:VisualElement name = "warning" >
|
||||
<ui:Image name = "icon"/>
|
||||
<ui:Label name = "warningLabel" text= "Sides can only be either 0 or anything between 3 and 128"/>
|
||||
</ui:VisualElement>
|
||||
<ui:Button name = "changeButton" text= "Change" />
|
||||
</ui:Box>
|
||||
</ui:VisualElement>
|
||||
</UXML>
|
@@ -0,0 +1,162 @@
|
||||
.unity-imgui-container {
|
||||
flex: 1 0 0;
|
||||
}
|
||||
.moduleWindow {
|
||||
position: absolute;
|
||||
}
|
||||
.bottomRight {
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
}
|
||||
.bottomLeft {
|
||||
bottom: 16px;
|
||||
left: 0;
|
||||
}
|
||||
.topLeft {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.topRight {
|
||||
top: 0;
|
||||
right: 16px;
|
||||
}
|
||||
.bottomRightFloating {
|
||||
bottom: 24px;
|
||||
right: 24px;
|
||||
}
|
||||
.bottomLeftFloating {
|
||||
bottom: 24px;
|
||||
left: 8px;
|
||||
}
|
||||
.topLeftFloating {
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
}
|
||||
.topRightFloating {
|
||||
top: 8px;
|
||||
right: 24px;
|
||||
}
|
||||
#moduleViewElement{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
#spriteFrameInspector{
|
||||
min-width: 330px;
|
||||
min-height: 170px;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
#spriteFrameModuleInspector {
|
||||
min-width: 330px;
|
||||
min-height: 170px;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
|
||||
#spriteFrameModuleInspector Label{
|
||||
-unity-text-align: upper-left;
|
||||
}
|
||||
|
||||
#spriteFrameModuleInspector #unity-text-input{
|
||||
-unity-text-align: upper-left;
|
||||
min-height : 18px;
|
||||
}
|
||||
|
||||
.spriteFrameModuleInspectorField {
|
||||
flex-direction: row;
|
||||
min-height: 18px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.spriteFrameModuleInspectorField > Label {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.spriteFrameModuleInspectorField > EnumField > .unity-label {
|
||||
width: 130px;
|
||||
min-width:0;
|
||||
}
|
||||
|
||||
|
||||
.spriteFrameModuleInspectorField > EnumField {
|
||||
flex: 1 0 0;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.spriteFrameModuleInspectorField > #SpriteName {
|
||||
flex: 1 0 0;
|
||||
flex-direction : row;
|
||||
}
|
||||
|
||||
#spriteName {
|
||||
flex : 1;
|
||||
}
|
||||
|
||||
#spriteName > Label {
|
||||
width: 130px;
|
||||
min-width:0;
|
||||
}
|
||||
|
||||
#spriteEditorWindowToolbar{
|
||||
height : 21px;
|
||||
flex: 0 0 auto;
|
||||
margin-top : 0;
|
||||
}
|
||||
#spriteEditorWindowMainView{
|
||||
flex: 1 0 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#polygonShapeWindow{
|
||||
width : 155px;
|
||||
min-height : 45px;
|
||||
}
|
||||
#polygonShapeWindowFrame{
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
flex: 1 0 0;
|
||||
flex-direction : column;
|
||||
}
|
||||
#polygonShapeWindowFrame > .labelIntegerField{
|
||||
flex: 1 0 0;
|
||||
flex-direction: row;
|
||||
}
|
||||
#polygonShapeWindowFrame > .labelIntegerField > Label{
|
||||
margin-right: 0;
|
||||
margin-top: 4px;
|
||||
min-width:0;
|
||||
align-self:center;
|
||||
}
|
||||
#polygonShapeWindowFrame > .labelIntegerField > IntegerField{
|
||||
flex: 1 0 auto;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
#polygonShapeWindowFrame > Button{
|
||||
height : 18px;
|
||||
align-self: flex-end;
|
||||
}
|
||||
#polygonShapeWindowFrame > #warning{
|
||||
flex-direction : row;
|
||||
border-color: #a2a2a2;
|
||||
border-left-width : 1px;
|
||||
border-right-width : 1px;
|
||||
border-top-width : 1px;
|
||||
border-bottom-width : 1px;
|
||||
margin-top: 4px;
|
||||
margin-left: 4px;
|
||||
margin-bottom: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
#polygonShapeWindowFrame > #warning > Image{
|
||||
background-image : resource("console.warnicon.png");
|
||||
width : 32px;
|
||||
height : 32px;
|
||||
}
|
||||
#polygonShapeWindowFrame > #warning > #warningLabel{
|
||||
white-space : normal;
|
||||
font-size : 9px;
|
||||
flex: 1 0 0;
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<UXML xmlns:ui = "UnityEngine.Experimental.UIElements" xmlns:uie = "UnityEditor.Experimental.UIElements">
|
||||
<ui:PopupWindow name = "spriteFrameModuleInspector" text = "Sprite">
|
||||
<ui:VisualElement name = "name" class = "spriteFrameModuleInspectorField">
|
||||
<ui:TextField name = "spriteName" label = "Name" value = "Square"/>
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name = "position">
|
||||
<ui:VisualElement class = "spriteFrameModuleInspectorField unity-composite-field unity-composite-field--multi-line unity-base-field">
|
||||
<ui:Label text = "Position" />
|
||||
<ui:VisualElement class = "unity-composite-field__input unity-base-field__input">
|
||||
<ui:VisualElement name = "positionXY" class = "unity-composite-field__field-group">
|
||||
<uie:IntegerField name = "positionX" class = "unity-composite-field__field" label = "X"/>
|
||||
<uie:IntegerField name = "positionY" class = "unity-composite-field__field" label = "Y"/>
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name = "positionWH" class = "unity-composite-field__field-group">
|
||||
<uie:IntegerField name = "positionW" class = "unity-composite-field__field" label = "W"/>
|
||||
<uie:IntegerField name = "positionH" class = "unity-composite-field__field" label = "H"/>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name = "border">
|
||||
<ui:VisualElement class = "spriteFrameModuleInspectorField unity-composite-field unity-composite-field--multi-line unity-base-field">
|
||||
<ui:Label text = "Border" />
|
||||
<ui:VisualElement class = "unity-composite-field__input unity-base-field__input">
|
||||
<ui:VisualElement name = "borderLT" class = "unity-composite-field__field-group">
|
||||
<uie:IntegerField name = "borderL" class = "unity-composite-field__field" label = "L"/>
|
||||
<uie:IntegerField name = "borderT" class = "unity-composite-field__field" label = "T"/>
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name = "borderRB" class = "unity-composite-field__field-group">
|
||||
<uie:IntegerField name = "borderR" class = "unity-composite-field__field" label = "R"/>
|
||||
<uie:IntegerField name = "borderB" class = "unity-composite-field__field" label = "B"/>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name = "pivot" class = "spriteFrameModuleInspectorField">
|
||||
<uie:EnumField name = "pivotField" label = "Pivot" class="unity-enum-field"/>
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name = "pivotUnitMode" class = "spriteFrameModuleInspectorField">
|
||||
<uie:EnumField name = "pivotUnitModeField" label ="Pivot Unit Mode" class="unity-enum-field"/>
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name = "customPivot" class = "spriteFrameModuleInspectorField unity-composite-field unity-base-field">
|
||||
<ui:Label text = "Custom Pivot" />
|
||||
<ui:VisualElement name = "customPivotField" class = "unity-composite-field__input">
|
||||
<uie:FloatField name = "customPivotX" class = "unity-composite-field__field" label = "X"/>
|
||||
<uie:FloatField name = "customPivotY" class = "unity-composite-field__field" label = "Y"/>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:PopupWindow>
|
||||
</UXML>
|
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "Unity.2D.Sprite.Editor",
|
||||
"references": [],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": []
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
com.unity.2d.sprite copyright © 2019 Unity Technologies
|
||||
|
||||
Licensed under the Unity Package Distribution License (see https://unity3d.com/legal/licenses/Unity_Package_Distribution_License ).
|
||||
|
||||
Unless expressly provided otherwise, the software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.
|
34
Library/PackageCache/com.unity.2d.sprite@1.0.0/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# About Sprite Editor
|
||||
|
||||
Use Unity’s Sprite Editor to create and edit Sprite assets. Sprite Editor provides user extensibility to add custom behaviour for editing various Sprite related data.
|
||||
|
||||
# Installing Sprite Editor
|
||||
|
||||
To install this package, follow the instructions in the [Package Manager documentation](https://docs.unity3d.com/Packages/com.unity.package-manager-ui@latest/index.html).
|
||||
|
||||
# Using Sprite Editor
|
||||
|
||||
The Sprite Editor Manual can be found [here] (https://docs.unity3d.com/Manual/SpriteEditor.html).
|
||||
|
||||
|
||||
# Technical details
|
||||
## Requirements
|
||||
|
||||
This version of Sprite Editor is compatible with the following versions of the Unity Editor:
|
||||
|
||||
* 2019.2 and later (recommended)
|
||||
|
||||
## Package contents
|
||||
|
||||
The following table indicates the folder structure of the Sprite package:
|
||||
|
||||
|Location|Description|
|
||||
|---|---|
|
||||
|`<Editor>`|Root folder containing the source for the Sprite Editor.|
|
||||
|`<Tests>`|Root folder containing the source for the tests for Sprite Editpr used the Unity Editor Test Runner.|
|
||||
|
||||
## Document revision history
|
||||
|
||||
|Date|Reason|
|
||||
|---|---|
|
||||
|January 25, 2019|Document created. Matches package version 1.0.0|
|
@@ -0,0 +1,13 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites.EditorTests
|
||||
{
|
||||
internal class SpritePackageEditorTests
|
||||
{
|
||||
[Test]
|
||||
public void SpriteEditorWindow_IsLoadedFromDll()
|
||||
{
|
||||
Assert.That(typeof(SpriteEditorWindow).Assembly.FullName, Contains.Substring("Unity.2D.Sprite.Editor"));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Unity.2D.Sprite.Package.EditorTests",
|
||||
"references": [
|
||||
"Unity.2D.Sprite.Editor"
|
||||
],
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
11
Library/PackageCache/com.unity.2d.sprite@1.0.0/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "com.unity.2d.sprite",
|
||||
"displayName":"2D Sprite",
|
||||
"version": "1.0.0",
|
||||
"unity": "2019.2",
|
||||
"keywords": ["2d", "sprite", "sprite editor window"],
|
||||
"category": "2D",
|
||||
"description": "Use Unity Sprite Editor Window to create and edit Sprite asset properties like pivot, borders and Physics shape",
|
||||
"dependencies": {
|
||||
}
|
||||
}
|