Initial commit

This commit is contained in:
AbstractConcept 2022-09-13 00:36:34 -05:00
commit 3c7cc0c973
8391 changed files with 704313 additions and 0 deletions

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5379fb539445eb24ea7ac709f8df31b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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() {}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cc0c24ed6655ecc449dcbe4a8f3bd744
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4e0025ed472de854692946726304a452
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d59ac20961f9b95419fd53d6c9663903
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9ceaf8ef48b796342bb695b046536af8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 455f18c8a1cd7574f84ff7236cfaadea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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() {}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ea21de3798988c84dbc83021a3e6ecee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6735c3e859fb15b4191b28f225534760
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: daabb48969fe3724f80d2c96d89d710e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10889aecec5b6ee41a2a1626c4cfc07c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 311e00422db8e4949bf926b0d039e05e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bee8f4b5ba5b4ee46a3b10bad91b8c97
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b3f77b5965944b8ba5c1beefa1eb533
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 237f0a0a8eeab0d46873db954d4abded
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 497003cbd9cf35349bb7a01db4d94b26
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; } }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 28c640dd2a903f0498148ed21c25a97f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9b92febd89128554b8579633a112c844
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 06c115e22e25aad4e9a720594b10fa29
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71855e9e46fcdd7408882b24732e4808
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 87771fd0f1d988d478a8f45923440eb8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 015d7144666ec57449ae83559d3e26c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b6edb0347e814797aa9c181f8e5918f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ea09cf179b192498284b7d89afdbff8a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ea4cc92a263807f4985d77508d2bb536
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 32b6c1f19cdc32d45aff3ce5c06f3ac3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aaae1a0ac32489e4db7ca7c10f984ade
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5796720807c5b4856aea84babbabd4ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 07512ec0d03c84285a2573a5747097d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61f1168dfbac54f04b6068f199c35704
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3610f44c4cf884dad93f89fe4f19b8db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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");
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b5575e7bca1590549b79930bc2df9679
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 54114b7cd6ae355489325fd90132f551
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3d7adf78a0bd60848be2470847fbf8ea
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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:

View file

@ -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:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5335ae1a0cfbc2c41903fdf8d489fcc7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5e4f2af0145cfe44797338834d0b013a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9aa898c2e4f1cbc4ba5d122fc7b47317
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aeeab8c4e75a1e8419b0eed0716d850a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8f503e223cd1e624d9aae16f915047f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 90e762d3b06944889a5d95fc12a8db68
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9ee4e8d7932896548ab9dd0fa6efebc5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 72bf58aecc1877e48b77ac8a0408b0c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 19c21ce977fd53a46857b0dac2845685
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ebefc406fa7f065458cfccf8e07be2f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aa2a7a8ee6a464dd9ae636e73111beb6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2a8a39f0e276884449c4610fee697330
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c1fa730356b2f54e932bbcfe5507f93
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae33c30ef3b5e474ca5a46d925273adc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0a68d56945b68344ea2f451f0e9f14b4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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>(); }
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10db00a3c51d23d4ab8a68837ffa21fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,7 @@
using System;
using UnityEngine;
namespace UnityEditor.U2D.Animation
{
internal interface IBoneSelection : ITransformSelection<BoneCache> {}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2e79c8e51ce4486409b7daf3012a1f4d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: df44828de9ac64d44ba1a553e06eb335
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -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