From 9254e188011908ecb0a855221a476f8611a78d42 Mon Sep 17 00:00:00 2001 From: pharan Date: Thu, 24 Mar 2016 00:34:22 +0800 Subject: [PATCH] Spine.Unity and Spine.Unity.Editor namespace --- .../Asset Types/Editor/AtlasAssetInspector.cs | 4 +- .../Editor/SkeletonDataAssetInspector.cs | 5 +- .../AssetDatabaseAvailabilityDetector.cs | 2 +- .../Editor/BoneFollowerInspector.cs | 103 +- .../Assets/spine-unity/Editor/Menus.cs | 2 +- .../Editor/SkeletonAnimationInspector.cs | 2 +- .../Editor/SkeletonAnimatorInspector.cs | 2 +- .../spine-unity/Editor/SkeletonBaker.cs | 2542 +++++++++-------- .../Editor/SkeletonRendererInspector.cs | 4 +- .../Editor/SpineAttributeDrawers.cs | 632 ++-- .../Editor/SpineEditorUtilities.cs | 1842 ++++++------ .../Editor/SpineInspectorUtility.cs | 3 +- .../Editor/BoundingBoxFollowerInspector.cs | 86 +- .../WaitForSpineAnimationComplete.cs | 2 +- .../YieldInstructions/WaitForSpineEvent.cs | 2 +- 15 files changed, 2630 insertions(+), 2603 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs b/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs index 77acc3885..fb5672492 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs @@ -38,10 +38,10 @@ using UnityEditor; using UnityEngine; using Spine; -namespace Spine.Unity { +namespace Spine.Unity.Editor { [CustomEditor(typeof(AtlasAsset))] - public class AtlasAssetInspector : Editor { + public class AtlasAssetInspector : UnityEditor.Editor { private SerializedProperty atlasFile, materials; private AtlasAsset atlasAsset; private List baked; diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs index 486315e57..661f7336e 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs @@ -14,9 +14,9 @@ using UnityEditor.AnimatedValues; using UnityEngine; using Spine; -namespace Spine.Unity { +namespace Spine.Unity.Editor { [CustomEditor(typeof(SkeletonDataAsset))] - public class SkeletonDataAssetInspector : Editor { + public class SkeletonDataAssetInspector : UnityEditor.Editor { static bool showAnimationStateData = true; static bool showAnimationList = true; static bool showSlotList = false; @@ -50,6 +50,7 @@ namespace Spine.Unity { try { atlasAssets = serializedObject.FindProperty("atlasAssets"); + atlasAssets.isExpanded = true; skeletonJSON = serializedObject.FindProperty("skeletonJSON"); scale = serializedObject.FindProperty("scale"); fromAnimation = serializedObject.FindProperty("fromAnimation"); diff --git a/spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs b/spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs index 5b8c7a4ab..82d4207dd 100644 --- a/spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs +++ b/spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace Spine { +namespace Spine.Unity.Editor { public static class AssetDatabaseAvailabilityDetector { const string MARKER_RESOURCE_NAME = "SpineAssetDatabaseMarker"; private static bool _isMarkerLoaded; diff --git a/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs b/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs index 6f7a01bc3..68944d170 100644 --- a/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs @@ -33,60 +33,65 @@ using System; using UnityEditor; using UnityEngine; -[CustomEditor(typeof(BoneFollower))] -public class BoneFollowerInspector : Editor { - SerializedProperty boneName, skeletonRenderer, followZPosition, followBoneRotation; - BoneFollower component; - bool needsReset; - void OnEnable () { - skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); - boneName = serializedObject.FindProperty("boneName"); - followBoneRotation = serializedObject.FindProperty("followBoneRotation"); - followZPosition = serializedObject.FindProperty("followZPosition"); - component = (BoneFollower)target; - component.skeletonRenderer.Initialize(false); - } - - override public void OnInspectorGUI () { - if (needsReset) { - component.Reset(); - component.DoUpdate(); - needsReset = false; - SceneView.RepaintAll(); +namespace Spine.Unity.Editor { + + [CustomEditor(typeof(BoneFollower))] + public class BoneFollowerInspector : UnityEditor.Editor { + SerializedProperty boneName, skeletonRenderer, followZPosition, followBoneRotation; + BoneFollower component; + bool needsReset; + + void OnEnable () { + skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); + boneName = serializedObject.FindProperty("boneName"); + followBoneRotation = serializedObject.FindProperty("followBoneRotation"); + followZPosition = serializedObject.FindProperty("followZPosition"); + component = (BoneFollower)target; + component.skeletonRenderer.Initialize(false); } - serializedObject.Update(); - // FindRenderer() - if (skeletonRenderer.objectReferenceValue == null) { - SkeletonRenderer parentRenderer = SkeletonUtility.GetInParent(component.transform); + override public void OnInspectorGUI () { + if (needsReset) { + component.Reset(); + component.DoUpdate(); + needsReset = false; + SceneView.RepaintAll(); + } + serializedObject.Update(); - if (parentRenderer != null) { - skeletonRenderer.objectReferenceValue = (UnityEngine.Object)parentRenderer; + // FindRenderer() + if (skeletonRenderer.objectReferenceValue == null) { + SkeletonRenderer parentRenderer = SkeletonUtility.GetInParent(component.transform); + + if (parentRenderer != null) { + skeletonRenderer.objectReferenceValue = (UnityEngine.Object)parentRenderer; + } + } + + EditorGUILayout.PropertyField(skeletonRenderer); + + if (component.valid) { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(boneName); + if (EditorGUI.EndChangeCheck()) { + serializedObject.ApplyModifiedProperties(); + needsReset = true; + serializedObject.Update(); + } + + EditorGUILayout.PropertyField(followBoneRotation); + EditorGUILayout.PropertyField(followZPosition); + } else { + GUILayout.Label("INVALID"); + } + + if (serializedObject.ApplyModifiedProperties() || + (UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed") + ) { + component.Reset(); } } - - EditorGUILayout.PropertyField(skeletonRenderer); - - if (component.valid) { - EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(boneName); - if (EditorGUI.EndChangeCheck()) { - serializedObject.ApplyModifiedProperties(); - needsReset = true; - serializedObject.Update(); - } - - EditorGUILayout.PropertyField(followBoneRotation); - EditorGUILayout.PropertyField(followZPosition); - } else { - GUILayout.Label("INVALID"); - } - - if (serializedObject.ApplyModifiedProperties() || - (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") - ) { - component.Reset(); - } } + } diff --git a/spine-unity/Assets/spine-unity/Editor/Menus.cs b/spine-unity/Assets/spine-unity/Editor/Menus.cs index 95685419e..f61aad4fa 100644 --- a/spine-unity/Assets/spine-unity/Editor/Menus.cs +++ b/spine-unity/Assets/spine-unity/Editor/Menus.cs @@ -33,7 +33,7 @@ using System.IO; using UnityEditor; using UnityEngine; -namespace Spine.Unity { +namespace Spine.Unity.Editor { public static class Menus { [MenuItem("Assets/Create/Spine Atlas")] static public void CreateAtlas () { diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs index 33c59c65c..73f797a0a 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs @@ -33,7 +33,7 @@ using UnityEditor; using UnityEngine; using Spine; -namespace Spine.Unity { +namespace Spine.Unity.Editor { [CustomEditor(typeof(SkeletonAnimation))] public class SkeletonAnimationInspector : SkeletonRendererInspector { diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs index ce6118f67..527e26878 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs @@ -6,7 +6,7 @@ using System; using UnityEditor; using UnityEngine; -namespace Spine.Unity { +namespace Spine.Unity.Editor { [CustomEditor(typeof(SkeletonAnimator))] public class SkeletonAnimatorInspector : SkeletonRendererInspector { protected SerializedProperty layerMixModes; diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs index 033235775..8759ac0f4 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs @@ -43,42 +43,43 @@ using Spine; /// Color Keys (Maybe one day when Unity supports full FBX standard and provides access with code) /// InheritScale (Never. Unity and Spine do scaling very differently) /// Draw Order Keyframes - /// -public static class SkeletonBaker { +/// +namespace Spine.Unity.Editor { + public static class SkeletonBaker { - #region SkeletonAnimator's Mecanim Clips -#if SPINE_SKELETON_ANIMATOR - public static void GenerateMecanimAnimationClips (SkeletonDataAsset skeletonDataAsset) { - //skeletonDataAsset.Clear(); - var data = skeletonDataAsset.GetSkeletonData(true); - if (data == null) { - Debug.LogError("SkeletonData failed!", skeletonDataAsset); - return; - } + #region SkeletonAnimator's Mecanim Clips + #if SPINE_SKELETON_ANIMATOR + public static void GenerateMecanimAnimationClips (SkeletonDataAsset skeletonDataAsset) { + //skeletonDataAsset.Clear(); + var data = skeletonDataAsset.GetSkeletonData(true); + if (data == null) { + Debug.LogError("SkeletonData failed!", skeletonDataAsset); + return; + } - string dataPath = AssetDatabase.GetAssetPath(skeletonDataAsset); - string controllerPath = dataPath.Replace("_SkeletonData", "_Controller").Replace(".asset", ".controller"); + string dataPath = AssetDatabase.GetAssetPath(skeletonDataAsset); + string controllerPath = dataPath.Replace("_SkeletonData", "_Controller").Replace(".asset", ".controller"); -#if UNITY_5 - UnityEditor.Animations.AnimatorController controller; + #if UNITY_5 + UnityEditor.Animations.AnimatorController controller; - if (skeletonDataAsset.controller != null) { - controller = (UnityEditor.Animations.AnimatorController)skeletonDataAsset.controller; - controllerPath = AssetDatabase.GetAssetPath(controller); - } else { - if (File.Exists(controllerPath)) { - if (EditorUtility.DisplayDialog("Controller Overwrite Warning", "Unknown Controller already exists at: " + controllerPath, "Update", "Overwrite")) { - controller = (UnityEditor.Animations.AnimatorController)AssetDatabase.LoadAssetAtPath(controllerPath, typeof(RuntimeAnimatorController)); + if (skeletonDataAsset.controller != null) { + controller = (UnityEditor.Animations.AnimatorController)skeletonDataAsset.controller; + controllerPath = AssetDatabase.GetAssetPath(controller); + } else { + if (File.Exists(controllerPath)) { + if (EditorUtility.DisplayDialog("Controller Overwrite Warning", "Unknown Controller already exists at: " + controllerPath, "Update", "Overwrite")) { + controller = (UnityEditor.Animations.AnimatorController)AssetDatabase.LoadAssetAtPath(controllerPath, typeof(RuntimeAnimatorController)); + } else { + controller = (UnityEditor.Animations.AnimatorController)UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); + } } else { controller = (UnityEditor.Animations.AnimatorController)UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); } - } else { - controller = (UnityEditor.Animations.AnimatorController)UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); - } - } -#else + } + #else UnityEditorInternal.AnimatorController controller; if (skeletonDataAsset.controller != null) { @@ -95,620 +96,620 @@ public static class SkeletonBaker { controller = (UnityEditorInternal.AnimatorController)UnityEditorInternal.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); } } -#endif + #endif - skeletonDataAsset.controller = controller; - EditorUtility.SetDirty(skeletonDataAsset); + skeletonDataAsset.controller = controller; + EditorUtility.SetDirty(skeletonDataAsset); - UnityEngine.Object[] objs = AssetDatabase.LoadAllAssetsAtPath(controllerPath); + UnityEngine.Object[] objs = AssetDatabase.LoadAllAssetsAtPath(controllerPath); - var unityAnimationClipTable = new Dictionary(); - var spineAnimationTable = new Dictionary(); + var unityAnimationClipTable = new Dictionary(); + var spineAnimationTable = new Dictionary(); - foreach (var o in objs) { - //Debug.LogFormat("({0}){1} : {3} + {2} + {4}", o.GetType(), o.name, o.hideFlags, o.GetInstanceID(), o.GetHashCode()); - // There is a bug in Unity 5.3.3 (and likely before) that creates - // a duplicate AnimationClip when you duplicate a Mecanim Animator State. - // These duplicates seem to be identifiable by their HideFlags, so we'll exclude them. - if (o is AnimationClip) { - var clip = o as AnimationClip; - if (!clip.HasFlag(HideFlags.HideInHierarchy)) { - if (unityAnimationClipTable.ContainsKey(clip.name)) { - Debug.LogWarningFormat("Duplicate AnimationClips were found named {0}", clip.name); - } - unityAnimationClipTable.Add(clip.name, clip); - } - } - } - - foreach (var anim in data.Animations) { - string name = anim.Name; - spineAnimationTable.Add(name, anim); - - if (unityAnimationClipTable.ContainsKey(name) == false) { - //generate new dummy clip - AnimationClip newClip = new AnimationClip(); - newClip.name = name; -#if !(UNITY_5) - AnimationUtility.SetAnimationType(newClip, ModelImporterAnimationType.Generic); -#endif - AssetDatabase.AddObjectToAsset(newClip, controller); - unityAnimationClipTable.Add(name, newClip); - } - - AnimationClip clip = unityAnimationClipTable[name]; - - clip.SetCurve("", typeof(GameObject), "dummy", AnimationCurve.Linear(0, 0, anim.Duration, 0)); - var settings = AnimationUtility.GetAnimationClipSettings(clip); - settings.stopTime = anim.Duration; - - SetAnimationSettings(clip, settings); - - AnimationUtility.SetAnimationEvents(clip, new AnimationEvent[0]); - - foreach (Timeline t in anim.Timelines) { - if (t is EventTimeline) { - ParseEventTimeline((EventTimeline)t, clip, SendMessageOptions.DontRequireReceiver); - } - } - - EditorUtility.SetDirty(clip); - - unityAnimationClipTable.Remove(name); - } - - //clear no longer used animations - foreach (var clip in unityAnimationClipTable.Values) { - AnimationClip.DestroyImmediate(clip, true); - } - - AssetDatabase.Refresh(); - AssetDatabase.SaveAssets(); - } - - static bool HasFlag (this UnityEngine.Object o, HideFlags flagToCheck) { - return (o.hideFlags & flagToCheck) == flagToCheck; - } -#endif - #endregion - - #region Baking - /// - /// Interval between key sampling for Bezier curves, IK controlled bones, and Inherit Rotation effected bones. - /// - const float bakeIncrement = 1 / 60f; - - public static void BakeToPrefab (SkeletonDataAsset skeletonDataAsset, ExposedList skins, string outputPath = "", bool bakeAnimations = true, bool bakeIK = true, SendMessageOptions eventOptions = SendMessageOptions.DontRequireReceiver) { - if (skeletonDataAsset == null || skeletonDataAsset.GetSkeletonData(true) == null) { - Debug.LogError("Could not export Spine Skeleton because SkeletonDataAsset is null or invalid!"); - return; - } - - if (outputPath == "") { - outputPath = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(skeletonDataAsset)) + "/Baked"; - System.IO.Directory.CreateDirectory(outputPath); - } - - var skeletonData = skeletonDataAsset.GetSkeletonData(true); - bool hasAnimations = bakeAnimations && skeletonData.Animations.Count > 0; -#if UNITY_5 - UnityEditor.Animations.AnimatorController controller = null; -#else - UnityEditorInternal.AnimatorController controller = null; -#endif - if (hasAnimations) { - string controllerPath = outputPath + "/" + skeletonDataAsset.skeletonJSON.name + " Controller.controller"; - bool newAnimContainer = false; - - var runtimeController = AssetDatabase.LoadAssetAtPath(controllerPath, typeof(RuntimeAnimatorController)); - -#if UNITY_5 - if (runtimeController != null) { - controller = (UnityEditor.Animations.AnimatorController)runtimeController; - } else { - controller = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); - newAnimContainer = true; - } -#else - if (runtimeController != null) { - controller = (UnityEditorInternal.AnimatorController)runtimeController; - } else { - controller = UnityEditorInternal.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); - newAnimContainer = true; - } -#endif - - var existingClipTable = new Dictionary(); - var unusedClipNames = new List(); - Object[] animObjs = AssetDatabase.LoadAllAssetsAtPath(controllerPath); - - foreach (Object o in animObjs) { + foreach (var o in objs) { + //Debug.LogFormat("({0}){1} : {3} + {2} + {4}", o.GetType(), o.name, o.hideFlags, o.GetInstanceID(), o.GetHashCode()); + // There is a bug in Unity 5.3.3 (and likely before) that creates + // a duplicate AnimationClip when you duplicate a Mecanim Animator State. + // These duplicates seem to be identifiable by their HideFlags, so we'll exclude them. if (o is AnimationClip) { - var clip = (AnimationClip)o; - existingClipTable.Add(clip.name, clip); - unusedClipNames.Add(clip.name); + var clip = o as AnimationClip; + if (!clip.HasFlag(HideFlags.HideInHierarchy)) { + if (unityAnimationClipTable.ContainsKey(clip.name)) { + Debug.LogWarningFormat("Duplicate AnimationClips were found named {0}", clip.name); + } + unityAnimationClipTable.Add(clip.name, clip); + } } } - Dictionary> slotLookup = new Dictionary>(); + foreach (var anim in data.Animations) { + string name = anim.Name; + spineAnimationTable.Add(name, anim); - int skinCount = skins.Count; + if (unityAnimationClipTable.ContainsKey(name) == false) { + //generate new dummy clip + AnimationClip newClip = new AnimationClip(); + newClip.name = name; + #if !(UNITY_5) + AnimationUtility.SetAnimationType(newClip, ModelImporterAnimationType.Generic); + #endif + AssetDatabase.AddObjectToAsset(newClip, controller); + unityAnimationClipTable.Add(name, newClip); + } - for (int s = 0; s < skeletonData.Slots.Count; s++) { - List attachmentNames = new List(); - for (int i = 0; i < skinCount; i++) { - var skin = skins.Items[i]; - List temp = new List(); - skin.FindNamesForSlot(s, temp); - foreach (string str in temp) { - if (!attachmentNames.Contains(str)) - attachmentNames.Add(str); + AnimationClip clip = unityAnimationClipTable[name]; + + clip.SetCurve("", typeof(GameObject), "dummy", AnimationCurve.Linear(0, 0, anim.Duration, 0)); + var settings = AnimationUtility.GetAnimationClipSettings(clip); + settings.stopTime = anim.Duration; + + SetAnimationSettings(clip, settings); + + AnimationUtility.SetAnimationEvents(clip, new AnimationEvent[0]); + + foreach (Timeline t in anim.Timelines) { + if (t is EventTimeline) { + ParseEventTimeline((EventTimeline)t, clip, SendMessageOptions.DontRequireReceiver); } } - slotLookup.Add(s, attachmentNames); + + EditorUtility.SetDirty(clip); + + unityAnimationClipTable.Remove(name); } - foreach (var anim in skeletonData.Animations) { - - AnimationClip clip = null; - if (existingClipTable.ContainsKey(anim.Name)) { - clip = existingClipTable[anim.Name]; - } - - clip = ExtractAnimation(anim.Name, skeletonData, slotLookup, bakeIK, eventOptions, clip); - - if (unusedClipNames.Contains(clip.name)) { - unusedClipNames.Remove(clip.name); - } else { - AssetDatabase.AddObjectToAsset(clip, controller); -#if UNITY_5 - controller.AddMotion(clip); -#else - UnityEditorInternal.AnimatorController.AddAnimationClipToController(controller, clip); -#endif - - } + //clear no longer used animations + foreach (var clip in unityAnimationClipTable.Values) { + AnimationClip.DestroyImmediate(clip, true); } - if (newAnimContainer) { - EditorUtility.SetDirty(controller); - AssetDatabase.SaveAssets(); - AssetDatabase.ImportAsset(controllerPath, ImportAssetOptions.ForceUpdate); - AssetDatabase.Refresh(); - } else { - - foreach (string str in unusedClipNames) { - AnimationClip.DestroyImmediate(existingClipTable[str], true); - } - - EditorUtility.SetDirty(controller); - AssetDatabase.SaveAssets(); - AssetDatabase.ImportAsset(controllerPath, ImportAssetOptions.ForceUpdate); - AssetDatabase.Refresh(); - } - } - - foreach (var skin in skins) { - bool newPrefab = false; - - string prefabPath = outputPath + "/" + skeletonDataAsset.skeletonJSON.name + " (" + skin.Name + ").prefab"; - - - Object prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)); - if (prefab == null) { - prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); - newPrefab = true; - } - - Dictionary meshTable = new Dictionary(); - List unusedMeshNames = new List(); - Object[] assets = AssetDatabase.LoadAllAssetsAtPath(prefabPath); - foreach (var obj in assets) { - if (obj is Mesh) { - meshTable.Add(obj.name, (Mesh)obj); - unusedMeshNames.Add(obj.name); - } - } - - GameObject prefabRoot = new GameObject("root"); - - Dictionary slotTable = new Dictionary(); - Dictionary boneTable = new Dictionary(); - List boneList = new List(); - - //create bones - for (int i = 0; i < skeletonData.Bones.Count; i++) { - var boneData = skeletonData.Bones.Items[i]; - Transform boneTransform = new GameObject(boneData.Name).transform; - boneTransform.parent = prefabRoot.transform; - boneTable.Add(boneTransform.name, boneTransform); - boneList.Add(boneTransform); - } - - for (int i = 0; i < skeletonData.Bones.Count; i++) { - - var boneData = skeletonData.Bones.Items[i]; - Transform boneTransform = boneTable[boneData.Name]; - Transform parentTransform = null; - if (i > 0) - parentTransform = boneTable[boneData.Parent.Name]; - else - parentTransform = boneTransform.parent; - - boneTransform.parent = parentTransform; - boneTransform.localPosition = new Vector3(boneData.X, boneData.Y, 0); - if (boneData.InheritRotation) - boneTransform.localRotation = Quaternion.Euler(0, 0, boneData.Rotation); - else - boneTransform.rotation = Quaternion.Euler(0, 0, boneData.Rotation); - - if (boneData.InheritScale) - boneTransform.localScale = new Vector3(boneData.ScaleX, boneData.ScaleY, 1); - } - - //create slots and attachments - for (int i = 0; i < skeletonData.Slots.Count; i++) { - var slotData = skeletonData.Slots.Items[i]; - Transform slotTransform = new GameObject(slotData.Name).transform; - slotTransform.parent = prefabRoot.transform; - slotTable.Add(slotData.Name, slotTransform); - - List attachments = new List(); - List attachmentNames = new List(); - - skin.FindAttachmentsForSlot(i, attachments); - skin.FindNamesForSlot(i, attachmentNames); - - if (skin != skeletonData.DefaultSkin) { - skeletonData.DefaultSkin.FindAttachmentsForSlot(i, attachments); - skeletonData.DefaultSkin.FindNamesForSlot(i, attachmentNames); - } - - for (int a = 0; a < attachments.Count; a++) { - var attachment = attachments[a]; - var attachmentName = attachmentNames[a]; - var attachmentMeshName = "[" + slotData.Name + "] " + attachmentName; - Vector3 offset = Vector3.zero; - float rotation = 0; - Mesh mesh = null; - Material material = null; - - if (meshTable.ContainsKey(attachmentMeshName)) - mesh = meshTable[attachmentMeshName]; - if (attachment is RegionAttachment) { - var regionAttachment = (RegionAttachment)attachment; - offset.x = regionAttachment.X; - offset.y = regionAttachment.Y; - rotation = regionAttachment.Rotation; - mesh = ExtractRegionAttachment(attachmentMeshName, regionAttachment, mesh); - material = ExtractMaterial((RegionAttachment)attachment); - unusedMeshNames.Remove(attachmentMeshName); - if (newPrefab || meshTable.ContainsKey(attachmentMeshName) == false) - AssetDatabase.AddObjectToAsset(mesh, prefab); - } else if (attachment is MeshAttachment) { - var meshAttachment = (MeshAttachment)attachment; - offset.x = 0; - offset.y = 0; - rotation = 0; - mesh = ExtractMeshAttachment(attachmentMeshName, meshAttachment, mesh); - material = ExtractMaterial((MeshAttachment)attachment); - unusedMeshNames.Remove(attachmentMeshName); - if (newPrefab || meshTable.ContainsKey(attachmentMeshName) == false) - AssetDatabase.AddObjectToAsset(mesh, prefab); - } else if (attachment is WeightedMeshAttachment) { - var meshAttachment = (WeightedMeshAttachment)attachment; - offset.x = 0; - offset.y = 0; - rotation = 0; - mesh = ExtractSkinnedMeshAttachment(attachmentMeshName, meshAttachment, i, skeletonData, boneList, mesh); - material = ExtractMaterial((WeightedMeshAttachment)attachment); - unusedMeshNames.Remove(attachmentMeshName); - if (newPrefab || meshTable.ContainsKey(attachmentMeshName) == false) - AssetDatabase.AddObjectToAsset(mesh, prefab); - } else - continue; //disregard unsupported types for now - - Transform attachmentTransform = new GameObject(attachmentName).transform; - - attachmentTransform.parent = slotTransform; - attachmentTransform.localPosition = offset; - attachmentTransform.localRotation = Quaternion.Euler(0, 0, rotation); - - if (attachment is WeightedMeshAttachment) { - attachmentTransform.position = Vector3.zero; - attachmentTransform.rotation = Quaternion.identity; - var skinnedMeshRenderer = attachmentTransform.gameObject.AddComponent(); - skinnedMeshRenderer.rootBone = boneList[0]; - skinnedMeshRenderer.bones = boneList.ToArray(); - skinnedMeshRenderer.sharedMesh = mesh; - - } else { - attachmentTransform.gameObject.AddComponent().sharedMesh = mesh; - attachmentTransform.gameObject.AddComponent(); - } - - attachmentTransform.GetComponent().sharedMaterial = material; - attachmentTransform.GetComponent().sortingOrder = i; - - if (attachmentName != slotData.AttachmentName) - attachmentTransform.gameObject.SetActive(false); - } - - } - - foreach (var slotData in skeletonData.Slots) { - Transform slotTransform = slotTable[slotData.Name]; - slotTransform.parent = boneTable[slotData.BoneData.Name]; - slotTransform.localPosition = Vector3.zero; - slotTransform.localRotation = Quaternion.identity; - slotTransform.localScale = Vector3.one; - } - - if (hasAnimations) { - var animator = prefabRoot.AddComponent(); - animator.applyRootMotion = false; - animator.runtimeAnimatorController = (RuntimeAnimatorController)controller; - EditorGUIUtility.PingObject(controller); - } - - - if (newPrefab) { - PrefabUtility.ReplacePrefab(prefabRoot, prefab, ReplacePrefabOptions.ConnectToPrefab); - } else { - - foreach (string str in unusedMeshNames) { - Mesh.DestroyImmediate(meshTable[str], true); - } - - PrefabUtility.ReplacePrefab(prefabRoot, prefab, ReplacePrefabOptions.ReplaceNameBased); - } - - EditorGUIUtility.PingObject(prefab); - AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); - - GameObject.DestroyImmediate(prefabRoot); } - } + static bool HasFlag (this UnityEngine.Object o, HideFlags flagToCheck) { + return (o.hideFlags & flagToCheck) == flagToCheck; + } + #endif + #endregion - static Bone extractionBone; - static Slot extractionSlot; + #region Baking + /// + /// Interval between key sampling for Bezier curves, IK controlled bones, and Inherit Rotation effected bones. + /// + const float bakeIncrement = 1 / 60f; + + public static void BakeToPrefab (SkeletonDataAsset skeletonDataAsset, ExposedList skins, string outputPath = "", bool bakeAnimations = true, bool bakeIK = true, SendMessageOptions eventOptions = SendMessageOptions.DontRequireReceiver) { + if (skeletonDataAsset == null || skeletonDataAsset.GetSkeletonData(true) == null) { + Debug.LogError("Could not export Spine Skeleton because SkeletonDataAsset is null or invalid!"); + return; + } + + if (outputPath == "") { + outputPath = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(skeletonDataAsset)) + "/Baked"; + System.IO.Directory.CreateDirectory(outputPath); + } + + var skeletonData = skeletonDataAsset.GetSkeletonData(true); + bool hasAnimations = bakeAnimations && skeletonData.Animations.Count > 0; + #if UNITY_5 + UnityEditor.Animations.AnimatorController controller = null; + #else + UnityEditorInternal.AnimatorController controller = null; + #endif + if (hasAnimations) { + string controllerPath = outputPath + "/" + skeletonDataAsset.skeletonJSON.name + " Controller.controller"; + bool newAnimContainer = false; + + var runtimeController = AssetDatabase.LoadAssetAtPath(controllerPath, typeof(RuntimeAnimatorController)); + + #if UNITY_5 + if (runtimeController != null) { + controller = (UnityEditor.Animations.AnimatorController)runtimeController; + } else { + controller = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); + newAnimContainer = true; + } + #else + if (runtimeController != null) { + controller = (UnityEditorInternal.AnimatorController)runtimeController; + } else { + controller = UnityEditorInternal.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); + newAnimContainer = true; + } + #endif + + var existingClipTable = new Dictionary(); + var unusedClipNames = new List(); + Object[] animObjs = AssetDatabase.LoadAllAssetsAtPath(controllerPath); + + foreach (Object o in animObjs) { + if (o is AnimationClip) { + var clip = (AnimationClip)o; + existingClipTable.Add(clip.name, clip); + unusedClipNames.Add(clip.name); + } + } + + Dictionary> slotLookup = new Dictionary>(); + + int skinCount = skins.Count; + + for (int s = 0; s < skeletonData.Slots.Count; s++) { + List attachmentNames = new List(); + for (int i = 0; i < skinCount; i++) { + var skin = skins.Items[i]; + List temp = new List(); + skin.FindNamesForSlot(s, temp); + foreach (string str in temp) { + if (!attachmentNames.Contains(str)) + attachmentNames.Add(str); + } + } + slotLookup.Add(s, attachmentNames); + } + + foreach (var anim in skeletonData.Animations) { + + AnimationClip clip = null; + if (existingClipTable.ContainsKey(anim.Name)) { + clip = existingClipTable[anim.Name]; + } + + clip = ExtractAnimation(anim.Name, skeletonData, slotLookup, bakeIK, eventOptions, clip); + + if (unusedClipNames.Contains(clip.name)) { + unusedClipNames.Remove(clip.name); + } else { + AssetDatabase.AddObjectToAsset(clip, controller); + #if UNITY_5 + controller.AddMotion(clip); + #else + UnityEditorInternal.AnimatorController.AddAnimationClipToController(controller, clip); + #endif + + } + } + + if (newAnimContainer) { + EditorUtility.SetDirty(controller); + AssetDatabase.SaveAssets(); + AssetDatabase.ImportAsset(controllerPath, ImportAssetOptions.ForceUpdate); + AssetDatabase.Refresh(); + } else { + + foreach (string str in unusedClipNames) { + AnimationClip.DestroyImmediate(existingClipTable[str], true); + } + + EditorUtility.SetDirty(controller); + AssetDatabase.SaveAssets(); + AssetDatabase.ImportAsset(controllerPath, ImportAssetOptions.ForceUpdate); + AssetDatabase.Refresh(); + } + } + + foreach (var skin in skins) { + bool newPrefab = false; + + string prefabPath = outputPath + "/" + skeletonDataAsset.skeletonJSON.name + " (" + skin.Name + ").prefab"; + + + Object prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)); + if (prefab == null) { + prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); + newPrefab = true; + } + + Dictionary meshTable = new Dictionary(); + List unusedMeshNames = new List(); + Object[] assets = AssetDatabase.LoadAllAssetsAtPath(prefabPath); + foreach (var obj in assets) { + if (obj is Mesh) { + meshTable.Add(obj.name, (Mesh)obj); + unusedMeshNames.Add(obj.name); + } + } + + GameObject prefabRoot = new GameObject("root"); + + Dictionary slotTable = new Dictionary(); + Dictionary boneTable = new Dictionary(); + List boneList = new List(); + + //create bones + for (int i = 0; i < skeletonData.Bones.Count; i++) { + var boneData = skeletonData.Bones.Items[i]; + Transform boneTransform = new GameObject(boneData.Name).transform; + boneTransform.parent = prefabRoot.transform; + boneTable.Add(boneTransform.name, boneTransform); + boneList.Add(boneTransform); + } + + for (int i = 0; i < skeletonData.Bones.Count; i++) { + + var boneData = skeletonData.Bones.Items[i]; + Transform boneTransform = boneTable[boneData.Name]; + Transform parentTransform = null; + if (i > 0) + parentTransform = boneTable[boneData.Parent.Name]; + else + parentTransform = boneTransform.parent; + + boneTransform.parent = parentTransform; + boneTransform.localPosition = new Vector3(boneData.X, boneData.Y, 0); + if (boneData.InheritRotation) + boneTransform.localRotation = Quaternion.Euler(0, 0, boneData.Rotation); + else + boneTransform.rotation = Quaternion.Euler(0, 0, boneData.Rotation); + + if (boneData.InheritScale) + boneTransform.localScale = new Vector3(boneData.ScaleX, boneData.ScaleY, 1); + } + + //create slots and attachments + for (int i = 0; i < skeletonData.Slots.Count; i++) { + var slotData = skeletonData.Slots.Items[i]; + Transform slotTransform = new GameObject(slotData.Name).transform; + slotTransform.parent = prefabRoot.transform; + slotTable.Add(slotData.Name, slotTransform); + + List attachments = new List(); + List attachmentNames = new List(); + + skin.FindAttachmentsForSlot(i, attachments); + skin.FindNamesForSlot(i, attachmentNames); + + if (skin != skeletonData.DefaultSkin) { + skeletonData.DefaultSkin.FindAttachmentsForSlot(i, attachments); + skeletonData.DefaultSkin.FindNamesForSlot(i, attachmentNames); + } + + for (int a = 0; a < attachments.Count; a++) { + var attachment = attachments[a]; + var attachmentName = attachmentNames[a]; + var attachmentMeshName = "[" + slotData.Name + "] " + attachmentName; + Vector3 offset = Vector3.zero; + float rotation = 0; + Mesh mesh = null; + Material material = null; + + if (meshTable.ContainsKey(attachmentMeshName)) + mesh = meshTable[attachmentMeshName]; + if (attachment is RegionAttachment) { + var regionAttachment = (RegionAttachment)attachment; + offset.x = regionAttachment.X; + offset.y = regionAttachment.Y; + rotation = regionAttachment.Rotation; + mesh = ExtractRegionAttachment(attachmentMeshName, regionAttachment, mesh); + material = ExtractMaterial((RegionAttachment)attachment); + unusedMeshNames.Remove(attachmentMeshName); + if (newPrefab || meshTable.ContainsKey(attachmentMeshName) == false) + AssetDatabase.AddObjectToAsset(mesh, prefab); + } else if (attachment is MeshAttachment) { + var meshAttachment = (MeshAttachment)attachment; + offset.x = 0; + offset.y = 0; + rotation = 0; + mesh = ExtractMeshAttachment(attachmentMeshName, meshAttachment, mesh); + material = ExtractMaterial((MeshAttachment)attachment); + unusedMeshNames.Remove(attachmentMeshName); + if (newPrefab || meshTable.ContainsKey(attachmentMeshName) == false) + AssetDatabase.AddObjectToAsset(mesh, prefab); + } else if (attachment is WeightedMeshAttachment) { + var meshAttachment = (WeightedMeshAttachment)attachment; + offset.x = 0; + offset.y = 0; + rotation = 0; + mesh = ExtractSkinnedMeshAttachment(attachmentMeshName, meshAttachment, i, skeletonData, boneList, mesh); + material = ExtractMaterial((WeightedMeshAttachment)attachment); + unusedMeshNames.Remove(attachmentMeshName); + if (newPrefab || meshTable.ContainsKey(attachmentMeshName) == false) + AssetDatabase.AddObjectToAsset(mesh, prefab); + } else + continue; //disregard unsupported types for now + + Transform attachmentTransform = new GameObject(attachmentName).transform; + + attachmentTransform.parent = slotTransform; + attachmentTransform.localPosition = offset; + attachmentTransform.localRotation = Quaternion.Euler(0, 0, rotation); + + if (attachment is WeightedMeshAttachment) { + attachmentTransform.position = Vector3.zero; + attachmentTransform.rotation = Quaternion.identity; + var skinnedMeshRenderer = attachmentTransform.gameObject.AddComponent(); + skinnedMeshRenderer.rootBone = boneList[0]; + skinnedMeshRenderer.bones = boneList.ToArray(); + skinnedMeshRenderer.sharedMesh = mesh; + + } else { + attachmentTransform.gameObject.AddComponent().sharedMesh = mesh; + attachmentTransform.gameObject.AddComponent(); + } + + attachmentTransform.GetComponent().sharedMaterial = material; + attachmentTransform.GetComponent().sortingOrder = i; + + if (attachmentName != slotData.AttachmentName) + attachmentTransform.gameObject.SetActive(false); + } + + } + + foreach (var slotData in skeletonData.Slots) { + Transform slotTransform = slotTable[slotData.Name]; + slotTransform.parent = boneTable[slotData.BoneData.Name]; + slotTransform.localPosition = Vector3.zero; + slotTransform.localRotation = Quaternion.identity; + slotTransform.localScale = Vector3.one; + } + + if (hasAnimations) { + var animator = prefabRoot.AddComponent(); + animator.applyRootMotion = false; + animator.runtimeAnimatorController = (RuntimeAnimatorController)controller; + EditorGUIUtility.PingObject(controller); + } + + + if (newPrefab) { + PrefabUtility.ReplacePrefab(prefabRoot, prefab, ReplacePrefabOptions.ConnectToPrefab); + } else { + + foreach (string str in unusedMeshNames) { + Mesh.DestroyImmediate(meshTable[str], true); + } + + PrefabUtility.ReplacePrefab(prefabRoot, prefab, ReplacePrefabOptions.ReplaceNameBased); + } + + EditorGUIUtility.PingObject(prefab); + + AssetDatabase.Refresh(); + AssetDatabase.SaveAssets(); + + GameObject.DestroyImmediate(prefabRoot); + } + + } + + static Bone extractionBone; + static Slot extractionSlot; + + static Bone GetExtractionBone () { + if (extractionBone != null) + return extractionBone; + + SkeletonData skelData = new SkeletonData(); + BoneData data = new BoneData("temp", null); + data.ScaleX = 1; + data.ScaleY = 1; + data.Length = 100; + + skelData.Bones.Add(data); + + Skeleton skeleton = new Skeleton(skelData); + + Bone bone = new Bone(data, skeleton, null); + bone.UpdateWorldTransform(); + + extractionBone = bone; - static Bone GetExtractionBone () { - if (extractionBone != null) return extractionBone; + } - SkeletonData skelData = new SkeletonData(); - BoneData data = new BoneData("temp", null); - data.ScaleX = 1; - data.ScaleY = 1; - data.Length = 100; + static Slot GetExtractionSlot () { + if (extractionSlot != null) + return extractionSlot; - skelData.Bones.Add(data); + Bone bone = GetExtractionBone(); - Skeleton skeleton = new Skeleton(skelData); - - Bone bone = new Bone(data, skeleton, null); - bone.UpdateWorldTransform(); - - extractionBone = bone; - - return extractionBone; - } - - static Slot GetExtractionSlot () { - if (extractionSlot != null) + SlotData data = new SlotData("temp", bone.Data); + Slot slot = new Slot(data, bone); + extractionSlot = slot; return extractionSlot; - - Bone bone = GetExtractionBone(); - - SlotData data = new SlotData("temp", bone.Data); - Slot slot = new Slot(data, bone); - extractionSlot = slot; - return extractionSlot; - } - - static Material ExtractMaterial (Attachment attachment) { - if (attachment == null || attachment is BoundingBoxAttachment) - return null; - - if (attachment is RegionAttachment) { - var att = (RegionAttachment)attachment; - return (Material)((AtlasRegion)att.RendererObject).page.rendererObject; - } else if (attachment is MeshAttachment) { - var att = (MeshAttachment)attachment; - return (Material)((AtlasRegion)att.RendererObject).page.rendererObject; - } else if (attachment is WeightedMeshAttachment) { - var att = (WeightedMeshAttachment)attachment; - return (Material)((AtlasRegion)att.RendererObject).page.rendererObject; - } else { - return null; - } - } - - static Mesh ExtractRegionAttachment (string name, RegionAttachment attachment, Mesh mesh = null) { - var bone = GetExtractionBone(); - - bone.X = -attachment.X; - bone.Y = -attachment.Y; - bone.UpdateWorldTransform(); - - Vector2[] uvs = ExtractUV(attachment.UVs); - float[] floatVerts = new float[8]; - attachment.ComputeWorldVertices(bone, floatVerts); - Vector3[] verts = ExtractVerts(floatVerts); - - //unrotate verts now that they're centered - for (int i = 0; i < verts.Length; i++) { - verts[i] = Quaternion.Euler(0, 0, -attachment.Rotation) * verts[i]; } - int[] triangles = new int[6] { 1, 3, 0, 2, 3, 1 }; - Color color = new Color(attachment.R, attachment.G, attachment.B, attachment.A); + static Material ExtractMaterial (Attachment attachment) { + if (attachment == null || attachment is BoundingBoxAttachment) + return null; - if (mesh == null) - mesh = new Mesh(); - - mesh.triangles = new int[0]; - - mesh.vertices = verts; - mesh.uv = uvs; - mesh.triangles = triangles; - mesh.colors = new Color[] { color, color, color, color }; - mesh.RecalculateBounds(); - mesh.RecalculateNormals(); - mesh.name = name; - - return mesh; - } - - static Mesh ExtractMeshAttachment (string name, MeshAttachment attachment, Mesh mesh = null) { - var slot = GetExtractionSlot(); - - slot.Bone.X = 0; - slot.Bone.Y = 0; - slot.Bone.UpdateWorldTransform(); - - Vector2[] uvs = ExtractUV(attachment.UVs); - float[] floatVerts = new float[attachment.Vertices.Length]; - attachment.ComputeWorldVertices(slot, floatVerts); - Vector3[] verts = ExtractVerts(floatVerts); - - int[] triangles = attachment.Triangles; - Color color = new Color(attachment.R, attachment.G, attachment.B, attachment.A); - - if (mesh == null) - mesh = new Mesh(); - - mesh.triangles = new int[0]; - - mesh.vertices = verts; - mesh.uv = uvs; - mesh.triangles = triangles; - Color[] colors = new Color[verts.Length]; - for (int i = 0; i < verts.Length; i++) - colors[i] = color; - - mesh.colors = colors; - mesh.RecalculateBounds(); - mesh.RecalculateNormals(); - mesh.name = name; - - return mesh; - } - - public class BoneWeightContainer { - public struct Pair { - public Transform bone; - public float weight; - - public Pair (Transform bone, float weight) { - this.bone = bone; - this.weight = weight; + if (attachment is RegionAttachment) { + var att = (RegionAttachment)attachment; + return (Material)((AtlasRegion)att.RendererObject).page.rendererObject; + } else if (attachment is MeshAttachment) { + var att = (MeshAttachment)attachment; + return (Material)((AtlasRegion)att.RendererObject).page.rendererObject; + } else if (attachment is WeightedMeshAttachment) { + var att = (WeightedMeshAttachment)attachment; + return (Material)((AtlasRegion)att.RendererObject).page.rendererObject; + } else { + return null; } } - public List bones; - public List weights; - public List pairs; + static Mesh ExtractRegionAttachment (string name, RegionAttachment attachment, Mesh mesh = null) { + var bone = GetExtractionBone(); + bone.X = -attachment.X; + bone.Y = -attachment.Y; + bone.UpdateWorldTransform(); - public BoneWeightContainer () { - this.bones = new List(); - this.weights = new List(); - this.pairs = new List(); + Vector2[] uvs = ExtractUV(attachment.UVs); + float[] floatVerts = new float[8]; + attachment.ComputeWorldVertices(bone, floatVerts); + Vector3[] verts = ExtractVerts(floatVerts); + + //unrotate verts now that they're centered + for (int i = 0; i < verts.Length; i++) { + verts[i] = Quaternion.Euler(0, 0, -attachment.Rotation) * verts[i]; + } + + int[] triangles = new int[6] { 1, 3, 0, 2, 3, 1 }; + Color color = new Color(attachment.R, attachment.G, attachment.B, attachment.A); + + if (mesh == null) + mesh = new Mesh(); + + mesh.triangles = new int[0]; + + mesh.vertices = verts; + mesh.uv = uvs; + mesh.triangles = triangles; + mesh.colors = new Color[] { color, color, color, color }; + mesh.RecalculateBounds(); + mesh.RecalculateNormals(); + mesh.name = name; + + return mesh; } - public void Add (Transform transform, float weight) { - bones.Add(transform); - weights.Add(weight); + static Mesh ExtractMeshAttachment (string name, MeshAttachment attachment, Mesh mesh = null) { + var slot = GetExtractionSlot(); - pairs.Add(new Pair(transform, weight)); + slot.Bone.X = 0; + slot.Bone.Y = 0; + slot.Bone.UpdateWorldTransform(); + + Vector2[] uvs = ExtractUV(attachment.UVs); + float[] floatVerts = new float[attachment.Vertices.Length]; + attachment.ComputeWorldVertices(slot, floatVerts); + Vector3[] verts = ExtractVerts(floatVerts); + + int[] triangles = attachment.Triangles; + Color color = new Color(attachment.R, attachment.G, attachment.B, attachment.A); + + if (mesh == null) + mesh = new Mesh(); + + mesh.triangles = new int[0]; + + mesh.vertices = verts; + mesh.uv = uvs; + mesh.triangles = triangles; + Color[] colors = new Color[verts.Length]; + for (int i = 0; i < verts.Length; i++) + colors[i] = color; + + mesh.colors = colors; + mesh.RecalculateBounds(); + mesh.RecalculateNormals(); + mesh.name = name; + + return mesh; } - } - static Mesh ExtractSkinnedMeshAttachment (string name, WeightedMeshAttachment attachment, int slotIndex, SkeletonData skeletonData, List boneList, Mesh mesh = null) { + public class BoneWeightContainer { + public struct Pair { + public Transform bone; + public float weight; - Skeleton skeleton = new Skeleton(skeletonData); - skeleton.UpdateWorldTransform(); - - float[] floatVerts = new float[attachment.UVs.Length]; - attachment.ComputeWorldVertices(skeleton.Slots.Items[slotIndex], floatVerts); - - Vector2[] uvs = ExtractUV(attachment.UVs); - Vector3[] verts = ExtractVerts(floatVerts); - - int[] triangles = attachment.Triangles; - Color color = new Color(attachment.R, attachment.G, attachment.B, attachment.A); - - if (mesh == null) - mesh = new Mesh(); - - mesh.triangles = new int[0]; - - mesh.vertices = verts; - mesh.uv = uvs; - mesh.triangles = triangles; - Color[] colors = new Color[verts.Length]; - for (int i = 0; i < verts.Length; i++) - colors[i] = color; - - mesh.colors = colors; - mesh.name = name; - mesh.RecalculateNormals(); - mesh.RecalculateBounds(); - - //Handle weights and binding - Dictionary weightTable = new Dictionary(); - System.Text.StringBuilder warningBuilder = new System.Text.StringBuilder(); - - int[] bones = attachment.Bones; - float[] weights = attachment.Weights; - for (int w = 0, v = 0, b = 0, n = bones.Length; v < n; w += 2) { - - int nn = bones[v++] + v; - for (; v < nn; v++, b += 3) { - Transform boneTransform = boneList[bones[v]]; - int vIndex = w / 2; - - float weight = weights[b + 2]; - - BoneWeightContainer container; - if (weightTable.ContainsKey(vIndex)) - container = weightTable[vIndex]; - else { - container = new BoneWeightContainer(); - weightTable.Add(vIndex, container); + public Pair (Transform bone, float weight) { + this.bone = bone; + this.weight = weight; } + } + + public List bones; + public List weights; + public List pairs; - container.Add(boneTransform, weight); + public BoneWeightContainer () { + this.bones = new List(); + this.weights = new List(); + this.pairs = new List(); + } + + public void Add (Transform transform, float weight) { + bones.Add(transform); + weights.Add(weight); + + pairs.Add(new Pair(transform, weight)); } } - BoneWeight[] boneWeights = new BoneWeight[weightTable.Count]; + static Mesh ExtractSkinnedMeshAttachment (string name, WeightedMeshAttachment attachment, int slotIndex, SkeletonData skeletonData, List boneList, Mesh mesh = null) { - for (int i = 0; i < weightTable.Count; i++) { - BoneWeight bw = new BoneWeight(); - var container = weightTable[i]; + Skeleton skeleton = new Skeleton(skeletonData); + skeleton.UpdateWorldTransform(); - var pairs = container.pairs.OrderByDescending(pair => pair.weight).ToList(); + float[] floatVerts = new float[attachment.UVs.Length]; + attachment.ComputeWorldVertices(skeleton.Slots.Items[slotIndex], floatVerts); - for (int b = 0; b < pairs.Count; b++) { - if (b > 3) { - if (warningBuilder.Length == 0) - warningBuilder.Insert(0, "[SkinnedMeshAttachment " + name + "]\r\nUnity only supports 4 weight influences per vertex! The 4 strongest influences will be used.\r\n"); + Vector2[] uvs = ExtractUV(attachment.UVs); + Vector3[] verts = ExtractVerts(floatVerts); - warningBuilder.AppendFormat("{0} ignored on vertex {1}!\r\n", pairs[b].bone.name, i); - continue; + int[] triangles = attachment.Triangles; + Color color = new Color(attachment.R, attachment.G, attachment.B, attachment.A); + + if (mesh == null) + mesh = new Mesh(); + + mesh.triangles = new int[0]; + + mesh.vertices = verts; + mesh.uv = uvs; + mesh.triangles = triangles; + Color[] colors = new Color[verts.Length]; + for (int i = 0; i < verts.Length; i++) + colors[i] = color; + + mesh.colors = colors; + mesh.name = name; + mesh.RecalculateNormals(); + mesh.RecalculateBounds(); + + //Handle weights and binding + Dictionary weightTable = new Dictionary(); + System.Text.StringBuilder warningBuilder = new System.Text.StringBuilder(); + + int[] bones = attachment.Bones; + float[] weights = attachment.Weights; + for (int w = 0, v = 0, b = 0, n = bones.Length; v < n; w += 2) { + + int nn = bones[v++] + v; + for (; v < nn; v++, b += 3) { + Transform boneTransform = boneList[bones[v]]; + int vIndex = w / 2; + + float weight = weights[b + 2]; + + BoneWeightContainer container; + if (weightTable.ContainsKey(vIndex)) + container = weightTable[vIndex]; + else { + container = new BoneWeightContainer(); + weightTable.Add(vIndex, container); + } + + + container.Add(boneTransform, weight); } + } - int boneIndex = boneList.IndexOf(pairs[b].bone); - float weight = pairs[b].weight; + BoneWeight[] boneWeights = new BoneWeight[weightTable.Count]; - switch (b) { + for (int i = 0; i < weightTable.Count; i++) { + BoneWeight bw = new BoneWeight(); + var container = weightTable[i]; + + var pairs = container.pairs.OrderByDescending(pair => pair.weight).ToList(); + + for (int b = 0; b < pairs.Count; b++) { + if (b > 3) { + if (warningBuilder.Length == 0) + warningBuilder.Insert(0, "[SkinnedMeshAttachment " + name + "]\r\nUnity only supports 4 weight influences per vertex! The 4 strongest influences will be used.\r\n"); + + warningBuilder.AppendFormat("{0} ignored on vertex {1}!\r\n", pairs[b].bone.name, i); + continue; + } + + int boneIndex = boneList.IndexOf(pairs[b].bone); + float weight = pairs[b].weight; + + switch (b) { case 0: bw.boneIndex0 = boneIndex; bw.weight0 = weight; @@ -725,762 +726,765 @@ public static class SkeletonBaker { bw.boneIndex3 = boneIndex; bw.weight3 = weight; break; + } } + + boneWeights[i] = bw; } - boneWeights[i] = bw; - } - - Matrix4x4[] bindPoses = new Matrix4x4[boneList.Count]; - for (int i = 0; i < boneList.Count; i++) { - bindPoses[i] = boneList[i].worldToLocalMatrix; - } - - mesh.boneWeights = boneWeights; - mesh.bindposes = bindPoses; - - string warningString = warningBuilder.ToString(); - if (warningString.Length > 0) - Debug.LogWarning(warningString); - - - return mesh; - } - - static Vector2[] ExtractUV (float[] floats) { - Vector2[] arr = new Vector2[floats.Length / 2]; - - for (int i = 0; i < floats.Length; i += 2) { - arr[i / 2] = new Vector2(floats[i], floats[i + 1]); - } - - return arr; - } - - static Vector3[] ExtractVerts (float[] floats) { - Vector3[] arr = new Vector3[floats.Length / 2]; - - for (int i = 0; i < floats.Length; i += 2) { - arr[i / 2] = new Vector3(floats[i], floats[i + 1], 0);// *scale; - } - - return arr; - } - - static AnimationClip ExtractAnimation (string name, SkeletonData skeletonData, Dictionary> slotLookup, bool bakeIK, SendMessageOptions eventOptions, AnimationClip clip = null) { - var animation = skeletonData.FindAnimation(name); - - var timelines = animation.Timelines; - - if (clip == null) { - clip = new AnimationClip(); - } else { - clip.ClearCurves(); - AnimationUtility.SetAnimationEvents(clip, new AnimationEvent[0]); - } - -#if UNITY_5 - -#else - AnimationUtility.SetAnimationType(clip, ModelImporterAnimationType.Generic); -#endif - - clip.name = name; - - Skeleton skeleton = new Skeleton(skeletonData); - - List ignoreRotateTimelineIndexes = new List(); - - if (bakeIK) { - foreach (IkConstraint i in skeleton.IkConstraints) { - foreach (Bone b in i.Bones) { - int index = skeleton.FindBoneIndex(b.Data.Name); - ignoreRotateTimelineIndexes.Add(index); - BakeBone(b, animation, clip); - } - } - } - - foreach (Bone b in skeleton.Bones) { - if (b.Data.InheritRotation == false) { - int index = skeleton.FindBoneIndex(b.Data.Name); - - if (ignoreRotateTimelineIndexes.Contains(index) == false) { - ignoreRotateTimelineIndexes.Add(index); - BakeBone(b, animation, clip); - } - } - } - - foreach (Timeline t in timelines) { - skeleton.SetToSetupPose(); - - if (t is ScaleTimeline) { - ParseScaleTimeline(skeleton, (ScaleTimeline)t, clip); - } else if (t is TranslateTimeline) { - ParseTranslateTimeline(skeleton, (TranslateTimeline)t, clip); - } else if (t is RotateTimeline) { - //bypass any rotation keys if they're going to get baked anyway to prevent localEulerAngles vs Baked collision - if (ignoreRotateTimelineIndexes.Contains(((RotateTimeline)t).BoneIndex) == false) - ParseRotateTimeline(skeleton, (RotateTimeline)t, clip); - } else if (t is AttachmentTimeline) { - ParseAttachmentTimeline(skeleton, (AttachmentTimeline)t, slotLookup, clip); - } else if (t is EventTimeline) { - ParseEventTimeline((EventTimeline)t, clip, eventOptions); + Matrix4x4[] bindPoses = new Matrix4x4[boneList.Count]; + for (int i = 0; i < boneList.Count; i++) { + bindPoses[i] = boneList[i].worldToLocalMatrix; } + mesh.boneWeights = boneWeights; + mesh.bindposes = bindPoses; + + string warningString = warningBuilder.ToString(); + if (warningString.Length > 0) + Debug.LogWarning(warningString); + + + return mesh; } - var settings = AnimationUtility.GetAnimationClipSettings(clip); - settings.loopTime = true; - settings.stopTime = Mathf.Max(clip.length, 0.001f); + static Vector2[] ExtractUV (float[] floats) { + Vector2[] arr = new Vector2[floats.Length / 2]; - SetAnimationSettings(clip, settings); + for (int i = 0; i < floats.Length; i += 2) { + arr[i / 2] = new Vector2(floats[i], floats[i + 1]); + } - clip.EnsureQuaternionContinuity(); - - EditorUtility.SetDirty(clip); - - return clip; - } - static int BinarySearch (float[] values, float target) { - int low = 0; - int high = values.Length - 2; - if (high == 0) return 1; - int current = (int)((uint)high >> 1); - while (true) { - if (values[(current + 1)] <= target) - low = current + 1; - else - high = current; - if (low == high) return (low + 1); - current = (int)((uint)(low + high) >> 1); + return arr; } - } - static void ParseEventTimeline (EventTimeline timeline, AnimationClip clip, SendMessageOptions eventOptions) { + static Vector3[] ExtractVerts (float[] floats) { + Vector3[] arr = new Vector3[floats.Length / 2]; - float[] frames = timeline.Frames; - var events = timeline.Events; + for (int i = 0; i < floats.Length; i += 2) { + arr[i / 2] = new Vector3(floats[i], floats[i + 1], 0);// *scale; + } - List animEvents = new List(); - for (int i = 0; i < frames.Length; i++) { - var ev = events[i]; + return arr; + } - AnimationEvent ae = new AnimationEvent(); - //TODO: Deal with Mecanim's zero-time missed event - ae.time = frames[i]; - ae.functionName = ev.Data.Name; - ae.messageOptions = eventOptions; + static AnimationClip ExtractAnimation (string name, SkeletonData skeletonData, Dictionary> slotLookup, bool bakeIK, SendMessageOptions eventOptions, AnimationClip clip = null) { + var animation = skeletonData.FindAnimation(name); - if (!string.IsNullOrEmpty(ev.String)) { - ae.stringParameter = ev.String; + var timelines = animation.Timelines; + + if (clip == null) { + clip = new AnimationClip(); } else { - if (ev.Int == 0 && ev.Float == 0) { - //do nothing, raw function - } else { - if (ev.Int != 0) - ae.floatParameter = (float)ev.Int; - else - ae.floatParameter = ev.Float; + clip.ClearCurves(); + AnimationUtility.SetAnimationEvents(clip, new AnimationEvent[0]); + } + + #if UNITY_5 + + #else + AnimationUtility.SetAnimationType(clip, ModelImporterAnimationType.Generic); + #endif + + clip.name = name; + + Skeleton skeleton = new Skeleton(skeletonData); + + List ignoreRotateTimelineIndexes = new List(); + + if (bakeIK) { + foreach (IkConstraint i in skeleton.IkConstraints) { + foreach (Bone b in i.Bones) { + int index = skeleton.FindBoneIndex(b.Data.Name); + ignoreRotateTimelineIndexes.Add(index); + BakeBone(b, animation, clip); + } + } + } + + foreach (Bone b in skeleton.Bones) { + if (b.Data.InheritRotation == false) { + int index = skeleton.FindBoneIndex(b.Data.Name); + + if (ignoreRotateTimelineIndexes.Contains(index) == false) { + ignoreRotateTimelineIndexes.Add(index); + BakeBone(b, animation, clip); + } + } + } + + foreach (Timeline t in timelines) { + skeleton.SetToSetupPose(); + + if (t is ScaleTimeline) { + ParseScaleTimeline(skeleton, (ScaleTimeline)t, clip); + } else if (t is TranslateTimeline) { + ParseTranslateTimeline(skeleton, (TranslateTimeline)t, clip); + } else if (t is RotateTimeline) { + //bypass any rotation keys if they're going to get baked anyway to prevent localEulerAngles vs Baked collision + if (ignoreRotateTimelineIndexes.Contains(((RotateTimeline)t).BoneIndex) == false) + ParseRotateTimeline(skeleton, (RotateTimeline)t, clip); + } else if (t is AttachmentTimeline) { + ParseAttachmentTimeline(skeleton, (AttachmentTimeline)t, slotLookup, clip); + } else if (t is EventTimeline) { + ParseEventTimeline((EventTimeline)t, clip, eventOptions); } } - animEvents.Add(ae); + var settings = AnimationUtility.GetAnimationClipSettings(clip); + settings.loopTime = true; + settings.stopTime = Mathf.Max(clip.length, 0.001f); + + SetAnimationSettings(clip, settings); + + clip.EnsureQuaternionContinuity(); + + EditorUtility.SetDirty(clip); + + return clip; + } + static int BinarySearch (float[] values, float target) { + int low = 0; + int high = values.Length - 2; + if (high == 0) return 1; + int current = (int)((uint)high >> 1); + while (true) { + if (values[(current + 1)] <= target) + low = current + 1; + else + high = current; + if (low == high) return (low + 1); + current = (int)((uint)(low + high) >> 1); + } } - AnimationUtility.SetAnimationEvents(clip, animEvents.ToArray()); - } + static void ParseEventTimeline (EventTimeline timeline, AnimationClip clip, SendMessageOptions eventOptions) { - static void ParseAttachmentTimeline (Skeleton skeleton, AttachmentTimeline timeline, Dictionary> slotLookup, AnimationClip clip) { - var attachmentNames = slotLookup[timeline.SlotIndex]; + float[] frames = timeline.Frames; + var events = timeline.Events; - string bonePath = GetPath(skeleton.Slots.Items[timeline.SlotIndex].Bone.Data); - string slotPath = bonePath + "/" + skeleton.Slots.Items[timeline.SlotIndex].Data.Name; + List animEvents = new List(); + for (int i = 0; i < frames.Length; i++) { + var ev = events[i]; - Dictionary curveTable = new Dictionary(); + AnimationEvent ae = new AnimationEvent(); + //TODO: Deal with Mecanim's zero-time missed event + ae.time = frames[i]; + ae.functionName = ev.Data.Name; + ae.messageOptions = eventOptions; - foreach (string str in attachmentNames) { - curveTable.Add(str, new AnimationCurve()); - } - - float[] frames = timeline.Frames; - - if (frames[0] != 0) { - string startingName = skeleton.Slots.Items[timeline.SlotIndex].Data.AttachmentName; - foreach (var pair in curveTable) { - if (startingName == "" || startingName == null) { - pair.Value.AddKey(new Keyframe(0, 0, float.PositiveInfinity, float.PositiveInfinity)); + if (!string.IsNullOrEmpty(ev.String)) { + ae.stringParameter = ev.String; } else { - if (pair.Key == startingName) { - pair.Value.AddKey(new Keyframe(0, 1, float.PositiveInfinity, float.PositiveInfinity)); + if (ev.Int == 0 && ev.Float == 0) { + //do nothing, raw function } else { + if (ev.Int != 0) + ae.floatParameter = (float)ev.Int; + else + ae.floatParameter = ev.Float; + } + + } + + animEvents.Add(ae); + } + + AnimationUtility.SetAnimationEvents(clip, animEvents.ToArray()); + } + + static void ParseAttachmentTimeline (Skeleton skeleton, AttachmentTimeline timeline, Dictionary> slotLookup, AnimationClip clip) { + var attachmentNames = slotLookup[timeline.SlotIndex]; + + string bonePath = GetPath(skeleton.Slots.Items[timeline.SlotIndex].Bone.Data); + string slotPath = bonePath + "/" + skeleton.Slots.Items[timeline.SlotIndex].Data.Name; + + Dictionary curveTable = new Dictionary(); + + foreach (string str in attachmentNames) { + curveTable.Add(str, new AnimationCurve()); + } + + float[] frames = timeline.Frames; + + if (frames[0] != 0) { + string startingName = skeleton.Slots.Items[timeline.SlotIndex].Data.AttachmentName; + foreach (var pair in curveTable) { + if (startingName == "" || startingName == null) { pair.Value.AddKey(new Keyframe(0, 0, float.PositiveInfinity, float.PositiveInfinity)); - } - } - } - } - - float currentTime = timeline.Frames[0]; - float endTime = frames[frames.Length - 1]; - int f = 0; - while (currentTime < endTime) { - float time = frames[f]; - - int frameIndex = (time >= frames[frames.Length - 1] ? frames.Length : BinarySearch(frames, time)) - 1; - - string name = timeline.AttachmentNames[frameIndex]; - foreach (var pair in curveTable) { - if (name == "") { - pair.Value.AddKey(new Keyframe(time, 0, float.PositiveInfinity, float.PositiveInfinity)); - } else { - if (pair.Key == name) { - pair.Value.AddKey(new Keyframe(time, 1, float.PositiveInfinity, float.PositiveInfinity)); } else { - pair.Value.AddKey(new Keyframe(time, 0, float.PositiveInfinity, float.PositiveInfinity)); + if (pair.Key == startingName) { + pair.Value.AddKey(new Keyframe(0, 1, float.PositiveInfinity, float.PositiveInfinity)); + } else { + pair.Value.AddKey(new Keyframe(0, 0, float.PositiveInfinity, float.PositiveInfinity)); + } } } } - currentTime = time; - f += 1; + float currentTime = timeline.Frames[0]; + float endTime = frames[frames.Length - 1]; + int f = 0; + while (currentTime < endTime) { + float time = frames[f]; + + int frameIndex = (time >= frames[frames.Length - 1] ? frames.Length : BinarySearch(frames, time)) - 1; + + string name = timeline.AttachmentNames[frameIndex]; + foreach (var pair in curveTable) { + if (name == "") { + pair.Value.AddKey(new Keyframe(time, 0, float.PositiveInfinity, float.PositiveInfinity)); + } else { + if (pair.Key == name) { + pair.Value.AddKey(new Keyframe(time, 1, float.PositiveInfinity, float.PositiveInfinity)); + } else { + pair.Value.AddKey(new Keyframe(time, 0, float.PositiveInfinity, float.PositiveInfinity)); + } + } + } + + currentTime = time; + f += 1; + } + + foreach (var pair in curveTable) { + string path = slotPath + "/" + pair.Key; + string prop = "m_IsActive"; + + clip.SetCurve(path, typeof(GameObject), prop, pair.Value); + } } - foreach (var pair in curveTable) { - string path = slotPath + "/" + pair.Key; - string prop = "m_IsActive"; + static float GetUninheritedRotation (Bone b) { - clip.SetCurve(path, typeof(GameObject), prop, pair.Value); + Bone parent = b.Parent; + float angle = b.AppliedRotation; + + while (parent != null) { + angle -= parent.AppliedRotation; + parent = parent.Parent; + } + + return angle; } - } + static void BakeBone (Bone bone, Spine.Animation animation, AnimationClip clip) { + Skeleton skeleton = bone.Skeleton; + bool inheritRotation = bone.Data.InheritRotation; - static float GetUninheritedRotation (Bone b) { - - Bone parent = b.Parent; - float angle = b.AppliedRotation; - - while (parent != null) { - angle -= parent.AppliedRotation; - parent = parent.Parent; - } - - return angle; - } - static void BakeBone (Bone bone, Spine.Animation animation, AnimationClip clip) { - Skeleton skeleton = bone.Skeleton; - bool inheritRotation = bone.Data.InheritRotation; - - skeleton.SetToSetupPose(); - animation.Apply(skeleton, 0, 0, true, null); - skeleton.UpdateWorldTransform(); - float duration = animation.Duration; - - AnimationCurve curve = new AnimationCurve(); - - List keys = new List(); - - float rotation = bone.AppliedRotation; - if (!inheritRotation) - rotation = GetUninheritedRotation(bone); - - keys.Add(new Keyframe(0, rotation, 0, 0)); - - int listIndex = 1; - - float r = rotation; - - int steps = Mathf.CeilToInt(duration / bakeIncrement); - - float currentTime = 0; - float lastTime = 0; - float angle = rotation; - - for (int i = 1; i <= steps; i++) { - currentTime += bakeIncrement; - if (i == steps) - currentTime = duration; - - animation.Apply(skeleton, lastTime, currentTime, true, null); + skeleton.SetToSetupPose(); + animation.Apply(skeleton, 0, 0, true, null); skeleton.UpdateWorldTransform(); + float duration = animation.Duration; - int pIndex = listIndex - 1; + AnimationCurve curve = new AnimationCurve(); - Keyframe pk = keys[pIndex]; + List keys = new List(); - pk = keys[pIndex]; - - if (inheritRotation) - rotation = bone.AppliedRotation; - else { + float rotation = bone.AppliedRotation; + if (!inheritRotation) rotation = GetUninheritedRotation(bone); - } - angle += Mathf.DeltaAngle(angle, rotation); + keys.Add(new Keyframe(0, rotation, 0, 0)); - r = angle; + int listIndex = 1; - float rOut = (r - pk.value) / (currentTime - pk.time); + float r = rotation; - pk.outTangent = rOut; + int steps = Mathf.CeilToInt(duration / bakeIncrement); - keys.Add(new Keyframe(currentTime, r, rOut, 0)); + float currentTime = 0; + float lastTime = 0; + float angle = rotation; - keys[pIndex] = pk; + for (int i = 1; i <= steps; i++) { + currentTime += bakeIncrement; + if (i == steps) + currentTime = duration; - listIndex++; - lastTime = currentTime; - } - - curve = EnsureCurveKeyCount(new AnimationCurve(keys.ToArray())); - - string path = GetPath(bone.Data); - string propertyName = "localEulerAnglesBaked"; - - EditorCurveBinding xBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".x"); - AnimationUtility.SetEditorCurve(clip, xBind, new AnimationCurve()); - EditorCurveBinding yBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".y"); - AnimationUtility.SetEditorCurve(clip, yBind, new AnimationCurve()); - EditorCurveBinding zBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".z"); - AnimationUtility.SetEditorCurve(clip, zBind, curve); - } - - static void ParseTranslateTimeline (Skeleton skeleton, TranslateTimeline timeline, AnimationClip clip) { - var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; - var bone = skeleton.Bones.Items[timeline.BoneIndex]; - - AnimationCurve xCurve = new AnimationCurve(); - AnimationCurve yCurve = new AnimationCurve(); - AnimationCurve zCurve = new AnimationCurve(); - - float endTime = timeline.Frames[(timeline.FrameCount * 3) - 3]; - - float currentTime = timeline.Frames[0]; - - List xKeys = new List(); - List yKeys = new List(); - - xKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[1] + boneData.X, 0, 0)); - yKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[2] + boneData.Y, 0, 0)); - - int listIndex = 1; - int frameIndex = 1; - int f = 3; - float[] frames = timeline.Frames; - skeleton.SetToSetupPose(); - float lastTime = 0; - while (currentTime < endTime) { - int pIndex = listIndex - 1; - - - - float curveType = timeline.GetCurveType(frameIndex - 1); - - if (curveType == 0) { - //linear - Keyframe px = xKeys[pIndex]; - Keyframe py = yKeys[pIndex]; - - float time = frames[f]; - float x = frames[f + 1] + boneData.X; - float y = frames[f + 2] + boneData.Y; - - float xOut = (x - px.value) / (time - px.time); - float yOut = (y - py.value) / (time - py.time); - - px.outTangent = xOut; - py.outTangent = yOut; - - xKeys.Add(new Keyframe(time, x, xOut, 0)); - yKeys.Add(new Keyframe(time, y, yOut, 0)); - - xKeys[pIndex] = px; - yKeys[pIndex] = py; - - currentTime = time; - - timeline.Apply(skeleton, lastTime, currentTime, null, 1); - - lastTime = time; - listIndex++; - } else if (curveType == 1) { - //stepped - Keyframe px = xKeys[pIndex]; - Keyframe py = yKeys[pIndex]; - - float time = frames[f]; - float x = frames[f + 1] + boneData.X; - float y = frames[f + 2] + boneData.Y; - - float xOut = float.PositiveInfinity; - float yOut = float.PositiveInfinity; - - px.outTangent = xOut; - py.outTangent = yOut; - - xKeys.Add(new Keyframe(time, x, xOut, 0)); - yKeys.Add(new Keyframe(time, y, yOut, 0)); - - xKeys[pIndex] = px; - yKeys[pIndex] = py; - - currentTime = time; - - timeline.Apply(skeleton, lastTime, currentTime, null, 1); - - lastTime = time; - listIndex++; - } else if (curveType == 2) { - - //bezier - Keyframe px = xKeys[pIndex]; - Keyframe py = yKeys[pIndex]; - - float time = frames[f]; - - int steps = Mathf.FloorToInt((time - px.time) / bakeIncrement); - - for (int i = 1; i <= steps; i++) { - currentTime += bakeIncrement; - if (i == steps) - currentTime = time; - - timeline.Apply(skeleton, lastTime, currentTime, null, 1); - - px = xKeys[listIndex - 1]; - py = yKeys[listIndex - 1]; - - float xOut = (bone.X - px.value) / (currentTime - px.time); - float yOut = (bone.Y - py.value) / (currentTime - py.time); - - px.outTangent = xOut; - py.outTangent = yOut; - - xKeys.Add(new Keyframe(currentTime, bone.X, xOut, 0)); - yKeys.Add(new Keyframe(currentTime, bone.Y, yOut, 0)); - - xKeys[listIndex - 1] = px; - yKeys[listIndex - 1] = py; - - listIndex++; - lastTime = currentTime; - } - } - - frameIndex++; - f += 3; - } - - xCurve = EnsureCurveKeyCount(new AnimationCurve(xKeys.ToArray())); - yCurve = EnsureCurveKeyCount(new AnimationCurve(yKeys.ToArray())); - - - - string path = GetPath(boneData); - string propertyName = "localPosition"; - - clip.SetCurve(path, typeof(Transform), propertyName + ".x", xCurve); - clip.SetCurve(path, typeof(Transform), propertyName + ".y", yCurve); - clip.SetCurve(path, typeof(Transform), propertyName + ".z", zCurve); - } - - static AnimationCurve EnsureCurveKeyCount (AnimationCurve curve) { - if (curve.length == 1) - curve.AddKey(curve.keys[0].time + 0.25f, curve.keys[0].value); - - return curve; - } - - static void ParseScaleTimeline (Skeleton skeleton, ScaleTimeline timeline, AnimationClip clip) { - var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; - var bone = skeleton.Bones.Items[timeline.BoneIndex]; - - AnimationCurve xCurve = new AnimationCurve(); - AnimationCurve yCurve = new AnimationCurve(); - AnimationCurve zCurve = new AnimationCurve(); - - float endTime = timeline.Frames[(timeline.FrameCount * 3) - 3]; - - float currentTime = timeline.Frames[0]; - - List xKeys = new List(); - List yKeys = new List(); - - xKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[1] * boneData.ScaleX, 0, 0)); - yKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[2] * boneData.ScaleY, 0, 0)); - - int listIndex = 1; - int frameIndex = 1; - int f = 3; - float[] frames = timeline.Frames; - skeleton.SetToSetupPose(); - float lastTime = 0; - while (currentTime < endTime) { - int pIndex = listIndex - 1; - float curveType = timeline.GetCurveType(frameIndex - 1); - - if (curveType == 0) { - //linear - Keyframe px = xKeys[pIndex]; - Keyframe py = yKeys[pIndex]; - - float time = frames[f]; - float x = frames[f + 1] * boneData.ScaleX; - float y = frames[f + 2] * boneData.ScaleY; - - float xOut = (x - px.value) / (time - px.time); - float yOut = (y - py.value) / (time - py.time); - - px.outTangent = xOut; - py.outTangent = yOut; - - xKeys.Add(new Keyframe(time, x, xOut, 0)); - yKeys.Add(new Keyframe(time, y, yOut, 0)); - - xKeys[pIndex] = px; - yKeys[pIndex] = py; - - currentTime = time; - - timeline.Apply(skeleton, lastTime, currentTime, null, 1); - - lastTime = time; - listIndex++; - } else if (curveType == 1) { - //stepped - Keyframe px = xKeys[pIndex]; - Keyframe py = yKeys[pIndex]; - - float time = frames[f]; - float x = frames[f + 1] * boneData.ScaleX; - float y = frames[f + 2] * boneData.ScaleY; - - float xOut = float.PositiveInfinity; - float yOut = float.PositiveInfinity; - - px.outTangent = xOut; - py.outTangent = yOut; - - xKeys.Add(new Keyframe(time, x, xOut, 0)); - yKeys.Add(new Keyframe(time, y, yOut, 0)); - - xKeys[pIndex] = px; - yKeys[pIndex] = py; - - currentTime = time; - - timeline.Apply(skeleton, lastTime, currentTime, null, 1); - - lastTime = time; - listIndex++; - } else if (curveType == 2) { - //bezier - Keyframe px = xKeys[pIndex]; - Keyframe py = yKeys[pIndex]; - - float time = frames[f]; - - int steps = Mathf.FloorToInt((time - px.time) / bakeIncrement); - - for (int i = 1; i <= steps; i++) { - currentTime += bakeIncrement; - if (i == steps) - currentTime = time; - - timeline.Apply(skeleton, lastTime, currentTime, null, 1); - - px = xKeys[listIndex - 1]; - py = yKeys[listIndex - 1]; - - float xOut = (bone.ScaleX - px.value) / (currentTime - px.time); - float yOut = (bone.ScaleY - py.value) / (currentTime - py.time); - - px.outTangent = xOut; - py.outTangent = yOut; - - xKeys.Add(new Keyframe(currentTime, bone.ScaleX, xOut, 0)); - yKeys.Add(new Keyframe(currentTime, bone.ScaleY, yOut, 0)); - - xKeys[listIndex - 1] = px; - yKeys[listIndex - 1] = py; - - listIndex++; - lastTime = currentTime; - } - } - - frameIndex++; - f += 3; - } - - xCurve = EnsureCurveKeyCount(new AnimationCurve(xKeys.ToArray())); - yCurve = EnsureCurveKeyCount(new AnimationCurve(yKeys.ToArray())); - - string path = GetPath(boneData); - string propertyName = "localScale"; - - clip.SetCurve(path, typeof(Transform), propertyName + ".x", xCurve); - clip.SetCurve(path, typeof(Transform), propertyName + ".y", yCurve); - clip.SetCurve(path, typeof(Transform), propertyName + ".z", zCurve); - } - - static void ParseRotateTimeline (Skeleton skeleton, RotateTimeline timeline, AnimationClip clip) { - var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; - var bone = skeleton.Bones.Items[timeline.BoneIndex]; - - AnimationCurve curve = new AnimationCurve(); - - float endTime = timeline.Frames[(timeline.FrameCount * 2) - 2]; - - float currentTime = timeline.Frames[0]; - - List keys = new List(); - - float rotation = timeline.Frames[1] + boneData.Rotation; - - keys.Add(new Keyframe(timeline.Frames[0], rotation, 0, 0)); - - int listIndex = 1; - int frameIndex = 1; - int f = 2; - float[] frames = timeline.Frames; - skeleton.SetToSetupPose(); - float lastTime = 0; - float angle = rotation; - while (currentTime < endTime) { - int pIndex = listIndex - 1; - float curveType = timeline.GetCurveType(frameIndex - 1); - - if (curveType == 0) { - //linear - Keyframe pk = keys[pIndex]; - - float time = frames[f]; - - rotation = frames[f + 1] + boneData.Rotation; - angle += Mathf.DeltaAngle(angle, rotation); - float r = angle; - - float rOut = (r - pk.value) / (time - pk.time); - - pk.outTangent = rOut; - - keys.Add(new Keyframe(time, r, rOut, 0)); - - keys[pIndex] = pk; - - currentTime = time; - - timeline.Apply(skeleton, lastTime, currentTime, null, 1); - - lastTime = time; - listIndex++; - } else if (curveType == 1) { - //stepped - - Keyframe pk = keys[pIndex]; - - float time = frames[f]; - - rotation = frames[f + 1] + boneData.Rotation; - angle += Mathf.DeltaAngle(angle, rotation); - float r = angle; - - float rOut = float.PositiveInfinity; - - pk.outTangent = rOut; - - keys.Add(new Keyframe(time, r, rOut, 0)); - - keys[pIndex] = pk; - - currentTime = time; - - timeline.Apply(skeleton, lastTime, currentTime, null, 1); - - lastTime = time; - listIndex++; - } else if (curveType == 2) { - //bezier - Keyframe pk = keys[pIndex]; - - float time = frames[f]; - - timeline.Apply(skeleton, lastTime, currentTime, null, 1); + animation.Apply(skeleton, lastTime, currentTime, true, null); skeleton.UpdateWorldTransform(); - rotation = frames[f + 1] + boneData.Rotation; + int pIndex = listIndex - 1; + + Keyframe pk = keys[pIndex]; + + pk = keys[pIndex]; + + if (inheritRotation) + rotation = bone.AppliedRotation; + else { + rotation = GetUninheritedRotation(bone); + } + angle += Mathf.DeltaAngle(angle, rotation); - float r = angle; - int steps = Mathf.FloorToInt((time - pk.time) / bakeIncrement); + r = angle; - for (int i = 1; i <= steps; i++) { - currentTime += bakeIncrement; - if (i == steps) - currentTime = time; + float rOut = (r - pk.value) / (currentTime - pk.time); + + pk.outTangent = rOut; + + keys.Add(new Keyframe(currentTime, r, rOut, 0)); + + keys[pIndex] = pk; + + listIndex++; + lastTime = currentTime; + } + + curve = EnsureCurveKeyCount(new AnimationCurve(keys.ToArray())); + + string path = GetPath(bone.Data); + string propertyName = "localEulerAnglesBaked"; + + EditorCurveBinding xBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".x"); + AnimationUtility.SetEditorCurve(clip, xBind, new AnimationCurve()); + EditorCurveBinding yBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".y"); + AnimationUtility.SetEditorCurve(clip, yBind, new AnimationCurve()); + EditorCurveBinding zBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".z"); + AnimationUtility.SetEditorCurve(clip, zBind, curve); + } + + static void ParseTranslateTimeline (Skeleton skeleton, TranslateTimeline timeline, AnimationClip clip) { + var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; + var bone = skeleton.Bones.Items[timeline.BoneIndex]; + + AnimationCurve xCurve = new AnimationCurve(); + AnimationCurve yCurve = new AnimationCurve(); + AnimationCurve zCurve = new AnimationCurve(); + + float endTime = timeline.Frames[(timeline.FrameCount * 3) - 3]; + + float currentTime = timeline.Frames[0]; + + List xKeys = new List(); + List yKeys = new List(); + + xKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[1] + boneData.X, 0, 0)); + yKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[2] + boneData.Y, 0, 0)); + + int listIndex = 1; + int frameIndex = 1; + int f = 3; + float[] frames = timeline.Frames; + skeleton.SetToSetupPose(); + float lastTime = 0; + while (currentTime < endTime) { + int pIndex = listIndex - 1; + + + + float curveType = timeline.GetCurveType(frameIndex - 1); + + if (curveType == 0) { + //linear + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + float x = frames[f + 1] + boneData.X; + float y = frames[f + 2] + boneData.Y; + + float xOut = (x - px.value) / (time - px.time); + float yOut = (y - py.value) / (time - py.time); + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(time, x, xOut, 0)); + yKeys.Add(new Keyframe(time, y, yOut, 0)); + + xKeys[pIndex] = px; + yKeys[pIndex] = py; + + currentTime = time; timeline.Apply(skeleton, lastTime, currentTime, null, 1); - skeleton.UpdateWorldTransform(); - pk = keys[listIndex - 1]; - rotation = bone.Rotation; + lastTime = time; + listIndex++; + } else if (curveType == 1) { + //stepped + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + float x = frames[f + 1] + boneData.X; + float y = frames[f + 2] + boneData.Y; + + float xOut = float.PositiveInfinity; + float yOut = float.PositiveInfinity; + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(time, x, xOut, 0)); + yKeys.Add(new Keyframe(time, y, yOut, 0)); + + xKeys[pIndex] = px; + yKeys[pIndex] = py; + + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1); + + lastTime = time; + listIndex++; + } else if (curveType == 2) { + + //bezier + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + + int steps = Mathf.FloorToInt((time - px.time) / bakeIncrement); + + for (int i = 1; i <= steps; i++) { + currentTime += bakeIncrement; + if (i == steps) + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1); + + px = xKeys[listIndex - 1]; + py = yKeys[listIndex - 1]; + + float xOut = (bone.X - px.value) / (currentTime - px.time); + float yOut = (bone.Y - py.value) / (currentTime - py.time); + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(currentTime, bone.X, xOut, 0)); + yKeys.Add(new Keyframe(currentTime, bone.Y, yOut, 0)); + + xKeys[listIndex - 1] = px; + yKeys[listIndex - 1] = py; + + listIndex++; + lastTime = currentTime; + } + } + + frameIndex++; + f += 3; + } + + xCurve = EnsureCurveKeyCount(new AnimationCurve(xKeys.ToArray())); + yCurve = EnsureCurveKeyCount(new AnimationCurve(yKeys.ToArray())); + + + + string path = GetPath(boneData); + string propertyName = "localPosition"; + + clip.SetCurve(path, typeof(Transform), propertyName + ".x", xCurve); + clip.SetCurve(path, typeof(Transform), propertyName + ".y", yCurve); + clip.SetCurve(path, typeof(Transform), propertyName + ".z", zCurve); + } + + static AnimationCurve EnsureCurveKeyCount (AnimationCurve curve) { + if (curve.length == 1) + curve.AddKey(curve.keys[0].time + 0.25f, curve.keys[0].value); + + return curve; + } + + static void ParseScaleTimeline (Skeleton skeleton, ScaleTimeline timeline, AnimationClip clip) { + var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; + var bone = skeleton.Bones.Items[timeline.BoneIndex]; + + AnimationCurve xCurve = new AnimationCurve(); + AnimationCurve yCurve = new AnimationCurve(); + AnimationCurve zCurve = new AnimationCurve(); + + float endTime = timeline.Frames[(timeline.FrameCount * 3) - 3]; + + float currentTime = timeline.Frames[0]; + + List xKeys = new List(); + List yKeys = new List(); + + xKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[1] * boneData.ScaleX, 0, 0)); + yKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[2] * boneData.ScaleY, 0, 0)); + + int listIndex = 1; + int frameIndex = 1; + int f = 3; + float[] frames = timeline.Frames; + skeleton.SetToSetupPose(); + float lastTime = 0; + while (currentTime < endTime) { + int pIndex = listIndex - 1; + float curveType = timeline.GetCurveType(frameIndex - 1); + + if (curveType == 0) { + //linear + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + float x = frames[f + 1] * boneData.ScaleX; + float y = frames[f + 2] * boneData.ScaleY; + + float xOut = (x - px.value) / (time - px.time); + float yOut = (y - py.value) / (time - py.time); + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(time, x, xOut, 0)); + yKeys.Add(new Keyframe(time, y, yOut, 0)); + + xKeys[pIndex] = px; + yKeys[pIndex] = py; + + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1); + + lastTime = time; + listIndex++; + } else if (curveType == 1) { + //stepped + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + float x = frames[f + 1] * boneData.ScaleX; + float y = frames[f + 2] * boneData.ScaleY; + + float xOut = float.PositiveInfinity; + float yOut = float.PositiveInfinity; + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(time, x, xOut, 0)); + yKeys.Add(new Keyframe(time, y, yOut, 0)); + + xKeys[pIndex] = px; + yKeys[pIndex] = py; + + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1); + + lastTime = time; + listIndex++; + } else if (curveType == 2) { + //bezier + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + + int steps = Mathf.FloorToInt((time - px.time) / bakeIncrement); + + for (int i = 1; i <= steps; i++) { + currentTime += bakeIncrement; + if (i == steps) + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1); + + px = xKeys[listIndex - 1]; + py = yKeys[listIndex - 1]; + + float xOut = (bone.ScaleX - px.value) / (currentTime - px.time); + float yOut = (bone.ScaleY - py.value) / (currentTime - py.time); + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(currentTime, bone.ScaleX, xOut, 0)); + yKeys.Add(new Keyframe(currentTime, bone.ScaleY, yOut, 0)); + + xKeys[listIndex - 1] = px; + yKeys[listIndex - 1] = py; + + listIndex++; + lastTime = currentTime; + } + } + + frameIndex++; + f += 3; + } + + xCurve = EnsureCurveKeyCount(new AnimationCurve(xKeys.ToArray())); + yCurve = EnsureCurveKeyCount(new AnimationCurve(yKeys.ToArray())); + + string path = GetPath(boneData); + string propertyName = "localScale"; + + clip.SetCurve(path, typeof(Transform), propertyName + ".x", xCurve); + clip.SetCurve(path, typeof(Transform), propertyName + ".y", yCurve); + clip.SetCurve(path, typeof(Transform), propertyName + ".z", zCurve); + } + + static void ParseRotateTimeline (Skeleton skeleton, RotateTimeline timeline, AnimationClip clip) { + var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; + var bone = skeleton.Bones.Items[timeline.BoneIndex]; + + AnimationCurve curve = new AnimationCurve(); + + float endTime = timeline.Frames[(timeline.FrameCount * 2) - 2]; + + float currentTime = timeline.Frames[0]; + + List keys = new List(); + + float rotation = timeline.Frames[1] + boneData.Rotation; + + keys.Add(new Keyframe(timeline.Frames[0], rotation, 0, 0)); + + int listIndex = 1; + int frameIndex = 1; + int f = 2; + float[] frames = timeline.Frames; + skeleton.SetToSetupPose(); + float lastTime = 0; + float angle = rotation; + while (currentTime < endTime) { + int pIndex = listIndex - 1; + float curveType = timeline.GetCurveType(frameIndex - 1); + + if (curveType == 0) { + //linear + Keyframe pk = keys[pIndex]; + + float time = frames[f]; + + rotation = frames[f + 1] + boneData.Rotation; angle += Mathf.DeltaAngle(angle, rotation); - r = angle; + float r = angle; - float rOut = (r - pk.value) / (currentTime - pk.time); + float rOut = (r - pk.value) / (time - pk.time); pk.outTangent = rOut; - keys.Add(new Keyframe(currentTime, r, rOut, 0)); + keys.Add(new Keyframe(time, r, rOut, 0)); - keys[listIndex - 1] = pk; + keys[pIndex] = pk; + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1); + + lastTime = time; listIndex++; - lastTime = currentTime; + } else if (curveType == 1) { + //stepped + + Keyframe pk = keys[pIndex]; + + float time = frames[f]; + + rotation = frames[f + 1] + boneData.Rotation; + angle += Mathf.DeltaAngle(angle, rotation); + float r = angle; + + float rOut = float.PositiveInfinity; + + pk.outTangent = rOut; + + keys.Add(new Keyframe(time, r, rOut, 0)); + + keys[pIndex] = pk; + + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1); + + lastTime = time; + listIndex++; + } else if (curveType == 2) { + //bezier + Keyframe pk = keys[pIndex]; + + float time = frames[f]; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1); + skeleton.UpdateWorldTransform(); + + rotation = frames[f + 1] + boneData.Rotation; + angle += Mathf.DeltaAngle(angle, rotation); + float r = angle; + + int steps = Mathf.FloorToInt((time - pk.time) / bakeIncrement); + + for (int i = 1; i <= steps; i++) { + currentTime += bakeIncrement; + if (i == steps) + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1); + skeleton.UpdateWorldTransform(); + pk = keys[listIndex - 1]; + + rotation = bone.Rotation; + angle += Mathf.DeltaAngle(angle, rotation); + r = angle; + + float rOut = (r - pk.value) / (currentTime - pk.time); + + pk.outTangent = rOut; + + keys.Add(new Keyframe(currentTime, r, rOut, 0)); + + keys[listIndex - 1] = pk; + + listIndex++; + lastTime = currentTime; + } } + + frameIndex++; + f += 2; } - frameIndex++; - f += 2; + curve = EnsureCurveKeyCount(new AnimationCurve(keys.ToArray())); + + string path = GetPath(boneData); + string propertyName = "localEulerAnglesBaked"; + + EditorCurveBinding xBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".x"); + AnimationUtility.SetEditorCurve(clip, xBind, new AnimationCurve()); + EditorCurveBinding yBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".y"); + AnimationUtility.SetEditorCurve(clip, yBind, new AnimationCurve()); + EditorCurveBinding zBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".z"); + AnimationUtility.SetEditorCurve(clip, zBind, curve); } - curve = EnsureCurveKeyCount(new AnimationCurve(keys.ToArray())); - - string path = GetPath(boneData); - string propertyName = "localEulerAnglesBaked"; - - EditorCurveBinding xBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".x"); - AnimationUtility.SetEditorCurve(clip, xBind, new AnimationCurve()); - EditorCurveBinding yBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".y"); - AnimationUtility.SetEditorCurve(clip, yBind, new AnimationCurve()); - EditorCurveBinding zBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".z"); - AnimationUtility.SetEditorCurve(clip, zBind, curve); - } - - static string GetPath (BoneData b) { - return GetPathRecurse(b).Substring(1); - } - - static string GetPathRecurse (BoneData b) { - if (b == null) { - return ""; + static string GetPath (BoneData b) { + return GetPathRecurse(b).Substring(1); } - return GetPathRecurse(b.Parent) + "/" + b.Name; + static string GetPathRecurse (BoneData b) { + if (b == null) { + return ""; + } + + return GetPathRecurse(b.Parent) + "/" + b.Name; + } + #endregion + + static void SetAnimationSettings (AnimationClip clip, AnimationClipSettings settings) { + #if UNITY_5 + AnimationUtility.SetAnimationClipSettings(clip, settings); + #else + MethodInfo methodInfo = typeof(AnimationUtility).GetMethod("SetAnimationClipSettings", BindingFlags.Static | BindingFlags.NonPublic); + methodInfo.Invoke(null, new object[] { clip, settings }); + + EditorUtility.SetDirty(clip); + #endif + } + + } - #endregion - - static void SetAnimationSettings (AnimationClip clip, AnimationClipSettings settings) { -#if UNITY_5 - AnimationUtility.SetAnimationClipSettings(clip, settings); -#else - MethodInfo methodInfo = typeof(AnimationUtility).GetMethod("SetAnimationClipSettings", BindingFlags.Static | BindingFlags.NonPublic); - methodInfo.Invoke(null, new object[] { clip, settings }); - - EditorUtility.SetDirty(clip); -#endif - } - } + diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs index 7fae030e8..150644436 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs @@ -33,10 +33,10 @@ using System; using UnityEditor; using UnityEngine; -namespace Spine.Unity { +namespace Spine.Unity.Editor { [CustomEditor(typeof(SkeletonRenderer))] - public class SkeletonRendererInspector : Editor { + public class SkeletonRendererInspector : UnityEditor.Editor { protected static bool advancedFoldout; protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles, separatorSlotNames, front, zSpacing; diff --git a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs index a2e7152b3..a4db8a87d 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs @@ -6,351 +6,351 @@ using UnityEngine; using UnityEditor; using System; -using System.Collections; using System.Collections.Generic; using System.Reflection; using Spine; +namespace Spine.Unity.Editor { + public struct SpineDrawerValuePair { + public string str; + public SerializedProperty property; - -public struct SpineDrawerValuePair { - public string str; - public SerializedProperty property; - - public SpineDrawerValuePair (string val, SerializedProperty property) { - this.str = val; - this.property = property; - } -} - -public abstract class SpineTreeItemDrawerBase : PropertyDrawer where T:SpineAttributeBase { - protected SkeletonDataAsset skeletonDataAsset; - internal const string NoneLabel = ""; - - protected T TargetAttribute { get { return (T)attribute; } } - - public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { - if (property.propertyType != SerializedPropertyType.String) { - EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); - return; + public SpineDrawerValuePair (string val, SerializedProperty property) { + this.str = val; + this.property = property; } - - var dataProperty = property.serializedObject.FindProperty(TargetAttribute.dataField); - - if (dataProperty != null) { - if (dataProperty.objectReferenceValue is SkeletonDataAsset) { - skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; - } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { - var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; - if (renderer != null) - skeletonDataAsset = renderer.skeletonDataAsset; - } else { - EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); + } + + public abstract class SpineTreeItemDrawerBase : PropertyDrawer where T:SpineAttributeBase { + protected SkeletonDataAsset skeletonDataAsset; + internal const string NoneLabel = ""; + + protected T TargetAttribute { get { return (T)attribute; } } + + public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { + if (property.propertyType != SerializedPropertyType.String) { + EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); return; } - - } else if (property.serializedObject.targetObject is Component) { - var component = (Component)property.serializedObject.targetObject; - if (component.GetComponentInChildren() != null) { - var skeletonRenderer = component.GetComponentInChildren(); - skeletonDataAsset = skeletonRenderer.skeletonDataAsset; - } - } - - if (skeletonDataAsset == null) { - EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset"); - return; - } - - position = EditorGUI.PrefixLabel(position, label); - var propertyStringValue = property.stringValue; - if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel : propertyStringValue, EditorStyles.popup)) { - Selector(property); - } - - } + var dataProperty = property.serializedObject.FindProperty(TargetAttribute.dataField); - protected virtual void Selector (SerializedProperty property) { - SkeletonData data = skeletonDataAsset.GetSkeletonData(true); - if (data == null) - return; - - GenericMenu menu = new GenericMenu(); - PopulateMenu(menu, property, this.TargetAttribute, data); - menu.ShowAsContext(); - } - - protected abstract void PopulateMenu (GenericMenu menu, SerializedProperty property, T targetAttribute, SkeletonData data); - - protected virtual void HandleSelect (object val) { - var pair = (SpineDrawerValuePair)val; - pair.property.stringValue = pair.str; - pair.property.serializedObject.ApplyModifiedProperties(); - } - - public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { - return 18; - } - -} - -[CustomPropertyDrawer(typeof(SpineSlot))] -public class SpineSlotDrawer : SpineTreeItemDrawerBase { - - protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) { - for (int i = 0; i < data.Slots.Count; i++) { - string name = data.Slots.Items[i].Name; - if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) { - if (targetAttribute.containsBoundingBoxes) { - - int slotIndex = i; - - List attachments = new List(); - foreach (var skin in data.Skins) { - skin.FindAttachmentsForSlot(slotIndex, attachments); - } - - bool hasBoundingBox = false; - foreach (var attachment in attachments) { - if (attachment is BoundingBoxAttachment) { - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); - hasBoundingBox = true; - break; - } - } - - if (!hasBoundingBox) - menu.AddDisabledItem(new GUIContent(name)); - - + if (dataProperty != null) { + if (dataProperty.objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; + } else if (dataProperty.objectReferenceValue is SkeletonRenderer) { + var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue; + if (renderer != null) + skeletonDataAsset = renderer.skeletonDataAsset; } else { - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); + return; + } + + } else if (property.serializedObject.targetObject is Component) { + var component = (Component)property.serializedObject.targetObject; + if (component.GetComponentInChildren() != null) { + var skeletonRenderer = component.GetComponentInChildren(); + skeletonDataAsset = skeletonRenderer.skeletonDataAsset; } - } - + + if (skeletonDataAsset == null) { + EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset"); + return; + } + + position = EditorGUI.PrefixLabel(position, label); + + var propertyStringValue = property.stringValue; + if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel : propertyStringValue, EditorStyles.popup)) { + Selector(property); + } + } + + protected virtual void Selector (SerializedProperty property) { + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + if (data == null) + return; + + GenericMenu menu = new GenericMenu(); + PopulateMenu(menu, property, this.TargetAttribute, data); + menu.ShowAsContext(); + } + + protected abstract void PopulateMenu (GenericMenu menu, SerializedProperty property, T targetAttribute, SkeletonData data); + + protected virtual void HandleSelect (object val) { + var pair = (SpineDrawerValuePair)val; + pair.property.stringValue = pair.str; + pair.property.serializedObject.ApplyModifiedProperties(); + } + + public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { + return 18; + } + } -} + [CustomPropertyDrawer(typeof(SpineSlot))] + public class SpineSlotDrawer : SpineTreeItemDrawerBase { -[CustomPropertyDrawer(typeof(SpineSkin))] -public class SpineSkinDrawer : SpineTreeItemDrawerBase { - - protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSkin targetAttribute, SkeletonData data) { - menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); - menu.AddSeparator(""); - - for (int i = 0; i < data.Skins.Count; i++) { - string name = data.Skins.Items[i].Name; - if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); - } - } - -} - -[CustomPropertyDrawer(typeof(SpineAnimation))] -public class SpineAnimationDrawer : SpineTreeItemDrawerBase { - protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) { - var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations; - - // item - menu.AddItem(new GUIContent(NoneLabel), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair("", property)); - - for (int i = 0; i < animations.Count; i++) { - string name = animations.Items[i].Name; - if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); - } - } - -} - -[CustomPropertyDrawer(typeof(SpineEvent))] -public class SpineEventNameDrawer : SpineTreeItemDrawerBase { - protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEvent targetAttribute, SkeletonData data) { - var events = skeletonDataAsset.GetSkeletonData(false).Events; - for (int i = 0; i < events.Count; i++) { - string name = events.Items[i].Name; - if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); - } - } - -} - -[CustomPropertyDrawer(typeof(SpineAttachment))] -public class SpineAttachmentDrawer : SpineTreeItemDrawerBase { - protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAttachment targetAttribute, SkeletonData data) { - List validSkins = new List(); - SkeletonRenderer skeletonRenderer = null; - - var component = property.serializedObject.targetObject as Component; - if (component != null) { - if (component.GetComponentInChildren() != null) { - skeletonRenderer = component.GetComponentInChildren(); - //if (skeletonDataAsset != skeletonRenderer.skeletonDataAsset) Debug.LogWarning("DataField SkeletonDataAsset and SkeletonRenderer/SkeletonAnimation's SkeletonDataAsset do not match. Remove the explicit dataField parameter of your [SpineAttachment] field."); - skeletonDataAsset = skeletonRenderer.skeletonDataAsset; - } - } - - if (skeletonRenderer != null && targetAttribute.currentSkinOnly) { - if (skeletonRenderer.skeleton.Skin != null) { - validSkins.Add(skeletonRenderer.skeleton.Skin); - } else { - validSkins.Add(data.Skins.Items[0]); - } - } else { - foreach (Skin skin in data.Skins) { - if (skin != null) - validSkins.Add(skin); - } - } - - List attachmentNames = new List(); - List placeholderNames = new List(); - - string prefix = ""; - - if (skeletonRenderer != null && targetAttribute.currentSkinOnly) - menu.AddDisabledItem(new GUIContent(skeletonRenderer.gameObject.name + " (SkeletonRenderer)")); - else - menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); - - menu.AddSeparator(""); - - menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property)); - - menu.AddSeparator(""); - - Skin defaultSkin = data.Skins.Items[0]; - - SerializedProperty slotProperty = property.serializedObject.FindProperty(targetAttribute.slotField); - string slotMatch = ""; - if (slotProperty != null) { - if (slotProperty.propertyType == SerializedPropertyType.String) { - slotMatch = slotProperty.stringValue.ToLower(); - } - } - - foreach (Skin skin in validSkins) { - string skinPrefix = skin.Name + "/"; - - if (validSkins.Count > 1) - prefix = skinPrefix; - + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) { for (int i = 0; i < data.Slots.Count; i++) { - if (slotMatch.Length > 0 && data.Slots.Items[i].Name.ToLower().Contains(slotMatch) == false) - continue; - - attachmentNames.Clear(); - placeholderNames.Clear(); - - skin.FindNamesForSlot(i, attachmentNames); - if (skin != defaultSkin) { - defaultSkin.FindNamesForSlot(i, attachmentNames); - skin.FindNamesForSlot(i, placeholderNames); - } - - - for (int a = 0; a < attachmentNames.Count; a++) { - - string attachmentPath = attachmentNames[a]; - string menuPath = prefix + data.Slots.Items[i].Name + "/" + attachmentPath; - string name = attachmentNames[a]; - - if (targetAttribute.returnAttachmentPath) - name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath; - - if (targetAttribute.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) { - menu.AddDisabledItem(new GUIContent(menuPath)); + string name = data.Slots.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) { + if (targetAttribute.containsBoundingBoxes) { + + int slotIndex = i; + + List attachments = new List(); + foreach (var skin in data.Skins) { + skin.FindAttachmentsForSlot(slotIndex, attachments); + } + + bool hasBoundingBox = false; + foreach (var attachment in attachments) { + if (attachment is BoundingBoxAttachment) { + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + hasBoundingBox = true; + break; + } + } + + if (!hasBoundingBox) + menu.AddDisabledItem(new GUIContent(name)); + + } else { - menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + } + + } + } + + } + + [CustomPropertyDrawer(typeof(SpineSkin))] + public class SpineSkinDrawer : SpineTreeItemDrawerBase { + + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSkin targetAttribute, SkeletonData data) { + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + menu.AddSeparator(""); + + for (int i = 0; i < data.Skins.Count; i++) { + string name = data.Skins.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + + } + + [CustomPropertyDrawer(typeof(SpineAnimation))] + public class SpineAnimationDrawer : SpineTreeItemDrawerBase { + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) { + var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations; + + // item + menu.AddItem(new GUIContent(NoneLabel), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair("", property)); + + for (int i = 0; i < animations.Count; i++) { + string name = animations.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + + } + + [CustomPropertyDrawer(typeof(SpineEvent))] + public class SpineEventNameDrawer : SpineTreeItemDrawerBase { + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEvent targetAttribute, SkeletonData data) { + var events = skeletonDataAsset.GetSkeletonData(false).Events; + for (int i = 0; i < events.Count; i++) { + string name = events.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + + } + + [CustomPropertyDrawer(typeof(SpineAttachment))] + public class SpineAttachmentDrawer : SpineTreeItemDrawerBase { + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAttachment targetAttribute, SkeletonData data) { + List validSkins = new List(); + SkeletonRenderer skeletonRenderer = null; + + var component = property.serializedObject.targetObject as Component; + if (component != null) { + if (component.GetComponentInChildren() != null) { + skeletonRenderer = component.GetComponentInChildren(); + //if (skeletonDataAsset != skeletonRenderer.skeletonDataAsset) Debug.LogWarning("DataField SkeletonDataAsset and SkeletonRenderer/SkeletonAnimation's SkeletonDataAsset do not match. Remove the explicit dataField parameter of your [SpineAttachment] field."); + skeletonDataAsset = skeletonRenderer.skeletonDataAsset; + } + } + + if (skeletonRenderer != null && targetAttribute.currentSkinOnly) { + if (skeletonRenderer.skeleton.Skin != null) { + validSkins.Add(skeletonRenderer.skeleton.Skin); + } else { + validSkins.Add(data.Skins.Items[0]); + } + } else { + foreach (Skin skin in data.Skins) { + if (skin != null) + validSkins.Add(skin); + } + } + + List attachmentNames = new List(); + List placeholderNames = new List(); + + string prefix = ""; + + if (skeletonRenderer != null && targetAttribute.currentSkinOnly) + menu.AddDisabledItem(new GUIContent(skeletonRenderer.gameObject.name + " (SkeletonRenderer)")); + else + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + + menu.AddSeparator(""); + + menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property)); + + menu.AddSeparator(""); + + Skin defaultSkin = data.Skins.Items[0]; + + SerializedProperty slotProperty = property.serializedObject.FindProperty(targetAttribute.slotField); + string slotMatch = ""; + if (slotProperty != null) { + if (slotProperty.propertyType == SerializedPropertyType.String) { + slotMatch = slotProperty.stringValue.ToLower(); + } + } + + foreach (Skin skin in validSkins) { + string skinPrefix = skin.Name + "/"; + + if (validSkins.Count > 1) + prefix = skinPrefix; + + for (int i = 0; i < data.Slots.Count; i++) { + if (slotMatch.Length > 0 && data.Slots.Items[i].Name.ToLower().Contains(slotMatch) == false) + continue; + + attachmentNames.Clear(); + placeholderNames.Clear(); + + skin.FindNamesForSlot(i, attachmentNames); + if (skin != defaultSkin) { + defaultSkin.FindNamesForSlot(i, attachmentNames); + skin.FindNamesForSlot(i, placeholderNames); + } + + + for (int a = 0; a < attachmentNames.Count; a++) { + + string attachmentPath = attachmentNames[a]; + string menuPath = prefix + data.Slots.Items[i].Name + "/" + attachmentPath; + string name = attachmentNames[a]; + + if (targetAttribute.returnAttachmentPath) + name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath; + + if (targetAttribute.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) { + menu.AddDisabledItem(new GUIContent(menuPath)); + } else { + menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + } - - } } } + } -} + [CustomPropertyDrawer(typeof(SpineBone))] + public class SpineBoneDrawer : SpineTreeItemDrawerBase { -[CustomPropertyDrawer(typeof(SpineBone))] -public class SpineBoneDrawer : SpineTreeItemDrawerBase { + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineBone targetAttribute, SkeletonData data) { + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + menu.AddSeparator(""); - protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineBone targetAttribute, SkeletonData data) { - menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); - menu.AddSeparator(""); - - for (int i = 0; i < data.Bones.Count; i++) { - string name = data.Bones.Items[i].Name; - if (name.StartsWith(targetAttribute.startsWith)) + for (int i = 0; i < data.Bones.Count; i++) { + string name = data.Bones.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + + } + + [CustomPropertyDrawer(typeof(SpineAtlasRegion))] + public class SpineAtlasRegionDrawer : PropertyDrawer { + Component component; + SerializedProperty atlasProp; + + public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { + if (property.propertyType != SerializedPropertyType.String) { + EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); + return; + } + + component = (Component)property.serializedObject.targetObject; + + if (component != null) + atlasProp = property.serializedObject.FindProperty("atlasAsset"); + else + atlasProp = null; + + + if (atlasProp == null) { + EditorGUI.LabelField(position, "ERROR:", "Must have AtlasAsset variable!"); + return; + } else if (atlasProp.objectReferenceValue == null) { + EditorGUI.LabelField(position, "ERROR:", "Atlas variable must not be null!"); + return; + } else if (atlasProp.objectReferenceValue.GetType() != typeof(AtlasAsset)) { + EditorGUI.LabelField(position, "ERROR:", "Atlas variable must be of type AtlasAsset!"); + } + + position = EditorGUI.PrefixLabel(position, label); + + if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { + Selector(property); + } + + } + + void Selector (SerializedProperty property) { + GenericMenu menu = new GenericMenu(); + AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue; + Atlas atlas = atlasAsset.GetAtlas(); + FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); + List regions = (List)field.GetValue(atlas); + + for (int i = 0; i < regions.Count; i++) { + string name = regions[i].name; menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + + menu.ShowAsContext(); } + + static void HandleSelect (object val) { + var pair = (SpineDrawerValuePair)val; + pair.property.stringValue = pair.str; + pair.property.serializedObject.ApplyModifiedProperties(); + } + } } - -[CustomPropertyDrawer(typeof(SpineAtlasRegion))] -public class SpineAtlasRegionDrawer : PropertyDrawer { - Component component; - SerializedProperty atlasProp; - - public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { - if (property.propertyType != SerializedPropertyType.String) { - EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); - return; - } - - component = (Component)property.serializedObject.targetObject; - - if (component != null) - atlasProp = property.serializedObject.FindProperty("atlasAsset"); - else - atlasProp = null; - - - if (atlasProp == null) { - EditorGUI.LabelField(position, "ERROR:", "Must have AtlasAsset variable!"); - return; - } else if (atlasProp.objectReferenceValue == null) { - EditorGUI.LabelField(position, "ERROR:", "Atlas variable must not be null!"); - return; - } else if (atlasProp.objectReferenceValue.GetType() != typeof(AtlasAsset)) { - EditorGUI.LabelField(position, "ERROR:", "Atlas variable must be of type AtlasAsset!"); - } - - position = EditorGUI.PrefixLabel(position, label); - - if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { - Selector(property); - } - - } - - void Selector (SerializedProperty property) { - GenericMenu menu = new GenericMenu(); - AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue; - Atlas atlas = atlasAsset.GetAtlas(); - FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); - List regions = (List)field.GetValue(atlas); - - for (int i = 0; i < regions.Count; i++) { - string name = regions[i].name; - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); - } - - - menu.ShowAsContext(); - } - - static void HandleSelect (object val) { - var pair = (SpineDrawerValuePair)val; - pair.property.stringValue = pair.str; - pair.property.serializedObject.ApplyModifiedProperties(); - } - -} diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs index d99750ab3..41474c240 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs @@ -35,6 +35,7 @@ * Spine Editor Utilities created by Mitch Thompson * Full irrevocable rights and permissions granted to Esoteric Software *****************************************************************************/ +#define SPINE_SKELETONANIMATOR using UnityEngine; using UnityEditor; using System.Collections; @@ -45,253 +46,257 @@ using System.Linq; using System.Reflection; using Spine; -[InitializeOnLoad] -public class SpineEditorUtilities : AssetPostprocessor { +namespace Spine.Unity.Editor { + + [InitializeOnLoad] + public class SpineEditorUtilities : AssetPostprocessor { - public static class Icons { - public static Texture2D skeleton; - public static Texture2D nullBone; - public static Texture2D bone; - public static Texture2D poseBones; - public static Texture2D boneNib; - public static Texture2D slot; - public static Texture2D slotRoot; - public static Texture2D skinPlaceholder; - public static Texture2D image; - public static Texture2D boundingBox; - public static Texture2D mesh; - public static Texture2D weights; - public static Texture2D skin; - public static Texture2D skinsRoot; - public static Texture2D animation; - public static Texture2D animationRoot; - public static Texture2D spine; - public static Texture2D _event; - public static Texture2D constraintNib; - public static Texture2D warning; - public static Texture2D skeletonUtility; - public static Texture2D hingeChain; - public static Texture2D subMeshRenderer; - public static Texture2D unityIcon; - public static Texture2D controllerIcon; + public static class Icons { + public static Texture2D skeleton; + public static Texture2D nullBone; + public static Texture2D bone; + public static Texture2D poseBones; + public static Texture2D boneNib; + public static Texture2D slot; + public static Texture2D slotRoot; + public static Texture2D skinPlaceholder; + public static Texture2D image; + public static Texture2D boundingBox; + public static Texture2D mesh; + public static Texture2D weights; + public static Texture2D skin; + public static Texture2D skinsRoot; + public static Texture2D animation; + public static Texture2D animationRoot; + public static Texture2D spine; + public static Texture2D _event; + public static Texture2D constraintNib; + public static Texture2D warning; + public static Texture2D skeletonUtility; + public static Texture2D hingeChain; + public static Texture2D subMeshRenderer; + public static Texture2D unityIcon; + public static Texture2D controllerIcon; - public static Mesh boneMesh { - get { - if (_boneMesh == null) { - _boneMesh = new Mesh(); - _boneMesh.vertices = new Vector3[4] { - Vector3.zero, - new Vector3(-0.1f, 0.1f, 0), - Vector3.up, - new Vector3(0.1f, 0.1f, 0) - }; - _boneMesh.uv = new Vector2[4]; - _boneMesh.triangles = new int[6] { 0, 1, 2, 2, 3, 0 }; - _boneMesh.RecalculateBounds(); - _boneMesh.RecalculateNormals(); + public static Mesh boneMesh { + get { + if (_boneMesh == null) { + _boneMesh = new Mesh(); + _boneMesh.vertices = new Vector3[4] { + Vector3.zero, + new Vector3(-0.1f, 0.1f, 0), + Vector3.up, + new Vector3(0.1f, 0.1f, 0) + }; + _boneMesh.uv = new Vector2[4]; + _boneMesh.triangles = new int[6] { 0, 1, 2, 2, 3, 0 }; + _boneMesh.RecalculateBounds(); + _boneMesh.RecalculateNormals(); + } + + return _boneMesh; } + } - return _boneMesh; + internal static Mesh _boneMesh; + + public static Material boneMaterial { + get { + if (_boneMaterial == null) { + #if UNITY_4_3 + _boneMaterial = new Material(Shader.Find("Particles/Alpha Blended")); + _boneMaterial.SetColor("_TintColor", new Color(0.4f, 0.4f, 0.4f, 0.25f)); + #else + _boneMaterial = new Material(Shader.Find("Spine/Bones")); + _boneMaterial.SetColor("_Color", new Color(0.4f, 0.4f, 0.4f, 0.25f)); + #endif + + } + + return _boneMaterial; + } + } + + internal static Material _boneMaterial; + + public static void Initialize () { + skeleton = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skeleton.png"); + nullBone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-null.png"); + bone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-bone.png"); + poseBones = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-poseBones.png"); + boneNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boneNib.png"); + slot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slot.png"); + slotRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slotRoot.png"); + skinPlaceholder = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png"); + image = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-image.png"); + boundingBox = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boundingBox.png"); + mesh = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-mesh.png"); + weights = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-weights.png"); + skin = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png"); + skinsRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinsRoot.png"); + animation = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animation.png"); + animationRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animationRoot.png"); + spine = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-spine.png"); + _event = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-event.png"); + constraintNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-constraintNib.png"); + warning = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-warning.png"); + skeletonUtility = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skeletonUtility.png"); + hingeChain = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-hingeChain.png"); + subMeshRenderer = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-subMeshRenderer.png"); + + unityIcon = EditorGUIUtility.FindTexture("SceneAsset Icon"); + + controllerIcon = EditorGUIUtility.FindTexture("AnimatorController Icon"); } } - internal static Mesh _boneMesh; + public static string editorPath = ""; + public static string editorGUIPath = ""; + static HashSet assetsImportedInWrongState; + static Dictionary skeletonRendererTable; + static Dictionary skeletonUtilityBoneTable; + static Dictionary boundingBoxFollowerTable; + public static float defaultScale = 0.01f; + public static float defaultMix = 0.2f; + public static string defaultShader = "Spine/Skeleton"; + public static bool initialized; - public static Material boneMaterial { - get { - if (_boneMaterial == null) { -#if UNITY_4_3 - _boneMaterial = new Material(Shader.Find("Particles/Alpha Blended")); - _boneMaterial.SetColor("_TintColor", new Color(0.4f, 0.4f, 0.4f, 0.25f)); -#else - _boneMaterial = new Material(Shader.Find("Spine/Bones")); - _boneMaterial.SetColor("_Color", new Color(0.4f, 0.4f, 0.4f, 0.25f)); -#endif + const string DEFAULT_MIX_KEY = "SPINE_DEFAULT_MIX"; - } - - return _boneMaterial; - } - } - - internal static Material _boneMaterial; - - public static void Initialize () { - skeleton = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skeleton.png"); - nullBone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-null.png"); - bone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-bone.png"); - poseBones = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-poseBones.png"); - boneNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boneNib.png"); - slot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slot.png"); - slotRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slotRoot.png"); - skinPlaceholder = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png"); - image = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-image.png"); - boundingBox = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boundingBox.png"); - mesh = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-mesh.png"); - weights = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-weights.png"); - skin = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png"); - skinsRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinsRoot.png"); - animation = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animation.png"); - animationRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animationRoot.png"); - spine = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-spine.png"); - _event = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-event.png"); - constraintNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-constraintNib.png"); - warning = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-warning.png"); - skeletonUtility = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skeletonUtility.png"); - hingeChain = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-hingeChain.png"); - subMeshRenderer = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-subMeshRenderer.png"); - - unityIcon = EditorGUIUtility.FindTexture("SceneAsset Icon"); - - controllerIcon = EditorGUIUtility.FindTexture("AnimatorController Icon"); - } - } - - public static string editorPath = ""; - public static string editorGUIPath = ""; - static HashSet assetsImportedInWrongState; - static Dictionary skeletonRendererTable; - static Dictionary skeletonUtilityBoneTable; - static Dictionary boundingBoxFollowerTable; - public static float defaultScale = 0.01f; - public static float defaultMix = 0.2f; - public static string defaultShader = "Spine/Skeleton"; - public static bool initialized; - - const string DEFAULT_MIX_KEY = "SPINE_DEFAULT_MIX"; - - static SpineEditorUtilities () { - Initialize(); - } - - static void Initialize () { - defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f); - - DirectoryInfo rootDir = new DirectoryInfo(Application.dataPath); - FileInfo[] files = rootDir.GetFiles("SpineEditorUtilities.cs", SearchOption.AllDirectories); - editorPath = Path.GetDirectoryName(files[0].FullName.Replace("\\", "/").Replace(Application.dataPath, "Assets")); - editorGUIPath = editorPath + "/GUI"; - - Icons.Initialize(); - - assetsImportedInWrongState = new HashSet(); - skeletonRendererTable = new Dictionary(); - skeletonUtilityBoneTable = new Dictionary(); - boundingBoxFollowerTable = new Dictionary(); - - EditorApplication.hierarchyWindowChanged += HierarchyWindowChanged; - EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI; - - HierarchyWindowChanged(); - initialized = true; - } - - public static void ConfirmInitialization () { - if (!initialized || Icons.skeleton == null) + static SpineEditorUtilities () { Initialize(); - } + } - static void HierarchyWindowChanged () { - skeletonRendererTable.Clear(); - skeletonUtilityBoneTable.Clear(); - boundingBoxFollowerTable.Clear(); + static void Initialize () { + defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f); - SkeletonRenderer[] arr = Object.FindObjectsOfType(); - foreach (SkeletonRenderer r in arr) - skeletonRendererTable.Add(r.gameObject.GetInstanceID(), r.gameObject); + DirectoryInfo rootDir = new DirectoryInfo(Application.dataPath); + FileInfo[] files = rootDir.GetFiles("SpineEditorUtilities.cs", SearchOption.AllDirectories); + editorPath = Path.GetDirectoryName(files[0].FullName.Replace("\\", "/").Replace(Application.dataPath, "Assets")); + editorGUIPath = editorPath + "/GUI"; - SkeletonUtilityBone[] boneArr = Object.FindObjectsOfType(); - foreach (SkeletonUtilityBone b in boneArr) - skeletonUtilityBoneTable.Add(b.gameObject.GetInstanceID(), b); + Icons.Initialize(); - BoundingBoxFollower[] bbfArr = Object.FindObjectsOfType(); - foreach (BoundingBoxFollower bbf in bbfArr) - boundingBoxFollowerTable.Add(bbf.gameObject.GetInstanceID(), bbf); - } + assetsImportedInWrongState = new HashSet(); + skeletonRendererTable = new Dictionary(); + skeletonUtilityBoneTable = new Dictionary(); + boundingBoxFollowerTable = new Dictionary(); - static void HierarchyWindowItemOnGUI (int instanceId, Rect selectionRect) { - if (skeletonRendererTable.ContainsKey(instanceId)) { - Rect r = new Rect(selectionRect); - r.x = r.width - 15; - r.width = 15; + EditorApplication.hierarchyWindowChanged += HierarchyWindowChanged; + EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI; - GUI.Label(r, Icons.spine); - } else if (skeletonUtilityBoneTable.ContainsKey(instanceId)) { - Rect r = new Rect(selectionRect); - r.x -= 26; + HierarchyWindowChanged(); + initialized = true; + } - if (skeletonUtilityBoneTable[instanceId] != null) { - if (skeletonUtilityBoneTable[instanceId].transform.childCount == 0) - r.x += 13; + public static void ConfirmInitialization () { + if (!initialized || Icons.skeleton == null) + Initialize(); + } - r.y += 2; + #region Hierarchy Icon + static void HierarchyWindowChanged () { + skeletonRendererTable.Clear(); + skeletonUtilityBoneTable.Clear(); + boundingBoxFollowerTable.Clear(); - r.width = 13; - r.height = 13; + SkeletonRenderer[] arr = Object.FindObjectsOfType(); + foreach (SkeletonRenderer r in arr) + skeletonRendererTable.Add(r.gameObject.GetInstanceID(), r.gameObject); - if (skeletonUtilityBoneTable[instanceId].mode == SkeletonUtilityBone.Mode.Follow) { - GUI.DrawTexture(r, Icons.bone); - } else { - GUI.DrawTexture(r, Icons.poseBones); + SkeletonUtilityBone[] boneArr = Object.FindObjectsOfType(); + foreach (SkeletonUtilityBone b in boneArr) + skeletonUtilityBoneTable.Add(b.gameObject.GetInstanceID(), b); + + BoundingBoxFollower[] bbfArr = Object.FindObjectsOfType(); + foreach (BoundingBoxFollower bbf in bbfArr) + boundingBoxFollowerTable.Add(bbf.gameObject.GetInstanceID(), bbf); + } + + static void HierarchyWindowItemOnGUI (int instanceId, Rect selectionRect) { + if (skeletonRendererTable.ContainsKey(instanceId)) { + Rect r = new Rect(selectionRect); + r.x = r.width - 15; + r.width = 15; + + GUI.Label(r, Icons.spine); + } else if (skeletonUtilityBoneTable.ContainsKey(instanceId)) { + Rect r = new Rect(selectionRect); + r.x -= 26; + + if (skeletonUtilityBoneTable[instanceId] != null) { + if (skeletonUtilityBoneTable[instanceId].transform.childCount == 0) + r.x += 13; + + r.y += 2; + + r.width = 13; + r.height = 13; + + if (skeletonUtilityBoneTable[instanceId].mode == SkeletonUtilityBone.Mode.Follow) { + GUI.DrawTexture(r, Icons.bone); + } else { + GUI.DrawTexture(r, Icons.poseBones); + } + } + + } else if (boundingBoxFollowerTable.ContainsKey(instanceId)) { + Rect r = new Rect(selectionRect); + r.x -= 26; + + if (boundingBoxFollowerTable[instanceId] != null) { + if (boundingBoxFollowerTable[instanceId].transform.childCount == 0) + r.x += 13; + + r.y += 2; + + r.width = 13; + r.height = 13; + + GUI.DrawTexture(r, Icons.boundingBox); } } - } else if (boundingBoxFollowerTable.ContainsKey(instanceId)) { - Rect r = new Rect(selectionRect); - r.x -= 26; + } + #endregion - if (boundingBoxFollowerTable[instanceId] != null) { - if (boundingBoxFollowerTable[instanceId].transform.childCount == 0) - r.x += 13; + static void OnPostprocessAllAssets (string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) { + if (imported.Length == 0) + return; - r.y += 2; - - r.width = 13; - r.height = 13; - - GUI.DrawTexture(r, Icons.boundingBox); + // In case user used "Assets -> Reimport All", during the import process, + // asset database is not initialized until some point. During that period, + // all attempts to load any assets using API (i.e. AssetDatabase.LoadAssetAtPath) + // will return null, and as result, assets won't be loaded even if they actually exists, + // which may lead to numerous importing errors. + // This situation also happens if Library folder is deleted from the project, which is a pretty + // common case, since when using version control systems, the Library folder must be excluded. + // + // So to avoid this, in case asset database is not available, we delay loading the assets + // until next time. + // + // Unity *always* reimports some internal assets after the process is done, so this method + // is always called once again in a state when asset database is available. + // + // Checking whether AssetDatabase is initialized is done by attempting to load + // a known "marker" asset that should always be available. Failing to load this asset + // means that AssetDatabase is not initialized. + assetsImportedInWrongState.UnionWith(imported); + if (AssetDatabaseAvailabilityDetector.IsAssetDatabaseAvailable()) { + string[] combinedAssets = assetsImportedInWrongState.ToArray(); + assetsImportedInWrongState.Clear(); + ImportSpineContent(combinedAssets); } } - } + public static void ImportSpineContent (string[] imported, bool reimport = false) { + List atlasPaths = new List(); + List imagePaths = new List(); + List skeletonPaths = new List(); - static void OnPostprocessAllAssets (string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) { - if (imported.Length == 0) - return; - - // In case user used "Assets -> Reimport All", during the import process, - // asset database is not initialized until some point. During that period, - // all attempts to load any assets using API (i.e. AssetDatabase.LoadAssetAtPath) - // will return null, and as result, assets won't be loaded even if they actually exists, - // which may lead to numerous importing errors. - // This situation also happens if Library folder is deleted from the project, which is a pretty - // common case, since when using version control systems, the Library folder must be excluded. - // - // So to avoid this, in case asset database is not available, we delay loading the assets - // until next time. - // - // Unity *always* reimports some internal assets after the process is done, so this method - // is always called once again in a state when asset database is available. - // - // Checking whether AssetDatabase is initialized is done by attempting to load - // a known "marker" asset that should always be available. Failing to load this asset - // means that AssetDatabase is not initialized. - assetsImportedInWrongState.UnionWith(imported); - if (AssetDatabaseAvailabilityDetector.IsAssetDatabaseAvailable()) { - string[] combinedAssets = assetsImportedInWrongState.ToArray(); - assetsImportedInWrongState.Clear(); - ImportSpineContent(combinedAssets); - } - } - - public static void ImportSpineContent (string[] imported, bool reimport = false) { - List atlasPaths = new List(); - List imagePaths = new List(); - List skeletonPaths = new List(); - - foreach (string str in imported) { - string extension = Path.GetExtension(str).ToLower(); - switch (extension) { + foreach (string str in imported) { + string extension = Path.GetExtension(str).ToLower(); + switch (extension) { case ".txt": if (str.EndsWith(".atlas.txt")) { atlasPaths.Add(str); @@ -311,44 +316,44 @@ public class SpineEditorUtilities : AssetPostprocessor { skeletonPaths.Add(str); } break; - } - } - - - List atlases = new List(); - - //import atlases first - foreach (string ap in atlasPaths) { - if (!reimport && CheckForValidAtlas(ap)) - continue; - - TextAsset atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(ap, typeof(TextAsset)); - AtlasAsset atlas = IngestSpineAtlas(atlasText); - atlases.Add(atlas); - } - - //import skeletons and match them with atlases - bool abortSkeletonImport = false; - foreach (string sp in skeletonPaths) { - if (!reimport && CheckForValidSkeletonData(sp)) { - ResetExistingSkeletonData(sp); - continue; + } } - string dir = Path.GetDirectoryName(sp); + List atlases = new List(); - var localAtlases = FindAtlasesAtPath(dir); - var requiredPaths = GetRequiredAtlasRegions(sp); - var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases); + //import atlases first + foreach (string ap in atlasPaths) { + if (!reimport && CheckForValidAtlas(ap)) + continue; - if (atlasMatch != null) { - IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch); - } else { - bool resolved = false; - while (!resolved) { - int result = EditorUtility.DisplayDialogComplex("Skeleton JSON Import Error!", "Could not find matching AtlasAsset for " + Path.GetFileNameWithoutExtension(sp), "Select", "Skip", "Abort"); - switch (result) { + TextAsset atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(ap, typeof(TextAsset)); + AtlasAsset atlas = IngestSpineAtlas(atlasText); + atlases.Add(atlas); + } + + //import skeletons and match them with atlases + bool abortSkeletonImport = false; + foreach (string sp in skeletonPaths) { + if (!reimport && CheckForValidSkeletonData(sp)) { + ResetExistingSkeletonData(sp); + continue; + } + + + string dir = Path.GetDirectoryName(sp); + + var localAtlases = FindAtlasesAtPath(dir); + var requiredPaths = GetRequiredAtlasRegions(sp); + var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases); + + if (atlasMatch != null) { + IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch); + } else { + bool resolved = false; + while (!resolved) { + int result = EditorUtility.DisplayDialogComplex("Skeleton JSON Import Error!", "Could not find matching AtlasAsset for " + Path.GetFileNameWithoutExtension(sp), "Select", "Skip", "Abort"); + switch (result) { case -1: Debug.Log("Select Atlas"); AtlasAsset selectedAtlas = GetAtlasDialog(Path.GetDirectoryName(sp)); @@ -383,104 +388,96 @@ public class SpineEditorUtilities : AssetPostprocessor { abortSkeletonImport = true; resolved = true; break; - } - } - } - - if (abortSkeletonImport) - break; - } - - //TODO: any post processing of images - } - - static bool CheckForValidSkeletonData (string skeletonJSONPath) { - - string dir = Path.GetDirectoryName(skeletonJSONPath); - TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset)); - DirectoryInfo dirInfo = new DirectoryInfo(dir); - - FileInfo[] files = dirInfo.GetFiles("*.asset"); - - foreach (var f in files) { - string localPath = dir + "/" + f.Name; - var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object)); - if (obj is SkeletonDataAsset) { - var skeletonDataAsset = (SkeletonDataAsset)obj; - if (skeletonDataAsset.skeletonJSON == textAsset) - return true; - } - } - - return false; - } - - static void ResetExistingSkeletonData (string skeletonJSONPath) { - - string dir = Path.GetDirectoryName(skeletonJSONPath); - TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset)); - DirectoryInfo dirInfo = new DirectoryInfo(dir); - - FileInfo[] files = dirInfo.GetFiles("*.asset"); - - foreach (var f in files) { - string localPath = dir + "/" + f.Name; - var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object)); - if (obj is SkeletonDataAsset) { - var skeletonDataAsset = (SkeletonDataAsset)obj; - - if (skeletonDataAsset.skeletonJSON == textAsset) { - if (Selection.activeObject == skeletonDataAsset) - Selection.activeObject = null; - - skeletonDataAsset.Reset(); - - string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(skeletonDataAsset)); - string lastHash = EditorPrefs.GetString(guid + "_hash"); - - // For some weird reason sometimes Unity loses the internal Object pointer, - // and as a result, all comparisons with null returns true. - // But the C# wrapper is still alive, so we can "restore" the object - // by reloading it from its Instance ID. - AtlasAsset[] skeletonDataAtlasAssets = skeletonDataAsset.atlasAssets; - if (skeletonDataAtlasAssets != null) { - for (int i = 0; i < skeletonDataAtlasAssets.Length; i++) { - if (!ReferenceEquals(null, skeletonDataAtlasAssets[i]) && - skeletonDataAtlasAssets[i].Equals(null) && - skeletonDataAtlasAssets[i].GetInstanceID() != 0 - ) { - skeletonDataAtlasAssets[i] = EditorUtility.InstanceIDToObject(skeletonDataAtlasAssets[i].GetInstanceID()) as AtlasAsset; - } } } + } - SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true); - string currentHash = skeletonData != null ? skeletonData.Hash : null; - if (currentHash == null || lastHash != currentHash) { - //do any upkeep on synchronized assets - UpdateMecanimClips(skeletonDataAsset); - } + if (abortSkeletonImport) + break; + } - if (currentHash != null) { - EditorPrefs.SetString(guid + "_hash", currentHash); + //TODO: any post processing of images + } + + static bool CheckForValidSkeletonData (string skeletonJSONPath) { + + string dir = Path.GetDirectoryName(skeletonJSONPath); + TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset)); + DirectoryInfo dirInfo = new DirectoryInfo(dir); + + FileInfo[] files = dirInfo.GetFiles("*.asset"); + + foreach (var f in files) { + string localPath = dir + "/" + f.Name; + var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object)); + if (obj is SkeletonDataAsset) { + var skeletonDataAsset = (SkeletonDataAsset)obj; + if (skeletonDataAsset.skeletonJSON == textAsset) + return true; + } + } + + return false; + } + + static void ResetExistingSkeletonData (string skeletonJSONPath) { + + string dir = Path.GetDirectoryName(skeletonJSONPath); + TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset)); + DirectoryInfo dirInfo = new DirectoryInfo(dir); + + FileInfo[] files = dirInfo.GetFiles("*.asset"); + + foreach (var f in files) { + string localPath = dir + "/" + f.Name; + var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object)); + if (obj is SkeletonDataAsset) { + var skeletonDataAsset = (SkeletonDataAsset)obj; + + if (skeletonDataAsset.skeletonJSON == textAsset) { + if (Selection.activeObject == skeletonDataAsset) + Selection.activeObject = null; + + skeletonDataAsset.Reset(); + + string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(skeletonDataAsset)); + string lastHash = EditorPrefs.GetString(guid + "_hash"); + + // For some weird reason sometimes Unity loses the internal Object pointer, + // and as a result, all comparisons with null returns true. + // But the C# wrapper is still alive, so we can "restore" the object + // by reloading it from its Instance ID. + AtlasAsset[] skeletonDataAtlasAssets = skeletonDataAsset.atlasAssets; + if (skeletonDataAtlasAssets != null) { + for (int i = 0; i < skeletonDataAtlasAssets.Length; i++) { + if (!ReferenceEquals(null, skeletonDataAtlasAssets[i]) && + skeletonDataAtlasAssets[i].Equals(null) && + skeletonDataAtlasAssets[i].GetInstanceID() != 0 + ) { + skeletonDataAtlasAssets[i] = EditorUtility.InstanceIDToObject(skeletonDataAtlasAssets[i].GetInstanceID()) as AtlasAsset; + } + } + } + + SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true); + string currentHash = skeletonData != null ? skeletonData.Hash : null; + if (currentHash == null || lastHash != currentHash) { + //do any upkeep on synchronized assets + UpdateMecanimClips(skeletonDataAsset); + } + + if (currentHash != null) { + EditorPrefs.SetString(guid + "_hash", currentHash); + } } } } } - } - static void UpdateMecanimClips (SkeletonDataAsset skeletonDataAsset) { - if (skeletonDataAsset.controller == null) - return; - - SkeletonBaker.GenerateMecanimAnimationClips(skeletonDataAsset); - } - - - static bool CheckForValidAtlas (string atlasPath) { - return false; - //////////////DEPRECATED - always check for new atlas data now - /* + static bool CheckForValidAtlas (string atlasPath) { + return false; + //////////////DEPRECATED - always check for new atlas data now + /* string dir = Path.GetDirectoryName(atlasPath); TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(TextAsset)); DirectoryInfo dirInfo = new DirectoryInfo(dir); @@ -523,51 +520,51 @@ public class SpineEditorUtilities : AssetPostprocessor { return false; */ - } + } - static List MultiAtlasDialog (List requiredPaths, string initialDirectory, string header = "") { + static List MultiAtlasDialog (List requiredPaths, string initialDirectory, string header = "") { - List atlasAssets = new List(); + List atlasAssets = new List(); - bool resolved = false; - string lastAtlasPath = initialDirectory; - while (!resolved) { - StringBuilder sb = new StringBuilder(); - sb.AppendLine(header); - sb.AppendLine("Atlases:"); - if (atlasAssets.Count == 0) { - sb.AppendLine("\t--none--"); - } - for (int i = 0; i < atlasAssets.Count; i++) { - sb.AppendLine("\t" + atlasAssets[i].name); - } + bool resolved = false; + string lastAtlasPath = initialDirectory; + while (!resolved) { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(header); + sb.AppendLine("Atlases:"); + if (atlasAssets.Count == 0) { + sb.AppendLine("\t--none--"); + } + for (int i = 0; i < atlasAssets.Count; i++) { + sb.AppendLine("\t" + atlasAssets[i].name); + } - sb.AppendLine(); - sb.AppendLine("Missing Regions:"); + sb.AppendLine(); + sb.AppendLine("Missing Regions:"); - List missingRegions = new List(requiredPaths); + List missingRegions = new List(requiredPaths); - foreach (var atlasAsset in atlasAssets) { - var atlas = atlasAsset.GetAtlas(); - for (int i = 0; i < missingRegions.Count; i++) { - if (atlas.FindRegion(missingRegions[i]) != null) { - missingRegions.RemoveAt(i); - i--; + foreach (var atlasAsset in atlasAssets) { + var atlas = atlasAsset.GetAtlas(); + for (int i = 0; i < missingRegions.Count; i++) { + if (atlas.FindRegion(missingRegions[i]) != null) { + missingRegions.RemoveAt(i); + i--; + } } } - } - if (missingRegions.Count == 0) { - break; - } + if (missingRegions.Count == 0) { + break; + } - for (int i = 0; i < missingRegions.Count; i++) { - sb.AppendLine("\t" + missingRegions[i]); - } + for (int i = 0; i < missingRegions.Count; i++) { + sb.AppendLine("\t" + missingRegions[i]); + } - int result = EditorUtility.DisplayDialogComplex("Atlas Selection", sb.ToString(), "Select", "Finish", "Abort"); + int result = EditorUtility.DisplayDialogComplex("Atlas Selection", sb.ToString(), "Select", "Finish", "Abort"); - switch (result) { + switch (result) { case 0: AtlasAsset selectedAtlasAsset = GetAtlasDialog(lastAtlasPath); if (selectedAtlasAsset != null) { @@ -592,618 +589,635 @@ public class SpineEditorUtilities : AssetPostprocessor { atlasAssets = null; resolved = true; break; - } - - - } - - - return atlasAssets; - } - - static AtlasAsset GetAtlasDialog (string dirPath) { - string path = EditorUtility.OpenFilePanel("Select AtlasAsset...", dirPath, "asset"); - if (path == "") - return null; - - int subLen = Application.dataPath.Length - 6; - string assetRelativePath = path.Substring(subLen, path.Length - subLen).Replace("\\", "/"); - - Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset)); - - if (obj == null || obj.GetType() != typeof(AtlasAsset)) - return null; - - return (AtlasAsset)obj; - } - - static void AddRequiredAtlasRegionsFromBinary (string skeletonDataPath, List requiredPaths) { - SkeletonBinary binary = new SkeletonBinary(new AtlasRequirementLoader(requiredPaths)); - TextAsset data = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonDataPath, typeof(TextAsset)); - MemoryStream input = new MemoryStream(data.bytes); - binary.ReadSkeletonData(input); - binary = null; - } - - public static List GetRequiredAtlasRegions (string skeletonDataPath) { - List requiredPaths = new List(); - - if (skeletonDataPath.Contains(".skel")) { - AddRequiredAtlasRegionsFromBinary(skeletonDataPath, requiredPaths); - return requiredPaths; - } - - TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonDataPath, typeof(TextAsset)); - - StringReader reader = new StringReader(spineJson.text); - var root = Json.Deserialize(reader) as Dictionary; - - foreach (KeyValuePair entry in (Dictionary)root["skins"]) { - foreach (KeyValuePair slotEntry in (Dictionary)entry.Value) { - - foreach (KeyValuePair attachmentEntry in ((Dictionary)slotEntry.Value)) { - var data = ((Dictionary)attachmentEntry.Value); - if (data.ContainsKey("type")) { - if ((string)data["type"] == "boundingbox") { - continue; - } - - } - if (data.ContainsKey("path")) - requiredPaths.Add((string)data["path"]); - else if (data.ContainsKey("name")) - requiredPaths.Add((string)data["name"]); - else - requiredPaths.Add(attachmentEntry.Key); - //requiredPaths.Add((string)sdf["path"]); } + + } + + + return atlasAssets; } - return requiredPaths; - } - static AtlasAsset GetMatchingAtlas (List requiredPaths, List atlasAssets) { - AtlasAsset atlasAssetMatch = null; + static AtlasAsset GetAtlasDialog (string dirPath) { + string path = EditorUtility.OpenFilePanel("Select AtlasAsset...", dirPath, "asset"); + if (path == "") + return null; - foreach (AtlasAsset a in atlasAssets) { - Atlas atlas = a.GetAtlas(); - bool failed = false; - foreach (string regionPath in requiredPaths) { - if (atlas.FindRegion(regionPath) == null) { - failed = true; - break; - } - } - - if (!failed) { - atlasAssetMatch = a; - break; - } - - } - - return atlasAssetMatch; - } - - static List FindAtlasesAtPath (string path) { - List arr = new List(); - - DirectoryInfo dir = new DirectoryInfo(path); - FileInfo[] assetInfoArr = dir.GetFiles("*.asset"); - - int subLen = Application.dataPath.Length - 6; - - foreach (var f in assetInfoArr) { - string assetRelativePath = f.FullName.Substring(subLen, f.FullName.Length - subLen).Replace("\\", "/"); + int subLen = Application.dataPath.Length - 6; + string assetRelativePath = path.Substring(subLen, path.Length - subLen).Replace("\\", "/"); Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset)); - if (obj != null) { - arr.Add(obj as AtlasAsset); + + if (obj == null || obj.GetType() != typeof(AtlasAsset)) + return null; + + return (AtlasAsset)obj; + } + + static void AddRequiredAtlasRegionsFromBinary (string skeletonDataPath, List requiredPaths) { + SkeletonBinary binary = new SkeletonBinary(new AtlasRequirementLoader(requiredPaths)); + TextAsset data = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonDataPath, typeof(TextAsset)); + MemoryStream input = new MemoryStream(data.bytes); + binary.ReadSkeletonData(input); + binary = null; + } + + public static List GetRequiredAtlasRegions (string skeletonDataPath) { + List requiredPaths = new List(); + + if (skeletonDataPath.Contains(".skel")) { + AddRequiredAtlasRegionsFromBinary(skeletonDataPath, requiredPaths); + return requiredPaths; } - } - - - return arr; - } - - public static bool IsValidSpineData (TextAsset asset) { - if (asset.name.Contains(".skel")) return true; - - object obj = null; - try { - obj = Json.Deserialize(new StringReader(asset.text)); - } catch (System.Exception) { - } - if (obj == null) { - Debug.LogError("Is not valid JSON"); - return false; - } - - Dictionary root = (Dictionary)obj; - - if (!root.ContainsKey("skeleton")) - return false; - - Dictionary skeletonInfo = (Dictionary)root["skeleton"]; - - string spineVersion = (string)skeletonInfo["spine"]; - //TODO: reject old versions - - return true; - } - - static AtlasAsset IngestSpineAtlas (TextAsset atlasText) { - if (atlasText == null) { - Debug.LogWarning("Atlas source cannot be null!"); - return null; - } - - string primaryName = Path.GetFileNameWithoutExtension(atlasText.name).Replace(".atlas", ""); - string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(atlasText)); - - string atlasPath = assetPath + "/" + primaryName + "_Atlas.asset"; - - AtlasAsset atlasAsset = (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset)); - - List vestigialMaterials = new List(); - - if (atlasAsset == null) - atlasAsset = AtlasAsset.CreateInstance(); - else { - foreach (Material m in atlasAsset.materials) - vestigialMaterials.Add(m); - } - - atlasAsset.atlasFile = atlasText; - - //strip CR - string atlasStr = atlasText.text; - atlasStr = atlasStr.Replace("\r", ""); - - string[] atlasLines = atlasStr.Split('\n'); - List pageFiles = new List(); - for (int i = 0; i < atlasLines.Length - 1; i++) { - if (atlasLines[i].Trim().Length == 0) - pageFiles.Add(atlasLines[i + 1].Trim()); - } - - atlasAsset.materials = new Material[pageFiles.Count]; - - for (int i = 0; i < pageFiles.Count; i++) { - string texturePath = assetPath + "/" + pageFiles[i]; - Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)); - - TextureImporter texImporter = (TextureImporter)TextureImporter.GetAtPath(texturePath); - texImporter.textureType = TextureImporterType.Advanced; - texImporter.textureFormat = TextureImporterFormat.AutomaticTruecolor; - texImporter.mipmapEnabled = false; - texImporter.alphaIsTransparency = false; - texImporter.maxTextureSize = 2048; - - EditorUtility.SetDirty(texImporter); - AssetDatabase.ImportAsset(texturePath); - AssetDatabase.SaveAssets(); - - string pageName = Path.GetFileNameWithoutExtension(pageFiles[i]); - - //because this looks silly - if (pageName == primaryName && pageFiles.Count == 1) - pageName = "Material"; - - string materialPath = assetPath + "/" + primaryName + "_" + pageName + ".mat"; - Material mat = (Material)AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material)); - - if (mat == null) { - mat = new Material(Shader.Find(defaultShader)); - AssetDatabase.CreateAsset(mat, materialPath); - } else { - vestigialMaterials.Remove(mat); - } - - mat.mainTexture = texture; - EditorUtility.SetDirty(mat); - - AssetDatabase.SaveAssets(); - - atlasAsset.materials[i] = mat; - } - - for (int i = 0; i < vestigialMaterials.Count; i++) - AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(vestigialMaterials[i])); - - if (AssetDatabase.GetAssetPath(atlasAsset) == "") - AssetDatabase.CreateAsset(atlasAsset, atlasPath); - else - atlasAsset.Reset(); - - EditorUtility.SetDirty(atlasAsset); - - AssetDatabase.SaveAssets(); - - - //iterate regions and bake marked - Atlas atlas = atlasAsset.GetAtlas(); - FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); - List regions = (List)field.GetValue(atlas); - string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); - string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); - string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); - - bool hasBakedRegions = false; - for (int i = 0; i < regions.Count; i++) { - AtlasRegion region = regions[i]; - string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/"); - GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject)); - - if (prefab != null) { - BakeRegion(atlasAsset, region, false); - hasBakedRegions = true; - } - } - - if (hasBakedRegions) { - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - } - - return (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset)); - } - - public static GameObject BakeRegion (AtlasAsset atlasAsset, AtlasRegion region, bool autoSave = true) { - Atlas atlas = atlasAsset.GetAtlas(); - string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); - string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); - string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); - string bakedPrefabPath = Path.Combine(bakedDirPath, GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/"); - - GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject)); - GameObject root; - Mesh mesh; - bool isNewPrefab = false; - - if (!Directory.Exists(bakedDirPath)) - Directory.CreateDirectory(bakedDirPath); - - if (prefab == null) { - root = new GameObject("temp", typeof(MeshFilter), typeof(MeshRenderer)); - prefab = (GameObject)PrefabUtility.CreatePrefab(bakedPrefabPath, root); - isNewPrefab = true; - Object.DestroyImmediate(root); - } - - mesh = (Mesh)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(Mesh)); - - Material mat = null; - mesh = atlasAsset.GenerateMesh(region.name, mesh, out mat); - if (isNewPrefab) { - AssetDatabase.AddObjectToAsset(mesh, prefab); - prefab.GetComponent().sharedMesh = mesh; - } - - EditorUtility.SetDirty(mesh); - EditorUtility.SetDirty(prefab); - - if (autoSave) { - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - } - - - prefab.GetComponent().sharedMaterial = mat; - - return prefab; - } - - public static string GetPathSafeRegionName (AtlasRegion region) { - return region.name.Replace("/", "_"); - } - - static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, params AtlasAsset[] atlasAssets) { - string primaryName = Path.GetFileNameWithoutExtension(spineJson.name); - string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson)); - string filePath = assetPath + "/" + primaryName + "_SkeletonData.asset"; - - if (spineJson != null && atlasAssets != null) { - - SkeletonDataAsset skelDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset)); - if (skelDataAsset == null) { - skelDataAsset = SkeletonDataAsset.CreateInstance(); - skelDataAsset.atlasAssets = atlasAssets; - skelDataAsset.skeletonJSON = spineJson; - skelDataAsset.fromAnimation = new string[0]; - skelDataAsset.toAnimation = new string[0]; - skelDataAsset.duration = new float[0]; - skelDataAsset.defaultMix = defaultMix; - skelDataAsset.scale = defaultScale; - - AssetDatabase.CreateAsset(skelDataAsset, filePath); - AssetDatabase.SaveAssets(); - } else { - skelDataAsset.atlasAssets = atlasAssets; - skelDataAsset.Reset(); - skelDataAsset.GetSkeletonData(true); - } - - return skelDataAsset; - } else { - EditorUtility.DisplayDialog("Error!", "Must specify both Spine JSON and AtlasAsset array", "OK"); - return null; - } - } - - [MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)")] - static void InstantiateSkeletonAnimation () { - Object[] arr = Selection.objects; - foreach (Object o in arr) { - string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); - string skinName = EditorPrefs.GetString(guid + "_lastSkin", ""); - - InstantiateSkeletonAnimation((SkeletonDataAsset)o, skinName); - SceneView.RepaintAll(); - } - } - - [MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)", true)] - static bool ValidateInstantiateSkeletonAnimation () { - Object[] arr = Selection.objects; - - if (arr.Length == 0) - return false; - - foreach (Object o in arr) { - if (o.GetType() != typeof(SkeletonDataAsset)) - return false; - } - - return true; - } - - public static SkeletonAnimation InstantiateSkeletonAnimation (SkeletonDataAsset skeletonDataAsset, string skinName) { - return InstantiateSkeletonAnimation(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName)); - } - - public static SkeletonAnimation InstantiateSkeletonAnimation (SkeletonDataAsset skeletonDataAsset, Skin skin = null) { - string spineGameObjectName = string.Format("Spine GameObject ({0})", skeletonDataAsset.name.Replace("_SkeletonData", "")); - GameObject go = new GameObject(spineGameObjectName, typeof(MeshFilter), typeof(MeshRenderer), typeof(SkeletonAnimation)); - SkeletonAnimation anim = go.GetComponent(); - anim.skeletonDataAsset = skeletonDataAsset; - - bool requiresNormals = false; - - foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) { - foreach (Material m in atlasAsset.materials) { - if (m.shader.name.Contains("Lit")) { - requiresNormals = true; - break; + TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonDataPath, typeof(TextAsset)); + + StringReader reader = new StringReader(spineJson.text); + var root = Json.Deserialize(reader) as Dictionary; + + foreach (KeyValuePair entry in (Dictionary)root["skins"]) { + foreach (KeyValuePair slotEntry in (Dictionary)entry.Value) { + + foreach (KeyValuePair attachmentEntry in ((Dictionary)slotEntry.Value)) { + var data = ((Dictionary)attachmentEntry.Value); + if (data.ContainsKey("type")) { + if ((string)data["type"] == "boundingbox") { + continue; + } + + } + if (data.ContainsKey("path")) + requiredPaths.Add((string)data["path"]); + else if (data.ContainsKey("name")) + requiredPaths.Add((string)data["name"]); + else + requiredPaths.Add(attachmentEntry.Key); + //requiredPaths.Add((string)sdf["path"]); + } } } + + return requiredPaths; } + static AtlasAsset GetMatchingAtlas (List requiredPaths, List atlasAssets) { + AtlasAsset atlasAssetMatch = null; + foreach (AtlasAsset a in atlasAssets) { + Atlas atlas = a.GetAtlas(); + bool failed = false; + foreach (string regionPath in requiredPaths) { + if (atlas.FindRegion(regionPath) == null) { + failed = true; + break; + } + } + if (!failed) { + atlasAssetMatch = a; + break; + } - anim.calculateNormals = requiresNormals; - - SkeletonData data = skeletonDataAsset.GetSkeletonData(true); - - if (data == null) { - for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) { - string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]); - skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset)); } - data = skeletonDataAsset.GetSkeletonData(true); + return atlasAssetMatch; } - if (skin == null) - skin = data.DefaultSkin; + static List FindAtlasesAtPath (string path) { + List arr = new List(); - if (skin == null) - skin = data.Skins.Items[0]; + DirectoryInfo dir = new DirectoryInfo(path); + FileInfo[] assetInfoArr = dir.GetFiles("*.asset"); - anim.Initialize(false); + int subLen = Application.dataPath.Length - 6; - anim.skeleton.SetSkin(skin); - anim.initialSkinName = skin.Name; + foreach (var f in assetInfoArr) { + string assetRelativePath = f.FullName.Substring(subLen, f.FullName.Length - subLen).Replace("\\", "/"); - anim.skeleton.Update(1); - anim.state.Update(1); - anim.state.Apply(anim.skeleton); - anim.skeleton.UpdateWorldTransform(); + Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset)); + if (obj != null) { + arr.Add(obj as AtlasAsset); + } - return anim; - } + } - [MenuItem("Assets/Spine/Instantiate (Mecanim)")] - static void InstantiateSkeletonAnimator () { - Object[] arr = Selection.objects; - foreach (Object o in arr) { - string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); - string skinName = EditorPrefs.GetString(guid + "_lastSkin", ""); - InstantiateSkeletonAnimator((SkeletonDataAsset)o, skinName); - SceneView.RepaintAll(); + return arr; } - } - [MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)", true)] - static bool ValidateInstantiateSkeletonAnimator () { - Object[] arr = Selection.objects; + public static bool IsValidSpineData (TextAsset asset) { + if (asset.name.Contains(".skel")) return true; - if (arr.Length == 0) - return false; - - foreach (Object o in arr) { - if (o.GetType() != typeof(SkeletonDataAsset)) + object obj = null; + try { + obj = Json.Deserialize(new StringReader(asset.text)); + } catch (System.Exception) { + } + if (obj == null) { + Debug.LogError("Is not valid JSON"); return false; + } + + Dictionary root = (Dictionary)obj; + + if (!root.ContainsKey("skeleton")) + return false; + + Dictionary skeletonInfo = (Dictionary)root["skeleton"]; + + string spineVersion = (string)skeletonInfo["spine"]; + //TODO: reject old versions + + return true; } - return true; - } + static AtlasAsset IngestSpineAtlas (TextAsset atlasText) { + if (atlasText == null) { + Debug.LogWarning("Atlas source cannot be null!"); + return null; + } - public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, string skinName) { - return InstantiateSkeletonAnimator(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName)); - } + string primaryName = Path.GetFileNameWithoutExtension(atlasText.name).Replace(".atlas", ""); + string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(atlasText)); - public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, Skin skin = null) { - string spineGameObjectName = string.Format("Spine Mecanim GameObject ({0})", skeletonDataAsset.name.Replace("_SkeletonData", "")); - GameObject go = new GameObject(spineGameObjectName, typeof(MeshFilter), typeof(MeshRenderer), typeof(Animator), typeof(SkeletonAnimator)); + string atlasPath = assetPath + "/" + primaryName + "_Atlas.asset"; + + AtlasAsset atlasAsset = (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset)); + + List vestigialMaterials = new List(); + + if (atlasAsset == null) + atlasAsset = AtlasAsset.CreateInstance(); + else { + foreach (Material m in atlasAsset.materials) + vestigialMaterials.Add(m); + } + + atlasAsset.atlasFile = atlasText; + + //strip CR + string atlasStr = atlasText.text; + atlasStr = atlasStr.Replace("\r", ""); + + string[] atlasLines = atlasStr.Split('\n'); + List pageFiles = new List(); + for (int i = 0; i < atlasLines.Length - 1; i++) { + if (atlasLines[i].Trim().Length == 0) + pageFiles.Add(atlasLines[i + 1].Trim()); + } + + atlasAsset.materials = new Material[pageFiles.Count]; + + for (int i = 0; i < pageFiles.Count; i++) { + string texturePath = assetPath + "/" + pageFiles[i]; + Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)); + + TextureImporter texImporter = (TextureImporter)TextureImporter.GetAtPath(texturePath); + texImporter.textureType = TextureImporterType.Advanced; + texImporter.textureFormat = TextureImporterFormat.AutomaticTruecolor; + texImporter.mipmapEnabled = false; + texImporter.alphaIsTransparency = false; + texImporter.maxTextureSize = 2048; + + EditorUtility.SetDirty(texImporter); + AssetDatabase.ImportAsset(texturePath); + AssetDatabase.SaveAssets(); + + string pageName = Path.GetFileNameWithoutExtension(pageFiles[i]); + + //because this looks silly + if (pageName == primaryName && pageFiles.Count == 1) + pageName = "Material"; + + string materialPath = assetPath + "/" + primaryName + "_" + pageName + ".mat"; + Material mat = (Material)AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material)); + + if (mat == null) { + mat = new Material(Shader.Find(defaultShader)); + AssetDatabase.CreateAsset(mat, materialPath); + } else { + vestigialMaterials.Remove(mat); + } + + mat.mainTexture = texture; + EditorUtility.SetDirty(mat); + + AssetDatabase.SaveAssets(); + + atlasAsset.materials[i] = mat; + } + + for (int i = 0; i < vestigialMaterials.Count; i++) + AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(vestigialMaterials[i])); + + if (AssetDatabase.GetAssetPath(atlasAsset) == "") + AssetDatabase.CreateAsset(atlasAsset, atlasPath); + else + atlasAsset.Reset(); + + EditorUtility.SetDirty(atlasAsset); + + AssetDatabase.SaveAssets(); + + + //iterate regions and bake marked + Atlas atlas = atlasAsset.GetAtlas(); + FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); + List regions = (List)field.GetValue(atlas); + string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); + + bool hasBakedRegions = false; + for (int i = 0; i < regions.Count; i++) { + AtlasRegion region = regions[i]; + string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/"); + GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject)); + + if (prefab != null) { + BakeRegion(atlasAsset, region, false); + hasBakedRegions = true; + } + } + + if (hasBakedRegions) { + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + return (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset)); + } + + public static GameObject BakeRegion (AtlasAsset atlasAsset, AtlasRegion region, bool autoSave = true) { + Atlas atlas = atlasAsset.GetAtlas(); + string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); + string bakedPrefabPath = Path.Combine(bakedDirPath, GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/"); + + GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject)); + GameObject root; + Mesh mesh; + bool isNewPrefab = false; + + if (!Directory.Exists(bakedDirPath)) + Directory.CreateDirectory(bakedDirPath); + + if (prefab == null) { + root = new GameObject("temp", typeof(MeshFilter), typeof(MeshRenderer)); + prefab = (GameObject)PrefabUtility.CreatePrefab(bakedPrefabPath, root); + isNewPrefab = true; + Object.DestroyImmediate(root); + } + + mesh = (Mesh)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(Mesh)); + + Material mat = null; + mesh = atlasAsset.GenerateMesh(region.name, mesh, out mat); + if (isNewPrefab) { + AssetDatabase.AddObjectToAsset(mesh, prefab); + prefab.GetComponent().sharedMesh = mesh; + } + + EditorUtility.SetDirty(mesh); + EditorUtility.SetDirty(prefab); + + if (autoSave) { + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + + prefab.GetComponent().sharedMaterial = mat; + + return prefab; + } + + public static string GetPathSafeRegionName (AtlasRegion region) { + return region.name.Replace("/", "_"); + } + + static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, params AtlasAsset[] atlasAssets) { + string primaryName = Path.GetFileNameWithoutExtension(spineJson.name); + string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson)); + string filePath = assetPath + "/" + primaryName + "_SkeletonData.asset"; + + if (spineJson != null && atlasAssets != null) { + + SkeletonDataAsset skelDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset)); + if (skelDataAsset == null) { + skelDataAsset = SkeletonDataAsset.CreateInstance(); + skelDataAsset.atlasAssets = atlasAssets; + skelDataAsset.skeletonJSON = spineJson; + skelDataAsset.fromAnimation = new string[0]; + skelDataAsset.toAnimation = new string[0]; + skelDataAsset.duration = new float[0]; + skelDataAsset.defaultMix = defaultMix; + skelDataAsset.scale = defaultScale; + + AssetDatabase.CreateAsset(skelDataAsset, filePath); + AssetDatabase.SaveAssets(); + } else { + skelDataAsset.atlasAssets = atlasAssets; + skelDataAsset.Reset(); + skelDataAsset.GetSkeletonData(true); + } + + return skelDataAsset; + } else { + EditorUtility.DisplayDialog("Error!", "Must specify both Spine JSON and AtlasAsset array", "OK"); + return null; + } + } + + #region SkeletonAnimation Menu + [MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)", false, 10)] + static void InstantiateSkeletonAnimation () { + Object[] arr = Selection.objects; + foreach (Object o in arr) { + string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); + string skinName = EditorPrefs.GetString(guid + "_lastSkin", ""); + + InstantiateSkeletonAnimation((SkeletonDataAsset)o, skinName); + SceneView.RepaintAll(); + } + } + + [MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)", true, 10)] + static bool ValidateInstantiateSkeletonAnimation () { + Object[] arr = Selection.objects; + + if (arr.Length == 0) + return false; + + foreach (Object o in arr) { + if (o.GetType() != typeof(SkeletonDataAsset)) + return false; + } + + return true; + } + + public static SkeletonAnimation InstantiateSkeletonAnimation (SkeletonDataAsset skeletonDataAsset, string skinName) { + return InstantiateSkeletonAnimation(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName)); + } + + public static SkeletonAnimation InstantiateSkeletonAnimation (SkeletonDataAsset skeletonDataAsset, Skin skin = null) { + string spineGameObjectName = string.Format("Spine GameObject ({0})", skeletonDataAsset.name.Replace("_SkeletonData", "")); + GameObject go = new GameObject(spineGameObjectName, typeof(MeshFilter), typeof(MeshRenderer), typeof(SkeletonAnimation)); + SkeletonAnimation anim = go.GetComponent(); + anim.skeletonDataAsset = skeletonDataAsset; + + bool requiresNormals = false; + + foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) { + foreach (Material m in atlasAsset.materials) { + if (m.shader.name.Contains("Lit")) { + requiresNormals = true; + break; + } + } + } + + + + anim.calculateNormals = requiresNormals; + + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + + if (data == null) { + for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) { + string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]); + skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset)); + } + + data = skeletonDataAsset.GetSkeletonData(true); + } + + if (skin == null) + skin = data.DefaultSkin; + + if (skin == null) + skin = data.Skins.Items[0]; + + anim.Initialize(false); + + anim.skeleton.SetSkin(skin); + anim.initialSkinName = skin.Name; + + anim.skeleton.Update(1); + anim.state.Update(1); + anim.state.Apply(anim.skeleton); + anim.skeleton.UpdateWorldTransform(); + + return anim; + } + #endregion + + #region SkeletonAnimator + #if SPINE_SKELETONANIMATOR + static void UpdateMecanimClips (SkeletonDataAsset skeletonDataAsset) { + if (skeletonDataAsset.controller == null) + return; - if (skeletonDataAsset.controller == null) { SkeletonBaker.GenerateMecanimAnimationClips(skeletonDataAsset); } - go.GetComponent().runtimeAnimatorController = skeletonDataAsset.controller; + [MenuItem("Assets/Spine/Instantiate (Mecanim)", false, 100)] + static void InstantiateSkeletonAnimator () { + Object[] arr = Selection.objects; + foreach (Object o in arr) { + string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); + string skinName = EditorPrefs.GetString(guid + "_lastSkin", ""); - SkeletonAnimator anim = go.GetComponent(); - anim.skeletonDataAsset = skeletonDataAsset; + InstantiateSkeletonAnimator((SkeletonDataAsset)o, skinName); + SceneView.RepaintAll(); + } + } - bool requiresNormals = false; + [MenuItem("Assets/Spine/Instantiate (Mecanim)", true, 100)] + static bool ValidateInstantiateSkeletonAnimator () { + Object[] arr = Selection.objects; - foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) { - foreach (Material m in atlasAsset.materials) { - if (m.shader.name.Contains("Lit")) { - requiresNormals = true; - break; + if (arr.Length == 0) + return false; + + foreach (Object o in arr) { + if (o.GetType() != typeof(SkeletonDataAsset)) + return false; + } + + return true; + } + + public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, string skinName) { + return InstantiateSkeletonAnimator(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName)); + } + + public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, Skin skin = null) { + string spineGameObjectName = string.Format("Spine Mecanim GameObject ({0})", skeletonDataAsset.name.Replace("_SkeletonData", "")); + GameObject go = new GameObject(spineGameObjectName, typeof(MeshFilter), typeof(MeshRenderer), typeof(Animator), typeof(SkeletonAnimator)); + + if (skeletonDataAsset.controller == null) { + SkeletonBaker.GenerateMecanimAnimationClips(skeletonDataAsset); + } + + go.GetComponent().runtimeAnimatorController = skeletonDataAsset.controller; + + SkeletonAnimator anim = go.GetComponent(); + anim.skeletonDataAsset = skeletonDataAsset; + + bool requiresNormals = false; + + foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) { + foreach (Material m in atlasAsset.materials) { + if (m.shader.name.Contains("Lit")) { + requiresNormals = true; + break; + } } } - } - anim.calculateNormals = requiresNormals; + anim.calculateNormals = requiresNormals; - SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); - if (data == null) { - for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) { - string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]); - skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset)); + if (data == null) { + for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) { + string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]); + skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset)); + } + + data = skeletonDataAsset.GetSkeletonData(true); } - data = skeletonDataAsset.GetSkeletonData(true); + if (skin == null) + skin = data.DefaultSkin; + + if (skin == null) + skin = data.Skins.Items[0]; + + anim.Initialize(false); + + anim.skeleton.SetSkin(skin); + anim.initialSkinName = skin.Name; + + anim.skeleton.Update(1); + anim.skeleton.UpdateWorldTransform(); + anim.LateUpdate(); + + return anim; } + #endif + #endregion - if (skin == null) - skin = data.DefaultSkin; + #region Spine Preferences + static bool preferencesLoaded = false; - if (skin == null) - skin = data.Skins.Items[0]; + [PreferenceItem("Spine")] + static void PreferencesGUI () { + if (!preferencesLoaded) { + preferencesLoaded = true; + defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f); + } - anim.Initialize(false); - anim.skeleton.SetSkin(skin); - anim.initialSkinName = skin.Name; + EditorGUILayout.LabelField("Auto-Import Settings", EditorStyles.boldLabel); + EditorGUI.BeginChangeCheck(); + defaultMix = EditorGUILayout.FloatField("Default Mix", defaultMix); + if (EditorGUI.EndChangeCheck()) + EditorPrefs.SetFloat(DEFAULT_MIX_KEY, defaultMix); - anim.skeleton.Update(1); - anim.skeleton.UpdateWorldTransform(); - anim.LateUpdate(); + GUILayout.Space(20); + EditorGUILayout.LabelField("3rd Party Settings", EditorStyles.boldLabel); + GUILayout.BeginHorizontal(); + EditorGUILayout.PrefixLabel("TK2D"); - return anim; - } - - static bool preferencesLoaded = false; - - [PreferenceItem("Spine")] - static void PreferencesGUI () { - if (!preferencesLoaded) { - preferencesLoaded = true; - defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f); + if (GUILayout.Button("Enable", GUILayout.Width(64))) + EnableTK2D(); + if (GUILayout.Button("Disable", GUILayout.Width(64))) + DisableTK2D(); + GUILayout.EndHorizontal(); } + #endregion + //TK2D Support + const string SPINE_TK2D_DEFINE = "SPINE_TK2D"; - EditorGUILayout.LabelField("Auto-Import Settings", EditorStyles.boldLabel); - EditorGUI.BeginChangeCheck(); - defaultMix = EditorGUILayout.FloatField("Default Mix", defaultMix); - if (EditorGUI.EndChangeCheck()) - EditorPrefs.SetFloat(DEFAULT_MIX_KEY, defaultMix); + static void EnableTK2D () { + bool added = false; + foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) { + if (group == BuildTargetGroup.Unknown) + continue; - GUILayout.Space(20); - EditorGUILayout.LabelField("3rd Party Settings", EditorStyles.boldLabel); - GUILayout.BeginHorizontal(); - EditorGUILayout.PrefixLabel("TK2D"); + string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); + if (!defines.Contains(SPINE_TK2D_DEFINE)) { + added = true; + if (defines.EndsWith(";", System.StringComparison.Ordinal)) + defines = defines + SPINE_TK2D_DEFINE; + else + defines = defines + ";" + SPINE_TK2D_DEFINE; - if (GUILayout.Button("Enable", GUILayout.Width(64))) - EnableTK2D(); - if (GUILayout.Button("Disable", GUILayout.Width(64))) - DisableTK2D(); - GUILayout.EndHorizontal(); - } + PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines); + } + } - - //TK2D Support - const string SPINE_TK2D_DEFINE = "SPINE_TK2D"; - - static void EnableTK2D () { - bool added = false; - foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) { - if (group == BuildTargetGroup.Unknown) - continue; - - string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); - if (!defines.Contains(SPINE_TK2D_DEFINE)) { - added = true; - if (defines.EndsWith(";", System.StringComparison.Ordinal)) - defines = defines + SPINE_TK2D_DEFINE; - else - defines = defines + ";" + SPINE_TK2D_DEFINE; - - PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines); + if (added) { + Debug.LogWarning("Setting Scripting Define Symbol " + SPINE_TK2D_DEFINE); + } else { + Debug.LogWarning("Already Set Scripting Define Symbol " + SPINE_TK2D_DEFINE); } } - if (added) { - Debug.LogWarning("Setting Scripting Define Symbol " + SPINE_TK2D_DEFINE); - } else { - Debug.LogWarning("Already Set Scripting Define Symbol " + SPINE_TK2D_DEFINE); - } - } + static void DisableTK2D () { + bool removed = false; + foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) { + string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); + if (defines.Contains(SPINE_TK2D_DEFINE)) { + removed = true; + if (defines.Contains(SPINE_TK2D_DEFINE + ";")) + defines = defines.Replace(SPINE_TK2D_DEFINE + ";", ""); + else + defines = defines.Replace(SPINE_TK2D_DEFINE, ""); - static void DisableTK2D () { - bool removed = false; - foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) { - string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); - if (defines.Contains(SPINE_TK2D_DEFINE)) { - removed = true; - if (defines.Contains(SPINE_TK2D_DEFINE + ";")) - defines = defines.Replace(SPINE_TK2D_DEFINE + ";", ""); - else - defines = defines.Replace(SPINE_TK2D_DEFINE, ""); + PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines); + } + } - PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines); + if (removed) { + Debug.LogWarning("Removing Scripting Define Symbol " + SPINE_TK2D_DEFINE); + } else { + Debug.LogWarning("Already Removed Scripting Define Symbol " + SPINE_TK2D_DEFINE); } } - if (removed) { - Debug.LogWarning("Removing Scripting Define Symbol " + SPINE_TK2D_DEFINE); - } else { - Debug.LogWarning("Already Removed Scripting Define Symbol " + SPINE_TK2D_DEFINE); + public class AtlasRequirementLoader : AttachmentLoader { + + List requirementList; + public AtlasRequirementLoader (List requirementList) { + this.requirementList = requirementList; + } + + public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) { + requirementList.Add(path); + return new RegionAttachment(name); + } + + public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) { + requirementList.Add(path); + return new MeshAttachment(name); + } + + public WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, string name, string path) { + requirementList.Add(path); + return new WeightedMeshAttachment(name); + } + + public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { + return new BoundingBoxAttachment(name); + } } } - public class AtlasRequirementLoader : AttachmentLoader { - - List requirementList; - public AtlasRequirementLoader (List requirementList) { - this.requirementList = requirementList; - } - - public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) { - requirementList.Add(path); - return new RegionAttachment(name); - } - - public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) { - requirementList.Add(path); - return new MeshAttachment(name); - } - - public WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, string name, string path) { - requirementList.Add(path); - return new WeightedMeshAttachment(name); - } - - public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { - return new BoundingBoxAttachment(name); - } - } } + diff --git a/spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs b/spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs index 4f2cef7d7..7bcf31343 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs @@ -35,7 +35,7 @@ using UnityEditor; using System.Reflection; using System; -namespace Spine.Unity { +namespace Spine.Unity.Editor { public static class SpineInspectorUtility { public static string Pluralize (int n, string singular, string plural) { @@ -100,5 +100,4 @@ namespace Spine.Unity { } #endregion } - } diff --git a/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs b/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs index 5b3aba77e..216fc2f81 100644 --- a/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs +++ b/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs @@ -33,52 +33,56 @@ using UnityEngine; using UnityEditor; using System.Collections; -[CustomEditor(typeof(BoundingBoxFollower))] -public class BoundingBoxFollowerInspector : Editor { - SerializedProperty skeletonRenderer, slotName; - BoundingBoxFollower follower; - bool needToReset = false; +namespace Spine.Unity.Editor { + + [CustomEditor(typeof(BoundingBoxFollower))] + public class BoundingBoxFollowerInspector : UnityEditor.Editor { + SerializedProperty skeletonRenderer, slotName; + BoundingBoxFollower follower; + bool needToReset = false; - void OnEnable () { - skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); - slotName = serializedObject.FindProperty("slotName"); - follower = (BoundingBoxFollower)target; - } - - public override void OnInspectorGUI () { - if (needToReset) { - follower.HandleReset(null); - needToReset = false; - } - EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(skeletonRenderer); - EditorGUILayout.PropertyField(slotName, new GUIContent("Slot")); - - if (EditorGUI.EndChangeCheck()){ - serializedObject.ApplyModifiedProperties(); - needToReset = true; + void OnEnable () { + skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); + slotName = serializedObject.FindProperty("slotName"); + follower = (BoundingBoxFollower)target; } - bool hasBone = follower.GetComponent() != null; + public override void OnInspectorGUI () { + if (needToReset) { + follower.HandleReset(null); + needToReset = false; + } + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(skeletonRenderer); + EditorGUILayout.PropertyField(slotName, new GUIContent("Slot")); - EditorGUI.BeginDisabledGroup(hasBone || follower.Slot == null); - { - if (GUILayout.Button(new GUIContent("Add Bone Follower", SpineEditorUtilities.Icons.bone))) { - var boneFollower = follower.gameObject.AddComponent(); - boneFollower.boneName = follower.Slot.Data.BoneData.Name; + if (EditorGUI.EndChangeCheck()){ + serializedObject.ApplyModifiedProperties(); + needToReset = true; + } + + bool hasBone = follower.GetComponent() != null; + + EditorGUI.BeginDisabledGroup(hasBone || follower.Slot == null); + { + if (GUILayout.Button(new GUIContent("Add Bone Follower", SpineEditorUtilities.Icons.bone))) { + var boneFollower = follower.gameObject.AddComponent(); + boneFollower.boneName = follower.Slot.Data.BoneData.Name; + } + } + EditorGUI.EndDisabledGroup(); + + + + //GUILayout.Space(20); + GUILayout.Label("Attachment Names", EditorStyles.boldLabel); + foreach (var kp in follower.attachmentNameTable) { + string name = kp.Value; + var collider = follower.colliderTable[kp.Key]; + bool isPlaceholder = name != kp.Key.Name; + collider.enabled = EditorGUILayout.ToggleLeft(new GUIContent(!isPlaceholder ? name : name + " [" + kp.Key.Name + "]", isPlaceholder ? SpineEditorUtilities.Icons.skinPlaceholder : SpineEditorUtilities.Icons.boundingBox), collider.enabled); } } - EditorGUI.EndDisabledGroup(); - - - - //GUILayout.Space(20); - GUILayout.Label("Attachment Names", EditorStyles.boldLabel); - foreach (var kp in follower.attachmentNameTable) { - string name = kp.Value; - var collider = follower.colliderTable[kp.Key]; - bool isPlaceholder = name != kp.Key.Name; - collider.enabled = EditorGUILayout.ToggleLeft(new GUIContent(!isPlaceholder ? name : name + " [" + kp.Key.Name + "]", isPlaceholder ? SpineEditorUtilities.Icons.skinPlaceholder : SpineEditorUtilities.Icons.boundingBox), collider.enabled); - } } + } diff --git a/spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs b/spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs index 51449ff5f..1d80ac3b1 100644 --- a/spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs +++ b/spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs @@ -37,7 +37,7 @@ using UnityEngine; using System.Collections; using Spine; -namespace Spine { +namespace Spine.Unity { /// /// Use this as a condition-blocking yield instruction for Unity Coroutines. /// The routine will pause until the AnimationState.TrackEntry fires its Complete event. diff --git a/spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs b/spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs index 8e69f8727..403bb0faf 100644 --- a/spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs +++ b/spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs @@ -37,7 +37,7 @@ using UnityEngine; using System.Collections; using Spine; -namespace Spine { +namespace Spine.Unity { /// /// Use this as a condition-blocking yield instruction for Unity Coroutines. /// The routine will pause until the AnimationState fires an event matching the given event name or EventData reference.