diff --git a/spine-csharp/spine-csharp.csproj b/spine-csharp/spine-csharp.csproj index 7cc986591..c5fc23107 100644 --- a/spine-csharp/spine-csharp.csproj +++ b/spine-csharp/spine-csharp.csproj @@ -70,6 +70,7 @@ + @@ -82,12 +83,12 @@ - - \ No newline at end of file + diff --git a/spine-csharp/spine-csharp_xna.csproj b/spine-csharp/spine-csharp_xna.csproj index df66b3e9e..95588324a 100644 --- a/spine-csharp/spine-csharp_xna.csproj +++ b/spine-csharp/spine-csharp_xna.csproj @@ -110,6 +110,7 @@ + @@ -136,4 +137,4 @@ --> - \ No newline at end of file + diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs index 4d34a2123..00e2c36e6 100644 --- a/spine-csharp/src/AnimationState.cs +++ b/spine-csharp/src/AnimationState.cs @@ -114,7 +114,10 @@ namespace Spine { } else { float previousTime = previous.time; if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime; - previous.animation.Apply(skeleton, previousTime, previousTime, previous.loop, null); + previous.animation.Apply(skeleton, previous.lastTime, previousTime, previous.loop, null); + // Remove the line above, and uncomment the line below, to allow previous animations to fire events during mixing. + //previous.animation.Apply(skeleton, previous.lastTime, previousTime, previous.loop, events); + previous.lastTime = previousTime; float alpha = current.mixTime / current.mixDuration * current.mix; if (alpha >= 1) { diff --git a/spine-csharp/src/Skeleton.cs b/spine-csharp/src/Skeleton.cs index dbd0ac831..0c97e1042 100644 --- a/spine-csharp/src/Skeleton.cs +++ b/spine-csharp/src/Skeleton.cs @@ -94,6 +94,7 @@ namespace Spine { ikConstraints.Add(new IkConstraint(ikConstraintData, this)); UpdateCache(); + UpdateWorldTransform(); } /// Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs index bd7bdecb1..80efaa998 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs @@ -36,19 +36,20 @@ using Spine; [CustomEditor(typeof(SkeletonAnimation))] public class SkeletonAnimationInspector : SkeletonRendererInspector { - protected SerializedProperty animationName, loop, timeScale; - protected bool isPrefab; + protected SerializedProperty animationName, loop, timeScale, autoReset; + protected bool m_isPrefab; + protected GUIContent autoResetLabel; protected override void OnEnable () { base.OnEnable(); animationName = serializedObject.FindProperty("_animationName"); loop = serializedObject.FindProperty("loop"); timeScale = serializedObject.FindProperty("timeScale"); + autoReset = serializedObject.FindProperty("autoReset"); + autoResetLabel = new GUIContent("Generic Auto-reset"); if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab) - isPrefab = true; - - + m_isPrefab = true; } protected override void gui () { @@ -96,11 +97,12 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector { EditorGUILayout.PropertyField(loop); EditorGUILayout.PropertyField(timeScale); + EditorGUILayout.PropertyField(autoReset, autoResetLabel); component.timeScale = Math.Max(component.timeScale, 0); EditorGUILayout.Space(); - if (!isPrefab) { + if (!m_isPrefab) { if (component.GetComponent() == null) { if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) { component.gameObject.AddComponent(); diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs index 5fc35fe02..3f18858dc 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs @@ -38,7 +38,7 @@ public class SkeletonDataAssetInspector : Editor { private bool needToSerialize; List warnings = new List(); - + void OnEnable () { SpineEditorUtilities.ConfirmInitialization(); @@ -117,7 +117,7 @@ public class SkeletonDataAssetInspector : Editor { DrawAnimationList(); DrawSlotList(); DrawUnityTools(); - + } else { DrawReimportButton(); @@ -131,8 +131,8 @@ public class SkeletonDataAssetInspector : Editor { } void DrawMecanim () { - - EditorGUILayout.PropertyField(controller, new GUIContent("Controller", SpineEditorUtilities.Icons.controllerIcon)); + + EditorGUILayout.PropertyField(controller, new GUIContent("Controller", SpineEditorUtilities.Icons.controllerIcon)); if (controller.objectReferenceValue == null) { GUILayout.BeginHorizontal(); GUILayout.Space(32); @@ -142,7 +142,7 @@ public class SkeletonDataAssetInspector : Editor { GUILayout.EndHorizontal(); EditorGUILayout.LabelField("Alternative to SkeletonAnimation, not required", EditorStyles.miniLabel); } - + } void DrawUnityTools () { @@ -292,7 +292,7 @@ public class SkeletonDataAssetInspector : Editor { serializedObject.ApplyModifiedProperties(); needToSerialize = true; } - + } void DrawAnimationList () { showAnimationList = EditorGUILayout.Foldout(showAnimationList, new GUIContent("Animations", SpineEditorUtilities.Icons.animationRoot)); @@ -648,7 +648,7 @@ public class SkeletonDataAssetInspector : Editor { this.m_previewUtility.m_Camera.orthographicSize = orthoSet; float dist = Vector3.Distance(m_previewUtility.m_Camera.transform.position, m_posGoal); - if (dist > 60f * ((SkeletonDataAsset)target).scale) { + if(dist > 0f) { Vector3 pos = Vector3.Lerp(this.m_previewUtility.m_Camera.transform.position, m_posGoal, 0.1f); pos.x = 0; this.m_previewUtility.m_Camera.transform.position = pos; @@ -676,7 +676,7 @@ public class SkeletonDataAssetInspector : Editor { - if (drawHandles) { + if (drawHandles) { Handles.SetCamera(m_previewUtility.m_Camera); Handles.color = m_originColor; @@ -724,8 +724,8 @@ public class SkeletonDataAssetInspector : Editor { Handles.DrawLine(lastVert, firstVert); - - + + } void Update () { @@ -857,7 +857,7 @@ public class SkeletonDataAssetInspector : Editor { case EventType.ScrollWheel: if (position.Contains(current.mousePosition)) { - m_orthoGoal += current.delta.y * ((SkeletonDataAsset)target).scale * 10; + m_orthoGoal += current.delta.y; GUIUtility.hotControl = controlID; current.Use(); } @@ -882,7 +882,7 @@ public class SkeletonDataAssetInspector : Editor { EditorGUIUtility.SetWantsMouseJumping(1); } return scrollPosition; - + case EventType.MouseUp: if (GUIUtility.hotControl == controlID) { @@ -890,10 +890,10 @@ public class SkeletonDataAssetInspector : Editor { } EditorGUIUtility.SetWantsMouseJumping(0); return scrollPosition; - + case EventType.MouseMove: return scrollPosition; - + case EventType.MouseDrag: if (GUIUtility.hotControl == controlID) { @@ -956,4 +956,4 @@ public class SkeletonDataAssetInspector : Editor { tex = this.m_previewUtility.EndStaticPreview(); return tex; } -} \ No newline at end of file +} diff --git a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs index 17810e6e0..6bb995da9 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs @@ -1,5 +1,4 @@ - /***************************************************************************** * Spine Attribute Drawers created by Mitch Thompson * Full irrevocable rights and permissions granted to Esoteric Software @@ -8,9 +7,6 @@ using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Linq; using System.Reflection; using Spine; @@ -25,21 +21,18 @@ public struct SpineDrawerValuePair { } } -[CustomPropertyDrawer(typeof(SpineSlot))] -public class SpineSlotDrawer : PropertyDrawer { - SkeletonDataAsset skeletonDataAsset; +public abstract class SpineTreeItemDrawerBase : PropertyDrawer where T:SpineAttributeBase { + protected SkeletonDataAsset skeletonDataAsset; + protected T TargetAttribute { get { return (T)attribute; } } - - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + 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; } - - SpineSlot attrib = (SpineSlot)attribute; - - var dataProperty = property.serializedObject.FindProperty(attrib.dataField); - + + var dataProperty = property.serializedObject.FindProperty(TargetAttribute.dataField); + if (dataProperty != null) { if (dataProperty.objectReferenceValue is SkeletonDataAsset) { skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue; @@ -51,7 +44,7 @@ public class SpineSlotDrawer : PropertyDrawer { 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) { @@ -59,43 +52,60 @@ public class SpineSlotDrawer : PropertyDrawer { skeletonDataAsset = skeletonRenderer.skeletonDataAsset; } } - + if (skeletonDataAsset == null) { EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset"); return; } - + position = EditorGUI.PrefixLabel(position, label); - + if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { Selector(property); } - + } - void Selector(SerializedProperty property) { - SpineSlot attrib = (SpineSlot)attribute; + 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(); + } - menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); - menu.AddSeparator(""); + 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(attrib.startsWith)) { - if (attrib.containsBoundingBoxes) { - + if (name.StartsWith(targetAttribute.startsWith)) { + 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) { @@ -104,315 +114,83 @@ public class SpineSlotDrawer : PropertyDrawer { break; } } - + if (!hasBoundingBox) menu.AddDisabledItem(new GUIContent(name)); - + } else { menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } } - + } - - menu.ShowAsContext(); } - 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(SpineSkin))] -public class SpineSkinDrawer : PropertyDrawer { - SkeletonDataAsset skeletonDataAsset; - - 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; - } - - SpineSkin attrib = (SpineSkin)attribute; - - var dataProperty = property.serializedObject.FindProperty(attrib.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"); - 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); - - if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { - Selector(property); - } - - } - - void Selector(SerializedProperty property) { - SpineSkin attrib = (SpineSkin)attribute; - SkeletonData data = skeletonDataAsset.GetSkeletonData(true); - if (data == null) - return; - - GenericMenu menu = new GenericMenu(); +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(attrib.startsWith)) + if (name.StartsWith(targetAttribute.startsWith)) menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } - - menu.ShowAsContext(); } - 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(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(); - } - - 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(SpineAnimation))] -public class SpineAnimationDrawer : PropertyDrawer { - SkeletonDataAsset skeletonDataAsset; - - - 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; - } - - SpineAnimation attrib = (SpineAnimation)attribute; - - var dataProperty = property.serializedObject.FindProperty(attrib.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"); - 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); - - if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { - Selector(property); - } - - } - - void Selector(SerializedProperty property) { - - SpineAnimation attrib = (SpineAnimation)attribute; - - GenericMenu menu = new GenericMenu(); - +public class SpineAnimationDrawer : SpineTreeItemDrawerBase { + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) { var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations; for (int i = 0; i < animations.Count; i++) { string name = animations.Items[i].Name; - if (name.StartsWith(attrib.startsWith)) + if (name.StartsWith(targetAttribute.startsWith)) menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } - - menu.ShowAsContext(); } - void HandleSelect(object val) { - var pair = (SpineDrawerValuePair)val; - pair.property.stringValue = pair.str; - pair.property.serializedObject.ApplyModifiedProperties(); +} + +[CustomPropertyDrawer(typeof(SpineEventData))] +public class SpineEventDataDrawer : SpineTreeItemDrawerBase { + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEventData 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)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } } - public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { - return 18; - } } [CustomPropertyDrawer(typeof(SpineAttachment))] -public class SpineAttachmentDrawer : PropertyDrawer { +public class SpineAttachmentDrawer : SpineTreeItemDrawerBase { + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAttachment targetAttribute, SkeletonData data) { + List validSkins = new List(); + SkeletonRenderer skeletonRenderer = null; - SkeletonDataAsset skeletonDataAsset; - SkeletonRenderer skeletonRenderer; - - 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; - } - - SpineAttachment attrib = (SpineAttachment)attribute; - - var dataProperty = property.serializedObject.FindProperty(attrib.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:", "No SkeletonRenderer"); - } - } else { - EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); - return; - } - - } else if (property.serializedObject.targetObject is Component) { + if (property.serializedObject.targetObject is Component) { var component = (Component)property.serializedObject.targetObject; if (component.GetComponentInChildren() != null) { skeletonRenderer = component.GetComponentInChildren(); + if (skeletonDataAsset != skeletonRenderer.skeletonDataAsset) { + Debug.LogError("DataField SkeletonDataAsset and SkeletonRenderer/SkeletonAnimation's SkeletonDataAsset do not match. Remove the explicit dataField parameter of your [SpineAttachment] field."); + } + skeletonDataAsset = skeletonRenderer.skeletonDataAsset; } } - if (skeletonDataAsset == null && skeletonRenderer == null) { - EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset or SkeletonRenderer"); - return; - } - - position = EditorGUI.PrefixLabel(position, label); - - if (GUI.Button(position, property.stringValue, EditorStyles.popup)) { - Selector(property); - } - - } - - void Selector(SerializedProperty property) { - SkeletonData data = skeletonDataAsset.GetSkeletonData(true); - - if (data == null) - return; - - SpineAttachment attrib = (SpineAttachment)attribute; - - List validSkins = new List(); - - if (skeletonRenderer != null && attrib.currentSkinOnly) { + if (skeletonRenderer != null && targetAttribute.currentSkinOnly) { if (skeletonRenderer.skeleton.Skin != null) { validSkins.Add(skeletonRenderer.skeleton.Skin); } else { @@ -424,62 +202,61 @@ public class SpineAttachmentDrawer : PropertyDrawer { validSkins.Add(skin); } } - - GenericMenu menu = new GenericMenu(); + List attachmentNames = new List(); List placeholderNames = new List(); - + string prefix = ""; - - if (skeletonRenderer != null && attrib.currentSkinOnly) + + 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(attrib.slotField); + + 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 (attrib.returnAttachmentPath) + + if (targetAttribute.returnAttachmentPath) name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath; - - if (attrib.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) { + + 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)); @@ -489,96 +266,83 @@ public class SpineAttachmentDrawer : PropertyDrawer { } } } - - - menu.ShowAsContext(); } - 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(SpineBone))] -public class SpineBoneDrawer : PropertyDrawer { - SkeletonDataAsset skeletonDataAsset; +public class SpineBoneDrawer : SpineTreeItemDrawerBase { + 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)) + 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; } - - SpineBone attrib = (SpineBone)attribute; - - var dataProperty = property.serializedObject.FindProperty(attrib.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"); - 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"); + + 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) { - SpineBone attrib = (SpineBone)attribute; - SkeletonData data = skeletonDataAsset.GetSkeletonData(true); - if (data == null) - return; - + + void Selector (SerializedProperty property) { GenericMenu menu = new GenericMenu(); - - 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(attrib.startsWith)) - menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + 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(); } - - void HandleSelect(object val) { + + static 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; - } -} \ No newline at end of file + +} diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs index b2ea647ae..a440393fa 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs @@ -1,1204 +1,1204 @@ -/****************************************************************************** - * Spine Runtimes Software License - * Version 2.3 - * - * Copyright (c) 2013-2015, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable and - * non-transferable license to use, install, execute and perform the Spine - * Runtimes Software (the "Software") and derivative works solely for personal - * or internal use. Without the written permission of Esoteric Software (see - * Section 2 of the Spine Software License Agreement), you may not (a) modify, - * translate, adapt or otherwise create derivative works, improvements of the - * Software or develop new applications using the Software or (b) remove, - * delete, alter or obscure any trademarks or any copyright, trademark, patent - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#pragma warning disable 0219 - -/***************************************************************************** - * Spine Editor Utilities created by Mitch Thompson - * Full irrevocable rights and permissions granted to Esoteric Software -*****************************************************************************/ -using UnityEngine; -using UnityEditor; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Linq; -using System.Reflection; -using Spine; - -[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 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; - } - } - - 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"); - } - } - - 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) - Initialize(); - } - - static void HierarchyWindowChanged () { - skeletonRendererTable.Clear(); - skeletonUtilityBoneTable.Clear(); - boundingBoxFollowerTable.Clear(); - - SkeletonRenderer[] arr = Object.FindObjectsOfType(); - foreach (SkeletonRenderer r in arr) - skeletonRendererTable.Add(r.gameObject.GetInstanceID(), r.gameObject); - - 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); - } - } - - } - - static void OnPostprocessAllAssets (string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) { - if (imported.Length == 0) +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.3 + * + * Copyright (c) 2013-2015, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to use, install, execute and perform the Spine + * Runtimes Software (the "Software") and derivative works solely for personal + * or internal use. Without the written permission of Esoteric Software (see + * Section 2 of the Spine Software License Agreement), you may not (a) modify, + * translate, adapt or otherwise create derivative works, improvements of the + * Software or develop new applications using the Software or (b) remove, + * delete, alter or obscure any trademarks or any copyright, trademark, patent + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#pragma warning disable 0219 + +/***************************************************************************** + * Spine Editor Utilities created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ +using UnityEngine; +using UnityEditor; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Linq; +using System.Reflection; +using Spine; + +[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 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; + } + } + + 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"); + } + } + + 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) + Initialize(); + } + + static void HierarchyWindowChanged () { + skeletonRendererTable.Clear(); + skeletonUtilityBoneTable.Clear(); + boundingBoxFollowerTable.Clear(); + + SkeletonRenderer[] arr = Object.FindObjectsOfType(); + foreach (SkeletonRenderer r in arr) + skeletonRendererTable.Add(r.gameObject.GetInstanceID(), r.gameObject); + + 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); + } + } + + } + + 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. - // + // 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) { - case ".txt": - if (str.EndsWith(".atlas.txt")) { - atlasPaths.Add(str); - } - break; - case ".png": - case ".jpg": - imagePaths.Add(str); - break; - case ".json": - if (IsValidSpineData((TextAsset)AssetDatabase.LoadAssetAtPath(str, typeof(TextAsset)))) - skeletonPaths.Add(str); - break; - case ".bytes": - if (str.ToLower().EndsWith(".skel.bytes")) { - if (IsValidSpineData((TextAsset)AssetDatabase.LoadAssetAtPath(str, typeof(TextAsset)))) - 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); - - 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)); - if (selectedAtlas != null) { - localAtlases.Clear(); - localAtlases.Add(selectedAtlas); - atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases); - if (atlasMatch != null) { - resolved = true; - IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch); - } - } - - break; - case 0: - var atlasList = MultiAtlasDialog(requiredPaths, Path.GetDirectoryName(sp), Path.GetFileNameWithoutExtension(sp)); - - if (atlasList != null) - IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasList.ToArray()); - - resolved = true; - break; - - case 1: - Debug.Log("Skipped importing: " + Path.GetFileName(sp)); - resolved = true; - break; - - - case 2: - //abort - 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)); + // 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) { + case ".txt": + if (str.EndsWith(".atlas.txt")) { + atlasPaths.Add(str); + } + break; + case ".png": + case ".jpg": + imagePaths.Add(str); + break; + case ".json": + if (IsValidSpineData((TextAsset)AssetDatabase.LoadAssetAtPath(str, typeof(TextAsset)))) + skeletonPaths.Add(str); + break; + case ".bytes": + if (str.ToLower().EndsWith(".skel.bytes")) { + if (IsValidSpineData((TextAsset)AssetDatabase.LoadAssetAtPath(str, typeof(TextAsset)))) + 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); + + 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)); + if (selectedAtlas != null) { + localAtlases.Clear(); + localAtlases.Add(selectedAtlas); + atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases); + if (atlasMatch != null) { + resolved = true; + IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch); + } + } + + break; + case 0: + var atlasList = MultiAtlasDialog(requiredPaths, Path.GetDirectoryName(sp), Path.GetFileNameWithoutExtension(sp)); + + if (atlasList != null) + IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasList.ToArray()); + + resolved = true; + break; + + case 1: + Debug.Log("Skipped importing: " + Path.GetFileName(sp)); + resolved = true; + break; + + + case 2: + //abort + 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; - } - } + 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 - /* - string dir = Path.GetDirectoryName(atlasPath); - TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(atlasPath, 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 AtlasAsset) { - var atlasAsset = (AtlasAsset)obj; - if (atlasAsset.atlasFile == textAsset) { - - - 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); - - 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) { - Debug.Log("Updating: " + region.name); - BakeRegion(atlasAsset, region); - } - } - - - return true; - } - - } - } - - return false; - - */ - } - - static List MultiAtlasDialog (List requiredPaths, string initialDirectory, string header = "") { - - 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); - } - - sb.AppendLine(); - sb.AppendLine("Missing Regions:"); - - 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--; - } - } - } - - if (missingRegions.Count == 0) { - break; - } - - for (int i = 0; i < missingRegions.Count; i++) { - sb.AppendLine("\t" + missingRegions[i]); - } - - int result = EditorUtility.DisplayDialogComplex("Atlas Selection", sb.ToString(), "Select", "Finish", "Abort"); - - switch (result) { - case 0: - AtlasAsset selectedAtlasAsset = GetAtlasDialog(lastAtlasPath); - if (selectedAtlasAsset != null) { - var atlas = selectedAtlasAsset.GetAtlas(); - bool hasValidRegion = false; - foreach (string str in missingRegions) { - if (atlas.FindRegion(str) != null) { - hasValidRegion = true; - break; - } - } - - atlasAssets.Add(selectedAtlasAsset); - } - break; - - case 1: - resolved = true; - break; - - case 2: - 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 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; - } - - } - - 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("\\", "/"); - - Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset)); - if (obj != null) { - arr.Add(obj as AtlasAsset); - } - - } - - - 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].Length == 0) - pageFiles.Add(atlasLines[i + 1]); - } - - 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) { - GameObject go = new GameObject(skeletonDataAsset.name.Replace("_SkeletonData", ""), 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.Reset(); - - 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; - } - - [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(); - } - } - - [MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)", true)] - static bool ValidateInstantiateSkeletonAnimator () { - 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 SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, string skinName) { - return InstantiateSkeletonAnimator(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName)); - } - - public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, Skin skin = null) { - GameObject go = new GameObject(skeletonDataAsset.name.Replace("_SkeletonData", ""), 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; - - 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.Reset(); - - anim.skeleton.SetSkin(skin); - anim.initialSkinName = skin.Name; - - anim.skeleton.Update(1); - anim.skeleton.UpdateWorldTransform(); - anim.LateUpdate(); - - return anim; - } - - static bool preferencesLoaded = false; - - [PreferenceItem("Spine")] - static void PreferencesGUI () { - if (!preferencesLoaded) { - preferencesLoaded = true; - defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f); - } - - - EditorGUILayout.LabelField("Auto-Import Settings", EditorStyles.boldLabel); - EditorGUI.BeginChangeCheck(); - defaultMix = EditorGUILayout.FloatField("Default Mix", defaultMix); - if (EditorGUI.EndChangeCheck()) - EditorPrefs.SetFloat(DEFAULT_MIX_KEY, defaultMix); - - GUILayout.Space(20); - EditorGUILayout.LabelField("3rd Party Settings", EditorStyles.boldLabel); - GUILayout.BeginHorizontal(); - EditorGUILayout.PrefixLabel("TK2D"); - - if (GUILayout.Button("Enable", GUILayout.Width(64))) - EnableTK2D(); - if (GUILayout.Button("Disable", GUILayout.Width(64))) - DisableTK2D(); - GUILayout.EndHorizontal(); - } - - - //TK2D Support - const string SPINE_TK2D_DEFINE = "SPINE_TK2D"; - - static void EnableTK2D () { - bool added = false; - foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) { - string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); - if (!defines.Contains(SPINE_TK2D_DEFINE)) { - added = true; - if (defines.EndsWith(";")) - 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); - } - } - - - 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); - } - } - - 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 SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, string name, string path) { - requirementList.Add(path); - return new SkinnedMeshAttachment(name); - } - - public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { - return new BoundingBoxAttachment(name); - } - } -} + 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 + /* + string dir = Path.GetDirectoryName(atlasPath); + TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(atlasPath, 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 AtlasAsset) { + var atlasAsset = (AtlasAsset)obj; + if (atlasAsset.atlasFile == textAsset) { + + + 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); + + 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) { + Debug.Log("Updating: " + region.name); + BakeRegion(atlasAsset, region); + } + } + + + return true; + } + + } + } + + return false; + + */ + } + + static List MultiAtlasDialog (List requiredPaths, string initialDirectory, string header = "") { + + 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); + } + + sb.AppendLine(); + sb.AppendLine("Missing Regions:"); + + 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--; + } + } + } + + if (missingRegions.Count == 0) { + break; + } + + for (int i = 0; i < missingRegions.Count; i++) { + sb.AppendLine("\t" + missingRegions[i]); + } + + int result = EditorUtility.DisplayDialogComplex("Atlas Selection", sb.ToString(), "Select", "Finish", "Abort"); + + switch (result) { + case 0: + AtlasAsset selectedAtlasAsset = GetAtlasDialog(lastAtlasPath); + if (selectedAtlasAsset != null) { + var atlas = selectedAtlasAsset.GetAtlas(); + bool hasValidRegion = false; + foreach (string str in missingRegions) { + if (atlas.FindRegion(str) != null) { + hasValidRegion = true; + break; + } + } + + atlasAssets.Add(selectedAtlasAsset); + } + break; + + case 1: + resolved = true; + break; + + case 2: + 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 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; + } + + } + + 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("\\", "/"); + + Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset)); + if (obj != null) { + arr.Add(obj as AtlasAsset); + } + + } + + + 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) { + GameObject go = new GameObject(skeletonDataAsset.name.Replace("_SkeletonData", ""), 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.Reset(); + + 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; + } + + [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(); + } + } + + [MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)", true)] + static bool ValidateInstantiateSkeletonAnimator () { + 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 SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, string skinName) { + return InstantiateSkeletonAnimator(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName)); + } + + public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, Skin skin = null) { + GameObject go = new GameObject(skeletonDataAsset.name.Replace("_SkeletonData", ""), 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; + + 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.Reset(); + + anim.skeleton.SetSkin(skin); + anim.initialSkinName = skin.Name; + + anim.skeleton.Update(1); + anim.skeleton.UpdateWorldTransform(); + anim.LateUpdate(); + + return anim; + } + + static bool preferencesLoaded = false; + + [PreferenceItem("Spine")] + static void PreferencesGUI () { + if (!preferencesLoaded) { + preferencesLoaded = true; + defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f); + } + + + EditorGUILayout.LabelField("Auto-Import Settings", EditorStyles.boldLabel); + EditorGUI.BeginChangeCheck(); + defaultMix = EditorGUILayout.FloatField("Default Mix", defaultMix); + if (EditorGUI.EndChangeCheck()) + EditorPrefs.SetFloat(DEFAULT_MIX_KEY, defaultMix); + + GUILayout.Space(20); + EditorGUILayout.LabelField("3rd Party Settings", EditorStyles.boldLabel); + GUILayout.BeginHorizontal(); + EditorGUILayout.PrefixLabel("TK2D"); + + if (GUILayout.Button("Enable", GUILayout.Width(64))) + EnableTK2D(); + if (GUILayout.Button("Disable", GUILayout.Width(64))) + DisableTK2D(); + GUILayout.EndHorizontal(); + } + + + //TK2D Support + const string SPINE_TK2D_DEFINE = "SPINE_TK2D"; + + static void EnableTK2D () { + bool added = false; + foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) { + string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); + if (!defines.Contains(SPINE_TK2D_DEFINE)) { + added = true; + if (defines.EndsWith(";")) + 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); + } + } + + + 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); + } + } + + 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 SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, string name, string path) { + requirementList.Add(path); + return new SkinnedMeshAttachment(name); + } + + public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { + return new BoundingBoxAttachment(name); + } + } +} diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index 7277ce233..1d2162b73 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs @@ -30,7 +30,6 @@ *****************************************************************************/ using System; -using System.IO; using System.Collections.Generic; using UnityEngine; using Spine; @@ -38,12 +37,12 @@ using Spine; [ExecuteInEditMode] [AddComponentMenu("Spine/SkeletonAnimation")] public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { - public float timeScale = 1; - public bool loop; + + /// + /// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it. + /// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start public Spine.AnimationState state; - - public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } @@ -63,18 +62,21 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { protected event UpdateBonesDelegate _UpdateWorld; protected event UpdateBonesDelegate _UpdateComplete; + // TODO: Make this a safe getter. Lazy-initialize and avoid double-initialization. public Skeleton Skeleton { - get { - return this.skeleton; - } + get { return this.skeleton; } } [SerializeField] - private String - _animationName; + private String _animationName; public String AnimationName { get { + if (!valid) { + Debug.LogWarning("You tried access AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors."); + return null; + } + TrackEntry entry = state.GetCurrent(0); return entry == null ? null : entry.Animation.Name; } @@ -82,6 +84,12 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { if (_animationName == value) return; _animationName = value; + + if (!valid) { + Debug.LogWarning("You tried to change AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors."); + return; + } + if (value == null || value.Length == 0) state.ClearTrack(0); else @@ -89,17 +97,62 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { } } + /// Whether or not an animation should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected. + #if UNITY_5 + [Tooltip("Whether or not an animation should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.")] + #endif + public bool loop; + + /// + /// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%. + /// AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively. + #if UNITY_5 + [Tooltip("The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.")] + #endif + public float timeScale = 1; + + #if UNITY_5 + [Tooltip("Setting this to true makes the SkeletonAnimation behave similar to Spine editor. New animations will not inherit the pose from a previous animation. If you need to intermittently and programmatically pose your skeleton, leave this false.")] + #endif + [SerializeField] + protected bool autoReset = false; + + /// + /// Setting this to true makes the SkeletonAnimation behave similar to Spine editor. + /// New animations will not inherit the pose from a previous animation. + /// If you need to intermittently and programmatically pose your skeleton, leave this false. + public bool AutoReset { + get { return this.autoReset; } + set { + if (!autoReset && value) { + state.Start -= HandleNewAnimationAutoreset; // make sure there isn't a double-subscription. + state.Start += HandleNewAnimationAutoreset; + } + autoReset = value; + } + } + public override void Reset () { base.Reset(); if (!valid) return; state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData()); + + if (autoReset) { + state.Start += HandleNewAnimationAutoreset; + } + if (_animationName != null && _animationName.Length > 0) { state.SetAnimation(0, _animationName, loop); Update(0); } } + + protected virtual void HandleNewAnimationAutoreset (Spine.AnimationState state, int trackIndex) { + if (!autoReset) return; + if (skeleton != null) skeleton.SetToSetupPose(); + } public virtual void Update () { Update(Time.deltaTime); diff --git a/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs b/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs index 6a8b991c5..b573681cb 100644 --- a/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs +++ b/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs @@ -1,10 +1,10 @@ /****************************************************************************** * Spine Runtimes Software License * Version 2.3 - * + * * Copyright (c) 2013-2015, Esoteric Software * All rights reserved. - * + * * You are granted a perpetual, non-exclusive, non-sublicensable and * non-transferable license to use, install, execute and perform the Spine * Runtimes Software (the "Software") and derivative works solely for personal @@ -16,7 +16,7 @@ * or other intellectual property or proprietary rights notices on or in the * Software, including any copy thereof. Redistributions in binary or source * form must include this license and terms. - * + * * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO @@ -108,7 +108,7 @@ public class SkeletonDataAsset : ScriptableObject { #else if (spriteCollection != null) { attachmentLoader = new SpriteCollectionAttachmentLoader(spriteCollection); - skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale) * 100f; + skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale); } else { if (atlasArr.Length == 0) { Reset(); diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index de919317e..6cbac024c 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -39,39 +39,46 @@ using Spine; public class SkeletonRenderer : MonoBehaviour { public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer); - public SkeletonRendererDelegate OnReset; - [System.NonSerialized] - public bool valid; - [System.NonSerialized] - public Skeleton skeleton; + public SkeletonDataAsset skeletonDataAsset; public String initialSkinName; + + #region Advanced public bool calculateNormals, calculateTangents; public float zSpacing; public bool renderMeshes = true, immutableTriangles; public bool frontFacing; public bool logErrors = false; - [SpineSlot] - public string[] submeshSeparators = new string[0]; + // Submesh Separation + [SpineSlot] public string[] submeshSeparators = new string[0]; + [HideInInspector] public List submeshSeparatorSlots = new List(); + #endregion - [HideInInspector] - public List submeshSeparatorSlots = new List(); + [System.NonSerialized] public bool valid; + [System.NonSerialized] public Skeleton skeleton; private MeshRenderer meshRenderer; private MeshFilter meshFilter; + private Mesh mesh1, mesh2; private bool useMesh1; + private float[] tempVertices = new float[8]; private Vector3[] vertices; private Color32[] colors; private Vector2[] uvs; private Material[] sharedMaterials = new Material[0]; + + private MeshState meshState = new MeshState(); private readonly ExposedList submeshMaterials = new ExposedList(); private readonly ExposedList submeshes = new ExposedList(); private SkeletonUtilitySubmeshRenderer[] submeshRenderers; - private MeshState meshState = new MeshState(); + + public virtual void Awake () { + Reset(); + } public virtual void Reset () { if (meshFilter != null) @@ -144,10 +151,6 @@ public class SkeletonRenderer : MonoBehaviour { submeshRenderers = GetComponentsInChildren(); } - public virtual void Awake () { - Reset(); - } - public virtual void OnDestroy () { if (mesh1 != null) { if (Application.isPlaying) @@ -167,7 +170,7 @@ public class SkeletonRenderer : MonoBehaviour { mesh2 = null; } - private Mesh newMesh () { + private static Mesh newMesh () { Mesh mesh = new Mesh(); mesh.name = "Skeleton Mesh"; mesh.hideFlags = HideFlags.HideAndDontSave; @@ -185,6 +188,7 @@ public class SkeletonRenderer : MonoBehaviour { // Count vertices and submesh triangles. int vertexCount = 0; + int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0; Material lastMaterial = null; ExposedList drawOrder = skeleton.drawOrder; @@ -193,26 +197,41 @@ public class SkeletonRenderer : MonoBehaviour { bool renderMeshes = this.renderMeshes; // Clear last state of attachments and submeshes - MeshState.SingleMeshState stateTemp = meshState.stateTemp; - stateTemp.attachments.Clear(true); - stateTemp.UpdateDrawOrderCount(drawOrderCount); + MeshState.SingleMeshState workingState = meshState.buffer; + var workingAttachments = workingState.attachments; + var workingFlips = workingState.attachmentsFlipState; + var workingSubmeshArguments = workingState.addSubmeshArguments; + workingAttachments.Clear(true); + workingState.UpdateAttachmentCount(drawOrderCount); + workingSubmeshArguments.Clear(false); + + MeshState.SingleMeshState storedState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; + var storedAttachments = storedState.attachments; + var storedFlips = storedState.attachmentsFlipState; + + bool mustUpdateMeshStructure = storedState.requiresUpdate || // Force update if the mesh was cleared. (prevents flickering due to incorrect state) + drawOrder.Count != storedAttachments.Count || // Number of slots changed (when does this happen?) + immutableTriangles != storedState.immutableTriangles; // Immutable Triangles flag changed. - stateTemp.addSubmeshArguments.Clear(false); for (int i = 0; i < drawOrderCount; i++) { Slot slot = drawOrder.Items[i]; Bone bone = slot.bone; Attachment attachment = slot.attachment; - object rendererObject; + object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object. int attachmentVertexCount, attachmentTriangleCount; - bool worldScaleXIsPositive = bone.worldScaleX >= 0f; - bool worldScaleYIsPositive = bone.worldScaleY >= 0f; - bool worldScaleIsSameSigns = (worldScaleXIsPositive && worldScaleYIsPositive) || - (!worldScaleXIsPositive && !worldScaleYIsPositive); - bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns); - stateTemp.attachmentsFlipState.Items[i] = flip; - stateTemp.attachments.Items[i] = attachment; + // Handle flipping for normals (for lighting). + bool worldScaleIsSameSigns = ((bone.worldScaleY >= 0f) == (bone.worldScaleX >= 0f)); + bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns); // TODO: bone flipX and flipY will be removed in Spine 3.0 + + workingFlips.Items[i] = flip; + workingAttachments.Items[i] = attachment; + + mustUpdateMeshStructure = mustUpdateMeshStructure || // Always prefer short circuited or. || and not |=. + (attachment != storedAttachments.Items[i]) || // Attachment order changed. // This relies on the drawOrder.Count != storedAttachments.Count check above as a bounds check. + (flip != storedFlips.Items[i]); // Flip states changed. + RegionAttachment regionAttachment = attachment as RegionAttachment; if (regionAttachment != null) { rendererObject = regionAttachment.RendererObject; @@ -237,17 +256,27 @@ public class SkeletonRenderer : MonoBehaviour { } } - // Populate submesh when material changes. -#if !SPINE_TK2D + #if !SPINE_TK2D Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; -#else + #else Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; -#endif + #endif + + // Populate submesh when material changes. (or when forced to separate by a submeshSeparator) if ((lastMaterial != null && lastMaterial.GetInstanceID() != material.GetInstanceID()) || (submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) { - stateTemp.addSubmeshArguments.Add( - new MeshState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false) - ); + + workingSubmeshArguments.Add( + new MeshState.AddSubmeshArguments { + material = lastMaterial, + startSlot = submeshStartSlotIndex, + endSlot = i, + triangleCount = submeshTriangleCount, + firstVertex = submeshFirstVertex, + isLastSubmesh = false + } + ); + submeshTriangleCount = 0; submeshFirstVertex = vertexCount; submeshStartSlotIndex = i; @@ -257,24 +286,41 @@ public class SkeletonRenderer : MonoBehaviour { submeshTriangleCount += attachmentTriangleCount; vertexCount += attachmentVertexCount; } - stateTemp.addSubmeshArguments.Add( - new MeshState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true) - ); - bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(stateTemp.attachments, stateTemp.attachmentsFlipState, stateTemp.addSubmeshArguments); + + workingSubmeshArguments.Add( + new MeshState.AddSubmeshArguments { + material = lastMaterial, + startSlot = submeshStartSlotIndex, + endSlot = drawOrderCount, + triangleCount = submeshTriangleCount, + firstVertex = submeshFirstVertex, + isLastSubmesh = true + } + ); + + mustUpdateMeshStructure = mustUpdateMeshStructure || + this.sharedMaterials.Length != workingSubmeshArguments.Count || // Material array changed in size + CheckIfMustUpdateMeshStructure(workingSubmeshArguments); // Submesh Argument Array changed. + + // CheckIfMustUpdateMaterialArray (workingMaterials, sharedMaterials) + if (!mustUpdateMeshStructure) { + // Narrow phase material array check. + var workingMaterials = workingSubmeshArguments.Items; + for (int i = 0, n = sharedMaterials.Length; i < n; i++) { + if (this.sharedMaterials[i] != workingMaterials[i].material) { // Bounds check is implied above. + mustUpdateMeshStructure = true; + break; + } + } + } + + // NOT ELSE + if (mustUpdateMeshStructure) { - submeshMaterials.Clear(); - for (int i = 0, n = stateTemp.addSubmeshArguments.Count; i < n; i++) { - MeshState.AddSubmeshArguments arguments = stateTemp.addSubmeshArguments.Items[i]; - AddSubmesh( - arguments.material, - arguments.startSlot, - arguments.endSlot, - arguments.triangleCount, - arguments.firstVertex, - arguments.lastSubmesh, - stateTemp.attachmentsFlipState - ); + this.submeshMaterials.Clear(); + for (int i = 0, n = workingSubmeshArguments.Count; i < n; i++) { + AddSubmesh(workingSubmeshArguments.Items[i], workingFlips); } // Set materials. @@ -286,6 +332,7 @@ public class SkeletonRenderer : MonoBehaviour { meshRenderer.sharedMaterials = sharedMaterials; } + // Ensure mesh data is the right size. Vector3[] vertices = this.vertices; bool newTriangles = vertexCount > vertices.Length; @@ -294,8 +341,12 @@ public class SkeletonRenderer : MonoBehaviour { this.vertices = vertices = new Vector3[vertexCount]; this.colors = new Color32[vertexCount]; this.uvs = new Vector2[vertexCount]; + mesh1.Clear(); mesh2.Clear(); + meshState.stateMesh1.requiresUpdate = true; + meshState.stateMesh2.requiresUpdate = true; + } else { // Too many vertices, zero the extra. Vector3 zero = Vector3.zero; @@ -502,6 +553,9 @@ public class SkeletonRenderer : MonoBehaviour { mesh.subMeshCount = submeshCount; for (int i = 0; i < submeshCount; ++i) mesh.SetTriangles(submeshes.Items[i].triangles, i); + + // Done updating mesh. + storedState.requiresUpdate = false; } Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin; @@ -526,24 +580,25 @@ public class SkeletonRenderer : MonoBehaviour { mesh2.tangents = tangents; } } - + // Update previous state - MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; - currentMeshState.immutableTriangles = immutableTriangles; + storedState.immutableTriangles = immutableTriangles; - currentMeshState.attachments.Clear(true); - currentMeshState.attachments.GrowIfNeeded(stateTemp.attachments.Capacity); - currentMeshState.attachments.Count = stateTemp.attachments.Count; - stateTemp.attachments.CopyTo(currentMeshState.attachments.Items); + storedAttachments.Clear(true); + storedAttachments.GrowIfNeeded(workingAttachments.Capacity); + storedAttachments.Count = workingAttachments.Count; + workingAttachments.CopyTo(storedAttachments.Items); - currentMeshState.attachmentsFlipState.GrowIfNeeded(stateTemp.attachmentsFlipState.Capacity); - currentMeshState.attachmentsFlipState.Count = stateTemp.attachmentsFlipState.Count; - stateTemp.attachmentsFlipState.CopyTo(currentMeshState.attachmentsFlipState.Items); + storedFlips.GrowIfNeeded(workingFlips.Capacity); + storedFlips.Count = workingFlips.Count; + workingFlips.CopyTo(storedFlips.Items); - currentMeshState.addSubmeshArguments.GrowIfNeeded(stateTemp.addSubmeshArguments.Capacity); - currentMeshState.addSubmeshArguments.Count = stateTemp.addSubmeshArguments.Count; - stateTemp.addSubmeshArguments.CopyTo(currentMeshState.addSubmeshArguments.Items); + storedState.addSubmeshArguments.GrowIfNeeded(workingSubmeshArguments.Capacity); + storedState.addSubmeshArguments.Count = workingSubmeshArguments.Count; + workingSubmeshArguments.CopyTo(storedState.addSubmeshArguments.Items); + + // Submesh Renderers if (submeshRenderers.Length > 0) { for (int i = 0; i < submeshRenderers.Length; i++) { SkeletonUtilitySubmeshRenderer submeshRenderer = submeshRenderers[i]; @@ -558,59 +613,32 @@ public class SkeletonRenderer : MonoBehaviour { useMesh1 = !useMesh1; } - private bool CheckIfMustUpdateMeshStructure (ExposedList attachmentsTemp, ExposedList attachmentsFlipStateTemp, ExposedList addSubmeshArgumentsTemp) { -#if UNITY_EDITOR + private bool CheckIfMustUpdateMeshStructure (ExposedList workingAddSubmeshArguments) { + #if UNITY_EDITOR if (!Application.isPlaying) return true; -#endif + #endif // Check if any mesh settings were changed - bool mustUpdateMeshStructure = - immutableTriangles != (useMesh1 ? meshState.stateMesh1.immutableTriangles : meshState.stateMesh2.immutableTriangles); - - if (mustUpdateMeshStructure) - return true; - - // Check if any attachments were enabled/disabled - // or submesh structures has changed MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; - ExposedList attachmentsCurrentMesh = currentMeshState.attachments; + + // Check if submesh structures has changed ExposedList addSubmeshArgumentsCurrentMesh = currentMeshState.addSubmeshArguments; - ExposedList attachmentsFlipStateCurrentMesh = currentMeshState.attachmentsFlipState; - - // Check attachments - int attachmentCount = attachmentsTemp.Count; - if (attachmentsCurrentMesh.Count != attachmentCount) - return true; - - for (int i = 0; i < attachmentCount; i++) { - if (attachmentsCurrentMesh.Items[i] != attachmentsTemp.Items[i]) - return true; - } - - // Check flip state - for (int i = 0; i < attachmentCount; i++) { - if (attachmentsFlipStateCurrentMesh.Items[i] != attachmentsFlipStateTemp.Items[i]) - return true; - } - - // Check submeshes - int submeshCount = addSubmeshArgumentsTemp.Count; + int submeshCount = workingAddSubmeshArguments.Count; if (addSubmeshArgumentsCurrentMesh.Count != submeshCount) return true; for (int i = 0; i < submeshCount; i++) { - if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref addSubmeshArgumentsTemp.Items[i])) + if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref workingAddSubmeshArguments.Items[i])) return true; } return false; } - /** Stores vertices and triangles for a single material. */ - private void AddSubmesh (Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh, ExposedList flipStates) { + private void AddSubmesh (MeshState.AddSubmeshArguments submeshArguments, ExposedList flipStates) { int submeshIndex = submeshMaterials.Count; - submeshMaterials.Add(material); + submeshMaterials.Add(submeshArguments.material); if (submeshes.Count <= submeshIndex) submeshes.Add(new Submesh()); @@ -618,10 +646,13 @@ public class SkeletonRenderer : MonoBehaviour { return; Submesh submesh = submeshes.Items[submeshIndex]; - int[] triangles = submesh.triangles; + + int triangleCount = submeshArguments.triangleCount; + int firstVertex = submeshArguments.firstVertex; + int trianglesCapacity = triangles.Length; - if (lastSubmesh && trianglesCapacity > triangleCount) { + if (submeshArguments.isLastSubmesh && trianglesCapacity > triangleCount) { // Last submesh may have more triangles than required, so zero triangles to the end. for (int i = triangleCount; i < trianglesCapacity; i++) triangles[i] = 0; @@ -637,8 +668,8 @@ public class SkeletonRenderer : MonoBehaviour { if (submesh.firstVertex != firstVertex || submesh.triangleCount < triangleCount) { submesh.triangleCount = triangleCount; submesh.firstVertex = firstVertex; - int drawOrderIndex = 0; - for (int i = 0; i < triangleCount; i += 6, firstVertex += 4, drawOrderIndex++) { + //int drawOrderIndex = 0; + for (int i = 0; i < triangleCount; i += 6, firstVertex += 4/*, drawOrderIndex++*/) { triangles[i] = firstVertex; triangles[i + 1] = firstVertex + 2; triangles[i + 2] = firstVertex + 1; @@ -650,14 +681,16 @@ public class SkeletonRenderer : MonoBehaviour { return; } - // Store triangles. + // Iterate through all slots and store their triangles. ExposedList drawOrder = skeleton.DrawOrder; - for (int i = startSlot, triangleIndex = 0; i < endSlot; i++) { + int triangleIndex = 0; // Modified by loop + for (int i = submeshArguments.startSlot, n = submeshArguments.endSlot; i < n; i++) { Slot slot = drawOrder.Items[i]; Attachment attachment = slot.attachment; bool flip = flipStates.Items[i]; + // Add RegionAttachment triangles if (attachment is RegionAttachment) { if (!flip) { triangles[triangleIndex] = firstVertex; @@ -679,16 +712,18 @@ public class SkeletonRenderer : MonoBehaviour { firstVertex += 4; continue; } + + // Add (Skinned)MeshAttachment triangles int[] attachmentTriangles; int attachmentVertexCount; MeshAttachment meshAttachment = attachment as MeshAttachment; if (meshAttachment != null) { - attachmentVertexCount = meshAttachment.vertices.Length >> 1; + attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2 attachmentTriangles = meshAttachment.triangles; } else { SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment; if (skinnedMeshAttachment != null) { - attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; + attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; // length/2 attachmentTriangles = skinnedMeshAttachment.triangles; } else continue; @@ -710,7 +745,7 @@ public class SkeletonRenderer : MonoBehaviour { } } -#if UNITY_EDITOR + #if UNITY_EDITOR void OnDrawGizmos () { // Make selection easier by drawing a clear gizmo over the skeleton. meshFilter = GetComponent(); @@ -724,26 +759,27 @@ public class SkeletonRenderer : MonoBehaviour { Gizmos.matrix = transform.localToWorldMatrix; Gizmos.DrawCube(meshBounds.center, meshBounds.size); } -#endif + #endif private class MeshState { public int vertexCount; - public readonly SingleMeshState stateTemp = new SingleMeshState(); + public readonly SingleMeshState buffer = new SingleMeshState(); public readonly SingleMeshState stateMesh1 = new SingleMeshState(); public readonly SingleMeshState stateMesh2 = new SingleMeshState(); public class SingleMeshState { public bool immutableTriangles; + public bool requiresUpdate; public readonly ExposedList attachments = new ExposedList(); public readonly ExposedList attachmentsFlipState = new ExposedList(); public readonly ExposedList addSubmeshArguments = new ExposedList(); - public void UpdateDrawOrderCount(int drawOrderCount) { - attachmentsFlipState.GrowIfNeeded(drawOrderCount); - attachmentsFlipState.Count = drawOrderCount; + public void UpdateAttachmentCount (int attachmentCount) { + attachmentsFlipState.GrowIfNeeded(attachmentCount); + attachmentsFlipState.Count = attachmentCount; - attachments.GrowIfNeeded(drawOrderCount); - attachments.Count = drawOrderCount; + attachments.GrowIfNeeded(attachmentCount); + attachments.Count = attachmentCount; } } @@ -753,22 +789,13 @@ public class SkeletonRenderer : MonoBehaviour { public int endSlot; public int triangleCount; public int firstVertex; - public bool lastSubmesh; - - public AddSubmeshArguments (Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh) { - this.material = material; - this.startSlot = startSlot; - this.endSlot = endSlot; - this.triangleCount = triangleCount; - this.firstVertex = firstVertex; - this.lastSubmesh = lastSubmesh; - } + public bool isLastSubmesh; public bool Equals (ref AddSubmeshArguments other) { return - !ReferenceEquals(material, null) && - !ReferenceEquals(other.material, null) && - material.GetInstanceID() == other.material.GetInstanceID() && + //!ReferenceEquals(material, null) && + //!ReferenceEquals(other.material, null) && + //material.GetInstanceID() == other.material.GetInstanceID() && startSlot == other.startSlot && endSlot == other.endSlot && triangleCount == other.triangleCount && diff --git a/spine-unity/Assets/spine-unity/SpineAttributes.cs b/spine-unity/Assets/spine-unity/SpineAttributes.cs index c102bdae4..fb60f2447 100644 --- a/spine-unity/Assets/spine-unity/SpineAttributes.cs +++ b/spine-unity/Assets/spine-unity/SpineAttributes.cs @@ -7,9 +7,12 @@ using UnityEngine; using System.Collections; -public class SpineSlot : PropertyAttribute { - public string startsWith = ""; +public abstract class SpineAttributeBase : PropertyAttribute { public string dataField = ""; + public string startsWith = ""; +} + +public class SpineSlot : SpineAttributeBase { public bool containsBoundingBoxes = false; /// @@ -21,17 +24,29 @@ public class SpineSlot : PropertyAttribute { /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. /// /// Disables popup results that don't contain bounding box attachments when true. - public SpineSlot (string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) { + public SpineSlot(string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) { this.startsWith = startsWith; this.dataField = dataField; this.containsBoundingBoxes = containsBoundingBoxes; } } -public class SpineSkin : PropertyAttribute { - public string startsWith = ""; - public string dataField = ""; +public class SpineEventData : SpineAttributeBase { + /// + /// Smart popup menu for Spine Events (Spine.EventData) + /// + /// Filters popup results to elements that begin with supplied string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineEventData(string startsWith = "", string dataField = "") { + this.startsWith = startsWith; + this.dataField = dataField; + } +} +public class SpineSkin : SpineAttributeBase { /// /// Smart popup menu for Spine Skins /// @@ -40,15 +55,12 @@ public class SpineSkin : PropertyAttribute { /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. /// - public SpineSkin (string startsWith = "", string dataField = "") { + public SpineSkin(string startsWith = "", string dataField = "") { this.startsWith = startsWith; this.dataField = dataField; } } -public class SpineAnimation : PropertyAttribute { - public string startsWith = ""; - public string dataField = ""; - +public class SpineAnimation : SpineAttributeBase { /// /// Smart popup menu for Spine Animations /// @@ -57,24 +69,18 @@ public class SpineAnimation : PropertyAttribute { /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. /// - public SpineAnimation (string startsWith = "", string dataField = "") { + public SpineAnimation(string startsWith = "", string dataField = "") { this.startsWith = startsWith; this.dataField = dataField; } } -public class SpineAttachment : PropertyAttribute { +public class SpineAttachment : SpineAttributeBase { public bool returnAttachmentPath = false; public bool currentSkinOnly = false; public bool placeholdersOnly = false; - public string dataField = ""; public string slotField = ""; - - public SpineAttachment () { - - } - /// /// Smart popup menu for Spine Attachments /// @@ -86,19 +92,19 @@ public class SpineAttachment : PropertyAttribute { /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. /// - public SpineAttachment (bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") { + public SpineAttachment(bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") { this.currentSkinOnly = currentSkinOnly; this.returnAttachmentPath = returnAttachmentPath; this.placeholdersOnly = placeholdersOnly; this.slotField = slotField; - this.dataField = dataField; + this.dataField = dataField; } - public static Hierarchy GetHierarchy (string fullPath) { + public static Hierarchy GetHierarchy(string fullPath) { return new Hierarchy(fullPath); } - public static Spine.Attachment GetAttachment (string attachmentPath, Spine.SkeletonData skeletonData) { + public static Spine.Attachment GetAttachment(string attachmentPath, Spine.SkeletonData skeletonData) { var hierarchy = SpineAttachment.GetHierarchy(attachmentPath); if (hierarchy.name == "") return null; @@ -106,7 +112,7 @@ public class SpineAttachment : PropertyAttribute { return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name); } - public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) { + public static Spine.Attachment GetAttachment(string attachmentPath, SkeletonDataAsset skeletonDataAsset) { return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true)); } @@ -115,14 +121,15 @@ public class SpineAttachment : PropertyAttribute { public string slot; public string name; - public Hierarchy (string fullPath) { - string[] chunks = fullPath.Split(new char[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries); + public Hierarchy(string fullPath) { + string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries); if (chunks.Length == 0) { skin = ""; slot = ""; name = ""; return; - } else if (chunks.Length < 2) { + } + else if (chunks.Length < 2) { throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]"); } skin = chunks[0]; @@ -135,10 +142,7 @@ public class SpineAttachment : PropertyAttribute { } } -public class SpineBone : PropertyAttribute { - public string startsWith = ""; - public string dataField = ""; - +public class SpineBone : SpineAttributeBase { /// /// Smart popup menu for Spine Bones /// @@ -147,19 +151,19 @@ public class SpineBone : PropertyAttribute { /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. /// - public SpineBone (string startsWith = "", string dataField = "") { + public SpineBone(string startsWith = "", string dataField = "") { this.startsWith = startsWith; this.dataField = dataField; } - public static Spine.Bone GetBone (string boneName, SkeletonRenderer renderer) { + public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) { if (renderer.skeleton == null) return null; return renderer.skeleton.FindBone(boneName); } - public static Spine.BoneData GetBoneData (string boneName, SkeletonDataAsset skeletonDataAsset) { + public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) { var data = skeletonDataAsset.GetSkeletonData(true); return data.FindBone(boneName); @@ -169,4 +173,4 @@ public class SpineBone : PropertyAttribute { public class SpineAtlasRegion : PropertyAttribute { //TODO: Standardize with Skeleton attributes //NOTE: For now, relies on locally scoped field named "atlasAsset" for source. -} \ No newline at end of file +}