testss
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user