mirror of
https://gitgud.io/AbstractConcept/rimworld-animation-studio.git
synced 2024-08-15 00:43:27 +00:00
Initial commit
This commit is contained in:
commit
3c7cc0c973
8391 changed files with 704313 additions and 0 deletions
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class AssociateBonesScope : IDisposable
|
||||
{
|
||||
private bool m_Disposed;
|
||||
private bool m_AssociateBones;
|
||||
private SpriteCache m_Sprite;
|
||||
|
||||
public AssociateBonesScope(SpriteCache sprite)
|
||||
{
|
||||
m_Sprite = sprite;
|
||||
m_AssociateBones = m_Sprite.AssociatePossibleBones();
|
||||
}
|
||||
|
||||
~AssociateBonesScope()
|
||||
{
|
||||
if (!m_Disposed)
|
||||
Debug.LogError("Scope was not disposed! You should use the 'using' keyword or manually call Dispose.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_Disposed)
|
||||
return;
|
||||
|
||||
m_Disposed = true;
|
||||
if (m_AssociateBones)
|
||||
m_Sprite.DeassociateUnusedBones();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5379fb539445eb24ea7ac709f8df31b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using UnityEditor.U2D.Layout;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal interface ITool {}
|
||||
|
||||
internal abstract class BaseTool : SkinningObject, ITool
|
||||
{
|
||||
[SerializeField]
|
||||
private LayoutOverlay m_LayoutOverlay;
|
||||
|
||||
internal LayoutOverlay layoutOverlay
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_LayoutOverlay;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool m_IsActive = false;
|
||||
public bool isActive
|
||||
{
|
||||
get { return m_IsActive; }
|
||||
private set { m_IsActive = value; }
|
||||
}
|
||||
|
||||
public virtual int defaultControlID { get { return 0; } }
|
||||
|
||||
public virtual IMeshPreviewBehaviour previewBehaviour
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
internal override void OnDestroy()
|
||||
{
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
isActive = true;
|
||||
OnActivate();
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
isActive = false;
|
||||
OnDeactivate();
|
||||
}
|
||||
|
||||
public void DoGUI()
|
||||
{
|
||||
if (isActive)
|
||||
OnGUI();
|
||||
}
|
||||
|
||||
public virtual void Initialize(LayoutOverlay layout)
|
||||
{
|
||||
m_LayoutOverlay = layout;
|
||||
}
|
||||
protected virtual void OnActivate() {}
|
||||
protected virtual void OnDeactivate() {}
|
||||
protected virtual void OnGUI() {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cc0c24ed6655ecc449dcbe4a8f3bd744
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,84 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal static class BoneDrawingUtility
|
||||
{
|
||||
public static float GetBoneRadius(Vector3 position, float scale = 1.0f)
|
||||
{
|
||||
if (Camera.current != null)
|
||||
{
|
||||
return 0.15f * scale * HandleUtility.GetHandleSize(position);
|
||||
}
|
||||
|
||||
return 10f * scale / Handles.matrix.GetColumn(0).magnitude;
|
||||
}
|
||||
|
||||
public static void DrawBoneNode(Vector3 position, Vector3 forward, Color color, float scale = 1.0f)
|
||||
{
|
||||
Color c = Handles.color;
|
||||
Handles.color = color;
|
||||
|
||||
Handles.DrawSolidDisc(position, -forward, GetBoneRadius(position, scale) * 0.3f);
|
||||
|
||||
Handles.color = c;
|
||||
}
|
||||
|
||||
public static void DrawBone(Vector3 position, Vector3 endPosition, Vector3 forward, Color color, float scale = 1.0f)
|
||||
{
|
||||
Color c = Handles.color;
|
||||
Handles.color = color;
|
||||
|
||||
var right = Vector3.right;
|
||||
var v = endPosition - position;
|
||||
|
||||
if (v.sqrMagnitude != 0)
|
||||
right = v.normalized;
|
||||
|
||||
var up = Vector3.Cross(right, forward).normalized;
|
||||
var radius = GetBoneRadius(position, scale) * 0.5f;
|
||||
var numSamples = 12;
|
||||
|
||||
if (v.sqrMagnitude <= radius * radius)
|
||||
DrawingUtility.DrawSolidArc(position, -forward, up, 360f, radius, numSamples * 2);
|
||||
else
|
||||
{
|
||||
DrawingUtility.DrawSolidArc(position, -forward, up, 180f, radius, numSamples);
|
||||
DrawingUtility.DrawLine(position, endPosition, forward, radius * 2f, 0f);
|
||||
}
|
||||
|
||||
Handles.color = c;
|
||||
}
|
||||
|
||||
public static void DrawBoneOutline(Vector3 position, Vector3 endPosition, Vector3 forward, Color color, float outlineScale = 1.35f, float scale = 1.0f)
|
||||
{
|
||||
outlineScale = Mathf.Max(1f, outlineScale);
|
||||
|
||||
Color c = Handles.color;
|
||||
Handles.color = color;
|
||||
|
||||
var right = Vector3.right;
|
||||
var v = endPosition - position;
|
||||
|
||||
if (v.sqrMagnitude != 0)
|
||||
right = v.normalized;
|
||||
|
||||
var up = Vector3.Cross(right, forward).normalized;
|
||||
var radius = GetBoneRadius(position, scale) * 0.5f;
|
||||
var outlineWidth = radius * (outlineScale - 1f);
|
||||
var numSamples = 12;
|
||||
|
||||
if (v.sqrMagnitude <= radius * radius)
|
||||
DrawingUtility.DrawSolidArc(position, -forward, up, 360f, radius, outlineScale, numSamples * 2);
|
||||
else
|
||||
{
|
||||
DrawingUtility.DrawSolidArc(position, -forward, up, 180f, radius, outlineScale, numSamples);
|
||||
DrawingUtility.DrawSolidArc(endPosition, -forward, -up, 180f, outlineWidth, 0f, numSamples);
|
||||
DrawingUtility.DrawLine(position + up * (radius + outlineWidth * 0.5f), endPosition + up * outlineWidth * 0.5f, forward, outlineWidth, outlineWidth);
|
||||
DrawingUtility.DrawLine(position - up * (radius + outlineWidth * 0.5f), endPosition - up * outlineWidth * 0.5f, forward, outlineWidth, outlineWidth);
|
||||
}
|
||||
|
||||
Handles.color = c;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4e0025ed472de854692946726304a452
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d59ac20961f9b95419fd53d6c9663903
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
#if CODE_COVERAGE
|
||||
internal class BaseObject
|
||||
{
|
||||
public static T CreateInstance<T>()
|
||||
{
|
||||
return Activator.CreateInstance<T>();
|
||||
}
|
||||
|
||||
public static void DestroyImmediate(object o)
|
||||
{
|
||||
if (o is BaseObject)
|
||||
{
|
||||
var obj = o as BaseObject;
|
||||
obj.OnDestroy();
|
||||
s_Objects.Remove(obj.GetInstanceID());
|
||||
}
|
||||
else if (o is UnityEngine.Object)
|
||||
{
|
||||
var obj = o as UnityEngine.Object;
|
||||
Undo.ClearUndo(obj);
|
||||
UnityEngine.Object.DestroyImmediate(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public static BaseObject InstanceIDToObject(int instanceID)
|
||||
{
|
||||
var obj = default(BaseObject);
|
||||
s_Objects.TryGetValue(instanceID, out obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static Dictionary<int, BaseObject> s_Objects = new Dictionary<int, BaseObject>();
|
||||
private static int s_InstanceID = 0;
|
||||
private int m_InstanceID;
|
||||
|
||||
public string name { get; set; }
|
||||
public HideFlags hideFlags = HideFlags.None;
|
||||
|
||||
public BaseObject()
|
||||
{
|
||||
m_InstanceID = ++s_InstanceID;
|
||||
s_Objects.Add(m_InstanceID, this);
|
||||
}
|
||||
|
||||
internal virtual void OnEnable() {}
|
||||
internal virtual void OnDestroy() {}
|
||||
|
||||
public int GetInstanceID()
|
||||
{
|
||||
return m_InstanceID;
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if ((other == null))
|
||||
return false;
|
||||
|
||||
return object.ReferenceEquals(this, other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_InstanceID.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool operator==(BaseObject t1, BaseObject t2)
|
||||
{
|
||||
if (object.ReferenceEquals(t1, null))
|
||||
return object.ReferenceEquals(t2, null);
|
||||
|
||||
return object.ReferenceEquals(t1, t2);
|
||||
}
|
||||
|
||||
public static bool operator!=(BaseObject t1, BaseObject t2)
|
||||
{
|
||||
return !(t1 == t2);
|
||||
}
|
||||
}
|
||||
#else
|
||||
internal class BaseObject : ScriptableObject
|
||||
{
|
||||
public static void DestroyImmediate(object o)
|
||||
{
|
||||
if (o is UnityEngine.Object)
|
||||
{
|
||||
var obj = o as UnityEngine.Object;
|
||||
Undo.ClearUndo(obj);
|
||||
UnityEngine.Object.DestroyImmediate(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public static BaseObject InstanceIDToObject(int instanceID)
|
||||
{
|
||||
return EditorUtility.InstanceIDToObject(instanceID) as BaseObject;
|
||||
}
|
||||
|
||||
internal virtual void OnEnable() {}
|
||||
internal virtual void OnDestroy() {}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9ceaf8ef48b796342bb695b046536af8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class Cache : BaseObject, ICacheUndo
|
||||
{
|
||||
public static T Create<T>() where T : Cache
|
||||
{
|
||||
var cache = CreateInstance<T>();
|
||||
cache.hideFlags = HideFlags.DontSave;
|
||||
return cache;
|
||||
}
|
||||
|
||||
public static void Destroy(Cache cache)
|
||||
{
|
||||
cache.Destroy();
|
||||
DestroyImmediate(cache);
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private List<CacheObject> m_CacheObjects = new List<CacheObject>();
|
||||
[SerializeField]
|
||||
private List<CacheObject> m_RemovedCacheObjects = new List<CacheObject>();
|
||||
private string m_UndoOperationName = null;
|
||||
private IUndo m_DefaultUndo = new UnityEngineUndo();
|
||||
private IUndo m_UndoOverride = null;
|
||||
|
||||
protected IUndo undo
|
||||
{
|
||||
get
|
||||
{
|
||||
if (undoOverride != null)
|
||||
return undoOverride;
|
||||
|
||||
return m_DefaultUndo;
|
||||
}
|
||||
}
|
||||
|
||||
public IUndo undoOverride
|
||||
{
|
||||
get { return m_UndoOverride; }
|
||||
set { m_UndoOverride = value; }
|
||||
}
|
||||
|
||||
public bool isUndoOperationSet
|
||||
{
|
||||
get { return string.IsNullOrEmpty(m_UndoOperationName) == false; }
|
||||
}
|
||||
|
||||
public void IncrementCurrentGroup()
|
||||
{
|
||||
undo.IncrementCurrentGroup();
|
||||
}
|
||||
|
||||
public virtual void BeginUndoOperation(string operationName)
|
||||
{
|
||||
if (isUndoOperationSet == false)
|
||||
{
|
||||
Debug.Assert(!m_CacheObjects.Contains(null));
|
||||
|
||||
m_UndoOperationName = operationName;
|
||||
undo.RegisterCompleteObjectUndo(this, m_UndoOperationName);
|
||||
undo.RegisterCompleteObjectUndo(m_CacheObjects.ToArray(), m_UndoOperationName);
|
||||
undo.RegisterCompleteObjectUndo(m_RemovedCacheObjects.ToArray(), m_UndoOperationName);
|
||||
}
|
||||
}
|
||||
|
||||
public void EndUndoOperation()
|
||||
{
|
||||
m_UndoOperationName = null;
|
||||
}
|
||||
|
||||
public bool IsRemoved(CacheObject cacheObject)
|
||||
{
|
||||
return m_RemovedCacheObjects.Contains(cacheObject);
|
||||
}
|
||||
|
||||
public T CreateCache<T>() where T : CacheObject
|
||||
{
|
||||
var cacheObject = FindRemovedCacheObject<T>();
|
||||
|
||||
if (cacheObject != null)
|
||||
{
|
||||
m_RemovedCacheObjects.Remove(cacheObject);
|
||||
cacheObject.OnEnable();
|
||||
}
|
||||
else
|
||||
{
|
||||
cacheObject = CacheObject.Create<T>(this);
|
||||
}
|
||||
|
||||
m_CacheObjects.Add(cacheObject);
|
||||
|
||||
cacheObject.OnCreate();
|
||||
|
||||
return cacheObject;
|
||||
}
|
||||
|
||||
private T FindRemovedCacheObject<T>() where T : CacheObject
|
||||
{
|
||||
return m_RemovedCacheObjects.FirstOrDefault((o) => o.GetType().Equals(typeof(T))) as T;
|
||||
}
|
||||
|
||||
public void Destroy(CacheObject cacheObject)
|
||||
{
|
||||
Debug.Assert(cacheObject != null);
|
||||
Debug.Assert(cacheObject.owner == this);
|
||||
Debug.Assert(m_CacheObjects.Contains(cacheObject));
|
||||
|
||||
m_CacheObjects.Remove(cacheObject);
|
||||
m_RemovedCacheObjects.Add(cacheObject);
|
||||
|
||||
cacheObject.OnDestroy();
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
Debug.Assert(!m_CacheObjects.Contains(null));
|
||||
|
||||
EndUndoOperation();
|
||||
|
||||
undo.ClearUndo(this);
|
||||
|
||||
var cacheObjects = m_CacheObjects.ToArray();
|
||||
|
||||
foreach (var cacheObject in cacheObjects)
|
||||
DestroyImmediate(cacheObject);
|
||||
|
||||
cacheObjects = m_RemovedCacheObjects.ToArray();
|
||||
|
||||
foreach (var cacheObject in cacheObjects)
|
||||
DestroyImmediate(cacheObject);
|
||||
|
||||
m_CacheObjects.Clear();
|
||||
m_RemovedCacheObjects.Clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 455f18c8a1cd7574f84ff7236cfaadea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class CacheObject : BaseObject, ISerializationCallbackReceiver
|
||||
{
|
||||
public static T Create<T>(Cache owner) where T : CacheObject
|
||||
{
|
||||
var cacheObject = CreateInstance<T>();
|
||||
cacheObject.hideFlags = HideFlags.HideAndDontSave;
|
||||
cacheObject.owner = owner;
|
||||
return cacheObject;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Cache m_Owner;
|
||||
|
||||
public Cache owner
|
||||
{
|
||||
get { return m_Owner; }
|
||||
set { m_Owner = value; }
|
||||
}
|
||||
|
||||
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
||||
{
|
||||
OnAfterDeserialize();
|
||||
}
|
||||
|
||||
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
||||
{
|
||||
OnBeforeSerialize();
|
||||
}
|
||||
|
||||
internal virtual void OnCreate() {}
|
||||
protected virtual void OnAfterDeserialize() {}
|
||||
protected virtual void OnBeforeSerialize() {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ea21de3798988c84dbc83021a3e6ecee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6735c3e859fb15b4191b28f225534760
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class SwitchModeTool : BaseTool
|
||||
{
|
||||
protected override void OnActivate()
|
||||
{
|
||||
if (skinningCache.mode != SkinningMode.SpriteSheet)
|
||||
{
|
||||
skinningCache.mode = SkinningMode.SpriteSheet;
|
||||
skinningCache.events.skinningModeChanged.Invoke(SkinningMode.SpriteSheet);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
if (skinningCache.mode != SkinningMode.Character)
|
||||
{
|
||||
skinningCache.mode = SkinningMode.Character;
|
||||
skinningCache.events.skinningModeChanged.Invoke(SkinningMode.Character);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: daabb48969fe3724f80d2c96d89d710e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal static class ColorExtensions
|
||||
{
|
||||
public static Color AlphaMultiplied(this Color c, float multiplier)
|
||||
{
|
||||
return new Color(c.r, c.g, c.b, c.a * multiplier);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 10889aecec5b6ee41a2a1626c4cfc07c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,661 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEditor.U2D.Layout;
|
||||
using UnityEngine.U2D;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal interface ICopyToolStringStore
|
||||
{
|
||||
string stringStore
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
internal class SystemCopyBufferStringStore : ICopyToolStringStore
|
||||
{
|
||||
public string stringStore
|
||||
{
|
||||
get { return EditorGUIUtility.systemCopyBuffer; }
|
||||
set { EditorGUIUtility.systemCopyBuffer = value; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class CopyTool : MeshToolWrapper
|
||||
{
|
||||
public class NewBonesStore
|
||||
{
|
||||
public BoneCache[] newBones;
|
||||
public Dictionary<string, string> newBoneNameDict;
|
||||
public NewBonesStore()
|
||||
{
|
||||
newBones = null;
|
||||
newBoneNameDict = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public void MapAllExistingBones()
|
||||
{
|
||||
foreach (var bone in newBones)
|
||||
newBoneNameDict.Add(bone.name, bone.name);
|
||||
}
|
||||
}
|
||||
|
||||
private ICopyToolStringStore m_CopyToolStringStore;
|
||||
private CopyToolView m_CopyToolView;
|
||||
|
||||
public float pixelsPerUnit
|
||||
{
|
||||
private get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ICopyToolStringStore copyToolStringStore
|
||||
{
|
||||
set { m_CopyToolStringStore = value; }
|
||||
}
|
||||
|
||||
internal override void OnCreate()
|
||||
{
|
||||
m_CopyToolView = new CopyToolView();
|
||||
m_CopyToolView.onPasteActivated += OnPasteActivated;
|
||||
m_CopyToolStringStore = new SystemCopyBufferStringStore();
|
||||
disableMeshEditor = true;
|
||||
}
|
||||
|
||||
public override void Initialize(LayoutOverlay layout)
|
||||
{
|
||||
m_CopyToolView.Initialize(layout);
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
base.OnActivate();
|
||||
m_CopyToolView.Show();
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
base.OnDeactivate();
|
||||
m_CopyToolView.Hide();
|
||||
}
|
||||
|
||||
private void CopyMeshFromSpriteCache(SpriteCache sprite, SkinningCopySpriteData skinningSpriteData)
|
||||
{
|
||||
if (meshTool == null)
|
||||
return;
|
||||
|
||||
meshTool.SetupSprite(sprite);
|
||||
skinningSpriteData.vertices = meshTool.mesh.vertices;
|
||||
skinningSpriteData.indices = meshTool.mesh.indices;
|
||||
skinningSpriteData.edges = meshTool.mesh.edges;
|
||||
skinningSpriteData.boneWeightNames = new List<string>();
|
||||
foreach (var bone in meshTool.mesh.bones)
|
||||
{
|
||||
skinningSpriteData.boneWeightNames.Add(bone.name);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCopyActivated()
|
||||
{
|
||||
SkinningCopyData skinningCopyData;
|
||||
var selectedSprite = skinningCache.selectedSprite;
|
||||
if (selectedSprite == null)
|
||||
{
|
||||
skinningCopyData = CopyAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
skinningCopyData = CopySingle(selectedSprite);
|
||||
}
|
||||
|
||||
if (skinningCopyData != null)
|
||||
m_CopyToolStringStore.stringStore = SkinningCopyUtility.SerializeSkinningCopyDataToString(skinningCopyData);
|
||||
skinningCache.events.copy.Invoke();
|
||||
}
|
||||
|
||||
public SkinningCopyData CopyAll()
|
||||
{
|
||||
var skinningCopyData = new SkinningCopyData();
|
||||
skinningCopyData.pixelsPerUnit = pixelsPerUnit;
|
||||
|
||||
var sprites = skinningCache.GetSprites();
|
||||
foreach (var sprite in sprites)
|
||||
{
|
||||
var skinningSpriteData = new SkinningCopySpriteData();
|
||||
skinningSpriteData.spriteName = sprite.name;
|
||||
|
||||
var skeleton = skinningCache.GetEffectiveSkeleton(sprite);
|
||||
if (skeleton != null && skeleton.BoneCount > 0)
|
||||
{
|
||||
if (skinningCache.hasCharacter)
|
||||
{
|
||||
// Order doesn't matter for character bones
|
||||
skinningSpriteData.spriteBones = skeleton.bones.ToSpriteBone(Matrix4x4.identity).Select(x => new SpriteBoneCopyData()
|
||||
{
|
||||
spriteBone = x,
|
||||
order = -1
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
skinningSpriteData.spriteBones = new List<SpriteBoneCopyData>();
|
||||
var bones = skeleton.bones.FindRoots();
|
||||
foreach (var bone in bones)
|
||||
GetSpriteBoneDataRecursively(skinningSpriteData.spriteBones, bone, skeleton.bones.ToList());
|
||||
}
|
||||
}
|
||||
if (meshTool != null)
|
||||
{
|
||||
CopyMeshFromSpriteCache(sprite, skinningSpriteData);
|
||||
}
|
||||
skinningCopyData.copyData.Add(skinningSpriteData);
|
||||
}
|
||||
|
||||
if (meshTool != null)
|
||||
{
|
||||
meshTool.SetupSprite(null);
|
||||
}
|
||||
|
||||
return skinningCopyData;
|
||||
}
|
||||
|
||||
public SkinningCopyData CopySingle(SpriteCache sprite)
|
||||
{
|
||||
var skinningCopyData = new SkinningCopyData();
|
||||
skinningCopyData.pixelsPerUnit = pixelsPerUnit;
|
||||
|
||||
// Mesh
|
||||
var skinningSpriteData = new SkinningCopySpriteData();
|
||||
skinningCopyData.copyData.Add(skinningSpriteData);
|
||||
|
||||
CopyMeshFromSpriteCache(sprite, skinningSpriteData);
|
||||
|
||||
// Bones
|
||||
var rootBones = new List<BoneCache>();
|
||||
BoneCache[] boneCache = null;
|
||||
if (skinningCache.hasCharacter)
|
||||
{
|
||||
var characterPart = skinningCache.GetCharacterPart(sprite);
|
||||
if (characterPart != null && characterPart.bones != null)
|
||||
{
|
||||
boneCache = characterPart.bones;
|
||||
var bones = characterPart.bones.FindRoots();
|
||||
foreach (var bone in bones)
|
||||
rootBones.Add(bone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var skeleton = skinningCache.GetEffectiveSkeleton(sprite);
|
||||
if (skeleton != null && skeleton.BoneCount > 0)
|
||||
{
|
||||
boneCache = skeleton.bones;
|
||||
var bones = boneCache.FindRoots();
|
||||
foreach (var bone in bones)
|
||||
rootBones.Add(bone);
|
||||
}
|
||||
}
|
||||
|
||||
if (rootBones.Count > 0)
|
||||
{
|
||||
skinningSpriteData.spriteBones = new List<SpriteBoneCopyData>();
|
||||
foreach (var rootBone in rootBones)
|
||||
{
|
||||
var rootBoneIndex = skinningSpriteData.spriteBones.Count;
|
||||
GetSpriteBoneDataRecursively(skinningSpriteData.spriteBones, rootBone, boneCache.ToList());
|
||||
if (skinningCache.hasCharacter)
|
||||
{
|
||||
// Offset the bones based on the currently selected Sprite in Character mode
|
||||
var characterPart = sprite.GetCharacterPart();
|
||||
if (characterPart != null)
|
||||
{
|
||||
var offset = characterPart.position;
|
||||
var rootSpriteBone = skinningSpriteData.spriteBones[rootBoneIndex];
|
||||
rootSpriteBone.spriteBone.position = rootSpriteBone.spriteBone.position - offset;
|
||||
skinningSpriteData.spriteBones[rootBoneIndex] = rootSpriteBone;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return skinningCopyData;
|
||||
}
|
||||
|
||||
private void GetSpriteBoneDataRecursively(List<SpriteBoneCopyData> bones, BoneCache rootBone, List<BoneCache> boneCache)
|
||||
{
|
||||
AppendSpriteBoneDataRecursively(bones, rootBone, -1, boneCache);
|
||||
}
|
||||
|
||||
private void AppendSpriteBoneDataRecursively(List<SpriteBoneCopyData> spriteBones, BoneCache bone, int parentIndex, List<BoneCache> boneCache)
|
||||
{
|
||||
int currentParentIndex = spriteBones.Count;
|
||||
|
||||
var boneCopyData = new SpriteBoneCopyData()
|
||||
{
|
||||
spriteBone = new SpriteBone()
|
||||
{
|
||||
name = bone.name,
|
||||
parentId = parentIndex
|
||||
},
|
||||
order = boneCache.FindIndex(x => x == bone)
|
||||
};
|
||||
if (boneCopyData.order < 0)
|
||||
{
|
||||
boneCopyData.order = boneCache.Count;
|
||||
boneCache.Add(bone);
|
||||
}
|
||||
|
||||
if (parentIndex == -1 && bone.parentBone != null)
|
||||
{
|
||||
boneCopyData.spriteBone.position = bone.position;
|
||||
boneCopyData.spriteBone.rotation = bone.rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
boneCopyData.spriteBone.position = bone.localPosition;
|
||||
boneCopyData.spriteBone.rotation = bone.localRotation;
|
||||
}
|
||||
boneCopyData.spriteBone.position = new Vector3(boneCopyData.spriteBone.position.x, boneCopyData.spriteBone.position.y, bone.depth);
|
||||
|
||||
boneCopyData.spriteBone.length = bone.localLength;
|
||||
spriteBones.Add(boneCopyData);
|
||||
foreach (var child in bone)
|
||||
{
|
||||
var childBone = child as BoneCache;
|
||||
if (childBone != null)
|
||||
AppendSpriteBoneDataRecursively(spriteBones, childBone, currentParentIndex, boneCache);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPasteActivated(bool bone, bool mesh, bool flipX, bool flipY)
|
||||
{
|
||||
var copyBuffer = m_CopyToolStringStore.stringStore;
|
||||
if (!SkinningCopyUtility.CanDeserializeStringToSkinningCopyData(copyBuffer))
|
||||
{
|
||||
Debug.LogError(TextContent.copyError1);
|
||||
return;
|
||||
}
|
||||
|
||||
var skinningCopyData = SkinningCopyUtility.DeserializeStringToSkinningCopyData(copyBuffer);
|
||||
if (skinningCopyData == null || skinningCopyData.copyData.Count == 0)
|
||||
{
|
||||
Debug.LogError(TextContent.copyError2);
|
||||
return;
|
||||
}
|
||||
|
||||
var scale = 1f;
|
||||
if (skinningCopyData.pixelsPerUnit > 0f)
|
||||
scale = pixelsPerUnit / skinningCopyData.pixelsPerUnit;
|
||||
|
||||
var sprites = skinningCache.GetSprites();
|
||||
var copyMultiple = skinningCopyData.copyData.Count > 1;
|
||||
if (copyMultiple && skinningCopyData.copyData.Count != sprites.Length && mesh)
|
||||
{
|
||||
Debug.LogError(String.Format(TextContent.copyError3, sprites.Length, skinningCopyData.copyData.Count));
|
||||
return;
|
||||
}
|
||||
|
||||
using (skinningCache.UndoScope(TextContent.pasteData))
|
||||
{
|
||||
NewBonesStore newBonesStore = null;
|
||||
if (bone && copyMultiple && skinningCache.hasCharacter)
|
||||
{
|
||||
newBonesStore = new NewBonesStore();
|
||||
var skinningSpriteData = skinningCopyData.copyData[0];
|
||||
newBonesStore.newBones = skinningCache.CreateBoneCacheFromSpriteBones(skinningSpriteData.spriteBones.Select(y => y.spriteBone).ToArray(), scale);
|
||||
if (flipX || flipY)
|
||||
{
|
||||
var characterRect = new Rect(Vector2.zero, skinningCache.character.dimension);
|
||||
var newPositions = new Vector3[newBonesStore.newBones.Length];
|
||||
var newRotations = new Quaternion[newBonesStore.newBones.Length];
|
||||
for (var i = 0; i < newBonesStore.newBones.Length; ++i)
|
||||
{
|
||||
newPositions[i] = GetFlippedBonePosition(newBonesStore.newBones[i], Vector2.zero, characterRect, flipX, flipY);
|
||||
newRotations[i] = GetFlippedBoneRotation(newBonesStore.newBones[i], flipX, flipY);
|
||||
}
|
||||
for (var i = 0; i < newBonesStore.newBones.Length; ++i)
|
||||
{
|
||||
newBonesStore.newBones[i].position = newPositions[i];
|
||||
newBonesStore.newBones[i].rotation = newRotations[i];
|
||||
}
|
||||
}
|
||||
newBonesStore.MapAllExistingBones();
|
||||
var skeleton = skinningCache.character.skeleton;
|
||||
skeleton.SetBones(newBonesStore.newBones);
|
||||
skinningCache.events.skeletonTopologyChanged.Invoke(skeleton);
|
||||
}
|
||||
|
||||
foreach (var skinningSpriteData in skinningCopyData.copyData)
|
||||
{
|
||||
SpriteCache sprite = null;
|
||||
if (!String.IsNullOrEmpty(skinningSpriteData.spriteName))
|
||||
{
|
||||
sprite = sprites.FirstOrDefault(x => x.name == skinningSpriteData.spriteName);
|
||||
}
|
||||
if (sprite == null && (skinningCopyData.copyData.Count == 1 || String.IsNullOrEmpty(skinningSpriteData.spriteName)))
|
||||
{
|
||||
sprite = skinningCache.selectedSprite;
|
||||
}
|
||||
if (sprite == null)
|
||||
continue;
|
||||
|
||||
if (bone && (!skinningCache.hasCharacter || !copyMultiple))
|
||||
{
|
||||
var spriteBones = new SpriteBone[skinningSpriteData.spriteBones.Count];
|
||||
for (int i = 0; i < skinningSpriteData.spriteBones.Count; ++i)
|
||||
{
|
||||
var order = skinningSpriteData.spriteBones[i].order;
|
||||
spriteBones[order] = skinningSpriteData.spriteBones[i].spriteBone;
|
||||
var parentId = spriteBones[order].parentId;
|
||||
if (parentId >= 0)
|
||||
{
|
||||
spriteBones[order].parentId = skinningSpriteData.spriteBones[parentId].order;
|
||||
}
|
||||
}
|
||||
newBonesStore = PasteSkeletonBones(sprite, spriteBones.ToList(), flipX, flipY, scale);
|
||||
}
|
||||
|
||||
if (mesh && meshTool != null)
|
||||
{
|
||||
PasteMesh(sprite, skinningSpriteData, flipX, flipY, scale, newBonesStore);
|
||||
}
|
||||
}
|
||||
|
||||
if (newBonesStore != null && newBonesStore.newBones != null)
|
||||
{
|
||||
skinningCache.skeletonSelection.elements = newBonesStore.newBones;
|
||||
skinningCache.events.boneSelectionChanged.Invoke();
|
||||
}
|
||||
}
|
||||
skinningCache.events.paste.Invoke(bone, mesh, flipX, flipY);
|
||||
}
|
||||
|
||||
private Vector3 GetFlippedBonePosition(BoneCache bone, Vector2 startPosition, Rect spriteRect
|
||||
, bool flipX, bool flipY)
|
||||
{
|
||||
Vector3 position = startPosition;
|
||||
if (flipX)
|
||||
{
|
||||
position.x += spriteRect.width - bone.position.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
position.x += bone.position.x;
|
||||
}
|
||||
|
||||
if (flipY)
|
||||
{
|
||||
position.y += spriteRect.height - bone.position.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
position.y += bone.position.y;
|
||||
}
|
||||
|
||||
position.z = bone.position.z;
|
||||
return position;
|
||||
}
|
||||
|
||||
private Quaternion GetFlippedBoneRotation(BoneCache bone, bool flipX, bool flipY)
|
||||
{
|
||||
var euler = bone.rotation.eulerAngles;
|
||||
if (flipX)
|
||||
{
|
||||
if (euler.z <= 180)
|
||||
{
|
||||
euler.z = 180 - euler.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
euler.z = 540 - euler.z;
|
||||
}
|
||||
}
|
||||
if (flipY)
|
||||
{
|
||||
euler.z = 360 - euler.z;
|
||||
}
|
||||
return Quaternion.Euler(euler);
|
||||
}
|
||||
|
||||
void SetBonePositionAndRotation(BoneCache[] boneCache, TransformCache bone, Vector3[] position, Quaternion[] rotation)
|
||||
{
|
||||
var index = Array.FindIndex(boneCache, x => x == bone);
|
||||
if (index >= 0)
|
||||
{
|
||||
bone.position = position[index];
|
||||
bone.rotation = rotation[index];
|
||||
}
|
||||
foreach (var child in bone.children)
|
||||
{
|
||||
SetBonePositionAndRotation(boneCache, child, position, rotation);
|
||||
}
|
||||
}
|
||||
|
||||
public NewBonesStore PasteSkeletonBones(SpriteCache sprite, List<SpriteBone> spriteBones, bool flipX, bool flipY, float scale = 1.0f)
|
||||
{
|
||||
NewBonesStore newBonesStore = new NewBonesStore();
|
||||
newBonesStore.newBones = skinningCache.CreateBoneCacheFromSpriteBones(spriteBones.ToArray(), scale);
|
||||
if (newBonesStore.newBones.Length == 0)
|
||||
return null;
|
||||
|
||||
if (sprite == null || (skinningCache.mode == SkinningMode.SpriteSheet && skinningCache.hasCharacter))
|
||||
return null;
|
||||
|
||||
var spriteRect = sprite.textureRect;
|
||||
var skeleton = skinningCache.GetEffectiveSkeleton(sprite);
|
||||
|
||||
var rectPosition = spriteRect.position;
|
||||
if (skinningCache.mode == SkinningMode.Character)
|
||||
{
|
||||
var characterPart = sprite.GetCharacterPart();
|
||||
if (characterPart == null)
|
||||
return null;
|
||||
rectPosition = characterPart.position;
|
||||
}
|
||||
|
||||
var newPositions = new Vector3[newBonesStore.newBones.Length];
|
||||
var newRotations = new Quaternion[newBonesStore.newBones.Length];
|
||||
for (var i = 0; i < newBonesStore.newBones.Length; ++i)
|
||||
{
|
||||
newPositions[i] = GetFlippedBonePosition(newBonesStore.newBones[i], rectPosition, spriteRect, flipX, flipY);
|
||||
newRotations[i] = GetFlippedBoneRotation(newBonesStore.newBones[i], flipX, flipY);
|
||||
}
|
||||
for (var i = 0; i < newBonesStore.newBones.Length; ++i)
|
||||
{
|
||||
if(newBonesStore.newBones[i].parent == null)
|
||||
SetBonePositionAndRotation(newBonesStore.newBones, newBonesStore.newBones[i], newPositions, newRotations);
|
||||
}
|
||||
|
||||
if (skinningCache.mode == SkinningMode.SpriteSheet)
|
||||
{
|
||||
newBonesStore.MapAllExistingBones();
|
||||
skeleton.SetBones(newBonesStore.newBones);
|
||||
}
|
||||
else
|
||||
{
|
||||
var existingBoneNames = skeleton.bones.Select(x => x.name).ToList();
|
||||
|
||||
skeleton.AddBones(newBonesStore.newBones);
|
||||
|
||||
var bones = skeleton.bones;
|
||||
|
||||
// Update names of all newly pasted bones
|
||||
foreach (var bone in newBonesStore.newBones)
|
||||
{
|
||||
if (existingBoneNames.Contains(bone.name))
|
||||
{
|
||||
var oldBoneName = bone.name;
|
||||
bone.name = SkeletonController.AutoBoneName(bone.parentBone, bones);
|
||||
existingBoneNames.Add(bone.name);
|
||||
newBonesStore.newBoneNameDict.Add(oldBoneName, bone.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
newBonesStore.newBoneNameDict.Add(bone.name, bone.name);
|
||||
}
|
||||
}
|
||||
|
||||
skeleton.SetDefaultPose();
|
||||
}
|
||||
|
||||
skinningCache.events.skeletonTopologyChanged.Invoke(skeleton);
|
||||
return newBonesStore;
|
||||
}
|
||||
|
||||
public void PasteMesh(SpriteCache sprite, SkinningCopySpriteData skinningSpriteData, bool flipX, bool flipY, float scale, NewBonesStore newBonesStore)
|
||||
{
|
||||
if (sprite == null)
|
||||
return;
|
||||
|
||||
meshTool.SetupSprite(sprite);
|
||||
meshTool.mesh.vertices = skinningSpriteData.vertices;
|
||||
if (!Mathf.Approximately(scale, 1f) || flipX || flipY)
|
||||
{
|
||||
var spriteRect = sprite.textureRect;
|
||||
foreach (var vertex in meshTool.mesh.vertices)
|
||||
{
|
||||
var position = vertex.position;
|
||||
if (!Mathf.Approximately(scale, 1f))
|
||||
position = position * scale;
|
||||
if (flipX)
|
||||
position.x = spriteRect.width - vertex.position.x;
|
||||
if (flipY)
|
||||
position.y = spriteRect.height - vertex.position.y;
|
||||
vertex.position = position;
|
||||
}
|
||||
}
|
||||
meshTool.mesh.indices = skinningSpriteData.indices;
|
||||
meshTool.mesh.edges = skinningSpriteData.edges;
|
||||
|
||||
int[] copyBoneToNewBones = new int[skinningSpriteData.boneWeightNames.Count];
|
||||
BoneCache[] setBones = null;
|
||||
|
||||
if (newBonesStore != null && newBonesStore.newBones != null)
|
||||
{
|
||||
// Update bone weights with new bone indices
|
||||
var setBonesList = new List<BoneCache>();
|
||||
copyBoneToNewBones = new int[skinningSpriteData.boneWeightNames.Count];
|
||||
int index = 0;
|
||||
for (int i = 0; i < skinningSpriteData.boneWeightNames.Count; ++i)
|
||||
{
|
||||
string oldBoneName = skinningSpriteData.boneWeightNames[i];
|
||||
string newBoneName;
|
||||
newBonesStore.newBoneNameDict.TryGetValue(oldBoneName, out newBoneName);
|
||||
var newBone = newBonesStore.newBones.FirstOrDefault(bone => bone.name == newBoneName);
|
||||
copyBoneToNewBones[i] = -1;
|
||||
if (newBone == null)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < skinningSpriteData.spriteBones.Count; ++j)
|
||||
{
|
||||
if (skinningSpriteData.spriteBones[j].spriteBone.name == oldBoneName)
|
||||
{
|
||||
copyBoneToNewBones[i] = index++;
|
||||
setBonesList.Add(newBone);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
setBones = setBonesList.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Attempt to link weights based on existing bone names
|
||||
var skeleton = skinningCache.GetEffectiveSkeleton(sprite);
|
||||
var characterBones = new List<BoneCache>();
|
||||
for (int i = 0; i < skinningSpriteData.boneWeightNames.Count; ++i)
|
||||
{
|
||||
copyBoneToNewBones[i] = -1;
|
||||
var boneName = skinningSpriteData.boneWeightNames[i];
|
||||
for (int j = 0; j < skeleton.bones.Length; ++j)
|
||||
{
|
||||
if (skeleton.bones[j].name == boneName)
|
||||
{
|
||||
copyBoneToNewBones[i] = characterBones.Count;
|
||||
characterBones.Add(skeleton.bones[j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
setBones = characterBones.ToArray();
|
||||
}
|
||||
|
||||
// Remap new bone indexes from copied bone indexes
|
||||
foreach (var vertex in meshTool.mesh.vertices)
|
||||
{
|
||||
var editableBoneWeight = vertex.editableBoneWeight;
|
||||
|
||||
for (var i = 0; i < editableBoneWeight.Count; ++i)
|
||||
{
|
||||
if (!editableBoneWeight[i].enabled)
|
||||
continue;
|
||||
|
||||
if (copyBoneToNewBones.Length > editableBoneWeight[i].boneIndex)
|
||||
{
|
||||
var boneIndex = copyBoneToNewBones[editableBoneWeight[i].boneIndex];
|
||||
if (boneIndex != -1)
|
||||
editableBoneWeight[i].boneIndex = boneIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update associated bones for mesh
|
||||
meshTool.mesh.SetCompatibleBoneSet(setBones);
|
||||
meshTool.mesh.bones = setBones; // Fixes weights for bones that do not exist
|
||||
|
||||
// Update associated bones for character
|
||||
if (skinningCache.hasCharacter)
|
||||
{
|
||||
var characterPart = sprite.GetCharacterPart();
|
||||
if (characterPart != null)
|
||||
{
|
||||
characterPart.bones = setBones;
|
||||
skinningCache.events.characterPartChanged.Invoke(characterPart);
|
||||
}
|
||||
}
|
||||
|
||||
meshTool.UpdateMesh();
|
||||
}
|
||||
}
|
||||
|
||||
internal class CopyToolView
|
||||
{
|
||||
private PastePanel m_PastePanel;
|
||||
|
||||
public event Action<bool, bool, bool, bool> onPasteActivated = (bone, mesh, flipX, flipY) => {};
|
||||
|
||||
public void Show()
|
||||
{
|
||||
m_PastePanel.SetHiddenFromLayout(false);
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
m_PastePanel.SetHiddenFromLayout(true);
|
||||
}
|
||||
|
||||
public void Initialize(LayoutOverlay layoutOverlay)
|
||||
{
|
||||
m_PastePanel = PastePanel.GenerateFromUXML();
|
||||
BindElements();
|
||||
layoutOverlay.rightOverlay.Add(m_PastePanel);
|
||||
m_PastePanel.SetHiddenFromLayout(true);
|
||||
}
|
||||
|
||||
void BindElements()
|
||||
{
|
||||
m_PastePanel.onPasteActivated += OnPasteActivated;
|
||||
}
|
||||
|
||||
void OnPasteActivated(bool bone, bool mesh, bool flipX, bool flipY)
|
||||
{
|
||||
onPasteActivated(bone, mesh, flipX, flipY);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 311e00422db8e4949bf926b0d039e05e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class DefaultPoseScope : IDisposable
|
||||
{
|
||||
private bool m_Disposed;
|
||||
private SkeletonCache m_Skeleton;
|
||||
private BonePose[] m_Pose;
|
||||
private bool m_DoRestorePose = false;
|
||||
private bool m_UseLocalPose;
|
||||
|
||||
public DefaultPoseScope(SkeletonCache skeleton, bool useLocalPose = true)
|
||||
{
|
||||
Debug.Assert(skeleton != null);
|
||||
|
||||
m_Skeleton = skeleton;
|
||||
m_UseLocalPose = useLocalPose;
|
||||
|
||||
if (m_Skeleton.isPosePreview)
|
||||
{
|
||||
m_DoRestorePose = true;
|
||||
|
||||
if(useLocalPose)
|
||||
m_Pose = m_Skeleton.GetLocalPose();
|
||||
else
|
||||
m_Pose = m_Skeleton.GetWorldPose();
|
||||
|
||||
m_Skeleton.RestoreDefaultPose();
|
||||
}
|
||||
}
|
||||
|
||||
~DefaultPoseScope()
|
||||
{
|
||||
if (!m_Disposed)
|
||||
Debug.LogError("Scope was not disposed! You should use the 'using' keyword or manually call Dispose.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_Disposed)
|
||||
return;
|
||||
|
||||
m_Disposed = true;
|
||||
if (m_Skeleton != null && m_DoRestorePose)
|
||||
{
|
||||
if(m_UseLocalPose)
|
||||
m_Skeleton.SetLocalPose(m_Pose);
|
||||
else
|
||||
m_Skeleton.SetWorldPose(m_Pose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bee8f4b5ba5b4ee46a3b10bad91b8c97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,261 @@
|
|||
using UnityEditor.U2D.Common;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class DrawingUtility
|
||||
{
|
||||
public static readonly Color kSpriteBorderColor = new Color(0.25f, 0.5f, 1f, 0.75f);
|
||||
|
||||
public static void DrawLine(Vector3 p1, Vector3 p2, Vector3 normal, float width)
|
||||
{
|
||||
DrawLine(p1, p2, normal, width, width);
|
||||
}
|
||||
|
||||
public static void DrawLine(Vector3 p1, Vector3 p2, Vector3 normal, float widthP1, float widthP2)
|
||||
{
|
||||
DrawLine(p1, p2, normal, widthP1, widthP2, Handles.color);
|
||||
}
|
||||
|
||||
public static void DrawLine(Vector3 p1, Vector3 p2, Vector3 normal, float widthP1, float widthP2, Color color)
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
Vector3 up = Vector3.Cross(normal, p2 - p1).normalized;
|
||||
|
||||
Shader.SetGlobalFloat("_HandleSize", 1);
|
||||
|
||||
InternalEditorBridge.ApplyWireMaterial();
|
||||
GL.PushMatrix();
|
||||
GL.MultMatrix(Handles.matrix);
|
||||
GL.Begin(4);
|
||||
GL.Color(color);
|
||||
GL.Vertex(p1 + up * widthP1 * 0.5f);
|
||||
GL.Vertex(p1 - up * widthP1 * 0.5f);
|
||||
GL.Vertex(p2 - up * widthP2 * 0.5f);
|
||||
GL.Vertex(p1 + up * widthP1 * 0.5f);
|
||||
GL.Vertex(p2 - up * widthP2 * 0.5f);
|
||||
GL.Vertex(p2 + up * widthP2 * 0.5f);
|
||||
GL.End();
|
||||
GL.PopMatrix();
|
||||
}
|
||||
|
||||
public static void BeginLines(Color color)
|
||||
{
|
||||
InternalEditorBridge.ApplyWireMaterial();
|
||||
GL.PushMatrix();
|
||||
GL.MultMatrix(Handles.matrix);
|
||||
GL.Begin(GL.LINES);
|
||||
GL.Color(color);
|
||||
}
|
||||
|
||||
public static void BeginSolidLines()
|
||||
{
|
||||
InternalEditorBridge.ApplyWireMaterial();
|
||||
GL.PushMatrix();
|
||||
GL.MultMatrix(Handles.matrix);
|
||||
GL.Begin(GL.TRIANGLES);
|
||||
}
|
||||
|
||||
public static void EndLines()
|
||||
{
|
||||
GL.End();
|
||||
GL.PopMatrix();
|
||||
}
|
||||
|
||||
public static void DrawLine(Vector3 p1, Vector3 p2)
|
||||
{
|
||||
GL.Vertex(p1);
|
||||
GL.Vertex(p2);
|
||||
}
|
||||
|
||||
public static void DrawSolidLine(float width, Vector3 p1, Vector3 p2)
|
||||
{
|
||||
DrawSolidLine(p1, p2, Vector3.forward, width, width);
|
||||
}
|
||||
|
||||
public static void DrawSolidLine(Vector3 p1, Vector3 p2, Vector3 normal, float widthP1, float widthP2)
|
||||
{
|
||||
GL.Color(Handles.color);
|
||||
|
||||
Vector3 right = Vector3.Cross(normal, p2 - p1).normalized;
|
||||
|
||||
GL.Vertex(p1 + right * widthP1 * 0.5f);
|
||||
GL.Vertex(p1 - right * widthP1 * 0.5f);
|
||||
GL.Vertex(p2 - right * widthP2 * 0.5f);
|
||||
GL.Vertex(p1 + right * widthP1 * 0.5f);
|
||||
GL.Vertex(p2 - right * widthP2 * 0.5f);
|
||||
GL.Vertex(p2 + right * widthP2 * 0.5f);
|
||||
}
|
||||
|
||||
public static void DrawBox(Rect position)
|
||||
{
|
||||
Vector3[] points = new Vector3[5];
|
||||
int i = 0;
|
||||
points[i++] = new Vector3(position.xMin, position.yMin, 0f);
|
||||
points[i++] = new Vector3(position.xMax, position.yMin, 0f);
|
||||
points[i++] = new Vector3(position.xMax, position.yMax, 0f);
|
||||
points[i++] = new Vector3(position.xMin, position.yMax, 0f);
|
||||
|
||||
DrawLine(points[0], points[1]);
|
||||
DrawLine(points[1], points[2]);
|
||||
DrawLine(points[2], points[3]);
|
||||
DrawLine(points[3], points[0]);
|
||||
}
|
||||
|
||||
public static void DrawMesh(Mesh mesh, Material material, Matrix4x4 matrix)
|
||||
{
|
||||
Debug.Assert(mesh != null);
|
||||
Debug.Assert(material != null);
|
||||
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
material.SetFloat("_AdjustLinearForGamma", PlayerSettings.colorSpace == ColorSpace.Linear ? 1.0f : 0.0f);
|
||||
material.SetPass(0);
|
||||
Graphics.DrawMeshNow(mesh, Handles.matrix * matrix);
|
||||
}
|
||||
|
||||
public static void DrawGUIStyleCap(int controlID, Vector3 position, Quaternion rotation, float size, GUIStyle guiStyle)
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
if (Camera.current && Vector3.Dot(position - Camera.current.transform.position, Camera.current.transform.forward) < 0f)
|
||||
return;
|
||||
|
||||
Handles.BeginGUI();
|
||||
guiStyle.Draw(GetGUIStyleRect(guiStyle, position), GUIContent.none, controlID);
|
||||
Handles.EndGUI();
|
||||
}
|
||||
|
||||
private static Rect GetGUIStyleRect(GUIStyle style, Vector3 position)
|
||||
{
|
||||
Vector2 vector = HandleUtility.WorldToGUIPoint(position);
|
||||
|
||||
float fixedWidth = style.fixedWidth;
|
||||
float fixedHeight = style.fixedHeight;
|
||||
|
||||
return new Rect(vector.x - fixedWidth / 2f, vector.y - fixedHeight / 2f, fixedWidth, fixedHeight);
|
||||
}
|
||||
|
||||
public static void DrawRect(Rect rect, Vector3 position, Quaternion rotation, Color color, float rectAlpha, float outlineAlpha)
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
Vector3[] corners = new Vector3[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
Vector3 point = GetLocalRectPoint(rect, i);
|
||||
corners[i] = rotation * point + position;
|
||||
}
|
||||
|
||||
Vector3[] points = new Vector3[]
|
||||
{
|
||||
corners[0],
|
||||
corners[1],
|
||||
corners[2],
|
||||
corners[3],
|
||||
corners[0]
|
||||
};
|
||||
|
||||
Color l_color = Handles.color;
|
||||
Handles.color = color;
|
||||
|
||||
Vector2 offset = new Vector2(1f, 1f);
|
||||
|
||||
if (!Camera.current)
|
||||
{
|
||||
offset.y *= -1;
|
||||
}
|
||||
|
||||
Handles.DrawSolidRectangleWithOutline(points, new Color(1f, 1f, 1f, rectAlpha), new Color(1f, 1f, 1f, outlineAlpha));
|
||||
|
||||
Handles.color = l_color;
|
||||
}
|
||||
|
||||
private static Vector2 GetLocalRectPoint(Rect rect, int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case (0): return new Vector2(rect.xMin, rect.yMax);
|
||||
case (1): return new Vector2(rect.xMax, rect.yMax);
|
||||
case (2): return new Vector2(rect.xMax, rect.yMin);
|
||||
case (3): return new Vector2(rect.xMin, rect.yMin);
|
||||
}
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
private static void SetDiscSectionPoints(Vector3[] dest, int count, Vector3 normal, Vector3 from, float angle)
|
||||
{
|
||||
from.Normalize();
|
||||
Quaternion rotation = Quaternion.AngleAxis(angle / (float)(count - 1), normal);
|
||||
Vector3 vector = from;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dest[i] = vector;
|
||||
vector = rotation * vector;
|
||||
}
|
||||
}
|
||||
|
||||
static Vector3[] s_array;
|
||||
public static void DrawSolidArc(Vector3 center, Vector3 normal, Vector3 from, float angle, float radius, int numSamples = 60)
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
numSamples = Mathf.Clamp(numSamples, 3, 60);
|
||||
|
||||
if (s_array == null)
|
||||
s_array = new Vector3[60];
|
||||
|
||||
Color color = Handles.color;
|
||||
SetDiscSectionPoints(s_array, numSamples, normal, from, angle);
|
||||
InternalEditorBridge.ApplyWireMaterial();
|
||||
GL.PushMatrix();
|
||||
GL.MultMatrix(Handles.matrix);
|
||||
GL.Begin(GL.TRIANGLES);
|
||||
for (int i = 1; i < numSamples; i++)
|
||||
{
|
||||
GL.Color(color);
|
||||
GL.Vertex(center);
|
||||
GL.Vertex(center + s_array[i - 1] * radius);
|
||||
GL.Vertex(center + s_array[i] * radius);
|
||||
}
|
||||
GL.End();
|
||||
GL.PopMatrix();
|
||||
}
|
||||
|
||||
public static void DrawSolidArc(Vector3 center, Vector3 normal, Vector3 from, float angle, float radius, float outlineScale, int numSamples = 60)
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
numSamples = Mathf.Clamp(numSamples, 3, 60);
|
||||
|
||||
if(s_array == null)
|
||||
s_array = new Vector3[60];
|
||||
|
||||
Color color = Handles.color;
|
||||
SetDiscSectionPoints(s_array, numSamples, normal, from, angle);
|
||||
InternalEditorBridge.ApplyWireMaterial();
|
||||
GL.PushMatrix();
|
||||
GL.MultMatrix(Handles.matrix);
|
||||
GL.Begin(4);
|
||||
for (int i = 1; i < numSamples; i++)
|
||||
{
|
||||
GL.Color(color);
|
||||
GL.Vertex(center + s_array[i - 1] * radius * outlineScale);
|
||||
GL.Vertex(center + s_array[i - 1] * radius);
|
||||
GL.Vertex(center + s_array[i] * radius);
|
||||
GL.Vertex(center + s_array[i - 1] * radius * outlineScale);
|
||||
GL.Vertex(center + s_array[i] * radius);
|
||||
GL.Vertex(center + s_array[i] * radius * outlineScale);
|
||||
}
|
||||
GL.End();
|
||||
GL.PopMatrix();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9b3f77b5965944b8ba5c1beefa1eb533
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,215 @@
|
|||
using System;
|
||||
using UnityEditor.U2D.Layout;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class GenerateGeometryTool : MeshToolWrapper
|
||||
{
|
||||
private const float kWeightTolerance = 0.1f;
|
||||
private SpriteMeshDataController m_SpriteMeshDataController = new SpriteMeshDataController();
|
||||
private ITriangulator m_Triangulator;
|
||||
private IOutlineGenerator m_OutlineGenerator;
|
||||
private IWeightsGenerator m_WeightGenerator;
|
||||
private GenerateGeometryPanel m_GenerateGeometryPanel;
|
||||
|
||||
internal override void OnCreate()
|
||||
{
|
||||
m_Triangulator = new Triangulator();
|
||||
m_OutlineGenerator = new OutlineGenerator();
|
||||
m_WeightGenerator = new BoundedBiharmonicWeightsGenerator();
|
||||
}
|
||||
|
||||
public override void Initialize(LayoutOverlay layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
m_GenerateGeometryPanel = GenerateGeometryPanel.GenerateFromUXML();
|
||||
m_GenerateGeometryPanel.skinningCache = skinningCache;
|
||||
|
||||
layout.rightOverlay.Add(m_GenerateGeometryPanel);
|
||||
|
||||
BindElements();
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void BindElements()
|
||||
{
|
||||
Debug.Assert(m_GenerateGeometryPanel != null);
|
||||
|
||||
m_GenerateGeometryPanel.onAutoGenerateGeometry += (float d, byte a, float s) =>
|
||||
{
|
||||
var selectedSprite = skinningCache.selectedSprite;
|
||||
|
||||
if (selectedSprite != null)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar(TextContent.generatingGeometry, selectedSprite.name, 0f);
|
||||
|
||||
using (skinningCache.UndoScope(TextContent.generateGeometry))
|
||||
{
|
||||
GenerateGeometry(selectedSprite, d / 100f, a, s);
|
||||
|
||||
if (m_GenerateGeometryPanel.generateWeights)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar(TextContent.generatingWeights, selectedSprite.name, 1f);
|
||||
GenerateWeights(selectedSprite);
|
||||
}
|
||||
|
||||
skinningCache.vertexSelection.Clear();
|
||||
skinningCache.events.meshChanged.Invoke(selectedSprite.GetMesh());
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
};
|
||||
|
||||
m_GenerateGeometryPanel.onAutoGenerateGeometryAll += (float d, byte a, float s) =>
|
||||
{
|
||||
var sprites = skinningCache.GetSprites();
|
||||
|
||||
using (skinningCache.UndoScope(TextContent.generateGeometry))
|
||||
{
|
||||
for (var i = 0; i < sprites.Length; ++i)
|
||||
{
|
||||
var sprite = sprites[i];
|
||||
|
||||
if (!sprite.IsVisible())
|
||||
continue;
|
||||
|
||||
EditorUtility.DisplayProgressBar(TextContent.generateGeometry, sprite.name, i * 2f / (sprites.Length * 2f));
|
||||
|
||||
GenerateGeometry(sprite, d / 100f, a, s);
|
||||
|
||||
if (m_GenerateGeometryPanel.generateWeights)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar(TextContent.generatingWeights, sprite.name, (i * 2f + 1) / (sprites.Length * 2f));
|
||||
GenerateWeights(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
foreach(var sprite in sprites)
|
||||
skinningCache.events.meshChanged.Invoke(sprite.GetMesh());
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
base.OnActivate();
|
||||
UpdateButton();
|
||||
Show();
|
||||
skinningCache.events.selectedSpriteChanged.AddListener(OnSelectedSpriteChanged);
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
base.OnDeactivate();
|
||||
Hide();
|
||||
skinningCache.events.selectedSpriteChanged.RemoveListener(OnSelectedSpriteChanged);
|
||||
}
|
||||
|
||||
private void Show()
|
||||
{
|
||||
m_GenerateGeometryPanel.SetHiddenFromLayout(false);
|
||||
}
|
||||
|
||||
private void Hide()
|
||||
{
|
||||
m_GenerateGeometryPanel.SetHiddenFromLayout(true);
|
||||
}
|
||||
|
||||
private void UpdateButton()
|
||||
{
|
||||
var selectedSprite = skinningCache.selectedSprite;
|
||||
|
||||
if (selectedSprite == null)
|
||||
m_GenerateGeometryPanel.SetMode(GenerateGeometryPanel.GenerateMode.Multiple);
|
||||
else
|
||||
m_GenerateGeometryPanel.SetMode(GenerateGeometryPanel.GenerateMode.Single);
|
||||
}
|
||||
|
||||
private void OnSelectedSpriteChanged(SpriteCache sprite)
|
||||
{
|
||||
UpdateButton();
|
||||
}
|
||||
|
||||
private void GenerateGeometry(SpriteCache sprite, float outlineDetail, byte alphaTolerance, float subdivide)
|
||||
{
|
||||
Debug.Assert(sprite != null);
|
||||
|
||||
var mesh = sprite.GetMesh();
|
||||
|
||||
Debug.Assert(mesh != null);
|
||||
|
||||
m_SpriteMeshDataController.spriteMeshData = mesh;
|
||||
m_SpriteMeshDataController.OutlineFromAlpha(m_OutlineGenerator, mesh.textureDataProvider, outlineDetail, alphaTolerance);
|
||||
m_SpriteMeshDataController.Triangulate(m_Triangulator);
|
||||
|
||||
if (subdivide > 0f)
|
||||
{
|
||||
var largestAreaFactor = Mathf.Lerp(0.5f, 0.05f, Math.Min(subdivide, 100f) / 100f);
|
||||
m_SpriteMeshDataController.Subdivide(m_Triangulator, largestAreaFactor);
|
||||
}
|
||||
|
||||
foreach (var vertex in mesh.vertices)
|
||||
vertex.position -= sprite.textureRect.position;
|
||||
}
|
||||
|
||||
private void GenerateWeights(SpriteCache sprite)
|
||||
{
|
||||
Debug.Assert(sprite != null);
|
||||
|
||||
var mesh = sprite.GetMesh();
|
||||
|
||||
Debug.Assert(mesh != null);
|
||||
|
||||
using (new DefaultPoseScope(skinningCache.GetEffectiveSkeleton(sprite)))
|
||||
{
|
||||
if (NeedsAssociateBones(sprite.GetCharacterPart()))
|
||||
{
|
||||
using (new AssociateBonesScope(sprite))
|
||||
{
|
||||
GenerateWeights(mesh);
|
||||
}
|
||||
}
|
||||
else
|
||||
GenerateWeights(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
private bool NeedsAssociateBones(CharacterPartCache characterPart)
|
||||
{
|
||||
if (characterPart == null)
|
||||
return false;
|
||||
|
||||
var skeleton = characterPart.skinningCache.character.skeleton;
|
||||
|
||||
return characterPart.BoneCount == 0 ||
|
||||
(characterPart.BoneCount == 1 && characterPart.GetBone(0) == skeleton.GetBone(0));
|
||||
}
|
||||
|
||||
private void GenerateWeights(MeshCache mesh)
|
||||
{
|
||||
Debug.Assert(mesh != null);
|
||||
|
||||
m_SpriteMeshDataController.spriteMeshData = mesh;
|
||||
m_SpriteMeshDataController.CalculateWeights(m_WeightGenerator, null, kWeightTolerance);
|
||||
m_SpriteMeshDataController.SortTrianglesByDepth();
|
||||
}
|
||||
|
||||
protected override void OnGUI()
|
||||
{
|
||||
m_MeshPreviewBehaviour.showWeightMap = m_GenerateGeometryPanel.generateWeights;
|
||||
m_MeshPreviewBehaviour.overlaySelected = m_GenerateGeometryPanel.generateWeights;
|
||||
|
||||
skeletonTool.skeletonStyle = SkeletonStyles.Default;
|
||||
|
||||
if (m_GenerateGeometryPanel.generateWeights)
|
||||
skeletonTool.skeletonStyle = SkeletonStyles.WeightMap;
|
||||
|
||||
DoSkeletonGUI();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 237f0a0a8eeab0d46873db954d4abded
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,147 @@
|
|||
using System;
|
||||
using UnityEditor.U2D.Layout;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class GenerateWeightsTool : MeshToolWrapper
|
||||
{
|
||||
private const float kWeightTolerance = 0.01f;
|
||||
private SpriteMeshDataController m_SpriteMeshDataController = new SpriteMeshDataController();
|
||||
private IWeightsGenerator m_WeightGenerator;
|
||||
private GenerateWeightsPanel m_GenerateWeightsPanel;
|
||||
|
||||
internal override void OnCreate()
|
||||
{
|
||||
m_WeightGenerator = new BoundedBiharmonicWeightsGenerator();
|
||||
}
|
||||
|
||||
public override void Initialize(LayoutOverlay layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
m_GenerateWeightsPanel = GenerateWeightsPanel.GenerateFromUXML();
|
||||
|
||||
layout.rightOverlay.Add(m_GenerateWeightsPanel);
|
||||
|
||||
BindElements();
|
||||
m_GenerateWeightsPanel.SetHiddenFromLayout(true);
|
||||
}
|
||||
|
||||
private void BindElements()
|
||||
{
|
||||
Debug.Assert(m_GenerateWeightsPanel != null);
|
||||
|
||||
m_GenerateWeightsPanel.onGenerateWeights += () =>
|
||||
{
|
||||
HandleWeights(GenerateWeights, TextContent.generateWeights);
|
||||
};
|
||||
|
||||
m_GenerateWeightsPanel.onNormalizeWeights += () =>
|
||||
{
|
||||
HandleWeights(NormalizeWeights, TextContent.normalizeWeights);
|
||||
};
|
||||
|
||||
m_GenerateWeightsPanel.onClearWeights += () =>
|
||||
{
|
||||
HandleWeights(ClearWeights, TextContent.clearWeights);
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
base.OnActivate();
|
||||
m_GenerateWeightsPanel.SetHiddenFromLayout(false);
|
||||
skinningCache.events.skinningModeChanged.AddListener(OnModeChanged);
|
||||
skinningCache.events.selectedSpriteChanged.AddListener(OnSpriteSelectionChanged);
|
||||
m_GenerateWeightsPanel.Update(skinningCache.mode == SkinningMode.Character);
|
||||
OnSpriteSelectionChanged(skinningCache.selectedSprite);
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
base.OnDeactivate();
|
||||
skinningCache.events.skinningModeChanged.RemoveListener(OnModeChanged);
|
||||
skinningCache.events.selectedSpriteChanged.RemoveListener(OnSpriteSelectionChanged);
|
||||
m_GenerateWeightsPanel.SetHiddenFromLayout(true);
|
||||
}
|
||||
|
||||
void OnModeChanged(SkinningMode mode)
|
||||
{
|
||||
m_GenerateWeightsPanel.Update(mode == SkinningMode.Character);
|
||||
}
|
||||
|
||||
void OnSpriteSelectionChanged(SpriteCache sprite)
|
||||
{
|
||||
m_GenerateWeightsPanel.generateButtonText = sprite != null ? TextContent.generate : TextContent.generateAll;
|
||||
}
|
||||
|
||||
private void HandleWeights(Action<SpriteCache> action, string undoName)
|
||||
{
|
||||
using (skinningCache.UndoScope(undoName))
|
||||
{
|
||||
var selectedSprite = skinningCache.selectedSprite;
|
||||
if (selectedSprite != null)
|
||||
HandleWeightsForSprite(selectedSprite, action);
|
||||
else
|
||||
{
|
||||
var sprites = skinningCache.GetSprites();
|
||||
|
||||
foreach (var sprite in sprites)
|
||||
{
|
||||
if (sprite.IsVisible())
|
||||
HandleWeightsForSprite(sprite, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleWeightsForSprite(SpriteCache sprite, Action<SpriteCache> action)
|
||||
{
|
||||
Debug.Assert(sprite != null);
|
||||
|
||||
using (new DefaultPoseScope(skinningCache.GetEffectiveSkeleton(sprite)))
|
||||
{
|
||||
action(sprite);
|
||||
}
|
||||
|
||||
skinningCache.events.meshChanged.Invoke(sprite.GetMesh());
|
||||
}
|
||||
|
||||
private void GenerateWeights(SpriteCache sprite)
|
||||
{
|
||||
using (m_GenerateWeightsPanel.associateBones ? new AssociateBonesScope(sprite) : null)
|
||||
{
|
||||
m_SpriteMeshDataController.spriteMeshData = sprite.GetMesh();
|
||||
m_SpriteMeshDataController.CalculateWeights(m_WeightGenerator, skinningCache.vertexSelection, kWeightTolerance);
|
||||
m_SpriteMeshDataController.SortTrianglesByDepth();
|
||||
}
|
||||
}
|
||||
|
||||
private void NormalizeWeights(SpriteCache sprite)
|
||||
{
|
||||
m_SpriteMeshDataController.spriteMeshData = sprite.GetMesh();
|
||||
m_SpriteMeshDataController.NormalizeWeights(skinningCache.vertexSelection);
|
||||
m_SpriteMeshDataController.SortTrianglesByDepth();
|
||||
}
|
||||
|
||||
private void ClearWeights(SpriteCache sprite)
|
||||
{
|
||||
m_SpriteMeshDataController.spriteMeshData = sprite.GetMesh();
|
||||
m_SpriteMeshDataController.ClearWeights(skinningCache.vertexSelection);
|
||||
}
|
||||
|
||||
protected override void OnGUI()
|
||||
{
|
||||
m_MeshPreviewBehaviour.showWeightMap = true;
|
||||
m_MeshPreviewBehaviour.overlaySelected = true;
|
||||
skeletonMode = SkeletonMode.EditPose;
|
||||
meshMode = SpriteMeshViewMode.EditGeometry;
|
||||
disableMeshEditor = true;
|
||||
skeletonTool.skeletonStyle = SkeletonStyles.WeightMap;
|
||||
|
||||
DoSkeletonGUI();
|
||||
DoMeshGUI();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 497003cbd9cf35349bb7a01db4d94b26
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,166 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class HorizontalToggleTools
|
||||
{
|
||||
private static class Styles
|
||||
{
|
||||
public static GUIContent visibilityCollapseIcon = new GUIContent(IconUtility.LoadIconResource("Visibility_Tool", IconUtility.k_LightIconResourcePath, IconUtility.k_DarkIconResourcePath), L10n.Tr(TextContent.visibilityIconTooltip));
|
||||
public static GUIContent visibilityIcon = new GUIContent(L10n.Tr(TextContent.visibilityIconText), IconUtility.LoadIconResource("Visibility_Tool", IconUtility.k_LightIconResourcePath, IconUtility.k_DarkIconResourcePath), L10n.Tr(TextContent.visibilityIconTooltip));
|
||||
public static GUIContent characterCollapseIcon = new GUIContent(IconUtility.LoadIconResource("character_Mode", IconUtility.k_LightIconResourcePath, IconUtility.k_DarkIconResourcePath), L10n.Tr(TextContent.restorePose));
|
||||
public static GUIContent characterIcon = new GUIContent(L10n.Tr(TextContent.characterIconText), IconUtility.LoadIconResource("character_Mode", IconUtility.k_LightIconResourcePath, IconUtility.k_DarkIconResourcePath), L10n.Tr(TextContent.restorePose));
|
||||
public static GUIContent spriteSheetIcon = new GUIContent(L10n.Tr(TextContent.spriteSheetIconText), IconUtility.LoadIconResource("Sprite_Mode", IconUtility.k_LightIconResourcePath, IconUtility.k_DarkIconResourcePath), L10n.Tr(TextContent.spriteSheetIconTooltip));
|
||||
public static GUIContent spriteSheetCollapseIcon = new GUIContent(IconUtility.LoadIconResource("Sprite_Mode", IconUtility.k_LightIconResourcePath, IconUtility.k_DarkIconResourcePath), L10n.Tr(TextContent.spriteSheetIconTooltip));
|
||||
public static GUIContent copyIcon = new GUIContent(L10n.Tr(TextContent.copyText), IconUtility.LoadIconResource("Copy", IconUtility.k_LightIconResourcePath, IconUtility.k_DarkIconResourcePath), L10n.Tr(TextContent.copyTooltip));
|
||||
public static GUIContent copyCollapseIcon = new GUIContent(IconUtility.LoadIconResource("Copy", IconUtility.k_LightIconResourcePath, IconUtility.k_DarkIconResourcePath), L10n.Tr(TextContent.copyTooltip));
|
||||
public static GUIContent pasteIcon = new GUIContent(L10n.Tr(TextContent.pasteText), IconUtility.LoadIconResource("Paste", IconUtility.k_LightIconResourcePath, IconUtility.k_DarkIconResourcePath), L10n.Tr(TextContent.pasteTooltip));
|
||||
public static GUIContent pasteCollapseIcon = new GUIContent(IconUtility.LoadIconResource("Paste", IconUtility.k_LightIconResourcePath, IconUtility.k_DarkIconResourcePath), L10n.Tr(TextContent.pasteTooltip));
|
||||
}
|
||||
|
||||
private SkinningCache skinningCache { get; set; }
|
||||
|
||||
private CopyTool copyTool
|
||||
{
|
||||
get { return skinningCache.GetTool(Tools.CopyPaste) as CopyTool; }
|
||||
}
|
||||
|
||||
private VisibilityTool visibilityTool
|
||||
{
|
||||
get { return skinningCache.GetTool(Tools.Visibility) as VisibilityTool; }
|
||||
}
|
||||
|
||||
private SwitchModeTool switchmodeTool
|
||||
{
|
||||
get { return skinningCache.GetTool(Tools.SwitchMode) as SwitchModeTool; }
|
||||
}
|
||||
|
||||
private GUIContent spriteSheetIcon
|
||||
{
|
||||
get { return collapseToolbar ? Styles.spriteSheetCollapseIcon : Styles.spriteSheetIcon; }
|
||||
}
|
||||
|
||||
private GUIContent copyIcon
|
||||
{
|
||||
get { return collapseToolbar ? Styles.copyCollapseIcon : Styles.copyIcon; }
|
||||
}
|
||||
private GUIContent pasteIcon
|
||||
{
|
||||
get { return collapseToolbar ? Styles.pasteCollapseIcon : Styles.pasteIcon; }
|
||||
}
|
||||
|
||||
internal Action<BaseTool> onActivateTool = (b) => {};
|
||||
private BaseTool m_PreviousTool;
|
||||
|
||||
public bool collapseToolbar { get; set; }
|
||||
|
||||
internal HorizontalToggleTools(SkinningCache s)
|
||||
{
|
||||
skinningCache = s;
|
||||
}
|
||||
|
||||
internal void DoGUI(Rect drawArea, BaseTool currentTool, bool isDisabled)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(isDisabled))
|
||||
{
|
||||
GUILayout.BeginArea(drawArea);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
DoPreviewToggle();
|
||||
DoModeToggle();
|
||||
DoCopyToggle(currentTool);
|
||||
GUILayout.FlexibleSpace();
|
||||
DoVisibilityToggle(currentTool);
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
}
|
||||
|
||||
private void StorePreviousTool(BaseTool currentTool)
|
||||
{
|
||||
if(currentTool != copyTool && currentTool != visibilityTool)
|
||||
m_PreviousTool = currentTool;
|
||||
}
|
||||
|
||||
private void DoModeToggle()
|
||||
{
|
||||
if (skinningCache.hasCharacter)
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var isActive = GUILayout.Toggle(switchmodeTool.isActive , spriteSheetIcon, EditorStyles.toolbarButton);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
using (skinningCache.UndoScope(TextContent.setMode))
|
||||
{
|
||||
if (isActive)
|
||||
switchmodeTool.Activate();
|
||||
else
|
||||
switchmodeTool.Deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DoCopyToggle(BaseTool currentTool)
|
||||
{
|
||||
if (GUILayout.Button(copyIcon, EditorStyles.toolbarButton))
|
||||
{
|
||||
copyTool.OnCopyActivated();
|
||||
}
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GUILayout.Toggle(copyTool.isActive, pasteIcon, EditorStyles.toolbarButton);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
TogglePasteTool(currentTool);
|
||||
}
|
||||
|
||||
internal void TogglePasteTool(BaseTool currentTool)
|
||||
{
|
||||
if (!copyTool.isActive)
|
||||
{
|
||||
onActivateTool(copyTool);
|
||||
StorePreviousTool(currentTool);
|
||||
}
|
||||
else if (m_PreviousTool != null)
|
||||
{
|
||||
onActivateTool(m_PreviousTool);
|
||||
}
|
||||
}
|
||||
|
||||
void DoVisibilityToggle(BaseTool currentTool)
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GUILayout.Toggle(visibilityTool.isActive, visbilityIcon, EditorStyles.toolbarButton);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
ToggleVisibilityTool(currentTool);
|
||||
}
|
||||
|
||||
GUIContent visbilityIcon { get { return collapseToolbar ? Styles.visibilityCollapseIcon : Styles.visibilityIcon; } }
|
||||
|
||||
internal void ToggleVisibilityTool(BaseTool currentTool)
|
||||
{
|
||||
onActivateTool(visibilityTool);
|
||||
}
|
||||
|
||||
private void DoPreviewToggle()
|
||||
{
|
||||
var skeleton = skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite);
|
||||
|
||||
EditorGUI.BeginDisabledGroup(skeleton == null || skeleton.isPosePreview == false);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GUILayout.Button(characterIcon, EditorStyles.toolbarButton);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
using (skinningCache.UndoScope("Restore Pose"))
|
||||
{
|
||||
skinningCache.RestoreBindPose();
|
||||
skinningCache.events.restoreBindPose.Invoke();
|
||||
}
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
GUIContent characterIcon { get { return collapseToolbar ? Styles.characterCollapseIcon : Styles.characterIcon; } }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 28c640dd2a903f0498148ed21c25a97f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9b92febd89128554b8579633a112c844
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class Brush
|
||||
{
|
||||
private static readonly float kWheelSizeSpeed = 1f;
|
||||
private static readonly int kBrushHashCode = "Brush".GetHashCode();
|
||||
private IGUIWrapper m_GUIWrapper;
|
||||
private float m_DeltaAcc = 0f;
|
||||
private int m_ControlID = -1;
|
||||
private SliderData m_SliderData = SliderData.zero;
|
||||
|
||||
public event Action<Brush> onMove = (b) => {};
|
||||
public event Action<Brush> onSize = (b) => {};
|
||||
public event Action<Brush> onRepaint = (b) => {};
|
||||
public event Action<Brush> onStrokeBegin = (b) => {};
|
||||
public event Action<Brush> onStrokeDelta = (b) => {};
|
||||
public event Action<Brush> onStrokeStep = (b) => {};
|
||||
public event Action<Brush> onStrokeEnd = (b) => {};
|
||||
|
||||
public bool isHot
|
||||
{
|
||||
get { return m_GUIWrapper.IsControlHot(m_ControlID); }
|
||||
}
|
||||
public bool isActivable
|
||||
{
|
||||
get { return m_GUIWrapper.IsControlHot(0) && m_GUIWrapper.IsControlNearest(m_ControlID); }
|
||||
}
|
||||
|
||||
public int controlID
|
||||
{
|
||||
get { return m_ControlID; }
|
||||
}
|
||||
|
||||
public float hardness { get; set; }
|
||||
public float step { get; set; }
|
||||
public float size { get; set; }
|
||||
public Vector3 position
|
||||
{
|
||||
get { return m_SliderData.position; }
|
||||
}
|
||||
|
||||
public Brush(IGUIWrapper guiWrapper)
|
||||
{
|
||||
m_GUIWrapper = guiWrapper;
|
||||
size = 25f;
|
||||
step = 20f;
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
m_ControlID = m_GUIWrapper.GetControlID(kBrushHashCode, FocusType.Passive);
|
||||
|
||||
var eventType = m_GUIWrapper.eventType;
|
||||
|
||||
if (!m_GUIWrapper.isAltDown)
|
||||
m_GUIWrapper.LayoutControl(controlID, 0f);
|
||||
|
||||
if (isActivable)
|
||||
{
|
||||
m_SliderData.position = m_GUIWrapper.GUIToWorld(m_GUIWrapper.mousePosition);
|
||||
|
||||
if (m_GUIWrapper.IsMouseDown(0))
|
||||
{
|
||||
m_DeltaAcc = 0f;
|
||||
onStrokeBegin(this);
|
||||
onStrokeStep(this);
|
||||
m_GUIWrapper.SetGuiChanged(true);
|
||||
}
|
||||
|
||||
if (eventType == EventType.MouseMove)
|
||||
{
|
||||
onMove(this);
|
||||
m_GUIWrapper.UseCurrentEvent();
|
||||
}
|
||||
|
||||
if (m_GUIWrapper.isShiftDown && eventType == EventType.ScrollWheel)
|
||||
{
|
||||
var sizeDelta = HandleUtility.niceMouseDeltaZoom * kWheelSizeSpeed;
|
||||
size = Mathf.Max(1f, size + sizeDelta);
|
||||
onSize(this);
|
||||
m_GUIWrapper.UseCurrentEvent();
|
||||
}
|
||||
}
|
||||
|
||||
if (isHot && m_GUIWrapper.IsMouseUp(0))
|
||||
onStrokeEnd(this);
|
||||
|
||||
if (m_GUIWrapper.IsRepainting() && (isHot || isActivable))
|
||||
onRepaint(this);
|
||||
|
||||
Vector3 position;
|
||||
if (m_GUIWrapper.DoSlider(m_ControlID, m_SliderData, out position))
|
||||
{
|
||||
step = Mathf.Max(step, 1f);
|
||||
|
||||
var delta = position - m_SliderData.position;
|
||||
var direction = delta.normalized;
|
||||
var magnitude = delta.magnitude;
|
||||
|
||||
m_SliderData.position -= direction * m_DeltaAcc;
|
||||
|
||||
m_DeltaAcc += magnitude;
|
||||
|
||||
if (m_DeltaAcc >= step)
|
||||
{
|
||||
var stepVector = direction * step;
|
||||
|
||||
while (m_DeltaAcc >= step)
|
||||
{
|
||||
m_SliderData.position += stepVector;
|
||||
|
||||
onMove(this);
|
||||
onStrokeStep(this);
|
||||
|
||||
m_DeltaAcc -= step;
|
||||
}
|
||||
}
|
||||
|
||||
m_SliderData.position = position;
|
||||
onStrokeDelta(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 06c115e22e25aad4e9a720594b10fa29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,230 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal struct SliderData
|
||||
{
|
||||
public Vector3 position;
|
||||
public Vector3 forward;
|
||||
public Vector3 up;
|
||||
public Vector3 right;
|
||||
|
||||
public static readonly SliderData zero = new SliderData() { position = Vector3.zero, forward = Vector3.forward, up = Vector3.up, right = Vector3.right };
|
||||
}
|
||||
|
||||
internal interface IGUIWrapper
|
||||
{
|
||||
Vector2 mousePosition { get; }
|
||||
int mouseButton { get; }
|
||||
int clickCount { get; }
|
||||
bool isShiftDown { get; }
|
||||
bool isAltDown { get; }
|
||||
bool isActionKeyDown { get; }
|
||||
EventType eventType { get; }
|
||||
string commandName { get; }
|
||||
bool IsMouseDown(int button);
|
||||
bool IsMouseUp(int button);
|
||||
bool IsKeyDown(KeyCode keyCode);
|
||||
int GetControlID(int hint, FocusType focusType);
|
||||
void LayoutControl(int controlID, float distance);
|
||||
bool IsControlNearest(int controlID);
|
||||
bool IsControlHot(int controlID);
|
||||
bool IsMultiStepControlHot(int controlID);
|
||||
void SetControlHot(int controlID);
|
||||
void SetMultiStepControlHot(int controlID);
|
||||
bool DoSlider(int id, SliderData sliderData, out Vector3 newPosition);
|
||||
void UseCurrentEvent();
|
||||
float DistanceToSegment(Vector3 p1, Vector3 p2);
|
||||
float DistanceToSegmentClamp(Vector3 p1, Vector3 p2);
|
||||
float DistanceToCircle(Vector3 center, float radius);
|
||||
Vector3 GUIToWorld(Vector2 guiPosition);
|
||||
Vector3 GUIToWorld(Vector2 guiPosition, Vector3 planeNormal, Vector3 planePosition);
|
||||
void Repaint();
|
||||
bool IsRepainting();
|
||||
bool IsEventOutsideWindow();
|
||||
void SetGuiChanged(bool changed);
|
||||
float GetHandleSize(Vector3 position);
|
||||
bool IsViewToolActive();
|
||||
bool HasCurrentCamera();
|
||||
}
|
||||
|
||||
internal class GUIWrapper : IGUIWrapper
|
||||
{
|
||||
private Handles.CapFunction nullCap = (int c, Vector3 p , Quaternion r, float s, EventType ev) => {};
|
||||
private int m_MultiStepHotControl = 0;
|
||||
|
||||
public Vector2 mousePosition
|
||||
{
|
||||
get { return Event.current.mousePosition; }
|
||||
}
|
||||
|
||||
public int mouseButton
|
||||
{
|
||||
get { return Event.current.button; }
|
||||
}
|
||||
|
||||
public int clickCount
|
||||
{
|
||||
get { return Event.current.clickCount; }
|
||||
}
|
||||
|
||||
public bool isShiftDown
|
||||
{
|
||||
get { return Event.current.shift; }
|
||||
}
|
||||
|
||||
public bool isAltDown
|
||||
{
|
||||
get { return Event.current.alt; }
|
||||
}
|
||||
|
||||
public bool isActionKeyDown
|
||||
{
|
||||
get { return EditorGUI.actionKey; }
|
||||
}
|
||||
|
||||
public EventType eventType
|
||||
{
|
||||
get { return Event.current.type; }
|
||||
}
|
||||
|
||||
public string commandName
|
||||
{
|
||||
get { return Event.current.commandName; }
|
||||
}
|
||||
|
||||
public bool IsMouseDown(int button)
|
||||
{
|
||||
return Event.current.type == EventType.MouseDown && Event.current.button == button;
|
||||
}
|
||||
|
||||
public bool IsMouseUp(int button)
|
||||
{
|
||||
return Event.current.type == EventType.MouseUp && Event.current.button == button;
|
||||
}
|
||||
|
||||
public bool IsKeyDown(KeyCode keyCode)
|
||||
{
|
||||
return Event.current.type == EventType.KeyDown && Event.current.keyCode == keyCode;
|
||||
}
|
||||
|
||||
public int GetControlID(int hint, FocusType focusType)
|
||||
{
|
||||
return GUIUtility.GetControlID(hint, focusType);
|
||||
}
|
||||
|
||||
public void LayoutControl(int controlID, float distance)
|
||||
{
|
||||
if (Event.current.type == EventType.Layout)
|
||||
HandleUtility.AddControl(controlID, distance);
|
||||
}
|
||||
|
||||
public bool IsControlNearest(int controlID)
|
||||
{
|
||||
return HandleUtility.nearestControl == controlID;
|
||||
}
|
||||
|
||||
public bool IsControlHot(int controlID)
|
||||
{
|
||||
return GUIUtility.hotControl == controlID;
|
||||
}
|
||||
|
||||
public bool IsMultiStepControlHot(int controlID)
|
||||
{
|
||||
return m_MultiStepHotControl == controlID;
|
||||
}
|
||||
|
||||
public void SetControlHot(int controlID)
|
||||
{
|
||||
GUIUtility.hotControl = controlID;
|
||||
}
|
||||
|
||||
public void SetMultiStepControlHot(int controlID)
|
||||
{
|
||||
m_MultiStepHotControl = controlID;
|
||||
}
|
||||
|
||||
public bool DoSlider(int id, SliderData sliderData, out Vector3 newPosition)
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
if (HasCurrentCamera())
|
||||
newPosition = Handles.Slider2D(id, sliderData.position, sliderData.forward, sliderData.right, sliderData.up, 1f, nullCap, Vector2.zero);
|
||||
else
|
||||
newPosition = Slider2D.Do(id, sliderData.position, null);
|
||||
|
||||
return EditorGUI.EndChangeCheck();
|
||||
}
|
||||
|
||||
public void UseCurrentEvent()
|
||||
{
|
||||
Event.current.Use();
|
||||
}
|
||||
|
||||
public float DistanceToSegment(Vector3 p1, Vector3 p2)
|
||||
{
|
||||
p1 = HandleUtility.WorldToGUIPoint(p1);
|
||||
p2 = HandleUtility.WorldToGUIPoint(p2);
|
||||
|
||||
return HandleUtility.DistancePointToLineSegment(mousePosition, p1, p2);
|
||||
}
|
||||
|
||||
public float DistanceToSegmentClamp(Vector3 p1, Vector3 p2)
|
||||
{
|
||||
p1 = HandleUtility.WorldToGUIPoint(p1);
|
||||
p2 = HandleUtility.WorldToGUIPoint(p2);
|
||||
|
||||
return MathUtility.DistanceToSegmentClamp(mousePosition, p1, p2);
|
||||
}
|
||||
|
||||
public float DistanceToCircle(Vector3 center, float radius)
|
||||
{
|
||||
return HandleUtility.DistanceToCircle(center, radius);
|
||||
}
|
||||
|
||||
public Vector3 GUIToWorld(Vector2 guiPosition)
|
||||
{
|
||||
return ModuleUtility.GUIToWorld(guiPosition);
|
||||
}
|
||||
|
||||
public Vector3 GUIToWorld(Vector2 guiPosition, Vector3 planeNormal, Vector3 planePosition)
|
||||
{
|
||||
return ModuleUtility.GUIToWorld(guiPosition, planeNormal, planePosition);
|
||||
}
|
||||
|
||||
public void Repaint()
|
||||
{
|
||||
HandleUtility.Repaint();
|
||||
}
|
||||
|
||||
public bool IsRepainting()
|
||||
{
|
||||
return eventType == EventType.Repaint;
|
||||
}
|
||||
|
||||
public void SetGuiChanged(bool changed)
|
||||
{
|
||||
GUI.changed = true;
|
||||
}
|
||||
|
||||
public bool IsEventOutsideWindow()
|
||||
{
|
||||
return Event.current.type == EventType.Ignore;
|
||||
}
|
||||
|
||||
public float GetHandleSize(Vector3 position)
|
||||
{
|
||||
return HandleUtility.GetHandleSize(position);
|
||||
}
|
||||
|
||||
public bool IsViewToolActive()
|
||||
{
|
||||
return UnityEditor.Tools.current == Tool.View || isAltDown || mouseButton == 1 || mouseButton == 2;
|
||||
}
|
||||
|
||||
public bool HasCurrentCamera()
|
||||
{
|
||||
return Camera.current != null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 71855e9e46fcdd7408882b24732e4808
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,67 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal enum SkeletonAction
|
||||
{
|
||||
None = 0,
|
||||
Select = 1 << 0,
|
||||
RotateBone = 1 << 2,
|
||||
MoveBone = 1 << 3,
|
||||
FreeMoveBone = 1 << 4,
|
||||
MoveEndPosition = 1 << 5,
|
||||
MoveJoint = 1 << 6,
|
||||
ChangeLength = 1 << 7,
|
||||
CreateBone = 1 << 8,
|
||||
SplitBone = 1 << 9,
|
||||
Remove = 1 << 10,
|
||||
}
|
||||
|
||||
internal enum SkeletonMode
|
||||
{
|
||||
Disabled = SkeletonAction.None,
|
||||
Selection = SkeletonAction.Select,
|
||||
EditPose = Selection | SkeletonAction.RotateBone | SkeletonAction.MoveBone,
|
||||
EditJoints = Selection | SkeletonAction.FreeMoveBone | SkeletonAction.MoveEndPosition | SkeletonAction.MoveJoint | SkeletonAction.Remove,
|
||||
CreateBone = Selection | SkeletonAction.MoveJoint | SkeletonAction.Remove | SkeletonAction.CreateBone,
|
||||
SplitBone = Selection | SkeletonAction.MoveEndPosition | SkeletonAction.MoveJoint | SkeletonAction.Remove | SkeletonAction.SplitBone,
|
||||
}
|
||||
|
||||
internal interface ISkeletonView
|
||||
{
|
||||
int InvalidID { get; set; }
|
||||
SkeletonMode mode { get; set; }
|
||||
int defaultControlID { get; set; }
|
||||
int hoveredBoneID { get; }
|
||||
int hoveredJointID { get; }
|
||||
int hoveredBodyID { get; }
|
||||
int hoveredTailID { get; }
|
||||
int hotBoneID { get; }
|
||||
void BeginLayout();
|
||||
void EndLayout();
|
||||
bool CanLayout();
|
||||
Vector3 GetMouseWorldPosition(Vector3 planeNormal, Vector3 planePosition);
|
||||
void LayoutBone(int id, Vector3 position, Vector3 endPosition, Vector3 forward, Vector3 up, Vector3 right, bool isChainEnd);
|
||||
bool DoSelectBone(out int id, out bool additive);
|
||||
bool DoRotateBone(Vector3 pivot, Vector3 normal, out float deltaAngle);
|
||||
bool DoMoveBone(out Vector3 deltaPosition);
|
||||
bool DoFreeMoveBone(out Vector3 deltaPosition);
|
||||
bool DoMoveJoint(out Vector3 deltaPosition);
|
||||
bool DoMoveEndPosition(out Vector3 endPosition);
|
||||
bool DoChangeLength(out Vector3 endPosition);
|
||||
bool DoCreateBoneStart(out Vector3 position);
|
||||
bool DoCreateBone(out Vector3 position);
|
||||
bool DoSplitBone(out int id, out Vector3 position);
|
||||
bool DoRemoveBone();
|
||||
bool DoCancelMultistepAction(bool force);
|
||||
bool IsActionActive(SkeletonAction action);
|
||||
bool IsActionHot(SkeletonAction action);
|
||||
bool IsActionTriggering(SkeletonAction action);
|
||||
bool IsActionFinishing(SkeletonAction action);
|
||||
bool IsRepainting();
|
||||
void DrawBone(Vector3 position, Vector3 right, Vector3 forward, float length, Color color, bool isChained, bool isSelected, bool isJointHovered, bool isTailHovered, bool isHot);
|
||||
void DrawBoneParentLink(Vector3 parentPosition, Vector3 position, Vector3 forward, Color color);
|
||||
void DrawBoneOutline(Vector3 position, Vector3 right, Vector3 forward, float length, Color color, float outlineScale);
|
||||
void DrawCursors(bool canBeActive);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 87771fd0f1d988d478a8f45923440eb8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,66 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal enum SpriteMeshViewMode
|
||||
{
|
||||
EditGeometry,
|
||||
CreateVertex,
|
||||
CreateEdge,
|
||||
SplitEdge
|
||||
}
|
||||
|
||||
internal enum MeshEditorAction
|
||||
{
|
||||
None,
|
||||
CreateVertex,
|
||||
MoveVertex,
|
||||
CreateEdge,
|
||||
SplitEdge,
|
||||
MoveEdge,
|
||||
SelectVertex,
|
||||
SelectEdge,
|
||||
Remove
|
||||
}
|
||||
|
||||
internal interface ISpriteMeshView
|
||||
{
|
||||
SpriteMeshViewMode mode { get; set; }
|
||||
ISelection<int> selection { get; set; }
|
||||
int defaultControlID { get; set; }
|
||||
Rect frame { get; set; }
|
||||
Vector2 mouseWorldPosition { get; }
|
||||
int hoveredVertex { get; }
|
||||
int hoveredEdge { get; }
|
||||
int closestEdge { get; }
|
||||
|
||||
void CancelMode();
|
||||
void BeginLayout();
|
||||
void EndLayout();
|
||||
void LayoutVertex(Vector2 position, int index);
|
||||
void LayoutEdge(Vector2 startPosition, Vector2 endPosition, int index);
|
||||
bool DoCreateVertex();
|
||||
bool DoSelectVertex(out bool additive);
|
||||
bool DoMoveVertex(out Vector2 delta);
|
||||
bool DoMoveEdge(out Vector2 delta);
|
||||
bool DoCreateEdge();
|
||||
bool DoSplitEdge();
|
||||
bool DoSelectEdge(out bool additive);
|
||||
bool DoRemove();
|
||||
void DrawVertex(Vector2 position);
|
||||
void DrawVertexHovered(Vector2 position);
|
||||
void DrawVertexSelected(Vector2 position);
|
||||
void BeginDrawEdges();
|
||||
void EndDrawEdges();
|
||||
void DrawEdge(Vector2 startPosition, Vector2 endPosition);
|
||||
void DrawEdgeHovered(Vector2 startPosition, Vector2 endPosition);
|
||||
void DrawEdgeSelected(Vector2 startPosition, Vector2 endPosition);
|
||||
bool IsActionTriggered(MeshEditorAction action);
|
||||
bool IsActionActive(MeshEditorAction action);
|
||||
bool IsActionHot(MeshEditorAction action);
|
||||
Vector2 WorldToScreen(Vector2 position);
|
||||
void DoRepaint();
|
||||
bool CanRepaint();
|
||||
bool CanLayout();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 015d7144666ec57449ae83559d3e26c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class RectSelectionTool<T>
|
||||
{
|
||||
private int m_HashCode = "RectSelectionTool".GetHashCode();
|
||||
private int m_ControlID = -1;
|
||||
private bool m_Moved = false;
|
||||
private RectSlider m_RectSlider = new RectSlider();
|
||||
public int controlID { get { return m_ControlID; } }
|
||||
public IRectSelector<T> rectSelector { get; set; }
|
||||
public ICacheUndo cacheUndo { get; set; }
|
||||
public Action onSelectionStart = () => {};
|
||||
public Action onSelectionUpdate = () => {};
|
||||
public Action onSelectionEnd = () => {};
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
Debug.Assert(rectSelector != null);
|
||||
Debug.Assert(cacheUndo != null);
|
||||
|
||||
m_ControlID = GUIUtility.GetControlID(m_HashCode, FocusType.Passive);
|
||||
|
||||
Event ev = Event.current;
|
||||
EventType eventType = ev.GetTypeForControl(m_ControlID);
|
||||
|
||||
if (GUIUtility.hotControl == 0 && HandleUtility.nearestControl == m_ControlID &&
|
||||
rectSelector.selection.Count > 0 && eventType == EventType.MouseDown && ev.button == 0 && !ev.alt)
|
||||
{
|
||||
m_Moved = false;
|
||||
onSelectionStart();
|
||||
}
|
||||
|
||||
if (m_Moved && GUIUtility.hotControl == m_ControlID && eventType == EventType.MouseUp && ev.button == 0)
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.selection);
|
||||
rectSelector.selection.EndSelection(true);
|
||||
onSelectionEnd();
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
rectSelector.rect = m_RectSlider.Do(m_ControlID);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if(!m_Moved)
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.selection);
|
||||
|
||||
if(!ev.shift)
|
||||
rectSelector.selection.Clear();
|
||||
|
||||
m_Moved = true;
|
||||
}
|
||||
|
||||
rectSelector.selection.BeginSelection();
|
||||
rectSelector.Select();
|
||||
onSelectionUpdate();
|
||||
}
|
||||
|
||||
if (eventType == EventType.Repaint && GUIUtility.hotControl == m_ControlID)
|
||||
{
|
||||
DrawingUtility.DrawRect(rectSelector.rect, Vector3.zero, Quaternion.identity, new Color(0f, 1f, 1f, 1f), 0.05f, 0.8f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7b6edb0347e814797aa9c181f8e5918f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,38 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class RectSlider
|
||||
{
|
||||
private static readonly int kRectSliderHashCode = "RectSlider".GetHashCode();
|
||||
private Vector2 m_StartPosition = Vector2.zero;
|
||||
private Vector2 m_Position = Vector2.zero;
|
||||
|
||||
internal Rect Do()
|
||||
{
|
||||
return Do(GUIUtility.GetControlID(kRectSliderHashCode, FocusType.Passive));
|
||||
}
|
||||
|
||||
internal Rect Do(int controlID)
|
||||
{
|
||||
var eventType = Event.current.GetTypeForControl(controlID);
|
||||
|
||||
if (eventType == EventType.MouseDown)
|
||||
{
|
||||
m_StartPosition = ModuleUtility.GUIToWorld(Event.current.mousePosition);
|
||||
m_Position = m_StartPosition;
|
||||
}
|
||||
|
||||
if (eventType == EventType.Layout)
|
||||
HandleUtility.AddDefaultControl(controlID);
|
||||
|
||||
m_Position = Slider2D.Do(controlID, m_Position);
|
||||
|
||||
var rect = new Rect();
|
||||
rect.min = m_StartPosition;
|
||||
rect.max = m_Position;
|
||||
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ea09cf179b192498284b7d89afdbff8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,644 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
[Serializable]
|
||||
internal class SkeletonController
|
||||
{
|
||||
private static readonly string k_DefaultRootName = "root";
|
||||
private static readonly string k_DefaultBoneName = "bone";
|
||||
private static Regex s_Regex = new Regex(@"\w+_\d+$", RegexOptions.IgnoreCase);
|
||||
|
||||
private SkeletonCache m_Skeleton;
|
||||
[SerializeField]
|
||||
private Vector3 m_CreateBoneStartPosition;
|
||||
[SerializeField]
|
||||
private BoneCache m_PrevCreatedBone;
|
||||
private bool m_Moved = false;
|
||||
private ISkeletonStyle style
|
||||
{
|
||||
get
|
||||
{
|
||||
if (styleOverride != null)
|
||||
return styleOverride;
|
||||
|
||||
return SkeletonStyles.Default;
|
||||
}
|
||||
}
|
||||
private SkinningCache skinningCache
|
||||
{
|
||||
get { return m_Skeleton.skinningCache; }
|
||||
}
|
||||
private BoneCache selectedBone
|
||||
{
|
||||
get { return selection.activeElement.ToSpriteSheetIfNeeded(); }
|
||||
set { selection.activeElement = value.ToCharacterIfNeeded(); }
|
||||
}
|
||||
private BoneCache[] selectedBones
|
||||
{
|
||||
get { return selection.elements.ToSpriteSheetIfNeeded(); }
|
||||
set { selection.elements = value.ToCharacterIfNeeded(); }
|
||||
}
|
||||
private BoneCache rootBone
|
||||
{
|
||||
get { return selection.root.ToSpriteSheetIfNeeded(); }
|
||||
}
|
||||
private BoneCache[] rootBones
|
||||
{
|
||||
get { return selection.roots.ToSpriteSheetIfNeeded(); }
|
||||
}
|
||||
|
||||
public ISkeletonView view { get; set; }
|
||||
public ISkeletonStyle styleOverride { get; set; }
|
||||
public IBoneSelection selection { get; set; }
|
||||
public bool editBindPose { get; set; }
|
||||
public SkeletonCache skeleton
|
||||
{
|
||||
get { return m_Skeleton; }
|
||||
set { SetSkeleton(value); }
|
||||
}
|
||||
public BoneCache hoveredBone
|
||||
{
|
||||
get { return GetBone(view.hoveredBoneID); }
|
||||
}
|
||||
public BoneCache hoveredTail
|
||||
{
|
||||
get { return GetBone(view.hoveredTailID); }
|
||||
}
|
||||
public BoneCache hoveredBody
|
||||
{
|
||||
get { return GetBone(view.hoveredBodyID); }
|
||||
}
|
||||
public BoneCache hoveredJoint
|
||||
{
|
||||
get { return GetBone(view.hoveredJointID); }
|
||||
}
|
||||
public BoneCache hotBone
|
||||
{
|
||||
get { return GetBone(view.hotBoneID); }
|
||||
}
|
||||
|
||||
private BoneCache GetBone(int instanceID)
|
||||
{
|
||||
return BaseObject.InstanceIDToObject(instanceID) as BoneCache;
|
||||
}
|
||||
|
||||
private void SetSkeleton(SkeletonCache newSkeleton)
|
||||
{
|
||||
if (skeleton != newSkeleton)
|
||||
{
|
||||
m_Skeleton = newSkeleton;
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
view.DoCancelMultistepAction(true);
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
if (skeleton == null)
|
||||
return;
|
||||
|
||||
view.BeginLayout();
|
||||
|
||||
if (view.CanLayout())
|
||||
LayoutBones();
|
||||
|
||||
view.EndLayout();
|
||||
|
||||
HandleSelectBone();
|
||||
HandleRotateBone();
|
||||
HandleMoveBone();
|
||||
HandleFreeMoveBone();
|
||||
HandleMoveJoint();
|
||||
HandleMoveEndPosition();
|
||||
HandleChangeLength();
|
||||
HandleCreateBone();
|
||||
HandleSplitBone();
|
||||
HandleRemoveBone();
|
||||
HandleCancelMultiStepAction();
|
||||
DrawSkeleton();
|
||||
DrawSplitBonePreview();
|
||||
DrawCreateBonePreview();
|
||||
DrawCursors();
|
||||
}
|
||||
|
||||
private void LayoutBones()
|
||||
{
|
||||
for (var i = 0; i < skeleton.BoneCount; ++i)
|
||||
{
|
||||
var bone = skeleton.GetBone(i);
|
||||
|
||||
if (bone.isVisible && bone != hotBone)
|
||||
view.LayoutBone(bone.GetInstanceID(), bone.position, bone.endPosition, bone.forward, bone.up, bone.right, bone.chainedChild == null);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSelectBone()
|
||||
{
|
||||
int instanceID;
|
||||
bool additive;
|
||||
if (view.DoSelectBone(out instanceID, out additive))
|
||||
{
|
||||
var bone = GetBone(instanceID).ToCharacterIfNeeded();
|
||||
|
||||
using (skinningCache.UndoScope(TextContent.boneSelection, true))
|
||||
{
|
||||
if (!additive)
|
||||
{
|
||||
if (!selection.Contains(bone))
|
||||
selectedBone = bone;
|
||||
}
|
||||
else
|
||||
selection.Select(bone, !selection.Contains(bone));
|
||||
|
||||
skinningCache.events.boneSelectionChanged.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleRotateBone()
|
||||
{
|
||||
if (view.IsActionTriggering(SkeletonAction.RotateBone))
|
||||
m_Moved = false;
|
||||
|
||||
var pivot = hoveredBone;
|
||||
|
||||
if (view.IsActionHot(SkeletonAction.RotateBone))
|
||||
pivot = hotBone;
|
||||
|
||||
if (pivot == null)
|
||||
return;
|
||||
|
||||
var rootBones = selection.roots.ToSpriteSheetIfNeeded();
|
||||
pivot = pivot.FindRoot<BoneCache>(rootBones);
|
||||
|
||||
if (pivot == null)
|
||||
return;
|
||||
|
||||
float deltaAngle;
|
||||
if (view.DoRotateBone(pivot.position, pivot.forward, out deltaAngle))
|
||||
{
|
||||
if (!m_Moved)
|
||||
{
|
||||
skinningCache.BeginUndoOperation(TextContent.rotateBone);
|
||||
m_Moved = true;
|
||||
}
|
||||
|
||||
m_Skeleton.RotateBones(selectedBones, deltaAngle);
|
||||
InvokePoseChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMoveBone()
|
||||
{
|
||||
if (view.IsActionTriggering(SkeletonAction.MoveBone))
|
||||
m_Moved = false;
|
||||
|
||||
Vector3 deltaPosition;
|
||||
if (view.DoMoveBone(out deltaPosition))
|
||||
{
|
||||
if (!m_Moved)
|
||||
{
|
||||
skinningCache.BeginUndoOperation(TextContent.moveBone);
|
||||
m_Moved = true;
|
||||
}
|
||||
|
||||
m_Skeleton.MoveBones(rootBones, deltaPosition);
|
||||
InvokePoseChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleFreeMoveBone()
|
||||
{
|
||||
if (view.IsActionTriggering(SkeletonAction.FreeMoveBone))
|
||||
m_Moved = false;
|
||||
|
||||
Vector3 deltaPosition;
|
||||
if (view.DoFreeMoveBone(out deltaPosition))
|
||||
{
|
||||
if (!m_Moved)
|
||||
{
|
||||
skinningCache.BeginUndoOperation(TextContent.freeMoveBone);
|
||||
m_Moved = true;
|
||||
}
|
||||
|
||||
m_Skeleton.FreeMoveBones(selectedBones, deltaPosition);
|
||||
InvokePoseChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMoveJoint()
|
||||
{
|
||||
if (view.IsActionTriggering(SkeletonAction.MoveJoint))
|
||||
m_Moved = false;
|
||||
|
||||
if (view.IsActionFinishing(SkeletonAction.MoveJoint))
|
||||
{
|
||||
if (hoveredTail != null && hoveredTail.chainedChild == null && hotBone.parent == hoveredTail)
|
||||
hoveredTail.chainedChild = hotBone;
|
||||
}
|
||||
|
||||
Vector3 deltaPosition;
|
||||
if (view.DoMoveJoint(out deltaPosition))
|
||||
{
|
||||
if (!m_Moved)
|
||||
{
|
||||
skinningCache.BeginUndoOperation(TextContent.moveJoint);
|
||||
m_Moved = true;
|
||||
}
|
||||
|
||||
//Snap to parent endPosition
|
||||
if (hoveredTail != null && hoveredTail.chainedChild == null && hotBone.parent == hoveredTail)
|
||||
deltaPosition = hoveredTail.endPosition - hotBone.position;
|
||||
|
||||
m_Skeleton.MoveJoints(selectedBones, deltaPosition);
|
||||
InvokePoseChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMoveEndPosition()
|
||||
{
|
||||
if (view.IsActionTriggering(SkeletonAction.MoveEndPosition))
|
||||
m_Moved = false;
|
||||
|
||||
if (view.IsActionFinishing(SkeletonAction.MoveEndPosition))
|
||||
{
|
||||
if (hoveredJoint != null && hoveredJoint.parent == hotBone)
|
||||
hotBone.chainedChild = hoveredJoint;
|
||||
}
|
||||
|
||||
Vector3 endPosition;
|
||||
if (view.DoMoveEndPosition(out endPosition))
|
||||
{
|
||||
if (!m_Moved)
|
||||
{
|
||||
skinningCache.BeginUndoOperation(TextContent.moveEndPoint);
|
||||
m_Moved = true;
|
||||
}
|
||||
|
||||
Debug.Assert(hotBone != null);
|
||||
Debug.Assert(hotBone.chainedChild == null);
|
||||
|
||||
if (hoveredJoint != null && hoveredJoint.parent == hotBone)
|
||||
endPosition = hoveredJoint.position;
|
||||
|
||||
m_Skeleton.SetEndPosition(hotBone, endPosition);
|
||||
InvokePoseChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleChangeLength()
|
||||
{
|
||||
if (view.IsActionTriggering(SkeletonAction.ChangeLength))
|
||||
m_Moved = false;
|
||||
|
||||
Vector3 endPosition;
|
||||
if (view.DoChangeLength(out endPosition))
|
||||
{
|
||||
if (!m_Moved)
|
||||
{
|
||||
skinningCache.BeginUndoOperation(TextContent.boneLength);
|
||||
m_Moved = true;
|
||||
}
|
||||
|
||||
Debug.Assert(hotBone != null);
|
||||
|
||||
var direction = (Vector3)endPosition - hotBone.position;
|
||||
hotBone.length = Vector3.Dot(direction, hotBone.right);
|
||||
|
||||
InvokePoseChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCreateBone()
|
||||
{
|
||||
Vector3 position;
|
||||
if (view.DoCreateBoneStart(out position))
|
||||
{
|
||||
m_PrevCreatedBone = null;
|
||||
|
||||
if (hoveredTail != null)
|
||||
{
|
||||
m_PrevCreatedBone = hoveredTail;
|
||||
m_CreateBoneStartPosition = hoveredTail.endPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CreateBoneStartPosition = position;
|
||||
}
|
||||
}
|
||||
|
||||
if (view.DoCreateBone(out position))
|
||||
{
|
||||
using (skinningCache.UndoScope(TextContent.createBone))
|
||||
{
|
||||
var isChained = m_PrevCreatedBone != null;
|
||||
var parentBone = isChained ? m_PrevCreatedBone : rootBone;
|
||||
|
||||
if (isChained)
|
||||
m_CreateBoneStartPosition = m_PrevCreatedBone.endPosition;
|
||||
|
||||
var name = AutoBoneName(parentBone, skeleton.bones);
|
||||
var bone = m_Skeleton.CreateBone(parentBone, m_CreateBoneStartPosition, position, isChained, name);
|
||||
|
||||
m_PrevCreatedBone = bone;
|
||||
m_CreateBoneStartPosition = bone.endPosition;
|
||||
|
||||
InvokeTopologyChanged();
|
||||
InvokePoseChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSplitBone()
|
||||
{
|
||||
int instanceID;
|
||||
Vector3 position;
|
||||
if (view.DoSplitBone(out instanceID, out position))
|
||||
{
|
||||
using (skinningCache.UndoScope(TextContent.splitBone))
|
||||
{
|
||||
var boneToSplit = GetBone(instanceID);
|
||||
|
||||
Debug.Assert(boneToSplit != null);
|
||||
|
||||
var splitLength = Vector3.Dot(hoveredBone.right, position - boneToSplit.position);
|
||||
var name = AutoBoneName(boneToSplit, skeleton.bones);
|
||||
|
||||
m_Skeleton.SplitBone(boneToSplit, splitLength, name);
|
||||
|
||||
InvokeTopologyChanged();
|
||||
InvokePoseChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleRemoveBone()
|
||||
{
|
||||
if (view.DoRemoveBone())
|
||||
{
|
||||
using (skinningCache.UndoScope(TextContent.removeBone))
|
||||
{
|
||||
m_Skeleton.DestroyBones(selectedBones);
|
||||
|
||||
selection.Clear();
|
||||
skinningCache.events.boneSelectionChanged.Invoke();
|
||||
InvokeTopologyChanged();
|
||||
InvokePoseChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCancelMultiStepAction()
|
||||
{
|
||||
if (view.DoCancelMultistepAction(false))
|
||||
m_PrevCreatedBone = null;
|
||||
}
|
||||
|
||||
private void DrawSkeleton()
|
||||
{
|
||||
if (!view.IsRepainting())
|
||||
return;
|
||||
|
||||
bool isNotOnVisualElement = !skinningCache.IsOnVisualElement();
|
||||
|
||||
if (view.IsActionActive(SkeletonAction.CreateBone) || view.IsActionHot(SkeletonAction.CreateBone))
|
||||
{
|
||||
if (isNotOnVisualElement)
|
||||
{
|
||||
var endPoint = view.GetMouseWorldPosition(Vector3.forward, Vector3.zero);
|
||||
|
||||
if (view.IsActionHot(SkeletonAction.CreateBone))
|
||||
endPoint = m_CreateBoneStartPosition;
|
||||
|
||||
if (m_PrevCreatedBone == null && hoveredTail == null)
|
||||
{
|
||||
var root = rootBone;
|
||||
if (root != null)
|
||||
view.DrawBoneParentLink(root.position, endPoint, Vector3.forward, style.GetParentLinkPreviewColor(skeleton.BoneCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < skeleton.BoneCount; ++i)
|
||||
{
|
||||
var bone = skeleton.GetBone(i);
|
||||
|
||||
if (bone.isVisible == false || bone.parentBone == null || bone.parentBone.chainedChild == bone)
|
||||
continue;
|
||||
|
||||
view.DrawBoneParentLink(bone.parent.position, bone.position, Vector3.forward, style.GetParentLinkColor(bone));
|
||||
}
|
||||
|
||||
for (var i = 0; i < skeleton.BoneCount; ++i)
|
||||
{
|
||||
var bone = skeleton.GetBone(i);
|
||||
|
||||
if ((view.IsActionActive(SkeletonAction.SplitBone) && hoveredBone == bone && isNotOnVisualElement) || bone.isVisible == false)
|
||||
continue;
|
||||
|
||||
var isSelected = selection.Contains(bone.ToCharacterIfNeeded());
|
||||
var isHovered = hoveredBody == bone && view.IsActionHot(SkeletonAction.None) && isNotOnVisualElement;
|
||||
|
||||
DrawBoneOutline(bone, style.GetOutlineColor(bone, isSelected, isHovered), style.GetOutlineScale(isSelected));
|
||||
}
|
||||
|
||||
for (var i = 0; i < skeleton.BoneCount; ++i)
|
||||
{
|
||||
var bone = skeleton.GetBone(i);
|
||||
|
||||
if ((view.IsActionActive(SkeletonAction.SplitBone) && hoveredBone == bone && isNotOnVisualElement) || bone.isVisible == false)
|
||||
continue;
|
||||
|
||||
DrawBone(bone, style.GetColor(bone));
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBone(BoneCache bone, Color color)
|
||||
{
|
||||
var isSelected = selection.Contains(bone.ToCharacterIfNeeded());
|
||||
var isNotOnVisualElement = !skinningCache.IsOnVisualElement();
|
||||
var isJointHovered = view.IsActionHot(SkeletonAction.None) && hoveredJoint == bone && isNotOnVisualElement;
|
||||
var isTailHovered = view.IsActionHot(SkeletonAction.None) && hoveredTail == bone && isNotOnVisualElement;
|
||||
|
||||
view.DrawBone(bone.position, bone.right, Vector3.forward, bone.length, color, bone.chainedChild != null, isSelected, isJointHovered, isTailHovered, bone == hotBone);
|
||||
}
|
||||
|
||||
private void DrawBoneOutline(BoneCache bone, Color color, float outlineScale)
|
||||
{
|
||||
view.DrawBoneOutline(bone.position, bone.right, Vector3.forward, bone.length, color, outlineScale);
|
||||
}
|
||||
|
||||
private void DrawSplitBonePreview()
|
||||
{
|
||||
if (!view.IsRepainting())
|
||||
return;
|
||||
|
||||
if (skinningCache.IsOnVisualElement())
|
||||
return;
|
||||
|
||||
if (view.IsActionActive(SkeletonAction.SplitBone) && hoveredBone != null)
|
||||
{
|
||||
var splitLength = Vector3.Dot(hoveredBone.right, view.GetMouseWorldPosition(hoveredBone.forward, hoveredBody.position) - hoveredBone.position);
|
||||
var position = hoveredBone.position + hoveredBone.right * splitLength;
|
||||
var length = hoveredBone.length - splitLength;
|
||||
var isSelected = selection.Contains(hoveredBone.ToCharacterIfNeeded());
|
||||
|
||||
{
|
||||
var color = style.GetOutlineColor(hoveredBone, false, false);
|
||||
if (color.a > 0f)
|
||||
view.DrawBoneOutline(hoveredBone.position, hoveredBone.right, Vector3.forward, splitLength, style.GetOutlineColor(hoveredBone, isSelected, true), style.GetOutlineScale(false));
|
||||
|
||||
}
|
||||
{
|
||||
var color = style.GetPreviewOutlineColor(skeleton.BoneCount);
|
||||
if (color.a > 0f)
|
||||
view.DrawBoneOutline(position, hoveredBone.right, Vector3.forward, length, style.GetPreviewOutlineColor(skeleton.BoneCount), style.GetOutlineScale(false));
|
||||
|
||||
}
|
||||
|
||||
view.DrawBone(hoveredBone.position,
|
||||
hoveredBone.right,
|
||||
Vector3.forward,
|
||||
splitLength,
|
||||
style.GetColor(hoveredBone),
|
||||
hoveredBone.chainedChild != null,
|
||||
false, false, false, false);
|
||||
view.DrawBone(position,
|
||||
hoveredBone.right,
|
||||
Vector3.forward,
|
||||
length,
|
||||
style.GetPreviewColor(skeleton.BoneCount),
|
||||
hoveredBone.chainedChild != null,
|
||||
false, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCreateBonePreview()
|
||||
{
|
||||
if (!view.IsRepainting())
|
||||
return;
|
||||
|
||||
if (skinningCache.IsOnVisualElement())
|
||||
return;
|
||||
|
||||
var color = style.GetPreviewColor(skeleton.BoneCount);
|
||||
var outlineColor = style.GetPreviewOutlineColor(skeleton.BoneCount);
|
||||
|
||||
var startPosition = m_CreateBoneStartPosition;
|
||||
var mousePosition = view.GetMouseWorldPosition(Vector3.forward, Vector3.zero);
|
||||
|
||||
if (view.IsActionActive(SkeletonAction.CreateBone))
|
||||
{
|
||||
startPosition = mousePosition;
|
||||
|
||||
if (hoveredTail != null)
|
||||
startPosition = hoveredTail.endPosition;
|
||||
|
||||
if (outlineColor.a > 0f)
|
||||
view.DrawBoneOutline(startPosition, Vector3.right, Vector3.forward, 0f, outlineColor, style.GetOutlineScale(false));
|
||||
|
||||
view.DrawBone(startPosition, Vector3.right, Vector3.forward, 0f, color, false, false, false, false, false);
|
||||
}
|
||||
|
||||
if (view.IsActionHot(SkeletonAction.CreateBone))
|
||||
{
|
||||
var direction = (mousePosition - startPosition);
|
||||
|
||||
if (outlineColor.a > 0f)
|
||||
view.DrawBoneOutline(startPosition, direction.normalized, Vector3.forward, direction.magnitude, outlineColor, style.GetOutlineScale(false));
|
||||
|
||||
view.DrawBone(startPosition, direction.normalized, Vector3.forward, direction.magnitude, color, false, false, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCursors()
|
||||
{
|
||||
if (!view.IsRepainting())
|
||||
return;
|
||||
|
||||
view.DrawCursors(!skinningCache.IsOnVisualElement());
|
||||
}
|
||||
|
||||
public static string AutoBoneName(BoneCache parent, IEnumerable<BoneCache> bones)
|
||||
{
|
||||
string parentName = "root";
|
||||
string inheritedName;
|
||||
int counter;
|
||||
|
||||
if (parent != null)
|
||||
parentName = parent.name;
|
||||
|
||||
DissectBoneName(parentName, out inheritedName, out counter);
|
||||
int nameCounter = FindBiggestNameCounter(bones);
|
||||
|
||||
if (inheritedName == k_DefaultRootName)
|
||||
inheritedName = k_DefaultBoneName;
|
||||
|
||||
return String.Format("{0}_{1}", inheritedName, ++nameCounter);
|
||||
}
|
||||
|
||||
private static int FindBiggestNameCounter(IEnumerable<BoneCache> bones)
|
||||
{
|
||||
int autoNameCounter = 0;
|
||||
string inheritedName;
|
||||
int counter;
|
||||
foreach (var bone in bones)
|
||||
{
|
||||
DissectBoneName(bone.name, out inheritedName, out counter);
|
||||
if (counter > autoNameCounter)
|
||||
autoNameCounter = counter;
|
||||
}
|
||||
return autoNameCounter;
|
||||
}
|
||||
|
||||
private static void DissectBoneName(string boneName, out string inheritedName, out int counter)
|
||||
{
|
||||
if (IsBoneNameMatchAutoFormat(boneName))
|
||||
{
|
||||
var tokens = boneName.Split('_');
|
||||
var lastTokenIndex = tokens.Length - 1;
|
||||
|
||||
var tokensWithoutLast = new string[lastTokenIndex];
|
||||
Array.Copy(tokens, tokensWithoutLast, lastTokenIndex);
|
||||
inheritedName = string.Join("_", tokensWithoutLast);
|
||||
counter = int.Parse(tokens[lastTokenIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
inheritedName = boneName;
|
||||
counter = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsBoneNameMatchAutoFormat(string boneName)
|
||||
{
|
||||
return s_Regex.IsMatch(boneName);
|
||||
}
|
||||
|
||||
private void InvokeTopologyChanged()
|
||||
{
|
||||
skinningCache.events.skeletonTopologyChanged.Invoke(skeleton);
|
||||
}
|
||||
|
||||
private void InvokePoseChanged()
|
||||
{
|
||||
skeleton.SetPosePreview();
|
||||
|
||||
if (editBindPose)
|
||||
{
|
||||
skeleton.SetDefaultPose();
|
||||
skinningCache.events.skeletonBindPoseChanged.Invoke(skeleton);
|
||||
}
|
||||
else
|
||||
skinningCache.events.skeletonPreviewPoseChanged.Invoke(skeleton);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ea4cc92a263807f4985d77508d2bb536
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,598 @@
|
|||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class SkeletonView : ISkeletonView
|
||||
{
|
||||
private const float kPickingRadius = 5f;
|
||||
internal const string kDeleteCommandName = "Delete";
|
||||
internal const string kSoftDeleteCommandName = "SoftDelete";
|
||||
private static readonly int kBodyHashCode = "Body".GetHashCode();
|
||||
private static readonly int kJointHashCode = "Joint".GetHashCode();
|
||||
private static readonly int kTailHashCode = "Tail".GetHashCode();
|
||||
private static readonly int kCreateBoneHashCode = "CreateBone".GetHashCode();
|
||||
|
||||
public int InvalidID { get; set; }
|
||||
public SkeletonMode mode { get; set; }
|
||||
public int defaultControlID { get; set; }
|
||||
public int hoveredBoneID { get { return m_HoveredBoneID; } }
|
||||
public int hoveredJointID { get { return m_HoveredJointID; } }
|
||||
public int hoveredBodyID { get { return m_HoveredBodyID; } }
|
||||
public int hoveredTailID { get { return m_HoveredTailID; } }
|
||||
public int hotBoneID { get { return m_HotBoneID; } }
|
||||
|
||||
private IGUIWrapper m_GUIWrapper;
|
||||
private int m_RotateControlID = -1;
|
||||
private int m_MoveControlID = -1;
|
||||
private int m_FreeMoveControlID = -1;
|
||||
private int m_MoveJointControlID = -1;
|
||||
private int m_MoveEndPositionControlID = -1;
|
||||
private int m_ChangeLengthControlID = -1;
|
||||
private int m_CreateBoneControlID = -1;
|
||||
private int m_HoveredBoneID = 0;
|
||||
private int m_PrevHoveredBoneID = 0;
|
||||
private int m_HoveredBodyID = 0;
|
||||
private int m_HoveredJointID = 0;
|
||||
private int m_HoveredTailID = 0;
|
||||
private int m_HotBoneID = 0;
|
||||
private int m_HoveredBodyControlID = -1;
|
||||
private int m_HoveredJointControlID = -1;
|
||||
private int m_HoveredTailControlID = -1;
|
||||
private float m_NearestDistance;
|
||||
private float m_NearestBodyDistance;
|
||||
private float m_NearestJointDistance;
|
||||
private float m_NearestTailDistance;
|
||||
private int m_NearestBodyId = 0;
|
||||
private int m_NearestJointId = 0;
|
||||
private int m_NearestTailId = 0;
|
||||
private SliderData m_HoveredSliderData = SliderData.zero;
|
||||
private SliderData m_HotSliderData = SliderData.zero;
|
||||
|
||||
public SkeletonView(IGUIWrapper gw)
|
||||
{
|
||||
m_GUIWrapper = gw;
|
||||
}
|
||||
|
||||
public void BeginLayout()
|
||||
{
|
||||
m_HoveredBodyControlID = m_GUIWrapper.GetControlID(kBodyHashCode, FocusType.Passive);
|
||||
m_HoveredJointControlID = m_GUIWrapper.GetControlID(kJointHashCode, FocusType.Passive);
|
||||
m_HoveredTailControlID = m_GUIWrapper.GetControlID(kTailHashCode, FocusType.Passive);
|
||||
m_CreateBoneControlID = m_GUIWrapper.GetControlID(kCreateBoneHashCode, FocusType.Passive);
|
||||
|
||||
if (m_GUIWrapper.eventType == EventType.Layout)
|
||||
{
|
||||
m_PrevHoveredBoneID = m_HoveredBoneID;
|
||||
m_NearestDistance = float.MaxValue;
|
||||
m_NearestBodyDistance = float.MaxValue;
|
||||
m_NearestJointDistance = float.MaxValue;
|
||||
m_NearestTailDistance = float.MaxValue;
|
||||
m_NearestBodyId = InvalidID;
|
||||
m_NearestJointId = InvalidID;
|
||||
m_NearestTailId = InvalidID;
|
||||
m_HoveredBoneID = InvalidID;
|
||||
m_HoveredBodyID = InvalidID;
|
||||
m_HoveredJointID = InvalidID;
|
||||
m_HoveredTailID = InvalidID;
|
||||
m_HoveredSliderData = SliderData.zero;
|
||||
|
||||
if (m_GUIWrapper.IsControlHot(0))
|
||||
{
|
||||
m_RotateControlID = -1;
|
||||
m_MoveControlID = -1;
|
||||
m_FreeMoveControlID = -1;
|
||||
m_MoveJointControlID = -1;
|
||||
m_MoveEndPositionControlID = -1;
|
||||
m_ChangeLengthControlID = -1;
|
||||
m_HotBoneID = InvalidID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EndLayout()
|
||||
{
|
||||
m_GUIWrapper.LayoutControl(m_HoveredBodyControlID, m_NearestBodyDistance * 0.25f);
|
||||
m_GUIWrapper.LayoutControl(m_HoveredJointControlID, m_NearestJointDistance);
|
||||
m_GUIWrapper.LayoutControl(m_HoveredTailControlID, m_NearestTailDistance);
|
||||
|
||||
if (m_GUIWrapper.IsControlNearest(m_HoveredBodyControlID))
|
||||
{
|
||||
m_HoveredBoneID = m_NearestBodyId;
|
||||
m_HoveredBodyID = m_NearestBodyId;
|
||||
}
|
||||
|
||||
if (m_GUIWrapper.IsControlNearest(m_HoveredJointControlID))
|
||||
{
|
||||
m_HoveredBoneID = m_NearestJointId;
|
||||
m_HoveredJointID = m_NearestJointId;
|
||||
}
|
||||
|
||||
if (m_GUIWrapper.IsControlNearest(m_HoveredTailControlID))
|
||||
{
|
||||
m_HoveredBoneID = m_NearestTailId;
|
||||
m_HoveredTailID = m_NearestTailId;
|
||||
}
|
||||
|
||||
if ((m_GUIWrapper.eventType == EventType.Layout && m_PrevHoveredBoneID != m_HoveredBoneID) || m_GUIWrapper.eventType == EventType.MouseMove)
|
||||
m_GUIWrapper.Repaint();
|
||||
}
|
||||
|
||||
public bool CanLayout()
|
||||
{
|
||||
return m_GUIWrapper.eventType == EventType.Layout;
|
||||
}
|
||||
|
||||
public void LayoutBone(int id, Vector3 position, Vector3 endPosition, Vector3 forward, Vector3 up, Vector3 right, bool isChainEnd)
|
||||
{
|
||||
if (mode == SkeletonMode.Disabled)
|
||||
return;
|
||||
|
||||
var sliderData = new SliderData()
|
||||
{
|
||||
position = GetMouseWorldPosition(forward, position),
|
||||
forward = forward,
|
||||
up = up,
|
||||
right = right
|
||||
};
|
||||
|
||||
{
|
||||
var distance = m_GUIWrapper.DistanceToSegmentClamp(position, endPosition);
|
||||
|
||||
if (distance <= m_NearestDistance)
|
||||
{
|
||||
m_NearestDistance = distance;
|
||||
m_NearestBodyDistance = distance;
|
||||
m_NearestBodyId = id;
|
||||
m_HoveredSliderData = sliderData;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var distance = m_GUIWrapper.DistanceToCircle(position, GetBoneRadiusForPicking(position) * 2f);
|
||||
|
||||
if (distance <= m_NearestDistance)
|
||||
{
|
||||
m_NearestDistance = distance;
|
||||
m_NearestJointDistance = distance;
|
||||
m_NearestJointId = id;
|
||||
m_HoveredSliderData = sliderData;
|
||||
}
|
||||
}
|
||||
|
||||
if (isChainEnd &&
|
||||
(IsCapable(SkeletonAction.ChangeLength) ||
|
||||
IsCapable(SkeletonAction.MoveEndPosition) ||
|
||||
IsCapable(SkeletonAction.CreateBone)))
|
||||
{
|
||||
var distance = m_GUIWrapper.DistanceToCircle(endPosition, GetBoneRadiusForPicking(endPosition));
|
||||
|
||||
if (distance <= m_NearestDistance)
|
||||
{
|
||||
m_NearestDistance = distance;
|
||||
m_NearestTailDistance = distance;
|
||||
m_NearestTailId = id;
|
||||
m_HoveredSliderData = sliderData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 GetMouseWorldPosition(Vector3 planeNormal, Vector3 planePosition)
|
||||
{
|
||||
return m_GUIWrapper.GUIToWorld(m_GUIWrapper.mousePosition, planeNormal, planePosition);
|
||||
}
|
||||
|
||||
private float GetBoneRadiusForPicking(Vector3 position)
|
||||
{
|
||||
if (m_GUIWrapper.HasCurrentCamera())
|
||||
return 0.1f * m_GUIWrapper.GetHandleSize(position);
|
||||
|
||||
return kPickingRadius;
|
||||
}
|
||||
|
||||
public bool DoSelectBone(out int id, out bool additive)
|
||||
{
|
||||
id = 0;
|
||||
additive = false;
|
||||
|
||||
if (IsActionTriggering(SkeletonAction.Select))
|
||||
{
|
||||
id = m_HoveredBoneID;
|
||||
additive = m_GUIWrapper.isActionKeyDown;
|
||||
|
||||
if (mode == SkeletonMode.Selection)
|
||||
{
|
||||
m_GUIWrapper.UseCurrentEvent();
|
||||
m_GUIWrapper.SetGuiChanged(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoRotateBone(Vector3 pivot, Vector3 normal, out float deltaAngle)
|
||||
{
|
||||
deltaAngle = 0f;
|
||||
|
||||
Vector3 oldPosition = m_HotSliderData.position;
|
||||
Vector3 newPosition;
|
||||
if (DoSliderAction(SkeletonAction.RotateBone, m_HoveredBodyControlID, ref m_RotateControlID, out newPosition))
|
||||
{
|
||||
deltaAngle = Vector3.SignedAngle(oldPosition - pivot, (Vector3)newPosition - pivot, normal);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoMoveBone(out Vector3 deltaPosition)
|
||||
{
|
||||
deltaPosition = Vector3.zero;
|
||||
|
||||
Vector3 oldPosition = m_HotSliderData.position;
|
||||
Vector3 newPosition;
|
||||
if (DoSliderAction(SkeletonAction.MoveBone, m_HoveredJointControlID, ref m_MoveControlID, out newPosition))
|
||||
{
|
||||
deltaPosition = newPosition - oldPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoFreeMoveBone(out Vector3 deltaPosition)
|
||||
{
|
||||
deltaPosition = Vector3.zero;
|
||||
|
||||
Vector3 oldPosition = m_HotSliderData.position;
|
||||
Vector3 newPosition;
|
||||
if (DoSliderAction(SkeletonAction.FreeMoveBone, m_HoveredBodyControlID, ref m_FreeMoveControlID, out newPosition))
|
||||
{
|
||||
deltaPosition = newPosition - oldPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoMoveJoint(out Vector3 deltaPosition)
|
||||
{
|
||||
deltaPosition = Vector3.zero;
|
||||
|
||||
Vector3 oldPosition = m_HotSliderData.position;
|
||||
Vector3 newPosition;
|
||||
if (DoSliderAction(SkeletonAction.MoveJoint, m_HoveredJointControlID, ref m_MoveJointControlID, out newPosition))
|
||||
{
|
||||
deltaPosition = newPosition - oldPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoMoveEndPosition(out Vector3 endPosition)
|
||||
{
|
||||
return DoSliderAction(SkeletonAction.MoveEndPosition, m_HoveredTailControlID, ref m_MoveEndPositionControlID, out endPosition);
|
||||
}
|
||||
|
||||
public bool DoChangeLength(out Vector3 endPosition)
|
||||
{
|
||||
return DoSliderAction(SkeletonAction.ChangeLength, m_HoveredTailControlID, ref m_ChangeLengthControlID, out endPosition);
|
||||
}
|
||||
|
||||
private bool DoSliderAction(SkeletonAction action, int controlID, ref int actionControlID, out Vector3 newPosition)
|
||||
{
|
||||
newPosition = m_HoveredSliderData.position;
|
||||
|
||||
if (IsActionTriggering(action))
|
||||
{
|
||||
actionControlID = controlID;
|
||||
m_HotSliderData = m_HoveredSliderData;
|
||||
m_HotBoneID = hoveredBoneID;
|
||||
}
|
||||
|
||||
if (m_GUIWrapper.DoSlider(actionControlID, m_HotSliderData, out newPosition))
|
||||
{
|
||||
m_HotSliderData.position = newPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoCreateBoneStart(out Vector3 position)
|
||||
{
|
||||
position = GetMouseWorldPosition(m_HoveredSliderData.forward, m_HoveredSliderData.position);
|
||||
|
||||
if (CanCreateBone())
|
||||
m_GUIWrapper.LayoutControl(m_CreateBoneControlID, 0f);
|
||||
|
||||
if (IsActionActive(SkeletonAction.CreateBone))
|
||||
ConsumeMouseMoveEvents();
|
||||
|
||||
if (IsActionTriggering(SkeletonAction.CreateBone))
|
||||
{
|
||||
m_HotBoneID = hoveredBoneID;
|
||||
m_GUIWrapper.SetMultiStepControlHot(m_CreateBoneControlID);
|
||||
m_GUIWrapper.UseCurrentEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanCreateBone()
|
||||
{
|
||||
return mode == SkeletonMode.CreateBone && (m_GUIWrapper.IsControlNearest(defaultControlID) || m_GUIWrapper.IsControlNearest(m_HoveredTailControlID));
|
||||
}
|
||||
|
||||
public bool DoCreateBone(out Vector3 position)
|
||||
{
|
||||
position = GetMouseWorldPosition(m_HoveredSliderData.forward, m_HoveredSliderData.position);
|
||||
|
||||
if (IsActionHot(SkeletonAction.CreateBone))
|
||||
ConsumeMouseMoveEvents();
|
||||
|
||||
if (IsActionFinishing(SkeletonAction.CreateBone))
|
||||
{
|
||||
m_GUIWrapper.UseCurrentEvent();
|
||||
m_GUIWrapper.SetGuiChanged(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoSplitBone(out int id, out Vector3 position)
|
||||
{
|
||||
id = m_HoveredBodyID;
|
||||
position = GetMouseWorldPosition(m_HoveredSliderData.forward, m_HoveredSliderData.position);
|
||||
|
||||
if (IsActionActive(SkeletonAction.SplitBone))
|
||||
ConsumeMouseMoveEvents();
|
||||
|
||||
if (IsActionTriggering(SkeletonAction.SplitBone))
|
||||
{
|
||||
m_GUIWrapper.UseCurrentEvent();
|
||||
m_GUIWrapper.SetGuiChanged(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoRemoveBone()
|
||||
{
|
||||
if (IsActionTriggering(SkeletonAction.Remove))
|
||||
{
|
||||
m_GUIWrapper.UseCurrentEvent();
|
||||
m_GUIWrapper.SetGuiChanged(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoCancelMultistepAction(bool force)
|
||||
{
|
||||
if (force)
|
||||
{
|
||||
m_GUIWrapper.SetMultiStepControlHot(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((!m_GUIWrapper.IsMultiStepControlHot(0) && (m_GUIWrapper.IsMouseDown(1) || m_GUIWrapper.IsKeyDown(KeyCode.Escape))))
|
||||
{
|
||||
m_GUIWrapper.SetMultiStepControlHot(0);
|
||||
m_GUIWrapper.UseCurrentEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsActionActive(SkeletonAction action)
|
||||
{
|
||||
if (m_GUIWrapper.isAltDown || !m_GUIWrapper.IsControlHot(0) || !m_GUIWrapper.IsMultiStepControlHot(0))
|
||||
return false;
|
||||
|
||||
if (action == SkeletonAction.None)
|
||||
return m_GUIWrapper.IsControlNearest(defaultControlID);
|
||||
|
||||
if (!IsCapable(action))
|
||||
return false;
|
||||
|
||||
if (action == SkeletonAction.RotateBone)
|
||||
return m_GUIWrapper.IsControlNearest(m_HoveredBodyControlID);
|
||||
|
||||
if (action == SkeletonAction.ChangeLength)
|
||||
return m_GUIWrapper.IsControlNearest(m_HoveredTailControlID) && !m_GUIWrapper.isShiftDown;
|
||||
|
||||
if (action == SkeletonAction.MoveJoint)
|
||||
return m_GUIWrapper.IsControlNearest(m_HoveredJointControlID);
|
||||
|
||||
if (action == SkeletonAction.MoveEndPosition)
|
||||
return m_GUIWrapper.IsControlNearest(m_HoveredTailControlID) && !m_GUIWrapper.isShiftDown;
|
||||
|
||||
if (action == SkeletonAction.FreeMoveBone)
|
||||
return m_GUIWrapper.IsControlNearest(m_HoveredBodyControlID);
|
||||
|
||||
if (action == SkeletonAction.MoveBone)
|
||||
return m_GUIWrapper.IsControlNearest(m_HoveredJointControlID);
|
||||
|
||||
bool canCreateBone = IsCapable(SkeletonAction.CreateBone) && m_GUIWrapper.IsControlNearest(m_CreateBoneControlID);
|
||||
bool canSplitBone = IsCapable(SkeletonAction.SplitBone) && m_GUIWrapper.IsControlNearest(m_HoveredBodyControlID);
|
||||
|
||||
if (action == SkeletonAction.CreateBone)
|
||||
return canCreateBone;
|
||||
|
||||
if (action == SkeletonAction.SplitBone)
|
||||
return canSplitBone;
|
||||
|
||||
if (action == SkeletonAction.Select)
|
||||
return (m_GUIWrapper.IsControlNearest(m_HoveredBodyControlID) && !canSplitBone) ||
|
||||
m_GUIWrapper.IsControlNearest(m_HoveredJointControlID) ||
|
||||
(m_GUIWrapper.IsControlNearest(m_HoveredTailControlID) && !canCreateBone);
|
||||
|
||||
if (action == SkeletonAction.Remove)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsActionHot(SkeletonAction action)
|
||||
{
|
||||
if (action == SkeletonAction.None)
|
||||
return m_GUIWrapper.IsControlHot(0) && m_GUIWrapper.IsMultiStepControlHot(0);
|
||||
|
||||
if (action == SkeletonAction.RotateBone)
|
||||
return m_GUIWrapper.IsControlHot(m_RotateControlID);
|
||||
|
||||
if (action == SkeletonAction.MoveBone)
|
||||
return m_GUIWrapper.IsControlHot(m_MoveControlID);
|
||||
|
||||
if (action == SkeletonAction.FreeMoveBone)
|
||||
return m_GUIWrapper.IsControlHot(m_FreeMoveControlID);
|
||||
|
||||
if (action == SkeletonAction.MoveJoint)
|
||||
return m_GUIWrapper.IsControlHot(m_MoveJointControlID);
|
||||
|
||||
if (action == SkeletonAction.MoveEndPosition)
|
||||
return m_GUIWrapper.IsControlHot(m_MoveEndPositionControlID);
|
||||
|
||||
if (action == SkeletonAction.ChangeLength)
|
||||
return m_GUIWrapper.IsControlHot(m_ChangeLengthControlID);
|
||||
|
||||
if (action == SkeletonAction.CreateBone)
|
||||
return m_GUIWrapper.IsMultiStepControlHot(m_CreateBoneControlID) && !m_GUIWrapper.isAltDown;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsActionTriggering(SkeletonAction action)
|
||||
{
|
||||
if (!IsActionActive(action))
|
||||
return false;
|
||||
|
||||
if (action == SkeletonAction.Remove)
|
||||
{
|
||||
if ((m_GUIWrapper.eventType == EventType.ValidateCommand || m_GUIWrapper.eventType == EventType.ExecuteCommand)
|
||||
&& (m_GUIWrapper.commandName == kSoftDeleteCommandName || m_GUIWrapper.commandName == kDeleteCommandName))
|
||||
{
|
||||
if (m_GUIWrapper.eventType == EventType.ExecuteCommand)
|
||||
return true;
|
||||
|
||||
m_GUIWrapper.UseCurrentEvent();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_GUIWrapper.IsMouseDown(0);
|
||||
}
|
||||
|
||||
public bool IsActionFinishing(SkeletonAction action)
|
||||
{
|
||||
if (!IsActionHot(action) || !IsCapable(action))
|
||||
return false;
|
||||
|
||||
if (m_GUIWrapper.IsEventOutsideWindow())
|
||||
return true;
|
||||
|
||||
if (action == SkeletonAction.CreateBone)
|
||||
return m_GUIWrapper.IsMouseDown(0);
|
||||
|
||||
return m_GUIWrapper.IsMouseUp(0);
|
||||
}
|
||||
|
||||
public bool IsRepainting()
|
||||
{
|
||||
return m_GUIWrapper.IsRepainting();
|
||||
}
|
||||
|
||||
public void DrawBone(Vector3 position, Vector3 right, Vector3 forward, float length, Color color, bool isChained, bool isSelected, bool isJointHovered, bool isTailHovered, bool isHot)
|
||||
{
|
||||
var endPosition = position + right * length;
|
||||
var rotation = Quaternion.LookRotation(forward, Vector3.Cross(right, forward));
|
||||
var boneBodyColor = color;
|
||||
var boneJointColor = new Color(0f, 0f, 0f, 0.75f * color.a);
|
||||
var tailColor = new Color(0f, 0f, 0f, 0.75f * color.a);
|
||||
var hoveredColor = Handles.preselectionColor;
|
||||
var selectedColor = Handles.selectedColor;
|
||||
var drawRectCap = false;
|
||||
|
||||
if (isJointHovered)
|
||||
boneJointColor = hoveredColor;
|
||||
if (isHot && (IsActionHot(SkeletonAction.MoveBone) || IsActionHot(SkeletonAction.MoveJoint)))
|
||||
boneJointColor = selectedColor;
|
||||
|
||||
if (mode == SkeletonMode.EditPose || mode == SkeletonMode.CreateBone)
|
||||
{
|
||||
if (isJointHovered || isSelected)
|
||||
drawRectCap = true;
|
||||
}
|
||||
else if (mode == SkeletonMode.EditJoints || mode == SkeletonMode.SplitBone)
|
||||
{
|
||||
rotation = Quaternion.identity;
|
||||
drawRectCap = true;
|
||||
}
|
||||
|
||||
if (drawRectCap)
|
||||
Handles.RectangleHandleCap(0, position, rotation, BoneDrawingUtility.GetBoneRadius(position), EventType.Repaint);
|
||||
|
||||
BoneDrawingUtility.DrawBone(position, endPosition, forward, boneBodyColor);
|
||||
BoneDrawingUtility.DrawBoneNode(position, forward, boneJointColor);
|
||||
|
||||
if (!isChained &&
|
||||
(IsCapable(SkeletonAction.ChangeLength) ||
|
||||
IsCapable(SkeletonAction.MoveEndPosition)))
|
||||
{
|
||||
if (isTailHovered)
|
||||
tailColor = hoveredColor;
|
||||
|
||||
if (isHot && (IsActionHot(SkeletonAction.ChangeLength) || IsActionHot(SkeletonAction.MoveEndPosition)))
|
||||
tailColor = selectedColor;
|
||||
|
||||
BoneDrawingUtility.DrawBoneNode(endPosition, forward, tailColor);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawBoneParentLink(Vector3 parentPosition, Vector3 position, Vector3 forward, Color color)
|
||||
{
|
||||
BoneDrawingUtility.DrawBone(position, parentPosition, forward, color);
|
||||
}
|
||||
|
||||
public void DrawBoneOutline(Vector3 position, Vector3 right, Vector3 forward, float length, Color color, float outlineScale)
|
||||
{
|
||||
BoneDrawingUtility.DrawBoneOutline(position, position + right * length, forward, color, outlineScale);
|
||||
}
|
||||
|
||||
public void DrawCursors(bool canBeActive)
|
||||
{
|
||||
var mouseScreenRect = new Rect(m_GUIWrapper.mousePosition.x - 100f, m_GUIWrapper.mousePosition.y - 100f, 200f, 200f);
|
||||
|
||||
var isRotateHot = IsActionHot(SkeletonAction.RotateBone);
|
||||
if ((canBeActive && IsActionActive(SkeletonAction.RotateBone)) || isRotateHot)
|
||||
EditorGUIUtility.AddCursorRect(mouseScreenRect, MouseCursor.RotateArrow);
|
||||
|
||||
if ((canBeActive && IsActionActive(SkeletonAction.MoveBone)) || IsActionHot(SkeletonAction.MoveBone) ||
|
||||
(canBeActive && IsActionActive(SkeletonAction.FreeMoveBone)) || IsActionHot(SkeletonAction.FreeMoveBone) ||
|
||||
(canBeActive && IsActionActive(SkeletonAction.MoveJoint)) || IsActionHot(SkeletonAction.MoveJoint) ||
|
||||
(canBeActive && IsActionActive(SkeletonAction.MoveEndPosition)) || IsActionHot(SkeletonAction.MoveEndPosition))
|
||||
EditorGUIUtility.AddCursorRect(mouseScreenRect, MouseCursor.MoveArrow);
|
||||
}
|
||||
|
||||
private void ConsumeMouseMoveEvents()
|
||||
{
|
||||
if (m_GUIWrapper.eventType == EventType.MouseMove || (m_GUIWrapper.eventType == EventType.MouseDrag && m_GUIWrapper.mouseButton == 0))
|
||||
m_GUIWrapper.UseCurrentEvent();
|
||||
}
|
||||
|
||||
private bool IsCapable(SkeletonAction action)
|
||||
{
|
||||
return ((int)mode & (int)action) != 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 32b6c1f19cdc32d45aff3ce5c06f3ac3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,78 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class Slider2D
|
||||
{
|
||||
private static Vector2 s_CurrentMousePosition;
|
||||
private static Vector2 s_DragStartScreenPosition;
|
||||
private static Vector2 s_DragScreenOffset;
|
||||
private static double s_Time;
|
||||
|
||||
public static Vector2 Do(int controlID, Vector2 position, Handles.CapFunction drawCapFunction = null)
|
||||
{
|
||||
EventType type = Event.current.GetTypeForControl(controlID);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
if (Event.current.button == 0 && HandleUtility.nearestControl == controlID && !Event.current.alt)
|
||||
{
|
||||
s_Time = EditorApplication.timeSinceStartup;
|
||||
|
||||
GUIUtility.keyboardControl = controlID;
|
||||
GUIUtility.hotControl = controlID;
|
||||
s_CurrentMousePosition = Event.current.mousePosition;
|
||||
s_DragStartScreenPosition = Event.current.mousePosition;
|
||||
Vector2 b = HandleUtility.WorldToGUIPoint(position);
|
||||
s_DragScreenOffset = s_CurrentMousePosition - b;
|
||||
|
||||
Event.current.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == controlID && (Event.current.button == 0 || Event.current.button == 2))
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
Event.current.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
if (GUIUtility.hotControl == controlID)
|
||||
{
|
||||
s_CurrentMousePosition = Event.current.mousePosition;
|
||||
float screenDisplacement = (s_CurrentMousePosition - s_DragStartScreenPosition).magnitude;
|
||||
Vector2 center = position;
|
||||
Vector2 screenPosition = s_CurrentMousePosition - s_DragScreenOffset;
|
||||
position = Handles.inverseMatrix.MultiplyPoint(screenPosition);
|
||||
float displacement = (center - position).magnitude;
|
||||
|
||||
if (!Mathf.Approximately(displacement, 0f) && (EditorApplication.timeSinceStartup - s_Time > 0.15 || screenDisplacement >= 10f))
|
||||
GUI.changed = true;
|
||||
|
||||
Event.current.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.KeyDown:
|
||||
if (GUIUtility.hotControl == controlID && Event.current.keyCode == KeyCode.Escape)
|
||||
{
|
||||
position = Handles.inverseMatrix.MultiplyPoint(s_DragStartScreenPosition - s_DragScreenOffset);
|
||||
GUIUtility.hotControl = 0;
|
||||
GUI.changed = true;
|
||||
Event.current.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.Layout:
|
||||
if (drawCapFunction != null)
|
||||
drawCapFunction(controlID, position, Quaternion.identity, 1f, EventType.Layout);
|
||||
break;
|
||||
case EventType.Repaint:
|
||||
if (drawCapFunction != null)
|
||||
drawCapFunction(controlID, position, Quaternion.identity, 1f, EventType.Repaint);
|
||||
break;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aaae1a0ac32489e4db7ca7c10f984ade
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,696 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class SpriteMeshController
|
||||
{
|
||||
private const float kSnapDistance = 10f;
|
||||
|
||||
private struct EdgeIntersectionResult
|
||||
{
|
||||
public int startVertexIndex;
|
||||
public int endVertexIndex;
|
||||
public int intersectEdgeIndex;
|
||||
public Vector2 endPosition;
|
||||
}
|
||||
|
||||
private SpriteMeshDataController m_SpriteMeshDataController = new SpriteMeshDataController();
|
||||
private EdgeIntersectionResult m_EdgeIntersectionResult;
|
||||
|
||||
public ISpriteMeshView spriteMeshView { get; set; }
|
||||
public ISpriteMeshData spriteMeshData
|
||||
{
|
||||
get { return m_SpriteMeshData; }
|
||||
set { m_SpriteMeshData = value; }
|
||||
}
|
||||
|
||||
public ISelection<int> selection { get; set; }
|
||||
public ICacheUndo cacheUndo { get; set; }
|
||||
public ITriangulator triangulator { get; set; }
|
||||
public bool disable { get; set; }
|
||||
public Rect frame { get; set; }
|
||||
private ISpriteMeshData m_SpriteMeshData;
|
||||
private bool m_Moved = false;
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
m_SpriteMeshDataController.spriteMeshData = m_SpriteMeshData;
|
||||
|
||||
Debug.Assert(spriteMeshView != null);
|
||||
Debug.Assert(m_SpriteMeshData != null);
|
||||
Debug.Assert(selection != null);
|
||||
Debug.Assert(cacheUndo != null);
|
||||
|
||||
spriteMeshView.selection = selection;
|
||||
spriteMeshView.frame = frame;
|
||||
|
||||
EditorGUI.BeginDisabledGroup(disable);
|
||||
|
||||
spriteMeshView.BeginLayout();
|
||||
|
||||
if(spriteMeshView.CanLayout())
|
||||
{
|
||||
LayoutVertices();
|
||||
LayoutEdges();
|
||||
}
|
||||
|
||||
spriteMeshView.EndLayout();
|
||||
|
||||
if(spriteMeshView.CanRepaint())
|
||||
{
|
||||
DrawEdges();
|
||||
|
||||
if(GUI.enabled)
|
||||
{
|
||||
PreviewCreateVertex();
|
||||
PreviewCreateEdge();
|
||||
PreviewSplitEdge();
|
||||
}
|
||||
|
||||
DrawVertices();
|
||||
}
|
||||
|
||||
|
||||
HandleSplitEdge();
|
||||
HandleCreateEdge();
|
||||
HandleCreateVertex();
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
HandleSelectVertex();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(disable);
|
||||
|
||||
HandleMoveVertex();
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
HandleSelectEdge();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(disable);
|
||||
|
||||
HandleMoveEdge();
|
||||
HandleRemoveEdge();
|
||||
HandleRemoveVertices();
|
||||
|
||||
spriteMeshView.DoRepaint();
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
private void LayoutVertices()
|
||||
{
|
||||
for (int i = 0; i < m_SpriteMeshData.vertexCount; i++)
|
||||
{
|
||||
Vector2 position = m_SpriteMeshData.GetPosition(i);
|
||||
spriteMeshView.LayoutVertex(position, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void LayoutEdges()
|
||||
{
|
||||
for (int i = 0; i < m_SpriteMeshData.edges.Count; i++)
|
||||
{
|
||||
Edge edge = m_SpriteMeshData.edges[i];
|
||||
Vector2 startPosition = m_SpriteMeshData.GetPosition(edge.index1);
|
||||
Vector2 endPosition = m_SpriteMeshData.GetPosition(edge.index2);
|
||||
|
||||
spriteMeshView.LayoutEdge(startPosition, endPosition, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawEdges()
|
||||
{
|
||||
UpdateEdgeInstersection();
|
||||
|
||||
spriteMeshView.BeginDrawEdges();
|
||||
|
||||
for (int i = 0; i < m_SpriteMeshData.edges.Count; ++i)
|
||||
{
|
||||
if (SkipDrawEdge(i))
|
||||
continue;
|
||||
|
||||
Edge edge = m_SpriteMeshData.edges[i];
|
||||
Vector2 startPosition = m_SpriteMeshData.GetPosition(edge.index1);
|
||||
Vector2 endPosition = m_SpriteMeshData.GetPosition(edge.index2);
|
||||
|
||||
if (selection.Contains(edge.index1) && selection.Contains(edge.index2))
|
||||
spriteMeshView.DrawEdgeSelected(startPosition, endPosition);
|
||||
else
|
||||
spriteMeshView.DrawEdge(startPosition, endPosition);
|
||||
}
|
||||
|
||||
if (spriteMeshView.IsActionActive(MeshEditorAction.SelectEdge))
|
||||
{
|
||||
Edge hoveredEdge = m_SpriteMeshData.edges[spriteMeshView.hoveredEdge];
|
||||
Vector2 startPosition = m_SpriteMeshData.GetPosition(hoveredEdge.index1);
|
||||
Vector2 endPosition = m_SpriteMeshData.GetPosition(hoveredEdge.index2);
|
||||
|
||||
spriteMeshView.DrawEdgeHovered(startPosition, endPosition);
|
||||
}
|
||||
|
||||
spriteMeshView.EndDrawEdges();
|
||||
}
|
||||
|
||||
private bool SkipDrawEdge(int edgeIndex)
|
||||
{
|
||||
if(GUI.enabled == false)
|
||||
return false;
|
||||
|
||||
return edgeIndex == -1 ||
|
||||
spriteMeshView.hoveredEdge == edgeIndex && spriteMeshView.IsActionActive(MeshEditorAction.SelectEdge) ||
|
||||
spriteMeshView.hoveredEdge == edgeIndex && spriteMeshView.IsActionActive(MeshEditorAction.CreateVertex) ||
|
||||
spriteMeshView.closestEdge == edgeIndex && spriteMeshView.IsActionActive(MeshEditorAction.SplitEdge) ||
|
||||
edgeIndex == m_EdgeIntersectionResult.intersectEdgeIndex && spriteMeshView.IsActionActive(MeshEditorAction.CreateEdge);
|
||||
}
|
||||
|
||||
private void PreviewCreateVertex()
|
||||
{
|
||||
if (spriteMeshView.mode == SpriteMeshViewMode.CreateVertex &&
|
||||
spriteMeshView.IsActionActive(MeshEditorAction.CreateVertex))
|
||||
{
|
||||
Vector2 clampedMousePos = ClampToFrame(spriteMeshView.mouseWorldPosition);
|
||||
|
||||
if (spriteMeshView.hoveredEdge != -1)
|
||||
{
|
||||
Edge edge = m_SpriteMeshData.edges[spriteMeshView.hoveredEdge];
|
||||
|
||||
spriteMeshView.BeginDrawEdges();
|
||||
|
||||
spriteMeshView.DrawEdge(m_SpriteMeshData.GetPosition(edge.index1), clampedMousePos);
|
||||
spriteMeshView.DrawEdge(m_SpriteMeshData.GetPosition(edge.index2), clampedMousePos);
|
||||
|
||||
spriteMeshView.EndDrawEdges();
|
||||
}
|
||||
|
||||
spriteMeshView.DrawVertex(clampedMousePos);
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviewCreateEdge()
|
||||
{
|
||||
if (!spriteMeshView.IsActionActive(MeshEditorAction.CreateEdge))
|
||||
return;
|
||||
|
||||
spriteMeshView.BeginDrawEdges();
|
||||
|
||||
spriteMeshView.DrawEdge(m_SpriteMeshData.GetPosition(m_EdgeIntersectionResult.startVertexIndex), m_EdgeIntersectionResult.endPosition);
|
||||
|
||||
if (m_EdgeIntersectionResult.intersectEdgeIndex != -1)
|
||||
{
|
||||
Edge intersectingEdge = m_SpriteMeshData.edges[m_EdgeIntersectionResult.intersectEdgeIndex];
|
||||
spriteMeshView.DrawEdge(m_SpriteMeshData.GetPosition(intersectingEdge.index1), m_EdgeIntersectionResult.endPosition);
|
||||
spriteMeshView.DrawEdge(m_SpriteMeshData.GetPosition(intersectingEdge.index2), m_EdgeIntersectionResult.endPosition);
|
||||
}
|
||||
|
||||
spriteMeshView.EndDrawEdges();
|
||||
|
||||
if (m_EdgeIntersectionResult.endVertexIndex == -1)
|
||||
spriteMeshView.DrawVertex(m_EdgeIntersectionResult.endPosition);
|
||||
}
|
||||
|
||||
private void PreviewSplitEdge()
|
||||
{
|
||||
if (!spriteMeshView.IsActionActive(MeshEditorAction.SplitEdge))
|
||||
return;
|
||||
|
||||
Vector2 clampedMousePos = ClampToFrame(spriteMeshView.mouseWorldPosition);
|
||||
|
||||
Edge closestEdge = m_SpriteMeshData.edges[spriteMeshView.closestEdge];
|
||||
|
||||
spriteMeshView.BeginDrawEdges();
|
||||
|
||||
spriteMeshView.DrawEdge(m_SpriteMeshData.GetPosition(closestEdge.index1), clampedMousePos);
|
||||
spriteMeshView.DrawEdge(m_SpriteMeshData.GetPosition(closestEdge.index2), clampedMousePos);
|
||||
|
||||
spriteMeshView.EndDrawEdges();
|
||||
|
||||
spriteMeshView.DrawVertex(clampedMousePos);
|
||||
}
|
||||
|
||||
private void DrawVertices()
|
||||
{
|
||||
for (int i = 0; i < m_SpriteMeshData.vertexCount; i++)
|
||||
{
|
||||
Vector3 position = m_SpriteMeshData.GetPosition(i);
|
||||
|
||||
if (selection.Contains(i))
|
||||
spriteMeshView.DrawVertexSelected(position);
|
||||
else if (i == spriteMeshView.hoveredVertex && spriteMeshView.IsActionHot(MeshEditorAction.None))
|
||||
spriteMeshView.DrawVertexHovered(position);
|
||||
else
|
||||
spriteMeshView.DrawVertex(position);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSelectVertex()
|
||||
{
|
||||
bool additive;
|
||||
if (spriteMeshView.DoSelectVertex(out additive))
|
||||
SelectVertex(spriteMeshView.hoveredVertex, additive);
|
||||
}
|
||||
|
||||
private void HandleSelectEdge()
|
||||
{
|
||||
bool additive;
|
||||
if (spriteMeshView.DoSelectEdge(out additive))
|
||||
SelectEdge(spriteMeshView.hoveredEdge, additive);
|
||||
}
|
||||
|
||||
private void HandleMoveVertex()
|
||||
{
|
||||
if(spriteMeshView.IsActionTriggered(MeshEditorAction.MoveVertex))
|
||||
m_Moved = false;
|
||||
|
||||
Vector2 delta;
|
||||
if (spriteMeshView.DoMoveVertex(out delta))
|
||||
{
|
||||
if(!m_Moved)
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.moveVertices);
|
||||
m_Moved = true;
|
||||
}
|
||||
|
||||
MoveSelectedVertices(delta);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCreateVertex()
|
||||
{
|
||||
if (spriteMeshView.DoCreateVertex())
|
||||
CreateVertex(spriteMeshView.mouseWorldPosition, spriteMeshView.hoveredEdge);
|
||||
}
|
||||
|
||||
private void HandleSplitEdge()
|
||||
{
|
||||
if (spriteMeshView.DoSplitEdge())
|
||||
SplitEdge(spriteMeshView.mouseWorldPosition, spriteMeshView.closestEdge);
|
||||
}
|
||||
|
||||
private void HandleCreateEdge()
|
||||
{
|
||||
if (spriteMeshView.DoCreateEdge())
|
||||
CreateEdge(spriteMeshView.mouseWorldPosition, spriteMeshView.hoveredVertex, spriteMeshView.hoveredEdge);
|
||||
}
|
||||
|
||||
private void HandleMoveEdge()
|
||||
{
|
||||
if(spriteMeshView.IsActionTriggered(MeshEditorAction.MoveEdge))
|
||||
m_Moved = false;
|
||||
|
||||
Vector2 delta;
|
||||
if (spriteMeshView.DoMoveEdge(out delta))
|
||||
{
|
||||
if(!m_Moved)
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.moveVertices);
|
||||
m_Moved = true;
|
||||
}
|
||||
|
||||
MoveSelectedVertices(delta);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleRemoveEdge()
|
||||
{
|
||||
Edge edge;
|
||||
if (GetSelectedEdge(out edge) && spriteMeshView.DoRemove())
|
||||
RemoveEdge(edge);
|
||||
}
|
||||
|
||||
private void HandleRemoveVertices()
|
||||
{
|
||||
if (spriteMeshView.DoRemove())
|
||||
RemoveSelectedVertices();
|
||||
}
|
||||
|
||||
private void CreateVertex(Vector2 position, int edgeIndex)
|
||||
{
|
||||
position = MathUtility.ClampPositionToRect(position, frame);
|
||||
cacheUndo.BeginUndoOperation(TextContent.createVertex);
|
||||
|
||||
BoneWeight boneWeight = new BoneWeight();
|
||||
|
||||
Vector3Int indices;
|
||||
Vector3 barycentricCoords;
|
||||
if (m_SpriteMeshDataController.FindTriangle(position, out indices, out barycentricCoords))
|
||||
{
|
||||
EditableBoneWeight bw1 = m_SpriteMeshData.GetWeight(indices.x);
|
||||
EditableBoneWeight bw2 = m_SpriteMeshData.GetWeight(indices.y);
|
||||
EditableBoneWeight bw3 = m_SpriteMeshData.GetWeight(indices.z);
|
||||
|
||||
EditableBoneWeight result = new EditableBoneWeight();
|
||||
|
||||
foreach (BoneWeightChannel channel in bw1)
|
||||
{
|
||||
if (!channel.enabled)
|
||||
continue;
|
||||
|
||||
var weight = channel.weight * barycentricCoords.x;
|
||||
|
||||
if (weight > 0f)
|
||||
result.AddChannel(channel.boneIndex, weight, true);
|
||||
}
|
||||
|
||||
foreach (BoneWeightChannel channel in bw2)
|
||||
{
|
||||
if (!channel.enabled)
|
||||
continue;
|
||||
|
||||
var weight = channel.weight * barycentricCoords.y;
|
||||
|
||||
if (weight > 0f)
|
||||
result.AddChannel(channel.boneIndex, weight, true);
|
||||
}
|
||||
|
||||
foreach (BoneWeightChannel channel in bw3)
|
||||
{
|
||||
if (!channel.enabled)
|
||||
continue;
|
||||
|
||||
var weight = channel.weight * barycentricCoords.z;
|
||||
|
||||
if (weight > 0f)
|
||||
result.AddChannel(channel.boneIndex, weight, true);
|
||||
}
|
||||
|
||||
result.UnifyChannelsWithSameBoneIndex();
|
||||
result.FilterChannels(0f);
|
||||
result.Clamp(4, true);
|
||||
|
||||
boneWeight = result.ToBoneWeight(true);
|
||||
}
|
||||
else if (edgeIndex != -1)
|
||||
{
|
||||
Edge edge = m_SpriteMeshData.edges[edgeIndex];
|
||||
Vector2 pos1 = m_SpriteMeshData.GetPosition(edge.index1);
|
||||
Vector2 pos2 = m_SpriteMeshData.GetPosition(edge.index2);
|
||||
Vector2 dir1 = (position - pos1);
|
||||
Vector2 dir2 = (pos2 - pos1);
|
||||
float t = Vector2.Dot(dir1, dir2.normalized) / dir2.magnitude;
|
||||
t = Mathf.Clamp01(t);
|
||||
BoneWeight bw1 = m_SpriteMeshData.GetWeight(edge.index1).ToBoneWeight(true);
|
||||
BoneWeight bw2 = m_SpriteMeshData.GetWeight(edge.index2).ToBoneWeight(true);
|
||||
|
||||
boneWeight = EditableBoneWeightUtility.Lerp(bw1, bw2, t);
|
||||
}
|
||||
|
||||
m_SpriteMeshDataController.CreateVertex(position, edgeIndex);
|
||||
m_SpriteMeshData.GetWeight(m_SpriteMeshData.vertexCount - 1).SetFromBoneWeight(boneWeight);
|
||||
Triangulate();
|
||||
}
|
||||
|
||||
private void SelectVertex(int index, bool additiveToggle)
|
||||
{
|
||||
if (index < 0)
|
||||
throw new ArgumentException("Index out of range");
|
||||
|
||||
bool selected = selection.Contains(index);
|
||||
if (selected)
|
||||
{
|
||||
if (additiveToggle)
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.selection);
|
||||
selection.Select(index, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.selection);
|
||||
|
||||
if (!additiveToggle)
|
||||
ClearSelection();
|
||||
|
||||
selection.Select(index, true);
|
||||
}
|
||||
|
||||
cacheUndo.IncrementCurrentGroup();
|
||||
}
|
||||
|
||||
private void SelectEdge(int index, bool additiveToggle)
|
||||
{
|
||||
Debug.Assert(index >= 0);
|
||||
|
||||
Edge edge = m_SpriteMeshData.edges[index];
|
||||
|
||||
cacheUndo.BeginUndoOperation(TextContent.selection);
|
||||
|
||||
bool selected = selection.Contains(edge.index1) && selection.Contains(edge.index2);
|
||||
if (selected)
|
||||
{
|
||||
if (additiveToggle)
|
||||
{
|
||||
selection.Select(edge.index1, false);
|
||||
selection.Select(edge.index2, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!additiveToggle)
|
||||
ClearSelection();
|
||||
|
||||
selection.Select(edge.index1, true);
|
||||
selection.Select(edge.index2, true);
|
||||
}
|
||||
|
||||
cacheUndo.IncrementCurrentGroup();
|
||||
}
|
||||
|
||||
private void ClearSelection()
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.selection);
|
||||
selection.Clear();
|
||||
}
|
||||
|
||||
private void MoveSelectedVertices(Vector2 delta)
|
||||
{
|
||||
delta = MathUtility.MoveRectInsideFrame(CalculateRectFromSelection(), frame, delta);
|
||||
|
||||
var indices = selection.elements;
|
||||
|
||||
foreach (int index in indices)
|
||||
{
|
||||
Vector2 v = m_SpriteMeshData.GetPosition(index);
|
||||
m_SpriteMeshData.SetPosition(index, ClampToFrame(v + delta));
|
||||
}
|
||||
|
||||
Triangulate();
|
||||
}
|
||||
|
||||
private void CreateEdge(Vector2 position, int hoveredVertexIndex, int hoveredEdgeIndex)
|
||||
{
|
||||
position = ClampToFrame(position);
|
||||
EdgeIntersectionResult edgeIntersectionResult = CalculateEdgeIntersection(selection.activeElement, hoveredVertexIndex, hoveredEdgeIndex, position);
|
||||
|
||||
cacheUndo.BeginUndoOperation(TextContent.createEdge);
|
||||
|
||||
int selectIndex = -1;
|
||||
|
||||
if (edgeIntersectionResult.endVertexIndex == -1)
|
||||
{
|
||||
CreateVertex(edgeIntersectionResult.endPosition, edgeIntersectionResult.intersectEdgeIndex);
|
||||
m_SpriteMeshDataController.CreateEdge(selection.activeElement, m_SpriteMeshData.vertexCount - 1);
|
||||
selectIndex = m_SpriteMeshData.vertexCount - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SpriteMeshDataController.CreateEdge(selection.activeElement, edgeIntersectionResult.endVertexIndex);
|
||||
Triangulate();
|
||||
selectIndex = edgeIntersectionResult.endVertexIndex;
|
||||
}
|
||||
|
||||
ClearSelection();
|
||||
selection.Select(selectIndex, true);
|
||||
|
||||
cacheUndo.IncrementCurrentGroup();
|
||||
}
|
||||
|
||||
private void SplitEdge(Vector2 position, int edgeIndex)
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.splitEdge);
|
||||
|
||||
Vector2 clampedMousePos = ClampToFrame(position);
|
||||
|
||||
CreateVertex(clampedMousePos, edgeIndex);
|
||||
|
||||
cacheUndo.IncrementCurrentGroup();
|
||||
}
|
||||
|
||||
private bool GetSelectedEdge(out Edge edge)
|
||||
{
|
||||
edge = default(Edge);
|
||||
|
||||
if (selection.Count != 2)
|
||||
return false;
|
||||
|
||||
var indices = selection.elements;
|
||||
|
||||
int index1 = indices[0];
|
||||
int index2 = indices[1];
|
||||
|
||||
edge = new Edge(index1, index2);
|
||||
|
||||
if (!m_SpriteMeshData.edges.Contains(edge))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RemoveEdge(Edge edge)
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.removeEdge);
|
||||
m_SpriteMeshDataController.RemoveEdge(edge);
|
||||
Triangulate();
|
||||
}
|
||||
|
||||
private void RemoveSelectedVertices()
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.removeVertices);
|
||||
|
||||
m_SpriteMeshDataController.RemoveVertex(selection.elements);
|
||||
|
||||
Triangulate();
|
||||
selection.Clear();
|
||||
}
|
||||
|
||||
private void Triangulate()
|
||||
{
|
||||
m_SpriteMeshDataController.Triangulate(triangulator);
|
||||
m_SpriteMeshDataController.SortTrianglesByDepth();
|
||||
}
|
||||
|
||||
private Vector2 ClampToFrame(Vector2 position)
|
||||
{
|
||||
return MathUtility.ClampPositionToRect(position, frame);
|
||||
}
|
||||
|
||||
private Rect CalculateRectFromSelection()
|
||||
{
|
||||
Rect rect = new Rect();
|
||||
|
||||
Vector2 min = new Vector2(float.MaxValue, float.MaxValue);
|
||||
Vector2 max = new Vector2(float.MinValue, float.MinValue);
|
||||
|
||||
var indices = selection.elements;
|
||||
|
||||
foreach (int index in indices)
|
||||
{
|
||||
Vector2 v = m_SpriteMeshData.GetPosition(index);
|
||||
|
||||
min.x = Mathf.Min(min.x, v.x);
|
||||
min.y = Mathf.Min(min.y, v.y);
|
||||
|
||||
max.x = Mathf.Max(max.x, v.x);
|
||||
max.y = Mathf.Max(max.y, v.y);
|
||||
}
|
||||
|
||||
rect.min = min;
|
||||
rect.max = max;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
private void UpdateEdgeInstersection()
|
||||
{
|
||||
if (selection.Count == 1)
|
||||
m_EdgeIntersectionResult = CalculateEdgeIntersection(selection.activeElement, spriteMeshView.hoveredVertex, spriteMeshView.hoveredEdge, ClampToFrame(spriteMeshView.mouseWorldPosition));
|
||||
}
|
||||
|
||||
private EdgeIntersectionResult CalculateEdgeIntersection(int vertexIndex, int hoveredVertexIndex, int hoveredEdgeIndex, Vector2 targetPosition)
|
||||
{
|
||||
Debug.Assert(vertexIndex >= 0);
|
||||
|
||||
EdgeIntersectionResult edgeIntersection = new EdgeIntersectionResult();
|
||||
|
||||
edgeIntersection.startVertexIndex = vertexIndex;
|
||||
edgeIntersection.endVertexIndex = hoveredVertexIndex;
|
||||
edgeIntersection.endPosition = targetPosition;
|
||||
edgeIntersection.intersectEdgeIndex = -1;
|
||||
|
||||
Vector2 startPoint = m_SpriteMeshData.GetPosition(edgeIntersection.startVertexIndex);
|
||||
|
||||
bool intersectsEdge = false;
|
||||
int lastIntersectingEdgeIndex = -1;
|
||||
|
||||
do
|
||||
{
|
||||
lastIntersectingEdgeIndex = edgeIntersection.intersectEdgeIndex;
|
||||
|
||||
if (intersectsEdge)
|
||||
{
|
||||
Vector2 dir = edgeIntersection.endPosition - startPoint;
|
||||
edgeIntersection.endPosition += dir.normalized * 10f;
|
||||
}
|
||||
|
||||
intersectsEdge = SegmentIntersectsEdge(startPoint, edgeIntersection.endPosition, vertexIndex, ref edgeIntersection.endPosition, out edgeIntersection.intersectEdgeIndex);
|
||||
|
||||
//if we are hovering a vertex and intersect an edge indexing it we forget about the intersection
|
||||
if (intersectsEdge && m_SpriteMeshData.edges[edgeIntersection.intersectEdgeIndex].Contains(edgeIntersection.endVertexIndex))
|
||||
{
|
||||
edgeIntersection.intersectEdgeIndex = -1;
|
||||
intersectsEdge = false;
|
||||
edgeIntersection.endPosition = m_SpriteMeshData.GetPosition(edgeIntersection.endVertexIndex);
|
||||
}
|
||||
|
||||
if (intersectsEdge)
|
||||
{
|
||||
edgeIntersection.endVertexIndex = -1;
|
||||
|
||||
Edge intersectingEdge = m_SpriteMeshData.edges[edgeIntersection.intersectEdgeIndex];
|
||||
Vector2 newPointScreen = spriteMeshView.WorldToScreen(edgeIntersection.endPosition);
|
||||
Vector2 edgeV1 = spriteMeshView.WorldToScreen(m_SpriteMeshData.GetPosition(intersectingEdge.index1));
|
||||
Vector2 edgeV2 = spriteMeshView.WorldToScreen(m_SpriteMeshData.GetPosition(intersectingEdge.index2));
|
||||
|
||||
if ((newPointScreen - edgeV1).magnitude <= kSnapDistance)
|
||||
edgeIntersection.endVertexIndex = intersectingEdge.index1;
|
||||
else if ((newPointScreen - edgeV2).magnitude <= kSnapDistance)
|
||||
edgeIntersection.endVertexIndex = intersectingEdge.index2;
|
||||
|
||||
if (edgeIntersection.endVertexIndex != -1)
|
||||
{
|
||||
edgeIntersection.intersectEdgeIndex = -1;
|
||||
intersectsEdge = false;
|
||||
edgeIntersection.endPosition = m_SpriteMeshData.GetPosition(edgeIntersection.endVertexIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (intersectsEdge && lastIntersectingEdgeIndex != edgeIntersection.intersectEdgeIndex);
|
||||
|
||||
edgeIntersection.intersectEdgeIndex = intersectsEdge ? edgeIntersection.intersectEdgeIndex : hoveredEdgeIndex;
|
||||
|
||||
if (edgeIntersection.endVertexIndex != -1 && !intersectsEdge)
|
||||
edgeIntersection.endPosition = m_SpriteMeshData.GetPosition(edgeIntersection.endVertexIndex);
|
||||
|
||||
return edgeIntersection;
|
||||
}
|
||||
|
||||
private bool SegmentIntersectsEdge(Vector2 p1, Vector2 p2, int ignoreIndex, ref Vector2 point, out int intersectingEdgeIndex)
|
||||
{
|
||||
intersectingEdgeIndex = -1;
|
||||
|
||||
float sqrDistance = float.MaxValue;
|
||||
|
||||
for (int i = 0; i < m_SpriteMeshData.edges.Count; i++)
|
||||
{
|
||||
Edge edge = m_SpriteMeshData.edges[i];
|
||||
Vector2 v1 = m_SpriteMeshData.GetPosition(edge.index1);
|
||||
Vector2 v2 = m_SpriteMeshData.GetPosition(edge.index2);
|
||||
Vector2 pointTmp = Vector2.zero;
|
||||
|
||||
if (!edge.Contains(ignoreIndex) && MathUtility.SegmentIntersection(p1, p2, v1, v2, ref pointTmp))
|
||||
{
|
||||
float sqrMagnitude = (pointTmp - p1).sqrMagnitude;
|
||||
if (sqrMagnitude < sqrDistance)
|
||||
{
|
||||
sqrDistance = sqrMagnitude;
|
||||
intersectingEdgeIndex = i;
|
||||
point = pointTmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return intersectingEdgeIndex != -1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5796720807c5b4856aea84babbabd4ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,539 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class SpriteMeshView : ISpriteMeshView
|
||||
{
|
||||
readonly int m_VertexHashCode = "Vertex".GetHashCode();
|
||||
readonly int m_EdgeHashCode = "Edge".GetHashCode();
|
||||
const string kDeleteCommandName = "Delete";
|
||||
const string kSoftDeleteCommandName = "SoftDelete";
|
||||
static readonly Color kEdgeColor = Color.cyan;
|
||||
static readonly Color kEdgeHoveredColor = Color.yellow;
|
||||
static readonly Color kEdgeSelectedColor = Color.yellow;
|
||||
const float kEdgeWidth = 2f;
|
||||
const float kVertexRadius = 2.5f;
|
||||
|
||||
private class Styles
|
||||
{
|
||||
public readonly GUIStyle pointNormalStyle;
|
||||
public readonly GUIStyle pointHoveredStyle;
|
||||
public readonly GUIStyle pointSelectedStyle;
|
||||
|
||||
public Styles()
|
||||
{
|
||||
Texture2D pointNormal = ResourceLoader.Load<Texture2D>("SkinningModule/dotCyan.png");
|
||||
Texture2D pointHovered = ResourceLoader.Load<Texture2D>("SkinningModule/dotYellow.png");
|
||||
Texture2D pointSelected = ResourceLoader.Load<Texture2D>("SkinningModule/dotYellow.png");
|
||||
|
||||
pointNormalStyle = new GUIStyle();
|
||||
pointNormalStyle.normal.background = pointNormal;
|
||||
pointNormalStyle.fixedWidth = 8f;
|
||||
pointNormalStyle.fixedHeight = 8f;
|
||||
|
||||
pointHoveredStyle = new GUIStyle();
|
||||
pointHoveredStyle.normal.background = pointHovered;
|
||||
pointHoveredStyle.fixedWidth = 10f;
|
||||
pointHoveredStyle.fixedHeight = 10f;
|
||||
|
||||
pointSelectedStyle = new GUIStyle();
|
||||
pointSelectedStyle.normal.background = pointSelected;
|
||||
pointSelectedStyle.fixedWidth = 10f;
|
||||
pointSelectedStyle.fixedHeight = 10f;
|
||||
}
|
||||
}
|
||||
|
||||
private Styles m_Styles;
|
||||
private Styles styles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Styles == null)
|
||||
m_Styles = new Styles();
|
||||
|
||||
return m_Styles;
|
||||
}
|
||||
}
|
||||
|
||||
int m_HoveredEdge = -1;
|
||||
int m_HoveredEdgeControlID = -1;
|
||||
int m_MoveEdgeControlID = -1;
|
||||
int m_HoveredVertex = -1;
|
||||
int m_PrevHoveredVertex = -1;
|
||||
int m_HoveredVertexControlID = -1;
|
||||
int m_MoveVertexControlID = -1;
|
||||
Color m_TempColor;
|
||||
SliderData m_HotSliderData = SliderData.zero;
|
||||
MeshEditorAction m_PreviousActiveAction = MeshEditorAction.None;
|
||||
private Vector2 m_MouseWorldPosition;
|
||||
private float m_NearestVertexDistance;
|
||||
private float m_NearestEdgeDistance;
|
||||
private int m_NearestVertex = -1;
|
||||
private int m_NearestEdge = -1;
|
||||
|
||||
|
||||
public SpriteMeshViewMode mode { get; set; }
|
||||
public ISelection<int> selection { get; set; }
|
||||
public int defaultControlID { get; set; }
|
||||
public Rect frame { get; set; }
|
||||
private IGUIWrapper guiWrapper { get; set; }
|
||||
|
||||
public Vector2 mouseWorldPosition
|
||||
{
|
||||
get { return m_MouseWorldPosition; }
|
||||
}
|
||||
|
||||
public int hoveredVertex
|
||||
{
|
||||
get { return m_HoveredVertex; }
|
||||
}
|
||||
|
||||
public int hoveredEdge
|
||||
{
|
||||
get { return m_HoveredEdge; }
|
||||
}
|
||||
|
||||
public int closestEdge
|
||||
{
|
||||
get { return m_NearestEdge; }
|
||||
}
|
||||
|
||||
public SpriteMeshView(IGUIWrapper gw)
|
||||
{
|
||||
guiWrapper = gw;
|
||||
}
|
||||
|
||||
public void CancelMode()
|
||||
{
|
||||
if (mode != SpriteMeshViewMode.EditGeometry)
|
||||
{
|
||||
if (guiWrapper.IsKeyDown(KeyCode.Escape) || guiWrapper.IsMouseDown(1))
|
||||
{
|
||||
mode = SpriteMeshViewMode.EditGeometry;
|
||||
guiWrapper.UseCurrentEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginLayout()
|
||||
{
|
||||
var vertexControlID = guiWrapper.GetControlID(m_VertexHashCode, FocusType.Passive);
|
||||
var edgeControlID = guiWrapper.GetControlID(m_EdgeHashCode, FocusType.Passive);
|
||||
|
||||
if (guiWrapper.eventType == EventType.Layout || guiWrapper.eventType == EventType.MouseMove)
|
||||
{
|
||||
m_NearestVertexDistance = float.MaxValue;
|
||||
m_NearestEdgeDistance = float.MaxValue;
|
||||
m_NearestVertex = -1;
|
||||
m_NearestEdge = -1;
|
||||
m_MouseWorldPosition = guiWrapper.GUIToWorld(guiWrapper.mousePosition);
|
||||
m_HoveredVertexControlID = vertexControlID;
|
||||
m_HoveredEdgeControlID = edgeControlID;
|
||||
m_PrevHoveredVertex = m_HoveredVertex;
|
||||
m_HoveredVertex = -1;
|
||||
m_HoveredEdge = -1;
|
||||
|
||||
if (guiWrapper.IsControlHot(0))
|
||||
{
|
||||
m_MoveVertexControlID = -1;
|
||||
m_MoveEdgeControlID = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EndLayout()
|
||||
{
|
||||
guiWrapper.LayoutControl(m_HoveredEdgeControlID, m_NearestEdgeDistance);
|
||||
guiWrapper.LayoutControl(m_HoveredVertexControlID, m_NearestVertexDistance);
|
||||
|
||||
if(guiWrapper.IsControlNearest(m_HoveredVertexControlID))
|
||||
m_HoveredVertex = m_NearestVertex;
|
||||
|
||||
if (guiWrapper.IsControlNearest(m_HoveredEdgeControlID))
|
||||
m_HoveredEdge = m_NearestEdge;
|
||||
|
||||
if (guiWrapper.eventType == EventType.Layout || guiWrapper.eventType == EventType.MouseMove)
|
||||
if (m_PrevHoveredVertex != m_HoveredVertex)
|
||||
guiWrapper.Repaint();
|
||||
}
|
||||
|
||||
public void LayoutVertex(Vector2 position, int index)
|
||||
{
|
||||
if (guiWrapper.eventType == EventType.Layout)
|
||||
{
|
||||
var distance = guiWrapper.DistanceToCircle(position, kVertexRadius);
|
||||
|
||||
if (distance <= m_NearestVertexDistance)
|
||||
{
|
||||
m_NearestVertexDistance = distance;
|
||||
m_NearestVertex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void LayoutEdge(Vector2 startPosition, Vector2 endPosition, int index)
|
||||
{
|
||||
if (guiWrapper.eventType == EventType.Layout)
|
||||
{
|
||||
var distance = guiWrapper.DistanceToSegment(startPosition, endPosition);
|
||||
|
||||
if (distance < m_NearestEdgeDistance)
|
||||
{
|
||||
m_NearestEdgeDistance = distance;
|
||||
m_NearestEdge = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool DoCreateVertex()
|
||||
{
|
||||
if (mode == SpriteMeshViewMode.CreateVertex && IsActionActive(MeshEditorAction.CreateVertex))
|
||||
ConsumeMouseMoveEvents();
|
||||
|
||||
if (IsActionTriggered(MeshEditorAction.CreateVertex))
|
||||
{
|
||||
guiWrapper.SetGuiChanged(true);
|
||||
guiWrapper.UseCurrentEvent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoSelectVertex(out bool additive)
|
||||
{
|
||||
additive = false;
|
||||
|
||||
if (IsActionTriggered(MeshEditorAction.SelectVertex))
|
||||
{
|
||||
additive = guiWrapper.isActionKeyDown;
|
||||
guiWrapper.Repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoMoveVertex(out Vector2 delta)
|
||||
{
|
||||
delta = Vector2.zero;
|
||||
|
||||
if (IsActionTriggered(MeshEditorAction.MoveVertex))
|
||||
{
|
||||
m_MoveVertexControlID = m_HoveredVertexControlID;
|
||||
m_HotSliderData.position = mouseWorldPosition;
|
||||
}
|
||||
|
||||
Vector3 newPosition;
|
||||
if (guiWrapper.DoSlider(m_MoveVertexControlID, m_HotSliderData, out newPosition))
|
||||
{
|
||||
delta = newPosition - m_HotSliderData.position;
|
||||
m_HotSliderData.position = newPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoMoveEdge(out Vector2 delta)
|
||||
{
|
||||
delta = Vector2.zero;
|
||||
|
||||
if (IsActionTriggered(MeshEditorAction.MoveEdge))
|
||||
{
|
||||
m_MoveEdgeControlID = m_HoveredEdgeControlID;
|
||||
m_HotSliderData.position = mouseWorldPosition;
|
||||
}
|
||||
|
||||
Vector3 newPosition;
|
||||
if (guiWrapper.DoSlider(m_MoveEdgeControlID, m_HotSliderData, out newPosition))
|
||||
{
|
||||
delta = newPosition - m_HotSliderData.position;
|
||||
m_HotSliderData.position = newPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoCreateEdge()
|
||||
{
|
||||
if (IsActionActive(MeshEditorAction.CreateEdge))
|
||||
ConsumeMouseMoveEvents();
|
||||
|
||||
if (IsActionTriggered(MeshEditorAction.CreateEdge))
|
||||
{
|
||||
guiWrapper.SetGuiChanged(true);
|
||||
guiWrapper.UseCurrentEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoSplitEdge()
|
||||
{
|
||||
if (IsActionActive(MeshEditorAction.SplitEdge))
|
||||
ConsumeMouseMoveEvents();
|
||||
|
||||
if (IsActionTriggered(MeshEditorAction.SplitEdge))
|
||||
{
|
||||
guiWrapper.UseCurrentEvent();
|
||||
guiWrapper.SetGuiChanged(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoSelectEdge(out bool additive)
|
||||
{
|
||||
additive = false;
|
||||
|
||||
if (IsActionTriggered(MeshEditorAction.SelectEdge))
|
||||
{
|
||||
additive = guiWrapper.isActionKeyDown;
|
||||
guiWrapper.Repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DoRemove()
|
||||
{
|
||||
if (IsActionTriggered(MeshEditorAction.Remove))
|
||||
{
|
||||
guiWrapper.UseCurrentEvent();
|
||||
guiWrapper.SetGuiChanged(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void DrawVertex(Vector2 position)
|
||||
{
|
||||
DrawingUtility.DrawGUIStyleCap(0, position, Quaternion.identity, 1f, styles.pointNormalStyle);
|
||||
}
|
||||
|
||||
public void DrawVertexHovered(Vector2 position)
|
||||
{
|
||||
DrawingUtility.DrawGUIStyleCap(0, position, Quaternion.identity, 1f, styles.pointHoveredStyle);
|
||||
}
|
||||
|
||||
public void DrawVertexSelected(Vector2 position)
|
||||
{
|
||||
DrawingUtility.DrawGUIStyleCap(0, position, Quaternion.identity, 1f, styles.pointSelectedStyle);
|
||||
}
|
||||
|
||||
public void BeginDrawEdges()
|
||||
{
|
||||
if (guiWrapper.eventType != EventType.Repaint)
|
||||
return;
|
||||
|
||||
DrawingUtility.BeginSolidLines();
|
||||
m_TempColor = Handles.color;
|
||||
}
|
||||
|
||||
public void EndDrawEdges()
|
||||
{
|
||||
if (guiWrapper.eventType != EventType.Repaint)
|
||||
return;
|
||||
|
||||
DrawingUtility.EndLines();
|
||||
Handles.color = m_TempColor;
|
||||
}
|
||||
|
||||
public void DrawEdge(Vector2 startPosition, Vector2 endPosition)
|
||||
{
|
||||
DrawEdge(startPosition, endPosition, kEdgeColor);
|
||||
}
|
||||
|
||||
public void DrawEdgeHovered(Vector2 startPosition, Vector2 endPosition)
|
||||
{
|
||||
DrawEdge(startPosition, endPosition, kEdgeHoveredColor);
|
||||
}
|
||||
|
||||
public void DrawEdgeSelected(Vector2 startPosition, Vector2 endPosition)
|
||||
{
|
||||
DrawEdge(startPosition, endPosition, kEdgeSelectedColor);
|
||||
}
|
||||
|
||||
public bool IsActionActive(MeshEditorAction action)
|
||||
{
|
||||
if (guiWrapper.isAltDown || !guiWrapper.IsControlHot(0))
|
||||
return false;
|
||||
|
||||
var canCreateEdge = CanCreateEdge();
|
||||
var canSplitEdge = CanSplitEdge();
|
||||
|
||||
if (action == MeshEditorAction.None)
|
||||
return guiWrapper.IsControlNearest(defaultControlID);
|
||||
|
||||
if (action == MeshEditorAction.CreateVertex)
|
||||
{
|
||||
if(!frame.Contains(mouseWorldPosition))
|
||||
return false;
|
||||
|
||||
if (mode == SpriteMeshViewMode.EditGeometry)
|
||||
return guiWrapper.IsControlNearest(defaultControlID);
|
||||
|
||||
if (mode == SpriteMeshViewMode.CreateVertex)
|
||||
return hoveredVertex == -1;
|
||||
}
|
||||
|
||||
if (action == MeshEditorAction.MoveVertex)
|
||||
return guiWrapper.IsControlNearest(m_HoveredVertexControlID);
|
||||
|
||||
if (action == MeshEditorAction.CreateEdge)
|
||||
return canCreateEdge;
|
||||
|
||||
if (action == MeshEditorAction.SplitEdge)
|
||||
return canSplitEdge;
|
||||
|
||||
if (action == MeshEditorAction.MoveEdge)
|
||||
return guiWrapper.IsControlNearest(m_HoveredEdgeControlID);
|
||||
|
||||
if (action == MeshEditorAction.SelectVertex)
|
||||
return guiWrapper.IsControlNearest(m_HoveredVertexControlID);
|
||||
|
||||
if (action == MeshEditorAction.SelectEdge)
|
||||
return mode == SpriteMeshViewMode.EditGeometry &&
|
||||
guiWrapper.IsControlNearest(m_HoveredEdgeControlID) &&
|
||||
!canCreateEdge && !canSplitEdge;
|
||||
|
||||
if (action == MeshEditorAction.Remove)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsActionHot(MeshEditorAction action)
|
||||
{
|
||||
if (action == MeshEditorAction.None)
|
||||
return guiWrapper.IsControlHot(0);
|
||||
|
||||
if (action == MeshEditorAction.MoveVertex)
|
||||
return guiWrapper.IsControlHot(m_HoveredVertexControlID);
|
||||
|
||||
if (action == MeshEditorAction.MoveEdge)
|
||||
return guiWrapper.IsControlHot(m_HoveredEdgeControlID);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsActionTriggered(MeshEditorAction action)
|
||||
{
|
||||
if (!IsActionActive(action))
|
||||
return false;
|
||||
|
||||
if (action == MeshEditorAction.CreateVertex)
|
||||
{
|
||||
if (mode == SpriteMeshViewMode.EditGeometry)
|
||||
return guiWrapper.IsMouseDown(0) && guiWrapper.clickCount == 2;
|
||||
}
|
||||
|
||||
if (action == MeshEditorAction.Remove)
|
||||
{
|
||||
if ((guiWrapper.eventType == EventType.ValidateCommand || guiWrapper.eventType == EventType.ExecuteCommand)
|
||||
&& (guiWrapper.commandName == kSoftDeleteCommandName || guiWrapper.commandName == kDeleteCommandName))
|
||||
{
|
||||
if (guiWrapper.eventType == EventType.ExecuteCommand)
|
||||
return true;
|
||||
|
||||
guiWrapper.UseCurrentEvent();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(action != MeshEditorAction.None)
|
||||
return guiWrapper.IsMouseDown(0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Vector2 WorldToScreen(Vector2 position)
|
||||
{
|
||||
return HandleUtility.WorldToGUIPoint(position);
|
||||
}
|
||||
|
||||
private void ConsumeMouseMoveEvents()
|
||||
{
|
||||
if (guiWrapper.eventType == EventType.MouseMove || (guiWrapper.eventType == EventType.MouseDrag && guiWrapper.mouseButton == 0))
|
||||
guiWrapper.UseCurrentEvent();
|
||||
}
|
||||
|
||||
private bool CanCreateEdge()
|
||||
{
|
||||
if(!frame.Contains(mouseWorldPosition) || !(guiWrapper.IsControlNearest(defaultControlID) || guiWrapper.IsControlNearest(m_HoveredVertexControlID) || guiWrapper.IsControlNearest(m_HoveredEdgeControlID)))
|
||||
return false;
|
||||
|
||||
if (mode == SpriteMeshViewMode.EditGeometry)
|
||||
return guiWrapper.isShiftDown && selection.Count == 1 && !selection.Contains(hoveredVertex);
|
||||
|
||||
if (mode == SpriteMeshViewMode.CreateEdge)
|
||||
return selection.Count == 1 && !selection.Contains(hoveredVertex);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CanSplitEdge()
|
||||
{
|
||||
if(!frame.Contains(mouseWorldPosition) || !(guiWrapper.IsControlNearest(defaultControlID) || guiWrapper.IsControlNearest(m_HoveredEdgeControlID)))
|
||||
return false;
|
||||
|
||||
if (mode == SpriteMeshViewMode.EditGeometry)
|
||||
return guiWrapper.isShiftDown && m_NearestEdge != -1 && hoveredVertex == -1 && selection.Count == 0;
|
||||
|
||||
if (mode == SpriteMeshViewMode.SplitEdge)
|
||||
return m_NearestEdge != -1 && hoveredVertex == -1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DrawEdge(Vector2 startPosition, Vector2 endPosition, Color color)
|
||||
{
|
||||
if (guiWrapper.eventType != EventType.Repaint)
|
||||
return;
|
||||
|
||||
Handles.color = color;
|
||||
float width = kEdgeWidth / Handles.matrix.m00;
|
||||
|
||||
DrawingUtility.DrawSolidLine(width, startPosition, endPosition);
|
||||
}
|
||||
|
||||
public void DoRepaint()
|
||||
{
|
||||
if(guiWrapper.eventType != EventType.Layout)
|
||||
return;
|
||||
|
||||
var action = MeshEditorAction.None;
|
||||
|
||||
if(IsActionActive(MeshEditorAction.CreateVertex))
|
||||
action = MeshEditorAction.CreateVertex;
|
||||
else if(IsActionActive(MeshEditorAction.CreateEdge))
|
||||
action = MeshEditorAction.CreateEdge;
|
||||
else if(IsActionActive(MeshEditorAction.SplitEdge))
|
||||
action = MeshEditorAction.SplitEdge;
|
||||
|
||||
if(m_PreviousActiveAction != action)
|
||||
{
|
||||
m_PreviousActiveAction = action;
|
||||
guiWrapper.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanRepaint()
|
||||
{
|
||||
return guiWrapper.eventType == EventType.Repaint;
|
||||
}
|
||||
|
||||
public bool CanLayout()
|
||||
{
|
||||
return guiWrapper.eventType == EventType.Layout;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 07512ec0d03c84285a2573a5747097d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class UnselectTool<T>
|
||||
{
|
||||
private Unselector<T> m_Unselector = new Unselector<T>();
|
||||
public ICacheUndo cacheUndo { get; set; }
|
||||
public ISelection<T> selection
|
||||
{
|
||||
get { return m_Unselector.selection; }
|
||||
set { m_Unselector.selection = value; }
|
||||
}
|
||||
public Action onUnselect = () => {};
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
Debug.Assert(cacheUndo != null);
|
||||
Debug.Assert(selection != null);
|
||||
|
||||
var e = Event.current;
|
||||
|
||||
if (selection.Count > 0 && e.type == EventType.MouseDown && e.button == 1 && !e.alt)
|
||||
{
|
||||
cacheUndo.BeginUndoOperation(TextContent.clearSelection);
|
||||
m_Unselector.Select();
|
||||
e.Use();
|
||||
onUnselect.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 61f1168dfbac54f04b6068f199c35704
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,129 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor.U2D.Sprites;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class WeightInspector
|
||||
{
|
||||
private SpriteMeshDataController m_SpriteMeshDataController = new SpriteMeshDataController();
|
||||
private GUIContent[] m_BoneNameContents;
|
||||
|
||||
public ISpriteMeshData spriteMeshData
|
||||
{
|
||||
get { return m_SpriteMeshDataController.spriteMeshData; }
|
||||
set
|
||||
{
|
||||
if (spriteMeshData != value)
|
||||
m_SpriteMeshDataController.spriteMeshData = value;
|
||||
}
|
||||
}
|
||||
|
||||
public GUIContent[] boneNames
|
||||
{
|
||||
get { return m_BoneNameContents; }
|
||||
set { m_BoneNameContents = value; }
|
||||
}
|
||||
|
||||
public ICacheUndo cacheUndo { get; set; }
|
||||
public ISelection<int> selection { get; set; }
|
||||
public int controlID { get { return 0; } }
|
||||
|
||||
private bool m_UndoRegistered = false;
|
||||
|
||||
protected ISpriteEditor spriteEditor
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public void OnInspectorGUI()
|
||||
{
|
||||
ChannelsGUI();
|
||||
}
|
||||
|
||||
private void ChannelsGUI()
|
||||
{
|
||||
if (GUIUtility.hotControl == 0)
|
||||
m_UndoRegistered = false;
|
||||
|
||||
for (int channel = 0; channel < 4; ++channel)
|
||||
{
|
||||
var enabled = false;
|
||||
var boneIndex = -1;
|
||||
var weight = 0f;
|
||||
var isChannelEnabledMixed = false;
|
||||
var isBoneIndexMixed = false;
|
||||
var isWeightMixed = false;
|
||||
|
||||
if (spriteMeshData != null)
|
||||
m_SpriteMeshDataController.GetMultiEditChannelData(selection, channel, out enabled, out boneIndex, out weight, out isChannelEnabledMixed, out isBoneIndexMixed, out isWeightMixed);
|
||||
|
||||
var newEnabled = enabled;
|
||||
var newBoneIndex = boneIndex;
|
||||
var newWeight = weight;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
WeightChannelDrawer(ref newEnabled, ref newBoneIndex, ref newWeight, isChannelEnabledMixed, isBoneIndexMixed, isWeightMixed);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
RegisterUndo();
|
||||
m_SpriteMeshDataController.SetMultiEditChannelData(selection, channel, enabled, newEnabled, boneIndex, newBoneIndex, weight, newWeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WeightChannelDrawer(
|
||||
ref bool isChannelEnabled, ref int boneIndex, ref float weight,
|
||||
bool isChannelEnabledMixed = false, bool isBoneIndexMixed = false, bool isWeightMixed = false)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
EditorGUIUtility.fieldWidth = 1f;
|
||||
EditorGUIUtility.labelWidth = 1f;
|
||||
|
||||
EditorGUI.showMixedValue = isChannelEnabledMixed;
|
||||
isChannelEnabled = EditorGUILayout.Toggle(GUIContent.none, isChannelEnabled);
|
||||
|
||||
EditorGUIUtility.fieldWidth = 30f;
|
||||
EditorGUIUtility.labelWidth = 30f;
|
||||
|
||||
using (new EditorGUI.DisabledScope(!isChannelEnabled && !isChannelEnabledMixed))
|
||||
{
|
||||
int tempBoneIndex = GUI.enabled ? boneIndex : -1;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUIUtility.fieldWidth = 80f;
|
||||
|
||||
EditorGUI.showMixedValue = GUI.enabled && isBoneIndexMixed;
|
||||
tempBoneIndex = EditorGUILayout.Popup(tempBoneIndex, m_BoneNameContents);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
boneIndex = tempBoneIndex;
|
||||
|
||||
EditorGUIUtility.fieldWidth = 32f;
|
||||
EditorGUI.showMixedValue = isWeightMixed;
|
||||
weight = EditorGUILayout.Slider(GUIContent.none, weight, 0f, 1f);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.showMixedValue = false;
|
||||
EditorGUIUtility.labelWidth = -1;
|
||||
EditorGUIUtility.fieldWidth = -1;
|
||||
}
|
||||
|
||||
private void RegisterUndo()
|
||||
{
|
||||
if (m_UndoRegistered)
|
||||
return;
|
||||
|
||||
Debug.Assert(cacheUndo != null);
|
||||
|
||||
cacheUndo.BeginUndoOperation(TextContent.editWeights);
|
||||
|
||||
m_UndoRegistered = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3610f44c4cf884dad93f89fe4f19b8db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,30 @@
|
|||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal static class IconUtility
|
||||
{
|
||||
static public readonly string k_LightIconResourcePath = "SkinningModule/Icons/Light";
|
||||
static public readonly string k_DarkIconResourcePath = "SkinningModule/Icons/Dark";
|
||||
static public readonly string k_SelectedResourceIconPath = "SkinningModule/Icons/Selected";
|
||||
|
||||
public static Texture2D LoadIconResource(string name, string personalPath, string proPath)
|
||||
{
|
||||
string iconPath = "";
|
||||
|
||||
if (EditorGUIUtility.isProSkin && !string.IsNullOrEmpty(proPath))
|
||||
iconPath = Path.Combine(proPath, "d_" + name);
|
||||
else
|
||||
iconPath = Path.Combine(personalPath, name);
|
||||
if (EditorGUIUtility.pixelsPerPoint > 1.0f)
|
||||
{
|
||||
var icon2x = ResourceLoader.Load<Texture2D>(iconPath + "@2x.png");
|
||||
if (icon2x != null)
|
||||
return icon2x;
|
||||
}
|
||||
|
||||
return ResourceLoader.Load<Texture2D>(iconPath+".png");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b5575e7bca1590549b79930bc2df9679
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 54114b7cd6ae355489325fd90132f551
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3d7adf78a0bd60848be2470847fbf8ea
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,83 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 780f94cfcb317444289dc159309d8b07
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
'': Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Editor: 0
|
||||
Exclude Linux: 1
|
||||
Exclude Linux64: 1
|
||||
Exclude LinuxUniversal: 1
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 1
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
DefaultValueInitialized: true
|
||||
OS: Windows
|
||||
- first:
|
||||
Facebook: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Facebook: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Linux
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: x86
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,83 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 35869f4d0e1d03e4ebffcc461c55773f
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
'': Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Editor: 0
|
||||
Exclude Linux: 1
|
||||
Exclude Linux64: 1
|
||||
Exclude LinuxUniversal: 1
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 1
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
DefaultValueInitialized: true
|
||||
OS: Linux
|
||||
- first:
|
||||
Facebook: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Facebook: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Linux
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: x86
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5335ae1a0cfbc2c41903fdf8d489fcc7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,83 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 83a049cd282c44dd3ba1c44001c8a6d5
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
'': Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Editor: 0
|
||||
Exclude Linux: 1
|
||||
Exclude Linux64: 1
|
||||
Exclude LinuxUniversal: 1
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 1
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: OSX
|
||||
- first:
|
||||
Facebook: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Facebook: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Linux
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: x86
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,136 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal static class MathUtility
|
||||
{
|
||||
public static float DistanceToSegmentClamp(Vector3 p, Vector3 p1, Vector3 p2)
|
||||
{
|
||||
float l2 = (p2 - p1).sqrMagnitude; // i.e. |b-a|^2 - avoid a sqrt
|
||||
if (l2 == 0.0)
|
||||
return float.MaxValue; // a == b case
|
||||
float t = Vector3.Dot(p - p1, p2 - p1) / l2;
|
||||
if (t < 0.0)
|
||||
return float.MaxValue; // Beyond the 'a' end of the segment
|
||||
if (t > 1.0)
|
||||
return float.MaxValue; // Beyond the 'b' end of the segment
|
||||
Vector3 projection = p1 + t * (p2 - p1); // Projection falls on the segment
|
||||
return (p - projection).magnitude;
|
||||
}
|
||||
|
||||
public static Vector2 ClampPositionToRect(Vector2 position, Rect rect)
|
||||
{
|
||||
return new Vector2(Mathf.Clamp(position.x, rect.xMin, rect.xMax), Mathf.Clamp(position.y, rect.yMin, rect.yMax));
|
||||
}
|
||||
|
||||
public static Vector2 MoveRectInsideFrame(Rect rect, Rect frame, Vector2 delta)
|
||||
{
|
||||
if (frame.size.x <= rect.size.x)
|
||||
delta.x = 0f;
|
||||
|
||||
if (frame.size.y <= rect.size.y)
|
||||
delta.y = 0f;
|
||||
|
||||
Vector2 min = rect.min + delta;
|
||||
Vector2 max = rect.max + delta;
|
||||
Vector2 size = rect.size;
|
||||
Vector2 position = rect.position;
|
||||
|
||||
max.x = Mathf.Clamp(max.x, frame.min.x, frame.max.x);
|
||||
max.y = Mathf.Clamp(max.y, frame.min.y, frame.max.y);
|
||||
|
||||
min = max - size;
|
||||
|
||||
min.x = Mathf.Clamp(min.x, frame.min.x, frame.max.x);
|
||||
min.y = Mathf.Clamp(min.y, frame.min.y, frame.max.y);
|
||||
|
||||
max = min + size;
|
||||
|
||||
rect.min = min;
|
||||
rect.max = max;
|
||||
|
||||
delta = rect.position - position;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
public static bool SegmentIntersection(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, ref Vector2 point)
|
||||
{
|
||||
Vector2 s1 = p1 - p0;
|
||||
Vector2 s2 = p3 - p2;
|
||||
|
||||
float s, t, determinant;
|
||||
determinant = (s1.x * s2.y - s2.x * s1.y);
|
||||
|
||||
if (Mathf.Approximately(determinant, 0f))
|
||||
return false;
|
||||
|
||||
s = (-s1.y * (p0.x - p2.x) + s1.x * (p0.y - p2.y)) / determinant;
|
||||
t = (s2.x * (p0.y - p2.y) - s2.y * (p0.x - p2.x)) / determinant;
|
||||
|
||||
if (s >= 0f && s <= 1f && t >= 0f && t <= 1f)
|
||||
{
|
||||
point = p0 + (t * s1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//https://gamedev.stackexchange.com/a/49370
|
||||
public static void Barycentric(Vector2 p, Vector2 a, Vector2 b, Vector2 c, out Vector3 coords)
|
||||
{
|
||||
Vector2 v0 = b - a, v1 = c - a, v2 = p - a;
|
||||
float d00 = Vector2.Dot(v0, v0);
|
||||
float d01 = Vector2.Dot(v0, v1);
|
||||
float d11 = Vector2.Dot(v1, v1);
|
||||
float d20 = Vector2.Dot(v2, v0);
|
||||
float d21 = Vector2.Dot(v2, v1);
|
||||
float invDenom = 1f / (d00 * d11 - d01 * d01);
|
||||
coords.y = (d11 * d20 - d01 * d21) * invDenom;
|
||||
coords.z = (d00 * d21 - d01 * d20) * invDenom;
|
||||
coords.x = 1f - coords.y - coords.z;
|
||||
}
|
||||
|
||||
public static Quaternion NormalizeQuaternion(Quaternion q)
|
||||
{
|
||||
Vector4 v = new Vector4(q.x, q.y, q.z, q.w).normalized;
|
||||
return new Quaternion(v.x, v.y, v.z, v.w);
|
||||
}
|
||||
|
||||
//From: https://answers.unity.com/questions/861719/a-fast-triangle-triangle-intersection-algorithm-fo.html
|
||||
public static bool Intersect(Vector3 p1, Vector3 p2, Vector3 p3, Ray ray)
|
||||
{
|
||||
Vector3 e1, e2;
|
||||
Vector3 p, q, t;
|
||||
float det, invDet, u, v;
|
||||
e1 = p2 - p1;
|
||||
e2 = p3 - p1;
|
||||
p = Vector3.Cross(ray.direction, e2);
|
||||
det = Vector3.Dot(e1, p);
|
||||
|
||||
if (Mathf.Approximately(det, 0f))
|
||||
return false;
|
||||
|
||||
invDet = 1.0f / det;
|
||||
|
||||
t = ray.origin - p1;
|
||||
u = Vector3.Dot(t, p) * invDet;
|
||||
|
||||
if (u < 0 || u > 1)
|
||||
return false;
|
||||
|
||||
q = Vector3.Cross(t, e1);
|
||||
v = Vector3.Dot(ray.direction, q) * invDet;
|
||||
|
||||
if (v < 0 || u + v > 1)
|
||||
return false;
|
||||
|
||||
if ((Vector3.Dot(e2, q) * invDet) > 0f)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5e4f2af0145cfe44797338834d0b013a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9aa898c2e4f1cbc4ba5d122fc7b47317
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal interface IMeshPreviewBehaviour
|
||||
{
|
||||
float GetWeightMapOpacity(SpriteCache sprite);
|
||||
bool DrawWireframe(SpriteCache sprite);
|
||||
bool Overlay(SpriteCache sprite);
|
||||
bool OverlayWireframe(SpriteCache sprite);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aeeab8c4e75a1e8419b0eed0716d850a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class DefaultPreviewBehaviour : IMeshPreviewBehaviour
|
||||
{
|
||||
public float GetWeightMapOpacity(SpriteCache sprite)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public bool DrawWireframe(SpriteCache sprite)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Overlay(SpriteCache sprite)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OverlayWireframe(SpriteCache sprite)
|
||||
{
|
||||
return sprite.IsVisible() && sprite.skinningCache.selectedSprite == sprite;
|
||||
}
|
||||
}
|
||||
|
||||
internal class MeshPreviewBehaviour : IMeshPreviewBehaviour
|
||||
{
|
||||
public bool showWeightMap { get; set; }
|
||||
public bool drawWireframe { get; set; }
|
||||
public bool overlaySelected { get; set; }
|
||||
|
||||
public float GetWeightMapOpacity(SpriteCache sprite)
|
||||
{
|
||||
var skinningCache = sprite.skinningCache;
|
||||
|
||||
if (showWeightMap)
|
||||
{
|
||||
if (skinningCache.selectedSprite == sprite || skinningCache.selectedSprite == null)
|
||||
return VisibilityToolSettings.meshOpacity;
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public bool DrawWireframe(SpriteCache sprite)
|
||||
{
|
||||
var skinningCache = sprite.skinningCache;
|
||||
|
||||
if (drawWireframe)
|
||||
return skinningCache.selectedSprite == null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Overlay(SpriteCache sprite)
|
||||
{
|
||||
var skinningCache = sprite.skinningCache;
|
||||
|
||||
if (overlaySelected && skinningCache.selectedSprite == sprite)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OverlayWireframe(SpriteCache sprite)
|
||||
{
|
||||
return sprite.IsVisible() && sprite.skinningCache.selectedSprite == sprite;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8f503e223cd1e624d9aae16f915047f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,331 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class MeshPreviewTool : BaseTool
|
||||
{
|
||||
[SerializeField]
|
||||
private Material m_Material;
|
||||
private List<SpriteCache> m_Sprites;
|
||||
private IMeshPreviewBehaviour m_DefaultPreviewBehaviour = new DefaultPreviewBehaviour();
|
||||
|
||||
public IMeshPreviewBehaviour previewBehaviourOverride { get; set; }
|
||||
public override IMeshPreviewBehaviour previewBehaviour
|
||||
{
|
||||
get
|
||||
{
|
||||
if (previewBehaviourOverride != null)
|
||||
return previewBehaviourOverride;
|
||||
|
||||
return m_DefaultPreviewBehaviour;
|
||||
}
|
||||
}
|
||||
|
||||
internal override void OnCreate()
|
||||
{
|
||||
m_Material = new Material(Shader.Find("Hidden/SkinningModule-GUITextureClip"));
|
||||
m_Material.hideFlags = HideFlags.DontSave;
|
||||
}
|
||||
|
||||
internal override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
Debug.Assert(m_Material != null);
|
||||
|
||||
DestroyImmediate(m_Material);
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
m_Sprites = new List<SpriteCache>(skinningCache.GetSprites());
|
||||
|
||||
DirtyMeshesAll();
|
||||
|
||||
skinningCache.events.meshChanged.AddListener(MeshChanged);
|
||||
skinningCache.events.characterPartChanged.AddListener(CharacterPartChanged);
|
||||
skinningCache.events.skeletonPreviewPoseChanged.AddListener(SkeletonChanged);
|
||||
skinningCache.events.skeletonBindPoseChanged.AddListener(SkeletonChanged);
|
||||
skinningCache.events.skinningModeChanged.AddListener(SkinningModuleModeChanged);
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
skinningCache.events.meshChanged.RemoveListener(MeshChanged);
|
||||
skinningCache.events.skeletonPreviewPoseChanged.RemoveListener(SkeletonChanged);
|
||||
skinningCache.events.skeletonBindPoseChanged.RemoveListener(SkeletonChanged);
|
||||
skinningCache.events.skinningModeChanged.RemoveListener(SkinningModuleModeChanged);
|
||||
}
|
||||
|
||||
protected override void OnGUI()
|
||||
{
|
||||
Prepare();
|
||||
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
{
|
||||
//DrawDefaultSpriteMeshes();
|
||||
DrawSpriteMeshes();
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawOverlay()
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
if (skinningCache.mode == SkinningMode.SpriteSheet)
|
||||
{
|
||||
foreach (var sprite in m_Sprites)
|
||||
{
|
||||
if (previewBehaviour.Overlay(sprite))
|
||||
DrawSpriteMesh(sprite);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var character = skinningCache.character;
|
||||
Debug.Assert(character != null);
|
||||
|
||||
var parts = character.parts;
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (part.isVisible && previewBehaviour.Overlay(part.sprite))
|
||||
DrawSpriteMesh(part.sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OverlayWireframe()
|
||||
{
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
foreach (var sprite in m_Sprites)
|
||||
if (previewBehaviour.OverlayWireframe(sprite))
|
||||
DrawWireframe(sprite);
|
||||
}
|
||||
|
||||
private void CharacterPartChanged(CharacterPartCache characterPart)
|
||||
{
|
||||
var meshPreview = characterPart.sprite.GetMeshPreview();
|
||||
Debug.Assert(meshPreview != null);
|
||||
meshPreview.SetSkinningDirty();
|
||||
}
|
||||
|
||||
private void MeshChanged(MeshCache mesh)
|
||||
{
|
||||
var meshPreview = mesh.sprite.GetMeshPreview();
|
||||
Debug.Assert(meshPreview != null);
|
||||
meshPreview.SetMeshDirty();
|
||||
}
|
||||
|
||||
private void SkeletonChanged(SkeletonCache skeleton)
|
||||
{
|
||||
DirtySkinningAll();
|
||||
}
|
||||
|
||||
private void SkinningModuleModeChanged(SkinningMode mode)
|
||||
{
|
||||
DirtyMeshesAll();
|
||||
}
|
||||
|
||||
private void DirtyMeshesAll()
|
||||
{
|
||||
foreach (var sprite in m_Sprites)
|
||||
{
|
||||
var meshPreview = sprite.GetMeshPreview();
|
||||
|
||||
if (meshPreview != null)
|
||||
meshPreview.SetMeshDirty();
|
||||
}
|
||||
}
|
||||
|
||||
private void DirtySkinningAll()
|
||||
{
|
||||
foreach (var sprite in m_Sprites)
|
||||
{
|
||||
var meshPreview = sprite.GetMeshPreview();
|
||||
Debug.Assert(meshPreview != null);
|
||||
meshPreview.SetSkinningDirty();
|
||||
}
|
||||
}
|
||||
|
||||
private void Prepare()
|
||||
{
|
||||
foreach (var sprite in m_Sprites)
|
||||
{
|
||||
var meshPreview = sprite.GetMeshPreview();
|
||||
Debug.Assert(meshPreview != null);
|
||||
meshPreview.enableSkinning = true;
|
||||
meshPreview.Prepare();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDefaultSpriteMeshes()
|
||||
{
|
||||
Debug.Assert(Event.current.type == EventType.Repaint);
|
||||
|
||||
if (skinningCache.mode == SkinningMode.SpriteSheet)
|
||||
{
|
||||
foreach (var sprite in m_Sprites)
|
||||
DrawDefaultSpriteMesh(sprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
var character = skinningCache.character;
|
||||
Debug.Assert(character != null);
|
||||
|
||||
var parts = character.parts;
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (part.isVisible)
|
||||
DrawDefaultSpriteMesh(part.sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDefaultSpriteMesh(SpriteCache sprite)
|
||||
{
|
||||
Debug.Assert(m_Material != null);
|
||||
|
||||
var meshPreview = sprite.GetMeshPreview();
|
||||
var meshCache = sprite.GetMesh();
|
||||
var skeleton = skinningCache.GetEffectiveSkeleton(sprite);
|
||||
|
||||
Debug.Assert(meshPreview != null);
|
||||
|
||||
if (meshPreview.canSkin == false || skeleton.isPosePreview == false)
|
||||
{
|
||||
m_Material.mainTexture = meshCache.textureDataProvider.texture;
|
||||
m_Material.SetFloat("_Opacity", 1f);
|
||||
m_Material.SetFloat("_VertexColorBlend", 0f);
|
||||
m_Material.color = new Color(1f, 1f, 1f, 1f);
|
||||
|
||||
DrawingUtility.DrawMesh(meshPreview.defaultMesh, m_Material, sprite.GetLocalToWorldMatrixFromMode());
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSpriteMeshes()
|
||||
{
|
||||
Debug.Assert(Event.current.type == EventType.Repaint);
|
||||
|
||||
if (skinningCache.mode == SkinningMode.SpriteSheet)
|
||||
{
|
||||
foreach (var sprite in m_Sprites)
|
||||
{
|
||||
if (previewBehaviour.Overlay(sprite))
|
||||
continue;
|
||||
|
||||
DrawSpriteMesh(sprite);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var character = skinningCache.character;
|
||||
Debug.Assert(character != null);
|
||||
|
||||
var parts = character.parts;
|
||||
var selected = skinningCache.selectedSprite;
|
||||
var selectedVisible = false;
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (previewBehaviour.Overlay(part.sprite))
|
||||
continue;
|
||||
|
||||
if (part.sprite == selected)
|
||||
selectedVisible = part.isVisible;
|
||||
else if (part.isVisible)
|
||||
DrawSpriteMesh(part.sprite);
|
||||
}
|
||||
|
||||
if (selectedVisible && selected != null)
|
||||
DrawSpriteMesh(selected);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSpriteMesh(SpriteCache sprite)
|
||||
{
|
||||
var weightMapOpacity = previewBehaviour.GetWeightMapOpacity(sprite);
|
||||
DrawSpriteMesh(sprite, weightMapOpacity);
|
||||
|
||||
if (previewBehaviour.DrawWireframe(sprite))
|
||||
DrawWireframe(sprite);
|
||||
}
|
||||
|
||||
private void DrawSpriteMesh(SpriteCache sprite, float weightMapOpacity)
|
||||
{
|
||||
Debug.Assert(m_Material != null);
|
||||
|
||||
var meshPreview = sprite.GetMeshPreview();
|
||||
var meshCache = sprite.GetMesh();
|
||||
|
||||
Debug.Assert(meshPreview != null);
|
||||
|
||||
if (meshPreview.mesh == null || meshPreview.mesh.vertexCount == 0)
|
||||
{
|
||||
DrawDefaultSpriteMesh(sprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Material.mainTexture = meshCache.textureDataProvider.texture;
|
||||
m_Material.SetFloat("_Opacity", 1f);
|
||||
m_Material.SetFloat("_VertexColorBlend", weightMapOpacity);
|
||||
|
||||
m_Material.color = Color.white;
|
||||
|
||||
DrawingUtility.DrawMesh(meshPreview.mesh, m_Material, sprite.GetLocalToWorldMatrixFromMode());
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSelectedSpriteWeightMap()
|
||||
{
|
||||
var selectedSprite = skinningCache.selectedSprite;
|
||||
|
||||
if (selectedSprite != null)
|
||||
{
|
||||
var opacity = GetWeightOpacityFromCurrentTool();
|
||||
|
||||
if (opacity > 0f)
|
||||
DrawSpriteMesh(selectedSprite, opacity);
|
||||
}
|
||||
}
|
||||
|
||||
private float GetWeightOpacityFromCurrentTool()
|
||||
{
|
||||
return IsWeightTool() ? VisibilityToolSettings.meshOpacity : 0f;
|
||||
}
|
||||
|
||||
private bool IsWeightTool()
|
||||
{
|
||||
var currentTool = skinningCache.selectedTool;
|
||||
|
||||
if (currentTool == skinningCache.GetTool(Tools.WeightSlider) ||
|
||||
currentTool == skinningCache.GetTool(Tools.WeightBrush) ||
|
||||
currentTool == skinningCache.GetTool(Tools.BoneInfluence) ||
|
||||
currentTool == skinningCache.GetTool(Tools.GenerateWeights))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DrawWireframe(SpriteCache sprite)
|
||||
{
|
||||
Debug.Assert(Event.current.type == EventType.Repaint);
|
||||
Debug.Assert(sprite != null);
|
||||
|
||||
var meshPreview = sprite.GetMeshPreview();
|
||||
Debug.Assert(meshPreview != null);
|
||||
|
||||
m_Material.mainTexture = null;
|
||||
m_Material.SetFloat("_Opacity", 0.35f);
|
||||
m_Material.SetFloat("_VertexColorBlend", 0f);
|
||||
m_Material.color = Color.white;
|
||||
|
||||
GL.wireframe = true;
|
||||
DrawingUtility.DrawMesh(meshPreview.mesh, m_Material, sprite.GetLocalToWorldMatrixFromMode());
|
||||
GL.wireframe = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 90e762d3b06944889a5d95fc12a8db68
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9ee4e8d7932896548ab9dd0fa6efebc5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,186 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal partial class MeshTool : BaseTool
|
||||
{
|
||||
private MeshCache m_Mesh;
|
||||
private ISelection<int> m_SelectionOverride;
|
||||
private SpriteMeshController m_SpriteMeshController;
|
||||
private SpriteMeshView m_SpriteMeshView;
|
||||
private RectSelectionTool<int> m_RectSelectionTool = new RectSelectionTool<int>();
|
||||
private RectVertexSelector m_RectVertexSelector = new RectVertexSelector();
|
||||
private UnselectTool<int> m_UnselectTool = new UnselectTool<int>();
|
||||
private ITriangulator m_Triangulator;
|
||||
|
||||
public MeshCache mesh
|
||||
{
|
||||
get { return m_Mesh; }
|
||||
}
|
||||
|
||||
public SpriteMeshViewMode mode
|
||||
{
|
||||
get { return m_SpriteMeshView.mode; }
|
||||
set { m_SpriteMeshView.mode = value; }
|
||||
}
|
||||
|
||||
public bool disable
|
||||
{
|
||||
get { return m_SpriteMeshController.disable; }
|
||||
set { m_SpriteMeshController.disable = value; }
|
||||
}
|
||||
|
||||
public ISelection<int> selectionOverride
|
||||
{
|
||||
get { return m_SelectionOverride; }
|
||||
set { m_SelectionOverride = value; }
|
||||
}
|
||||
|
||||
public override int defaultControlID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Mesh == null)
|
||||
return 0;
|
||||
|
||||
return m_RectSelectionTool.controlID;
|
||||
}
|
||||
}
|
||||
|
||||
private ISelection<int> selection
|
||||
{
|
||||
get
|
||||
{
|
||||
if(selectionOverride != null)
|
||||
return selectionOverride;
|
||||
return skinningCache.vertexSelection;
|
||||
}
|
||||
}
|
||||
|
||||
internal override void OnCreate()
|
||||
{
|
||||
m_SpriteMeshController = new SpriteMeshController();
|
||||
m_SpriteMeshView = new SpriteMeshView(new GUIWrapper());
|
||||
m_Triangulator = new Triangulator();
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
m_SpriteMeshController.disable = false;
|
||||
m_SelectionOverride = null;
|
||||
|
||||
SetupSprite(skinningCache.selectedSprite);
|
||||
|
||||
skinningCache.events.selectedSpriteChanged.AddListener(OnSelectedSpriteChanged);
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
skinningCache.events.selectedSpriteChanged.RemoveListener(OnSelectedSpriteChanged);
|
||||
}
|
||||
|
||||
private void OnSelectedSpriteChanged(SpriteCache sprite)
|
||||
{
|
||||
SetupSprite(sprite);
|
||||
}
|
||||
|
||||
internal void SetupSprite(SpriteCache sprite)
|
||||
{
|
||||
var mesh = sprite.GetMesh();
|
||||
|
||||
if (m_Mesh != null
|
||||
&& m_Mesh != mesh
|
||||
&& selection.Count > 0)
|
||||
selection.Clear();
|
||||
|
||||
m_Mesh = mesh;
|
||||
m_SpriteMeshController.spriteMeshData = m_Mesh;
|
||||
}
|
||||
|
||||
private void SetupGUI()
|
||||
{
|
||||
m_SpriteMeshController.spriteMeshView = m_SpriteMeshView;
|
||||
m_SpriteMeshController.triangulator = m_Triangulator;
|
||||
m_SpriteMeshController.cacheUndo = skinningCache;
|
||||
m_RectSelectionTool.cacheUndo = skinningCache;
|
||||
m_RectSelectionTool.rectSelector = m_RectVertexSelector;
|
||||
m_RectVertexSelector.selection = selection;
|
||||
m_UnselectTool.cacheUndo = skinningCache;
|
||||
m_UnselectTool.selection = selection;
|
||||
|
||||
m_SpriteMeshController.frame = new Rect(Vector2.zero, m_Mesh.sprite.textureRect.size);
|
||||
m_SpriteMeshController.selection = selection;
|
||||
m_SpriteMeshView.defaultControlID = defaultControlID;
|
||||
m_RectVertexSelector.spriteMeshData = m_Mesh;
|
||||
}
|
||||
|
||||
protected override void OnGUI()
|
||||
{
|
||||
if (m_Mesh == null)
|
||||
return;
|
||||
|
||||
SetupGUI();
|
||||
|
||||
var handlesMatrix = Handles.matrix;
|
||||
Handles.matrix *= m_Mesh.sprite.GetLocalToWorldMatrixFromMode();
|
||||
|
||||
BeginPositionOverride();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
var guiEnabled = GUI.enabled;
|
||||
var moveAction = m_SpriteMeshController.spriteMeshView.IsActionHot(MeshEditorAction.MoveEdge) || m_SpriteMeshController.spriteMeshView.IsActionHot(MeshEditorAction.MoveVertex);
|
||||
GUI.enabled = (!skinningCache.IsOnVisualElement() && guiEnabled) || moveAction;
|
||||
m_SpriteMeshController.OnGUI();
|
||||
GUI.enabled = guiEnabled;
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
UpdateMesh();
|
||||
|
||||
m_RectSelectionTool.OnGUI();
|
||||
m_UnselectTool.OnGUI();
|
||||
|
||||
Handles.matrix = handlesMatrix;
|
||||
|
||||
EndPositionOverride();
|
||||
}
|
||||
|
||||
public void BeginPositionOverride()
|
||||
{
|
||||
if(m_Mesh != null)
|
||||
{
|
||||
m_Mesh.vertexPositionOverride = null;
|
||||
|
||||
var skeleton = skinningCache.GetEffectiveSkeleton(m_Mesh.sprite);
|
||||
|
||||
Debug.Assert(skeleton != null);
|
||||
|
||||
if (skeleton.isPosePreview)
|
||||
m_Mesh.vertexPositionOverride = m_Mesh.sprite.GetMeshPreview().vertices;
|
||||
}
|
||||
}
|
||||
|
||||
public void EndPositionOverride()
|
||||
{
|
||||
if(m_Mesh != null)
|
||||
m_Mesh.vertexPositionOverride = null;
|
||||
}
|
||||
|
||||
public void UpdateWeights()
|
||||
{
|
||||
InvokeMeshChanged();
|
||||
}
|
||||
|
||||
public void UpdateMesh()
|
||||
{
|
||||
InvokeMeshChanged();
|
||||
}
|
||||
|
||||
private void InvokeMeshChanged()
|
||||
{
|
||||
if(m_Mesh != null)
|
||||
skinningCache.events.meshChanged.Invoke(m_Mesh);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 72bf58aecc1877e48b77ac8a0408b0c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class MeshToolWrapper : BaseTool
|
||||
{
|
||||
private MeshTool m_MeshTool;
|
||||
private SkeletonTool m_SkeletonTool;
|
||||
private SpriteMeshViewMode m_MeshMode;
|
||||
private bool m_Disable = false;
|
||||
private SkeletonMode m_SkeletonMode;
|
||||
protected MeshPreviewBehaviour m_MeshPreviewBehaviour = new MeshPreviewBehaviour();
|
||||
|
||||
public MeshTool meshTool
|
||||
{
|
||||
get { return m_MeshTool; }
|
||||
set { m_MeshTool = value; }
|
||||
}
|
||||
|
||||
public SkeletonTool skeletonTool
|
||||
{
|
||||
get { return m_SkeletonTool; }
|
||||
set { m_SkeletonTool = value; }
|
||||
}
|
||||
|
||||
public SpriteMeshViewMode meshMode
|
||||
{
|
||||
get { return m_MeshMode; }
|
||||
set { m_MeshMode = value; }
|
||||
}
|
||||
|
||||
public bool disableMeshEditor
|
||||
{
|
||||
get { return m_Disable; }
|
||||
set { m_Disable = value; }
|
||||
}
|
||||
|
||||
public SkeletonMode skeletonMode
|
||||
{
|
||||
get { return m_SkeletonMode; }
|
||||
set { m_SkeletonMode = value; }
|
||||
}
|
||||
|
||||
public override int defaultControlID
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(meshTool != null);
|
||||
|
||||
return meshTool.defaultControlID;
|
||||
}
|
||||
}
|
||||
|
||||
public override IMeshPreviewBehaviour previewBehaviour
|
||||
{
|
||||
get { return m_MeshPreviewBehaviour; }
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
Debug.Assert(meshTool != null);
|
||||
skeletonTool.enableBoneInspector = false;
|
||||
skeletonTool.Activate();
|
||||
meshTool.Activate();
|
||||
m_MeshPreviewBehaviour.drawWireframe = true;
|
||||
m_MeshPreviewBehaviour.showWeightMap = false;
|
||||
m_MeshPreviewBehaviour.overlaySelected = false;
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
skeletonTool.Deactivate();
|
||||
meshTool.Deactivate();
|
||||
}
|
||||
|
||||
protected override void OnGUI()
|
||||
{
|
||||
DoSkeletonGUI();
|
||||
DoMeshGUI();
|
||||
}
|
||||
|
||||
protected void DoSkeletonGUI()
|
||||
{
|
||||
Debug.Assert(skeletonTool != null);
|
||||
|
||||
skeletonTool.mode = skeletonMode;
|
||||
skeletonTool.editBindPose = false;
|
||||
skeletonTool.DoGUI();
|
||||
}
|
||||
|
||||
protected void DoMeshGUI()
|
||||
{
|
||||
Debug.Assert(meshTool != null);
|
||||
|
||||
meshTool.disable = disableMeshEditor;
|
||||
meshTool.mode = meshMode;
|
||||
meshTool.DoGUI();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 19c21ce977fd53a46857b0dac2845685
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal class ModuleToolGroup
|
||||
{
|
||||
class ToolGroupEntry
|
||||
{
|
||||
public BaseTool tool;
|
||||
public Action activateCallback;
|
||||
}
|
||||
|
||||
class ToolGroup
|
||||
{
|
||||
public int groupId;
|
||||
public List<ToolGroupEntry> tools = new List<ToolGroupEntry>();
|
||||
public int previousToolIndex;
|
||||
}
|
||||
List<ToolGroup> m_ToolGroups = new List<ToolGroup>();
|
||||
|
||||
public void AddToolToGroup(int groupId, BaseTool tool, Action toolActivatedCallback)
|
||||
{
|
||||
List<ToolGroupEntry> tools = null;
|
||||
for (int i = 0; i < m_ToolGroups.Count; ++i)
|
||||
{
|
||||
if (m_ToolGroups[i].groupId == groupId)
|
||||
{
|
||||
tools = m_ToolGroups[i].tools;
|
||||
}
|
||||
|
||||
var toolIndex = m_ToolGroups[i].tools.FindIndex(x => x.tool == tool);
|
||||
if (toolIndex != -1)
|
||||
{
|
||||
Debug.LogError(string.Format("{0} already exist in group.", tool.name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (tools == null)
|
||||
{
|
||||
var toolGroup = new ToolGroup()
|
||||
{
|
||||
groupId = groupId
|
||||
};
|
||||
tools = toolGroup.tools;
|
||||
m_ToolGroups.Add(toolGroup);
|
||||
}
|
||||
|
||||
tools.Add(new ToolGroupEntry()
|
||||
{
|
||||
tool = tool,
|
||||
activateCallback = toolActivatedCallback
|
||||
});
|
||||
}
|
||||
|
||||
public void ActivateTool(BaseTool tool)
|
||||
{
|
||||
var toolGroupIndex = -1;
|
||||
var groupTool = m_ToolGroups.FirstOrDefault(x =>
|
||||
{
|
||||
toolGroupIndex = x.tools.FindIndex(y => y.tool == tool);
|
||||
return toolGroupIndex >= 0;
|
||||
});
|
||||
|
||||
if (groupTool != null && toolGroupIndex >= 0)
|
||||
{
|
||||
var previousTool = groupTool.previousToolIndex >= 0 ? groupTool.tools[groupTool.previousToolIndex] : null;
|
||||
if (tool.isActive) // we want to deactivate the tool and switch to original
|
||||
{
|
||||
tool.Deactivate();
|
||||
if (previousTool != null && previousTool.tool != tool && previousTool.tool != null)
|
||||
{
|
||||
previousTool.tool.Activate();
|
||||
groupTool.previousToolIndex = toolGroupIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < groupTool.tools.Count; ++i)
|
||||
{
|
||||
var gt = groupTool.tools[i];
|
||||
if (gt.tool.isActive)
|
||||
{
|
||||
groupTool.previousToolIndex = i;
|
||||
gt.tool.Deactivate();
|
||||
}
|
||||
}
|
||||
tool.Activate();
|
||||
if (groupTool.tools[toolGroupIndex].activateCallback != null)
|
||||
groupTool.tools[toolGroupIndex].activateCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ebefc406fa7f065458cfccf8e07be2f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,128 @@
|
|||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal static class ModuleUtility
|
||||
{
|
||||
public static Vector3 GUIToWorld(Vector3 guiPosition)
|
||||
{
|
||||
return GUIToWorld(guiPosition, Vector3.forward, Vector3.zero);
|
||||
}
|
||||
|
||||
public static Vector3 GUIToWorld(Vector3 guiPosition, Vector3 planeNormal, Vector3 planePos)
|
||||
{
|
||||
Vector3 worldPos = Handles.inverseMatrix.MultiplyPoint(guiPosition);
|
||||
|
||||
if (Camera.current)
|
||||
{
|
||||
Ray ray = HandleUtility.GUIPointToWorldRay(guiPosition);
|
||||
|
||||
planeNormal = Handles.matrix.MultiplyVector(planeNormal);
|
||||
|
||||
planePos = Handles.matrix.MultiplyPoint(planePos);
|
||||
|
||||
Plane plane = new Plane(planeNormal, planePos);
|
||||
|
||||
float distance = 0f;
|
||||
|
||||
if (plane.Raycast(ray, out distance))
|
||||
{
|
||||
worldPos = Handles.inverseMatrix.MultiplyPoint(ray.GetPoint(distance));
|
||||
}
|
||||
}
|
||||
|
||||
return worldPos;
|
||||
}
|
||||
|
||||
public static GUIContent[] ToGUIContentArray(string[] names)
|
||||
{
|
||||
return Array.ConvertAll(names, n => new GUIContent(n));
|
||||
}
|
||||
|
||||
public static Color CalculateNiceColor(int index, int numColors)
|
||||
{
|
||||
numColors = Mathf.Clamp(numColors, 1, int.MaxValue);
|
||||
|
||||
int loops = index / numColors;
|
||||
index = index % 360;
|
||||
|
||||
int hueAngleStep = 360 / numColors;
|
||||
float hueLoopOffset = hueAngleStep * 0.5f;
|
||||
float hue = index * hueAngleStep + loops * hueLoopOffset;
|
||||
|
||||
return Color.HSVToRGB(Mathf.Repeat(hue, 360f) / 360f, 1f, 1f);
|
||||
}
|
||||
|
||||
public static void UpdateLocalToWorldMatrices(List<SpriteBoneData> spriteBoneDataList, Matrix4x4 rootMatrix, ref Matrix4x4[] localToWorldMatrices)
|
||||
{
|
||||
if (localToWorldMatrices == null || localToWorldMatrices.Length != spriteBoneDataList.Count)
|
||||
localToWorldMatrices = new Matrix4x4[spriteBoneDataList.Count];
|
||||
|
||||
bool[] calculatedMatrix = new bool[spriteBoneDataList.Count];
|
||||
|
||||
var processedBoneCount = 0;
|
||||
while (processedBoneCount < spriteBoneDataList.Count)
|
||||
{
|
||||
int oldCount = processedBoneCount;
|
||||
|
||||
for (var i = 0; i < spriteBoneDataList.Count; ++i)
|
||||
{
|
||||
if (calculatedMatrix[i])
|
||||
continue;
|
||||
|
||||
var sourceBone = spriteBoneDataList[i];
|
||||
if (sourceBone.parentId != -1 && !calculatedMatrix[sourceBone.parentId])
|
||||
continue;
|
||||
|
||||
var localToWorldMatrix = Matrix4x4.identity;
|
||||
localToWorldMatrix.SetTRS(sourceBone.localPosition, sourceBone.localRotation, Vector3.one);
|
||||
|
||||
if (sourceBone.parentId == -1)
|
||||
localToWorldMatrix = rootMatrix * localToWorldMatrix;
|
||||
else if (calculatedMatrix[sourceBone.parentId])
|
||||
localToWorldMatrix = localToWorldMatrices[sourceBone.parentId] * localToWorldMatrix;
|
||||
|
||||
localToWorldMatrices[i] = localToWorldMatrix;
|
||||
calculatedMatrix[i] = true;
|
||||
processedBoneCount++;
|
||||
}
|
||||
|
||||
if (oldCount == processedBoneCount)
|
||||
throw new ArgumentException("Invalid hierarchy detected");
|
||||
}
|
||||
}
|
||||
|
||||
public static List<SpriteBoneData> CreateSpriteBoneData(UnityEngine.U2D.SpriteBone[] spriteBoneList, Matrix4x4 rootMatrix)
|
||||
{
|
||||
List<SpriteBoneData> spriteBoneDataList = new List<SpriteBoneData>(spriteBoneList.Length);
|
||||
|
||||
foreach (var spriteBone in spriteBoneList)
|
||||
{
|
||||
spriteBoneDataList.Add(new SpriteBoneData()
|
||||
{
|
||||
name = spriteBone.name,
|
||||
parentId = spriteBone.parentId,
|
||||
localPosition = spriteBone.position,
|
||||
localRotation = spriteBone.rotation,
|
||||
depth = spriteBone.position.z,
|
||||
length = spriteBone.length
|
||||
});
|
||||
}
|
||||
|
||||
Matrix4x4[] localToWorldMatrices = null;
|
||||
UpdateLocalToWorldMatrices(spriteBoneDataList, rootMatrix, ref localToWorldMatrices);
|
||||
|
||||
for (int i = 0; i < spriteBoneDataList.Count; ++i)
|
||||
{
|
||||
var spriteBoneData = spriteBoneDataList[i];
|
||||
spriteBoneData.position = localToWorldMatrices[i].MultiplyPoint(Vector2.zero);
|
||||
spriteBoneData.endPosition = localToWorldMatrices[i].MultiplyPoint(Vector2.right * spriteBoneData.length);
|
||||
}
|
||||
|
||||
return spriteBoneDataList;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aa2a7a8ee6a464dd9ae636e73111beb6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2a8a39f0e276884449c4610fee697330
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
using UnityEditor.U2D.Sprites;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal interface IOutlineGenerator
|
||||
{
|
||||
void GenerateOutline(ITextureDataProvider textureDataProvider, Rect rect, float detail, byte alphaTolerance, bool holeDetection, out Vector2[][] paths);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1c1fa730356b2f54e932bbcfe5507f93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,377 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor.U2D.Common;
|
||||
using UnityEditor.U2D.Animation.ClipperLib;
|
||||
using UnityEditor.U2D.Sprites;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
using Path = List<IntPoint>;
|
||||
using Paths = List<List<IntPoint>>;
|
||||
|
||||
internal class OutlineGenerator : IOutlineGenerator
|
||||
{
|
||||
const double kClipperScale = 1000.0;
|
||||
|
||||
private const float kEpsilon = 1.2e-12f;
|
||||
private const float kMinLinearizeDistance = 5f;
|
||||
private Texture2D m_CurrentTexture;
|
||||
private Rect m_CurrentRect;
|
||||
private byte m_CurrentAlphaTolerance;
|
||||
|
||||
public void GenerateOutline(ITextureDataProvider textureDataProvider, Rect rect, float detail, byte alphaTolerance, bool holeDetection, out Vector2[][] paths)
|
||||
{
|
||||
if (alphaTolerance >= 255)
|
||||
throw new ArgumentException("Alpha tolerance should be lower than 255");
|
||||
|
||||
m_CurrentTexture = textureDataProvider.GetReadableTexture2D();
|
||||
m_CurrentRect = rect;
|
||||
m_CurrentAlphaTolerance = alphaTolerance;
|
||||
|
||||
InternalEditorBridge.GenerateOutline(textureDataProvider.texture, rect, 1f, alphaTolerance, holeDetection, out paths);
|
||||
|
||||
if (paths.Length > 0)
|
||||
{
|
||||
ClipPaths(ref paths);
|
||||
|
||||
Debug.Assert(paths.Length > 0);
|
||||
|
||||
var rectSizeMagnitude = rect.size.magnitude;
|
||||
var minDistance = Mathf.Max(rectSizeMagnitude / 10f, kMinLinearizeDistance);
|
||||
var maxDistance = Mathf.Max(rectSizeMagnitude / 100f, kMinLinearizeDistance);
|
||||
var distance = Mathf.Lerp(minDistance, maxDistance, detail);
|
||||
|
||||
for (var pathIndex = 0; pathIndex < paths.Length; ++pathIndex)
|
||||
{
|
||||
var pathLength = CalculatePathLength(paths[pathIndex]);
|
||||
if (pathLength > distance)
|
||||
{
|
||||
var newPath = Linearize(new List<Vector2>(paths[pathIndex]), distance);
|
||||
|
||||
if (newPath.Count > 3)
|
||||
paths[pathIndex] = newPath.ToArray();
|
||||
|
||||
SmoothPath(paths[pathIndex], 5, 0.1f, 135f);
|
||||
}
|
||||
}
|
||||
|
||||
ClipPaths(ref paths);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClipPaths(ref Vector2[][] paths)
|
||||
{
|
||||
Debug.Assert(paths.Length > 0);
|
||||
|
||||
var subj = ToClipper(paths);
|
||||
var solution = new Paths();
|
||||
var clipper = new Clipper(Clipper.ioPreserveCollinear);
|
||||
|
||||
clipper.AddPaths(subj, PolyType.ptSubject, true);
|
||||
clipper.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive);
|
||||
FilterNestedPaths(solution);
|
||||
paths = ToVector2(solution);
|
||||
}
|
||||
|
||||
private void FilterNestedPaths(Paths paths)
|
||||
{
|
||||
var filtered = new List<Path>(paths);
|
||||
|
||||
for (var i = 0; i < paths.Count; ++i)
|
||||
{
|
||||
var path = paths[i];
|
||||
|
||||
if (!filtered.Contains(path))
|
||||
continue;
|
||||
|
||||
for (var j = i + 1; j < paths.Count; ++j)
|
||||
{
|
||||
if (!filtered.Contains(path))
|
||||
continue;
|
||||
|
||||
var other = paths[j];
|
||||
|
||||
if (IsPathContainedInOtherPath(path, other))
|
||||
{
|
||||
filtered.Remove(path);
|
||||
break;
|
||||
}
|
||||
else if (IsPathContainedInOtherPath(other, path))
|
||||
filtered.Remove(other);
|
||||
}
|
||||
}
|
||||
|
||||
paths.Clear();
|
||||
paths.AddRange(filtered);
|
||||
}
|
||||
|
||||
private bool IsPathContainedInOtherPath(Path path, Path other)
|
||||
{
|
||||
foreach (var p in path)
|
||||
{
|
||||
if (Clipper.PointInPolygon(p, other) < 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Paths ToClipper(Vector2[][] paths)
|
||||
{
|
||||
return new Paths(Array.ConvertAll(paths, p => ToClipper(p)));
|
||||
}
|
||||
|
||||
private Path ToClipper(Vector2[] path)
|
||||
{
|
||||
return new Path(Array.ConvertAll(path, p => new IntPoint(p.x * kClipperScale, p.y * kClipperScale)));
|
||||
}
|
||||
|
||||
private Vector2[][] ToVector2(Paths paths)
|
||||
{
|
||||
return paths.ConvertAll(p => ToVector2(p)).ToArray();
|
||||
}
|
||||
|
||||
private Vector2[] ToVector2(Path path)
|
||||
{
|
||||
return path.ConvertAll(p => new Vector2((float)(p.X / kClipperScale), (float)(p.Y / kClipperScale))).ToArray();
|
||||
}
|
||||
|
||||
private float CalculatePathLength(Vector2[] path)
|
||||
{
|
||||
var sum = 0f;
|
||||
for (var i = 0; i < path.Length; i++)
|
||||
{
|
||||
var nextIndex = NextIndex(i, path.Length);
|
||||
var p0 = path[i];
|
||||
var p1 = path[nextIndex];
|
||||
sum += Vector2.Distance(p0, p1);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
//Adapted from https://github.com/burningmime/curves
|
||||
private List<Vector2> Linearize(List<Vector2> src, float pointDistance)
|
||||
{
|
||||
if (src == null) throw new ArgumentNullException("src");
|
||||
if (pointDistance <= kEpsilon) throw new InvalidOperationException("pointDistance " + pointDistance + " is less than epislon " + kEpsilon);
|
||||
|
||||
var dst = new List<Vector2>();
|
||||
|
||||
if (src.Count > 0)
|
||||
{
|
||||
var accDistance = 0f;
|
||||
var lastIndex = 0;
|
||||
var lastPoint = src[0];
|
||||
|
||||
dst.Add(lastPoint);
|
||||
|
||||
for (var i = 0; i < src.Count; i++)
|
||||
{
|
||||
var nextIndex = NextIndex(i, src.Count);
|
||||
var p0 = src[i];
|
||||
var p1 = src[nextIndex];
|
||||
var edgeDistance = Vector2.Distance(p0, p1);
|
||||
|
||||
if (accDistance + edgeDistance > pointDistance || nextIndex == 0)
|
||||
{
|
||||
var partialDistance = pointDistance - accDistance;
|
||||
var newPoint = Vector2.Lerp(p0, p1, partialDistance / edgeDistance);
|
||||
var remainingDistance = edgeDistance - partialDistance;
|
||||
|
||||
//Roll back until we do not intersect any pixel
|
||||
var step = 1f;
|
||||
bool finish = false;
|
||||
while (!finish && IsLineOverImage(newPoint, lastPoint))
|
||||
{
|
||||
partialDistance = Vector2.Distance(p0, newPoint) - step;
|
||||
|
||||
while (partialDistance < 0f)
|
||||
{
|
||||
if (i > lastIndex + 1)
|
||||
{
|
||||
accDistance -= edgeDistance;
|
||||
--i;
|
||||
p1 = p0;
|
||||
p0 = src[i];
|
||||
edgeDistance = Vector2.Distance(p0, p1);
|
||||
partialDistance += edgeDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
partialDistance = 0f;
|
||||
finish = true;
|
||||
}
|
||||
|
||||
remainingDistance = edgeDistance - partialDistance;
|
||||
}
|
||||
|
||||
newPoint = Vector2.Lerp(p0, p1, partialDistance / edgeDistance);
|
||||
}
|
||||
|
||||
Debug.Assert(lastIndex <= i, "Generate Outline failed");
|
||||
|
||||
nextIndex = NextIndex(i, src.Count);
|
||||
if (nextIndex != 0 || !EqualsOrClose(newPoint, p1))
|
||||
{
|
||||
dst.Add(newPoint);
|
||||
lastPoint = newPoint;
|
||||
lastIndex = i;
|
||||
}
|
||||
|
||||
while (remainingDistance > pointDistance)
|
||||
{
|
||||
remainingDistance -= pointDistance;
|
||||
newPoint = Vector2.Lerp(p0, p1, (edgeDistance - remainingDistance) / edgeDistance);
|
||||
if (!EqualsOrClose(newPoint, lastPoint))
|
||||
{
|
||||
dst.Add(newPoint);
|
||||
lastPoint = newPoint;
|
||||
}
|
||||
}
|
||||
accDistance = remainingDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
accDistance += edgeDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
private bool EqualsOrClose(Vector2 v1, Vector2 v2)
|
||||
{
|
||||
return (v1 - v2).sqrMagnitude < kEpsilon;
|
||||
}
|
||||
|
||||
private void SmoothPath(Vector2[] path, int iterations, float velocity, float minAngle)
|
||||
{
|
||||
Debug.Assert(iterations > 0f);
|
||||
Debug.Assert(minAngle >= 0f);
|
||||
Debug.Assert(minAngle < 180f);
|
||||
|
||||
var cosTolerance = Mathf.Cos(minAngle * Mathf.Deg2Rad);
|
||||
|
||||
for (int iteration = 0; iteration < iterations; ++iteration)
|
||||
for (int i = 0; i < path.Length; ++i)
|
||||
{
|
||||
var prevPoint = path[PreviousIndex(i, path.Length)];
|
||||
var point = path[i];
|
||||
var nextPoint = path[NextIndex(i, path.Length)];
|
||||
|
||||
var t1 = prevPoint - point;
|
||||
var t2 = nextPoint - point;
|
||||
|
||||
var dot = Vector2.Dot(t1.normalized, t2.normalized);
|
||||
|
||||
if (dot > cosTolerance)
|
||||
continue;
|
||||
|
||||
var w1 = 1f / (point - prevPoint).magnitude;
|
||||
var w2 = 1f / (point - nextPoint).magnitude;
|
||||
var laplacian = (w1 * prevPoint + w2 * nextPoint) / (w1 + w2) - point;
|
||||
point += laplacian * velocity;
|
||||
|
||||
if (!IsLineOverImage(point, nextPoint) && !IsLineOverImage(point, prevPoint))
|
||||
path[i] = point;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2Int ToVector2Int(Vector2 v)
|
||||
{
|
||||
return new Vector2Int(Mathf.RoundToInt(v.x), Mathf.RoundToInt(v.y));
|
||||
}
|
||||
|
||||
private bool IsLineOverImage(Vector2 pointA, Vector2 pointB)
|
||||
{
|
||||
var pointAInt = ToVector2Int(pointA);
|
||||
var pointBInt = ToVector2Int(pointB);
|
||||
|
||||
if (IsPointInRectEdge(pointA) && IsPointInRectEdge(pointB) && (pointAInt.x == pointBInt.x || pointAInt.y == pointBInt.y))
|
||||
return false;
|
||||
|
||||
foreach (var point in GetPointsOnLine(pointAInt.x, pointAInt.y, pointBInt.x, pointBInt.y))
|
||||
{
|
||||
if (IsPointOverImage(point))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsPointOverImage(Vector2 point)
|
||||
{
|
||||
Debug.Assert(m_CurrentTexture != null);
|
||||
point += m_CurrentRect.center;
|
||||
return m_CurrentTexture.GetPixel((int)point.x, (int)point.y).a * 255 > m_CurrentAlphaTolerance;
|
||||
}
|
||||
|
||||
private bool IsPointInRectEdge(Vector2 point)
|
||||
{
|
||||
point += m_CurrentRect.center;
|
||||
var pointInt = ToVector2Int(point);
|
||||
var minInt = ToVector2Int(m_CurrentRect.min);
|
||||
var maxInt = ToVector2Int(m_CurrentRect.max);
|
||||
return minInt.x >= pointInt.x || maxInt.x <= pointInt.x || minInt.y >= pointInt.y || maxInt.y <= pointInt.y;
|
||||
}
|
||||
|
||||
//From http://ericw.ca/notes/bresenhams-line-algorithm-in-csharp.html
|
||||
private IEnumerable<Vector2Int> GetPointsOnLine(int x0, int y0, int x1, int y1)
|
||||
{
|
||||
bool steep = Mathf.Abs(y1 - y0) > Math.Abs(x1 - x0);
|
||||
if (steep)
|
||||
{
|
||||
int t;
|
||||
t = x0; // swap x0 and y0
|
||||
x0 = y0;
|
||||
y0 = t;
|
||||
t = x1; // swap x1 and y1
|
||||
x1 = y1;
|
||||
y1 = t;
|
||||
}
|
||||
if (x0 > x1)
|
||||
{
|
||||
int t;
|
||||
t = x0; // swap x0 and x1
|
||||
x0 = x1;
|
||||
x1 = t;
|
||||
t = y0; // swap y0 and y1
|
||||
y0 = y1;
|
||||
y1 = t;
|
||||
}
|
||||
int dx = x1 - x0;
|
||||
int dy = Mathf.Abs(y1 - y0);
|
||||
int error = dx / 2;
|
||||
int ystep = (y0 < y1) ? 1 : -1;
|
||||
int y = y0;
|
||||
for (int x = x0; x <= x1; x++)
|
||||
{
|
||||
yield return new Vector2Int((steep ? y : x), (steep ? x : y));
|
||||
error = error - dy;
|
||||
if (error < 0)
|
||||
{
|
||||
y += ystep;
|
||||
error += dx;
|
||||
}
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
private int NextIndex(int index, int pointCount)
|
||||
{
|
||||
return Mod(index + 1, pointCount);
|
||||
}
|
||||
|
||||
private int PreviousIndex(int index, int pointCount)
|
||||
{
|
||||
return Mod(index - 1, pointCount);
|
||||
}
|
||||
|
||||
private int Mod(int x, int m)
|
||||
{
|
||||
int r = x % m;
|
||||
return r < 0 ? r + m : r;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ae33c30ef3b5e474ca5a46d925273adc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0a68d56945b68344ea2f451f0e9f14b4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
[Serializable]
|
||||
internal class BoneSelection : SerializableSelection<BoneCache>, IBoneSelection
|
||||
{
|
||||
protected override BoneCache GetInvalidElement() { return null; }
|
||||
|
||||
public BoneCache root
|
||||
{
|
||||
get { return activeElement.FindRoot<BoneCache>(elements); }
|
||||
}
|
||||
|
||||
public BoneCache[] roots
|
||||
{
|
||||
get { return elements.FindRoots<BoneCache>(); }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 10db00a3c51d23d4ab8a68837ffa21fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal interface IBoneSelection : ITransformSelection<BoneCache> {}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2e79c8e51ce4486409b7daf3012a1f4d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,14 @@
|
|||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal interface ISelection<T>
|
||||
{
|
||||
int Count { get; }
|
||||
T activeElement { get; set; }
|
||||
T[] elements { get; set; }
|
||||
void Clear();
|
||||
void BeginSelection();
|
||||
void EndSelection(bool select);
|
||||
void Select(T element, bool select);
|
||||
bool Contains(T element);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: df44828de9ac64d44ba1a553e06eb335
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Animation
|
||||
{
|
||||
internal interface ITransformSelection<T> : ISelection<T> where T : TransformCache
|
||||
{
|
||||
T root { get; }
|
||||
T[] roots { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 08c104e7d99d71c4eb0702b4e3e4b7d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue