From 22e283ac6c8a7b76fd6a35faf3af47e028efa04e Mon Sep 17 00:00:00 2001 From: John Date: Tue, 20 Jan 2015 01:01:52 +0800 Subject: [PATCH 01/40] AnimationState.cs now fires events during mixing Not sure if the same "fix" needs to be applied to other runtimes, or if this was intended behavior at all. But someone in the forums was looking for this fix. --- spine-csharp/src/AnimationState.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs index 3a774b330..97f8d3ade 100644 --- a/spine-csharp/src/AnimationState.cs +++ b/spine-csharp/src/AnimationState.cs @@ -113,7 +113,8 @@ 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, events); + previous.lastTime = previousTime; float alpha = current.mixTime / current.mixDuration * current.mix; if (alpha >= 1) { From 4551a62d4b6e3717c9f43aaa40eeac6a55ba9a29 Mon Sep 17 00:00:00 2001 From: invicticide Date: Fri, 5 Jun 2015 00:12:40 -0600 Subject: [PATCH 02/40] Fix for unexpected huge skeleton render scale when using SPINE_TK2D --- .../Editor/SkeletonDataAssetInspector.cs | 30 +++++++++---------- .../Assets/spine-unity/SkeletonDataAsset.cs | 10 +++---- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs index aa204943e..2f83cfc58 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/SkeletonDataAsset.cs b/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs index d378a338c..b6186b344 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 @@ -98,7 +98,7 @@ public class SkeletonDataAsset : ScriptableObject { if (skeletonData != null) return skeletonData; - + AttachmentLoader attachmentLoader; float skeletonDataScale; @@ -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(); From 14803b4fab189e4cb258a19c0456566a8225c9dd Mon Sep 17 00:00:00 2001 From: Tomasz Jaworski Date: Thu, 18 Jun 2015 18:01:59 +0200 Subject: [PATCH 03/40] Windows Phone 8 complation fixed (BufferedStream changed to FileStream) RaggedySpineboy - changed fixedAngle to freezeRotation for Unity 5.1 Changed SkeletonRagdoll2D #if UNITY_5_0 to UNITY_5 --- spine-csharp/src/SkeletonBinary.cs | 8 +++++++- spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs | 4 ++++ .../Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs index ab12c506f..310d7e766 100644 --- a/spine-csharp/src/SkeletonBinary.cs +++ b/spine-csharp/src/SkeletonBinary.cs @@ -86,7 +86,13 @@ namespace Spine { using (var input = new BufferedStream(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) { #else - using (var input = new BufferedStream(new FileStream(path, FileMode.Open))) { +#if UNITY_WP8 || UNITY_WP8_1 + using (var input = new FileStream(path, FileMode.Open)) + { +#else + using (var input = new BufferedStream(new FileStream(path, FileMode.Open))) + { +#endif #endif SkeletonData skeletonData = ReadSkeletonData(input); skeletonData.name = Path.GetFileNameWithoutExtension(path); diff --git a/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs b/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs index f15e773fc..8599c9598 100644 --- a/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs +++ b/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs @@ -18,7 +18,11 @@ public class RaggedySpineboy : MonoBehaviour { void AddRigidbody () { var rb = gameObject.AddComponent(); +#if UNITY_5_1 + rb.freezeRotation = true; +#else rb.fixedAngle = true; +#endif naturalCollider.enabled = true; } diff --git a/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs index 690e10dd2..962acf543 100644 --- a/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs +++ b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs @@ -280,7 +280,7 @@ public class SkeletonRagdoll2D : MonoBehaviour { if (colliders.Count == 0) { var box = go.AddComponent(); box.size = new Vector2(length, thickness); -#if UNITY_5_0 +#if UNITY_5 box.offset = new Vector2((b.WorldFlipX ? -length : length) / 2, 0); #else box.center = new Vector2((b.WorldFlipX ? -length : length) / 2, 0); From fa7992c7b306a7d10573b82d355c00329cbab582 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 28 Jul 2015 05:51:00 +0800 Subject: [PATCH 04/40] Add ExposedList.cs to C# csproj. --- spine-csharp/spine-csharp.csproj | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) 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 + From dde37d703c7eed7fd24132e386c7eb98a24dbd07 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 28 Jul 2015 05:53:56 +0800 Subject: [PATCH 05/40] Add ExposedList.cs to C#-XNA csproj. --- spine-csharp/spine-csharp_xna.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 + From 7d60e4e255847565bc9e70277757cfa779cf0e96 Mon Sep 17 00:00:00 2001 From: Serhii Yolkin Date: Sun, 11 Oct 2015 20:26:42 +0200 Subject: [PATCH 06/40] improved handling of Reimport All and some other weird asset import cases --- .../AssetDatabaseAvailabilityDetector.cs | 21 +++++++ .../AssetDatabaseAvailabilityDetector.cs.meta | 12 ++++ .../Assets/spine-unity/Editor/Resources.meta | 9 +++ .../Resources/SpriteSharpPersistentMarker.txt | 1 + .../SpriteSharpPersistentMarker.txt.meta | 8 +++ .../Editor/SpineEditorUtilities.cs | 58 +++++++++++++++++-- 6 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs create mode 100644 spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs.meta create mode 100644 spine-unity/Assets/spine-unity/Editor/Resources.meta create mode 100644 spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt create mode 100644 spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt.meta diff --git a/spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs b/spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs new file mode 100644 index 000000000..5b8c7a4ab --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs @@ -0,0 +1,21 @@ +using UnityEngine; + +namespace Spine { + public static class AssetDatabaseAvailabilityDetector { + const string MARKER_RESOURCE_NAME = "SpineAssetDatabaseMarker"; + private static bool _isMarkerLoaded; + + public static bool IsAssetDatabaseAvailable (bool forceCheck = false) { + if (!forceCheck && _isMarkerLoaded) + return true; + + TextAsset markerTextAsset = Resources.Load(MARKER_RESOURCE_NAME); + _isMarkerLoaded = markerTextAsset != null; + if (markerTextAsset != null) { + Resources.UnloadAsset(markerTextAsset); + } + + return _isMarkerLoaded; + } + } +} diff --git a/spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs.meta b/spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs.meta new file mode 100644 index 000000000..00b99c134 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 25086cd81e3158b439761b73d7366c47 +timeCreated: 1444587791 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Editor/Resources.meta b/spine-unity/Assets/spine-unity/Editor/Resources.meta new file mode 100644 index 000000000..8d1a705d5 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/Resources.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 24903fdac57ee784b9597fcb751ec22f +folderAsset: yes +timeCreated: 1444565388 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt b/spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt new file mode 100644 index 000000000..1a3c1a3ea --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt @@ -0,0 +1 @@ +DO NOT MOVE OR DELETE THIS FILE \ No newline at end of file diff --git a/spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt.meta b/spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt.meta new file mode 100644 index 000000000..7569884b2 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 57281c00bdd90ad4392f811f2b9f0da1 +timeCreated: 1444565392 +licenseType: Pro +TextScriptImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs index 36dc47ae3..b2ea647ae 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs @@ -149,6 +149,7 @@ public class SpineEditorUtilities : AssetPostprocessor { public static string editorPath = ""; public static string editorGUIPath = ""; + static HashSet assetsImportedInWrongState; static Dictionary skeletonRendererTable; static Dictionary skeletonUtilityBoneTable; static Dictionary boundingBoxFollowerTable; @@ -173,6 +174,7 @@ public class SpineEditorUtilities : AssetPostprocessor { Icons.Initialize(); + assetsImportedInWrongState = new HashSet(); skeletonRendererTable = new Dictionary(); skeletonUtilityBoneTable = new Dictionary(); boundingBoxFollowerTable = new Dictionary(); @@ -254,8 +256,34 @@ public class SpineEditorUtilities : AssetPostprocessor { } static void OnPostprocessAllAssets (string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) { - ImportSpineContent(imported, false); + if (imported.Length == 0) + return; + + // In case user used "Assets -> Reimport All", during the import process, + // asset database is not initialized until some point. During that period, + // all attempts to load any assets using API (i.e. AssetDatabase.LoadAssetAtPath) + // will return null, and as result, assets won't be loaded even if they actually exists, + // which may lead to numerous importing errors. + // This situation also happens if Library folder is deleted from the project, which is a pretty + // common case, since when using version control systems, the Library folder must be excluded. + // + // So to avoid this, in case asset database is not available, we delay loading the assets + // until next time. + // + // Unity *always* reimports some internal assets after the process is done, so this method + // is always called once again in a state when asset database is available. + // + // Checking whether AssetDatabase is initialized is done by attempting to load + // a known "marker" asset that should always be available. Failing to load this asset + // means that AssetDatabase is not initialized. + assetsImportedInWrongState.UnionWith(imported); + if (AssetDatabaseAvailabilityDetector.IsAssetDatabaseAvailable()) { + string[] combinedAssets = assetsImportedInWrongState.ToArray(); + assetsImportedInWrongState.Clear(); + ImportSpineContent(combinedAssets); + } } + public static void ImportSpineContent (string[] imported, bool reimport = false) { List atlasPaths = new List(); List imagePaths = new List(); @@ -408,14 +436,34 @@ public class SpineEditorUtilities : AssetPostprocessor { skeletonDataAsset.Reset(); string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(skeletonDataAsset)); - string lastHash = EditorPrefs.GetString(guid + "_hash"); - - if (lastHash != skeletonDataAsset.GetSkeletonData(true).Hash) { + string lastHash = EditorPrefs.GetString(guid + "_hash"); + + // For some weird reason sometimes Unity loses the internal Object pointer, + // and as a result, all comparisons with null returns true. + // But the C# wrapper is still alive, so we can "restore" the object + // by reloading it from its Instance ID. + AtlasAsset[] skeletonDataAtlasAssets = skeletonDataAsset.atlasAssets; + if (skeletonDataAtlasAssets != null) { + for (int i = 0; i < skeletonDataAtlasAssets.Length; i++) { + if (!ReferenceEquals(null, skeletonDataAtlasAssets[i]) && + skeletonDataAtlasAssets[i].Equals(null) && + skeletonDataAtlasAssets[i].GetInstanceID() != 0 + ) { + skeletonDataAtlasAssets[i] = EditorUtility.InstanceIDToObject(skeletonDataAtlasAssets[i].GetInstanceID()) as AtlasAsset; + } + } + } + + SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true); + string currentHash = skeletonData != null ? skeletonData.Hash : null; + if (currentHash == null || lastHash != currentHash) { //do any upkeep on synchronized assets UpdateMecanimClips(skeletonDataAsset); } - EditorPrefs.SetString(guid + "_hash", skeletonDataAsset.GetSkeletonData(true).Hash); + if (currentHash != null) { + EditorPrefs.SetString(guid + "_hash", currentHash); + } } } } From 3cbf42d743a5be5c2d6aac5a6ef363aa4176f2dc Mon Sep 17 00:00:00 2001 From: Serhii Yolkin Date: Tue, 13 Oct 2015 00:00:38 +0200 Subject: [PATCH 07/40] derp --- ...riteSharpPersistentMarker.txt => SpineAssetDatabaseMarker.txt} | 0 ...ersistentMarker.txt.meta => SpineAssetDatabaseMarker.txt.meta} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename spine-unity/Assets/spine-unity/Editor/Resources/{SpriteSharpPersistentMarker.txt => SpineAssetDatabaseMarker.txt} (100%) rename spine-unity/Assets/spine-unity/Editor/Resources/{SpriteSharpPersistentMarker.txt.meta => SpineAssetDatabaseMarker.txt.meta} (100%) diff --git a/spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt b/spine-unity/Assets/spine-unity/Editor/Resources/SpineAssetDatabaseMarker.txt similarity index 100% rename from spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt rename to spine-unity/Assets/spine-unity/Editor/Resources/SpineAssetDatabaseMarker.txt diff --git a/spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt.meta b/spine-unity/Assets/spine-unity/Editor/Resources/SpineAssetDatabaseMarker.txt.meta similarity index 100% rename from spine-unity/Assets/spine-unity/Editor/Resources/SpriteSharpPersistentMarker.txt.meta rename to spine-unity/Assets/spine-unity/Editor/Resources/SpineAssetDatabaseMarker.txt.meta From 990eb6a8f0b540a3a48f5cbf4d2d44c467dc3df0 Mon Sep 17 00:00:00 2001 From: Serhii Yolkin Date: Sat, 21 Nov 2015 17:33:00 +0100 Subject: [PATCH 08/40] fix occasional broken frame at the moment node transition happened --- .../Assets/spine-unity/SkeletonAnimator.cs | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs index cd8d7726a..0fb1b9482 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs @@ -102,7 +102,8 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { if (mode == MixMode.AlwaysMix) { //always use Mix instead of Applying the first non-zero weighted clip - foreach (var info in clipInfo) { + for (int c = 0; c < clipInfo.Length; c++) { + var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; @@ -111,13 +112,16 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight); } - foreach (var info in nextClipInfo) { - float weight = info.weight * layerWeight; - if (weight == 0) - continue; + if (nextStateInfo.fullPathHash != 0) { + for (int c = 0; c < nextClipInfo.Length; c++) { + var info = nextClipInfo[c]; + float weight = info.weight * layerWeight; + if (weight == 0) + continue; - float time = nextStateInfo.normalizedTime * info.clip.length; - animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight); + float time = nextStateInfo.normalizedTime * info.clip.length; + animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight); + } } } else if (mode >= MixMode.MixNext) { //apply first non-zero weighted clip @@ -147,8 +151,22 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { c = 0; - //apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights) - if (mode == MixMode.SpineStyle) { + if (nextStateInfo.fullPathHash != 0) { + //apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights) + if (mode == MixMode.SpineStyle) { + for (; c < nextClipInfo.Length; c++) { + var info = nextClipInfo[c]; + float weight = info.weight * layerWeight; + if (weight == 0) + continue; + + float time = nextStateInfo.normalizedTime * info.clip.length; + animationTable[GetAnimationClipNameHashCode(info.clip)].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null); + break; + } + } + + //mix the rest for (; c < nextClipInfo.Length; c++) { var info = nextClipInfo[c]; float weight = info.weight * layerWeight; @@ -156,21 +174,9 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { continue; float time = nextStateInfo.normalizedTime * info.clip.length; - animationTable[GetAnimationClipNameHashCode(info.clip)].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null); - break; + animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight); } } - - //mix the rest - for (; c < nextClipInfo.Length; c++) { - var info = nextClipInfo[c]; - float weight = info.weight * layerWeight; - if (weight == 0) - continue; - - float time = nextStateInfo.normalizedTime * info.clip.length; - animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight); - } } } From 8ebf27de24b6886908c86d2afe9f1953eec05a09 Mon Sep 17 00:00:00 2001 From: Serhii Yolkin Date: Sat, 21 Nov 2015 17:56:14 +0100 Subject: [PATCH 09/40] some refactoring --- .../Assets/spine-unity/SkeletonRenderer.cs | 134 ++++++++---------- 1 file changed, 59 insertions(+), 75 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 3a36eded3..7f1024216 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -71,7 +71,7 @@ public class SkeletonRenderer : MonoBehaviour { private readonly ExposedList submeshMaterials = new ExposedList(); private readonly ExposedList submeshes = new ExposedList(); private SkeletonUtilitySubmeshRenderer[] submeshRenderers; - private LastState lastState = new LastState(); + private MeshState meshState = new MeshState(); public virtual void Reset () { if (meshFilter != null) @@ -94,7 +94,7 @@ public class SkeletonRenderer : MonoBehaviour { DestroyImmediate(mesh2); } - lastState = new LastState(); + meshState = new MeshState(); mesh1 = null; mesh2 = null; vertices = null; @@ -193,15 +193,10 @@ public class SkeletonRenderer : MonoBehaviour { bool renderMeshes = this.renderMeshes; // Clear last state of attachments and submeshes - ExposedList attachmentsTriangleCountTemp = lastState.attachmentsTriangleCountTemp; - attachmentsTriangleCountTemp.GrowIfNeeded(drawOrderCount); - attachmentsTriangleCountTemp.Count = drawOrderCount; - ExposedList attachmentsFlipStateTemp = lastState.attachmentsFlipStateTemp; - attachmentsFlipStateTemp.GrowIfNeeded(drawOrderCount); - attachmentsFlipStateTemp.Count = drawOrderCount; + MeshState.SingleMeshState stateTemp = meshState.stateTemp; + stateTemp.UpdateDrawOrderCount(drawOrderCount); - ExposedList addSubmeshArgumentsTemp = lastState.addSubmeshArgumentsTemp; - addSubmeshArgumentsTemp.Clear(false); + stateTemp.addSubmeshArguments.Clear(false); for (int i = 0; i < drawOrderCount; i++) { Slot slot = drawOrder.Items[i]; Bone bone = slot.bone; @@ -214,9 +209,9 @@ public class SkeletonRenderer : MonoBehaviour { bool worldScaleIsSameSigns = (worldScaleXIsPositive && worldScaleYIsPositive) || (!worldScaleXIsPositive && !worldScaleYIsPositive); bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns); - attachmentsFlipStateTemp.Items[i] = flip; + stateTemp.attachmentsFlipState.Items[i] = flip; - attachmentsTriangleCountTemp.Items[i] = -1; + stateTemp.attachmentsTriangleCount.Items[i] = -1; RegionAttachment regionAttachment = attachment as RegionAttachment; if (regionAttachment != null) { rendererObject = regionAttachment.RendererObject; @@ -249,8 +244,8 @@ public class SkeletonRenderer : MonoBehaviour { #endif if ((lastMaterial != null && lastMaterial.GetInstanceID() != material.GetInstanceID()) || (submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) { - addSubmeshArgumentsTemp.Add( - new LastState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false) + stateTemp.addSubmeshArguments.Add( + new MeshState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false) ); submeshTriangleCount = 0; submeshFirstVertex = vertexCount; @@ -261,17 +256,17 @@ public class SkeletonRenderer : MonoBehaviour { submeshTriangleCount += attachmentTriangleCount; vertexCount += attachmentVertexCount; - attachmentsTriangleCountTemp.Items[i] = attachmentTriangleCount; + stateTemp.attachmentsTriangleCount.Items[i] = attachmentTriangleCount; } - addSubmeshArgumentsTemp.Add( - new LastState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true) + stateTemp.addSubmeshArguments.Add( + new MeshState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true) ); - bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(attachmentsTriangleCountTemp, attachmentsFlipStateTemp, addSubmeshArgumentsTemp); + bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(stateTemp.attachmentsTriangleCount, stateTemp.attachmentsFlipState, stateTemp.addSubmeshArguments); if (mustUpdateMeshStructure) { submeshMaterials.Clear(); - for (int i = 0, n = addSubmeshArgumentsTemp.Count; i < n; i++) { - LastState.AddSubmeshArguments arguments = addSubmeshArgumentsTemp.Items[i]; + for (int i = 0, n = stateTemp.addSubmeshArguments.Count; i < n; i++) { + MeshState.AddSubmeshArguments arguments = stateTemp.addSubmeshArguments.Items[i]; AddSubmesh( arguments.material, arguments.startSlot, @@ -279,7 +274,7 @@ public class SkeletonRenderer : MonoBehaviour { arguments.triangleCount, arguments.firstVertex, arguments.lastSubmesh, - attachmentsFlipStateTemp + stateTemp.attachmentsFlipState ); } @@ -305,10 +300,10 @@ public class SkeletonRenderer : MonoBehaviour { } else { // Too many vertices, zero the extra. Vector3 zero = Vector3.zero; - for (int i = vertexCount, n = lastState.vertexCount; i < n; i++) + for (int i = vertexCount, n = meshState.vertexCount; i < n; i++) vertices[i] = zero; } - lastState.vertexCount = vertexCount; + meshState.vertexCount = vertexCount; // Setup mesh. float zSpacing = this.zSpacing; @@ -534,32 +529,20 @@ public class SkeletonRenderer : MonoBehaviour { } // Update previous state - ExposedList attachmentsTriangleCountCurrentMesh; - ExposedList attachmentsFlipStateCurrentMesh; - ExposedList addSubmeshArgumentsCurrentMesh; - if (useMesh1) { - attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh1; - addSubmeshArgumentsCurrentMesh = lastState.addSubmeshArgumentsMesh1; - attachmentsFlipStateCurrentMesh = lastState.attachmentsFlipStateMesh1; - lastState.immutableTrianglesMesh1 = immutableTriangles; - } else { - attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh2; - addSubmeshArgumentsCurrentMesh = lastState.addSubmeshArgumentsMesh2; - attachmentsFlipStateCurrentMesh = lastState.attachmentsFlipStateMesh2; - lastState.immutableTrianglesMesh2 = immutableTriangles; - } + MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; + currentMeshState.immutableTriangles = immutableTriangles; - attachmentsTriangleCountCurrentMesh.GrowIfNeeded(attachmentsTriangleCountTemp.Capacity); - attachmentsTriangleCountCurrentMesh.Count = attachmentsTriangleCountTemp.Count; - attachmentsTriangleCountTemp.CopyTo(attachmentsTriangleCountCurrentMesh.Items, 0); + currentMeshState.attachmentsTriangleCount.GrowIfNeeded(stateTemp.attachmentsTriangleCount.Capacity); + currentMeshState.attachmentsTriangleCount.Count = stateTemp.attachmentsTriangleCount.Count; + stateTemp.attachmentsTriangleCount.CopyTo(currentMeshState.attachmentsTriangleCount.Items); - attachmentsFlipStateCurrentMesh.GrowIfNeeded(attachmentsFlipStateTemp.Capacity); - attachmentsFlipStateCurrentMesh.Count = attachmentsFlipStateTemp.Count; - attachmentsFlipStateTemp.CopyTo(attachmentsFlipStateCurrentMesh.Items, 0); + currentMeshState.attachmentsFlipState.GrowIfNeeded(stateTemp.attachmentsFlipState.Capacity); + currentMeshState.attachmentsFlipState.Count = stateTemp.attachmentsFlipState.Count; + stateTemp.attachmentsFlipState.CopyTo(currentMeshState.attachmentsFlipState.Items); - addSubmeshArgumentsCurrentMesh.GrowIfNeeded(addSubmeshArgumentsTemp.Count); - addSubmeshArgumentsCurrentMesh.Count = addSubmeshArgumentsTemp.Count; - addSubmeshArgumentsTemp.CopyTo(addSubmeshArgumentsCurrentMesh.Items); + currentMeshState.addSubmeshArguments.GrowIfNeeded(stateTemp.addSubmeshArguments.Capacity); + currentMeshState.addSubmeshArguments.Count = stateTemp.addSubmeshArguments.Count; + stateTemp.addSubmeshArguments.CopyTo(currentMeshState.addSubmeshArguments.Items); if (submeshRenderers.Length > 0) { for (int i = 0; i < submeshRenderers.Length; i++) { @@ -575,31 +558,25 @@ public class SkeletonRenderer : MonoBehaviour { useMesh1 = !useMesh1; } - private bool CheckIfMustUpdateMeshStructure (ExposedList attachmentsTriangleCountTemp, ExposedList attachmentsFlipStateTemp, ExposedList addSubmeshArgumentsTemp) { + private bool CheckIfMustUpdateMeshStructure (ExposedList attachmentsTriangleCountTemp, ExposedList attachmentsFlipStateTemp, ExposedList addSubmeshArgumentsTemp) { +#if UNITY_EDITOR + if (!Application.isPlaying) + return true; +#endif + // Check if any mesh settings were changed bool mustUpdateMeshStructure = - immutableTriangles != (useMesh1 ? lastState.immutableTrianglesMesh1 : lastState.immutableTrianglesMesh2); -#if UNITY_EDITOR - mustUpdateMeshStructure |= !Application.isPlaying; -#endif + immutableTriangles != (useMesh1 ? meshState.stateMesh1.immutableTriangles : meshState.stateMesh2.immutableTriangles); if (mustUpdateMeshStructure) return true; // Check if any attachments were enabled/disabled // or submesh structures has changed - ExposedList attachmentsTriangleCountCurrentMesh; - ExposedList attachmentsFlipStateCurrentMesh; - ExposedList addSubmeshArgumentsCurrentMesh; - if (useMesh1) { - attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh1; - addSubmeshArgumentsCurrentMesh = lastState.addSubmeshArgumentsMesh1; - attachmentsFlipStateCurrentMesh = lastState.attachmentsFlipStateMesh1; - } else { - attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh2; - addSubmeshArgumentsCurrentMesh = lastState.addSubmeshArgumentsMesh2; - attachmentsFlipStateCurrentMesh = lastState.attachmentsFlipStateMesh2; - } + MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; + ExposedList attachmentsTriangleCountCurrentMesh = currentMeshState.attachmentsTriangleCount; + ExposedList addSubmeshArgumentsCurrentMesh = currentMeshState.addSubmeshArguments; + ExposedList attachmentsFlipStateCurrentMesh = currentMeshState.attachmentsFlipState; // Check attachments int attachmentCount = attachmentsTriangleCountTemp.Count; @@ -749,19 +726,26 @@ public class SkeletonRenderer : MonoBehaviour { } #endif - private class LastState { - public bool immutableTrianglesMesh1; - public bool immutableTrianglesMesh2; + private class MeshState { public int vertexCount; - public readonly ExposedList attachmentsFlipStateTemp = new ExposedList(); - public readonly ExposedList attachmentsFlipStateMesh1 = new ExposedList(); - public readonly ExposedList attachmentsFlipStateMesh2 = new ExposedList(); - public readonly ExposedList attachmentsTriangleCountTemp = new ExposedList(); - public readonly ExposedList attachmentsTriangleCountMesh1 = new ExposedList(); - public readonly ExposedList attachmentsTriangleCountMesh2 = new ExposedList(); - public readonly ExposedList addSubmeshArgumentsTemp = new ExposedList(); - public readonly ExposedList addSubmeshArgumentsMesh1 = new ExposedList(); - public readonly ExposedList addSubmeshArgumentsMesh2 = new ExposedList(); + public readonly SingleMeshState stateTemp = new SingleMeshState(); + public readonly SingleMeshState stateMesh1 = new SingleMeshState(); + public readonly SingleMeshState stateMesh2 = new SingleMeshState(); + + public class SingleMeshState { + public bool immutableTriangles; + public readonly ExposedList attachmentsFlipState = new ExposedList(); + public readonly ExposedList attachmentsTriangleCount = new ExposedList(); + public readonly ExposedList addSubmeshArguments = new ExposedList(); + + public void UpdateDrawOrderCount(int drawOrderCount) { + attachmentsFlipState.GrowIfNeeded(drawOrderCount); + attachmentsFlipState.Count = drawOrderCount; + + attachmentsTriangleCount.GrowIfNeeded(drawOrderCount); + attachmentsTriangleCount.Count = drawOrderCount; + } + } public struct AddSubmeshArguments { public Material material; From 3218c78239bd2f48f896020b68841c32ad929235 Mon Sep 17 00:00:00 2001 From: Serhii Yolkin Date: Sat, 21 Nov 2015 18:08:17 +0100 Subject: [PATCH 10/40] compare references to attachments instead of comparing attachment properties --- .../Assets/spine-unity/SkeletonRenderer.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 7f1024216..de919317e 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -194,6 +194,7 @@ public class SkeletonRenderer : MonoBehaviour { // Clear last state of attachments and submeshes MeshState.SingleMeshState stateTemp = meshState.stateTemp; + stateTemp.attachments.Clear(true); stateTemp.UpdateDrawOrderCount(drawOrderCount); stateTemp.addSubmeshArguments.Clear(false); @@ -211,7 +212,7 @@ public class SkeletonRenderer : MonoBehaviour { bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns); stateTemp.attachmentsFlipState.Items[i] = flip; - stateTemp.attachmentsTriangleCount.Items[i] = -1; + stateTemp.attachments.Items[i] = attachment; RegionAttachment regionAttachment = attachment as RegionAttachment; if (regionAttachment != null) { rendererObject = regionAttachment.RendererObject; @@ -255,14 +256,12 @@ public class SkeletonRenderer : MonoBehaviour { submeshTriangleCount += attachmentTriangleCount; vertexCount += attachmentVertexCount; - - stateTemp.attachmentsTriangleCount.Items[i] = attachmentTriangleCount; } stateTemp.addSubmeshArguments.Add( new MeshState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true) ); - bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(stateTemp.attachmentsTriangleCount, stateTemp.attachmentsFlipState, stateTemp.addSubmeshArguments); + bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(stateTemp.attachments, stateTemp.attachmentsFlipState, stateTemp.addSubmeshArguments); if (mustUpdateMeshStructure) { submeshMaterials.Clear(); for (int i = 0, n = stateTemp.addSubmeshArguments.Count; i < n; i++) { @@ -532,9 +531,10 @@ public class SkeletonRenderer : MonoBehaviour { MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; currentMeshState.immutableTriangles = immutableTriangles; - currentMeshState.attachmentsTriangleCount.GrowIfNeeded(stateTemp.attachmentsTriangleCount.Capacity); - currentMeshState.attachmentsTriangleCount.Count = stateTemp.attachmentsTriangleCount.Count; - stateTemp.attachmentsTriangleCount.CopyTo(currentMeshState.attachmentsTriangleCount.Items); + currentMeshState.attachments.Clear(true); + currentMeshState.attachments.GrowIfNeeded(stateTemp.attachments.Capacity); + currentMeshState.attachments.Count = stateTemp.attachments.Count; + stateTemp.attachments.CopyTo(currentMeshState.attachments.Items); currentMeshState.attachmentsFlipState.GrowIfNeeded(stateTemp.attachmentsFlipState.Capacity); currentMeshState.attachmentsFlipState.Count = stateTemp.attachmentsFlipState.Count; @@ -558,7 +558,7 @@ public class SkeletonRenderer : MonoBehaviour { useMesh1 = !useMesh1; } - private bool CheckIfMustUpdateMeshStructure (ExposedList attachmentsTriangleCountTemp, ExposedList attachmentsFlipStateTemp, ExposedList addSubmeshArgumentsTemp) { + private bool CheckIfMustUpdateMeshStructure (ExposedList attachmentsTemp, ExposedList attachmentsFlipStateTemp, ExposedList addSubmeshArgumentsTemp) { #if UNITY_EDITOR if (!Application.isPlaying) return true; @@ -574,17 +574,17 @@ public class SkeletonRenderer : MonoBehaviour { // Check if any attachments were enabled/disabled // or submesh structures has changed MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; - ExposedList attachmentsTriangleCountCurrentMesh = currentMeshState.attachmentsTriangleCount; + ExposedList attachmentsCurrentMesh = currentMeshState.attachments; ExposedList addSubmeshArgumentsCurrentMesh = currentMeshState.addSubmeshArguments; ExposedList attachmentsFlipStateCurrentMesh = currentMeshState.attachmentsFlipState; // Check attachments - int attachmentCount = attachmentsTriangleCountTemp.Count; - if (attachmentsTriangleCountCurrentMesh.Count != attachmentCount) + int attachmentCount = attachmentsTemp.Count; + if (attachmentsCurrentMesh.Count != attachmentCount) return true; for (int i = 0; i < attachmentCount; i++) { - if (attachmentsTriangleCountCurrentMesh.Items[i] != attachmentsTriangleCountTemp.Items[i]) + if (attachmentsCurrentMesh.Items[i] != attachmentsTemp.Items[i]) return true; } @@ -734,16 +734,16 @@ public class SkeletonRenderer : MonoBehaviour { public class SingleMeshState { public bool immutableTriangles; + public readonly ExposedList attachments = new ExposedList(); public readonly ExposedList attachmentsFlipState = new ExposedList(); - public readonly ExposedList attachmentsTriangleCount = new ExposedList(); public readonly ExposedList addSubmeshArguments = new ExposedList(); public void UpdateDrawOrderCount(int drawOrderCount) { attachmentsFlipState.GrowIfNeeded(drawOrderCount); attachmentsFlipState.Count = drawOrderCount; - attachmentsTriangleCount.GrowIfNeeded(drawOrderCount); - attachmentsTriangleCount.Count = drawOrderCount; + attachments.GrowIfNeeded(drawOrderCount); + attachments.Count = drawOrderCount; } } From e15c1b70e3eed2176a7128386894a081c169cd00 Mon Sep 17 00:00:00 2001 From: Thomas Steinholz Date: Mon, 30 Nov 2015 11:36:29 -0500 Subject: [PATCH 11/40] Update spine-sfml.h --- spine-sfml/src/spine/spine-sfml.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spine-sfml/src/spine/spine-sfml.h b/spine-sfml/src/spine/spine-sfml.h index e51e64e66..77385ab20 100644 --- a/spine-sfml/src/spine/spine-sfml.h +++ b/spine-sfml/src/spine/spine-sfml.h @@ -34,8 +34,12 @@ #define SPINE_SHORT_NAMES #include +#include #include #include +#include +#include +#include namespace spine { From 9e67cb4f0c465aab46e7c6ad894e1f6c4c9c0f7b Mon Sep 17 00:00:00 2001 From: Thomas Steinholz Date: Mon, 30 Nov 2015 11:36:45 -0500 Subject: [PATCH 12/40] Update spine-sfml.cpp --- spine-sfml/src/spine/spine-sfml.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/spine-sfml/src/spine/spine-sfml.cpp b/spine-sfml/src/spine/spine-sfml.cpp index e1157bb98..262e2aae9 100644 --- a/spine-sfml/src/spine/spine-sfml.cpp +++ b/spine-sfml/src/spine/spine-sfml.cpp @@ -30,12 +30,6 @@ *****************************************************************************/ #include -#include -#include -#include -#include -#include -#include #ifndef SPINE_MESH_VERTEX_COUNT_MAX #define SPINE_MESH_VERTEX_COUNT_MAX 1000 From e4f9c464129c8dc6000e8473967eb333bc52aaf2 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 15 Dec 2015 17:19:18 +0800 Subject: [PATCH 13/40] [Unity] Fix GroundConstraint -y pose values Fix for this issue: https://github.com/EsotericSoftware/spine-runtimes/issues/484 http://esotericsoftware.com/forum/Unity-IK-SkeletonUtilityGroundConstraint-problem-5464 --- .../SkeletonUtility/SkeletonUtilityGroundConstraint.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityGroundConstraint.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityGroundConstraint.cs index 371b5f170..2b8837eac 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityGroundConstraint.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityGroundConstraint.cs @@ -71,6 +71,7 @@ public class SkeletonUtilityGroundConstraint : SkeletonUtilityConstraint { protected override void OnEnable () { base.OnEnable(); + lastHitY = transform.position.y; } protected override void OnDisable () { From 6778a039c6bad0a8e2808ac2b83cf25b918a402d Mon Sep 17 00:00:00 2001 From: John Date: Sat, 19 Dec 2015 20:02:10 +0800 Subject: [PATCH 14/40] Flickering fix Force it to update both meshes when new triangles need to be pushed. http://esotericsoftware.com/forum/Flickering-and-a-solution-for-it-5299 --- .../Assets/spine-unity/SkeletonRenderer.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index de919317e..d348251a9 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -294,8 +294,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.forceUpdate = true; + meshState.stateMesh2.forceUpdate = true; + } else { // Too many vertices, zero the extra. Vector3 zero = Vector3.zero; @@ -502,6 +506,13 @@ public class SkeletonRenderer : MonoBehaviour { mesh.subMeshCount = submeshCount; for (int i = 0; i < submeshCount; ++i) mesh.SetTriangles(submeshes.Items[i].triangles, i); + + // Done updating mesh. Clear the force update state. + if (useMesh1) { + meshState.stateMesh1.forceUpdate = false; + } else { + meshState.stateMesh2.forceUpdate = false; + } } Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin; @@ -565,15 +576,16 @@ public class SkeletonRenderer : MonoBehaviour { #endif // Check if any mesh settings were changed + MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; + bool mustUpdateMeshStructure = - immutableTriangles != (useMesh1 ? meshState.stateMesh1.immutableTriangles : meshState.stateMesh2.immutableTriangles); + (immutableTriangles != currentMeshState.immutableTriangles) || currentMeshState.forceUpdate; 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; ExposedList addSubmeshArgumentsCurrentMesh = currentMeshState.addSubmeshArguments; ExposedList attachmentsFlipStateCurrentMesh = currentMeshState.attachmentsFlipState; @@ -734,6 +746,7 @@ public class SkeletonRenderer : MonoBehaviour { public class SingleMeshState { public bool immutableTriangles; + public bool forceUpdate; public readonly ExposedList attachments = new ExposedList(); public readonly ExposedList attachmentsFlipState = new ExposedList(); public readonly ExposedList addSubmeshArguments = new ExposedList(); From 205e0dc5adbb66ff0d2d92089c676098c54d4abb Mon Sep 17 00:00:00 2001 From: John Date: Wed, 23 Dec 2015 03:31:56 +0800 Subject: [PATCH 15/40] [Unity] Fix material array incorrect persistence This fixes a separate flickering bug. Reported in the forum here: http://esotericsoftware.com/forum/Weird-flicker-between-some-of-the-animations-5577 The solution was to compare `this.sharedMaterials` against the immediately-determined material array. Also included: Moved attachment and flip array checks outside of extra loops in CheckIfMustUpdateMeshStructure to prevent O(2n) behavior. Renamed some identifiers. "temp" is replaced with "working". The analogy is human brain's "working memory". Added some cached references within the method's stack (where their members are frequently accessed). Added comments for easy update, in preparation for incoming changes in Spine v3. --- .../Assets/spine-unity/SkeletonRenderer.cs | 288 +++++++++--------- 1 file changed, 151 insertions(+), 137 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index d348251a9..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; @@ -297,8 +344,8 @@ public class SkeletonRenderer : MonoBehaviour { mesh1.Clear(); mesh2.Clear(); - meshState.stateMesh1.forceUpdate = true; - meshState.stateMesh2.forceUpdate = true; + meshState.stateMesh1.requiresUpdate = true; + meshState.stateMesh2.requiresUpdate = true; } else { // Too many vertices, zero the extra. @@ -507,12 +554,8 @@ public class SkeletonRenderer : MonoBehaviour { for (int i = 0; i < submeshCount; ++i) mesh.SetTriangles(submeshes.Items[i].triangles, i); - // Done updating mesh. Clear the force update state. - if (useMesh1) { - meshState.stateMesh1.forceUpdate = false; - } else { - meshState.stateMesh2.forceUpdate = false; - } + // Done updating mesh. + storedState.requiresUpdate = false; } Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin; @@ -537,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]; @@ -569,60 +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 MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; - bool mustUpdateMeshStructure = - (immutableTriangles != currentMeshState.immutableTriangles) || currentMeshState.forceUpdate; - - if (mustUpdateMeshStructure) - return true; - - // Check if any attachments were enabled/disabled - // or submesh structures has changed - 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()); @@ -630,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; @@ -649,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; @@ -662,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; @@ -691,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; @@ -722,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(); @@ -736,27 +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 forceUpdate; + 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; } } @@ -766,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 && From f67a5457a0ed9e7b9d51d1c76ecdfb9d962e3d35 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 24 Dec 2015 06:53:59 +0800 Subject: [PATCH 16/40] Opt-in Generic Autoreset for SkeletonAnimation This allows users to easily let the SkeletonAnimation act like Spine editor (not inheriting poses from previous animations) without adding extra code. --- .../Assets/spine-unity/SkeletonAnimation.cs | 62 ++++++++++++++++--- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index 7277ce233..d85c569d5 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,15 +62,13 @@ 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 { @@ -89,17 +86,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); From 5378b7979e740fc2f2ebe49d48eef642dca37c8b Mon Sep 17 00:00:00 2001 From: John Date: Thu, 24 Dec 2015 06:56:04 +0800 Subject: [PATCH 17/40] Auto-reset checkbox in SkeletonAnimation inspector --- .../Editor/SkeletonAnimationInspector.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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(); From f2e8340a24a159aeb31a4836fea3e7680823cffc Mon Sep 17 00:00:00 2001 From: John Date: Fri, 25 Dec 2015 23:11:34 +0800 Subject: [PATCH 18/40] Optional fix code for events during mixing To maintain consistency across runtimes, this fix code is commented out. To use it, remove the old line and uncomment the fix line. --- spine-csharp/src/AnimationState.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs index 97f8d3ade..3b1798563 100644 --- a/spine-csharp/src/AnimationState.cs +++ b/spine-csharp/src/AnimationState.cs @@ -113,7 +113,9 @@ namespace Spine { } else { float previousTime = previous.time; if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime; - previous.animation.Apply(skeleton, previous.lastTime, previousTime, previous.loop, events); + 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; From 0ea9bd216f6ed9d32e76d7718a5d915d4185f9da Mon Sep 17 00:00:00 2001 From: John Date: Tue, 29 Dec 2015 04:12:05 +0800 Subject: [PATCH 19/40] [Unity] Atlas ingestion safety https://github.com/EsotericSoftware/spine-runtimes/pull/259 --- .../Editor/SpineEditorUtilities.cs | 2378 ++++++++--------- 1 file changed, 1189 insertions(+), 1189 deletions(-) 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); + } + } +} From e37a9dd16b55dc6da6bb1018250982574758b442 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 29 Dec 2015 04:33:03 +0800 Subject: [PATCH 20/40] [C#] Have world transforms ready on instatiation This also allows [Spine-Unity] SkeletonRenderer to work by itself: https://github.com/EsotericSoftware/spine-runtimes/issues/453 --- spine-csharp/src/Skeleton.cs | 1 + 1 file changed, 1 insertion(+) 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 From bb7d131c28d53681cfec3c70a81668467bd80f9f Mon Sep 17 00:00:00 2001 From: John Date: Tue, 29 Dec 2015 04:46:36 +0800 Subject: [PATCH 21/40] [Unity] AnimationName warnings https://github.com/EsotericSoftware/spine-runtimes/issues/444 This makes AnimationName getter and setter safer but still warns the user of unusual circumstances. --- spine-unity/Assets/spine-unity/SkeletonAnimation.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index d85c569d5..1d2162b73 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs @@ -72,6 +72,11 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { 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; } @@ -79,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 From dd28645a0b665063d1c82c1d47260703dc05eb16 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 30 Dec 2015 14:44:33 +0800 Subject: [PATCH 22/40] [Unity] Fix for Unity UWP/Windows Phone https://github.com/EsotericSoftware/spine-runtimes/issues/495 http://de.esotericsoftware.com/forum/Can-t-build-a-Windows-Store-App-with-Unity-Errors-4354 --- spine-csharp/src/Atlas.cs | 51 +++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/spine-csharp/src/Atlas.cs b/spine-csharp/src/Atlas.cs index d6f0227d5..6fa83425b 100644 --- a/spine-csharp/src/Atlas.cs +++ b/spine-csharp/src/Atlas.cs @@ -45,7 +45,8 @@ namespace Spine { List regions = new List(); TextureLoader textureLoader; -#if WINDOWS_STOREAPP + #if !(UNITY_5 || UNITY_4 || UNITY_3) // Code inside this is not used by Unity and breaks when building for Unity-UWP + #if WINDOWS_STOREAPP private async Task ReadFile(string path, TextureLoader textureLoader) { var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false); @@ -61,24 +62,28 @@ namespace Spine { public Atlas(String path, TextureLoader textureLoader) { this.ReadFile(path, textureLoader).Wait(); } -#else + #else + public Atlas (String path, TextureLoader textureLoader) { -#if WINDOWS_PHONE - Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path); - using (StreamReader reader = new StreamReader(stream)) - { -#else + #if WINDOWS_PHONE + Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path); + using (StreamReader reader = new StreamReader(stream)) { + #else using (StreamReader reader = new StreamReader(path)) { -#endif + #endif // WINDOWS_PHONE + try { Load(reader, Path.GetDirectoryName(path), textureLoader); } catch (Exception ex) { throw new Exception("Error reading atlas file: " + path, ex); } + } } -#endif + #endif // WINDOWS_STOREAPP + + #endif // !(UNITY) public Atlas (TextReader reader, String dir, TextureLoader textureLoader) { Load(reader, dir, textureLoader); @@ -105,18 +110,18 @@ namespace Spine { page = new AtlasPage(); page.name = line; - if (readTuple(reader, tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker. + if (ReadTuple(reader, tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker. page.width = int.Parse(tuple[0]); page.height = int.Parse(tuple[1]); - readTuple(reader, tuple); + ReadTuple(reader, tuple); } page.format = (Format)Enum.Parse(typeof(Format), tuple[0], false); - readTuple(reader, tuple); + ReadTuple(reader, tuple); page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0], false); page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1], false); - String direction = readValue(reader); + String direction = ReadValue(reader); page.uWrap = TextureWrap.ClampToEdge; page.vWrap = TextureWrap.ClampToEdge; if (direction == "x") @@ -135,13 +140,13 @@ namespace Spine { region.name = line; region.page = page; - region.rotate = Boolean.Parse(readValue(reader)); + region.rotate = Boolean.Parse(ReadValue(reader)); - readTuple(reader, tuple); + ReadTuple(reader, tuple); int x = int.Parse(tuple[0]); int y = int.Parse(tuple[1]); - readTuple(reader, tuple); + ReadTuple(reader, tuple); int width = int.Parse(tuple[0]); int height = int.Parse(tuple[1]); @@ -159,33 +164,33 @@ namespace Spine { region.width = Math.Abs(width); region.height = Math.Abs(height); - if (readTuple(reader, tuple) == 4) { // split is optional + if (ReadTuple(reader, tuple) == 4) { // split is optional region.splits = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]), int.Parse(tuple[2]), int.Parse(tuple[3])}; - if (readTuple(reader, tuple) == 4) { // pad is optional, but only present with splits + if (ReadTuple(reader, tuple) == 4) { // pad is optional, but only present with splits region.pads = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]), int.Parse(tuple[2]), int.Parse(tuple[3])}; - readTuple(reader, tuple); + ReadTuple(reader, tuple); } } region.originalWidth = int.Parse(tuple[0]); region.originalHeight = int.Parse(tuple[1]); - readTuple(reader, tuple); + ReadTuple(reader, tuple); region.offsetX = int.Parse(tuple[0]); region.offsetY = int.Parse(tuple[1]); - region.index = int.Parse(readValue(reader)); + region.index = int.Parse(ReadValue(reader)); regions.Add(region); } } } - static String readValue (TextReader reader) { + static String ReadValue (TextReader reader) { String line = reader.ReadLine(); int colon = line.IndexOf(':'); if (colon == -1) throw new Exception("Invalid line: " + line); @@ -193,7 +198,7 @@ namespace Spine { } /// Returns the number of tuple values read (1, 2 or 4). - static int readTuple (TextReader reader, String[] tuple) { + static int ReadTuple (TextReader reader, String[] tuple) { String line = reader.ReadLine(); int colon = line.IndexOf(':'); if (colon == -1) throw new Exception("Invalid line: " + line); From 8006c7b4b966af151db35032e2e3ae5e1c80df17 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 30 Dec 2015 14:55:05 +0800 Subject: [PATCH 23/40] [Unity] Fix for Unity UWP/Windows Phone https://github.com/EsotericSoftware/spine-runtimes/issues/495 --- spine-csharp/src/SkeletonBinary.cs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs index 04d65bfa2..f72bb83ae 100644 --- a/spine-csharp/src/SkeletonBinary.cs +++ b/spine-csharp/src/SkeletonBinary.cs @@ -67,7 +67,9 @@ namespace Spine { Scale = 1; } -#if WINDOWS_STOREAPP + #if !(UNITY_5 || UNITY_4 || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1) + #if WINDOWS_STOREAPP + private async Task ReadFile(string path) { var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; using (var input = new BufferedStream(await folder.GetFileAsync(path).AsTask().ConfigureAwait(false))) { @@ -80,26 +82,21 @@ namespace Spine { public SkeletonData ReadSkeletonData (String path) { return this.ReadFile(path).Result; } -#else + #else public SkeletonData ReadSkeletonData (String path) { -#if WINDOWS_PHONE - using (var input = new BufferedStream(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) - { -#else -#if UNITY_WP8 || UNITY_WP8_1 - using (var input = new FileStream(path, FileMode.Open)) - { -#else - using (var input = new BufferedStream(new FileStream(path, FileMode.Open))) - { -#endif -#endif + #if WINDOWS_PHONE + using (var input = new BufferedStream(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) { + #else + using (var input = new BufferedStream(new FileStream(path, FileMode.Open))) { + #endif // WINDOWS_PHONE SkeletonData skeletonData = ReadSkeletonData(input); skeletonData.name = Path.GetFileNameWithoutExtension(path); return skeletonData; } } -#endif + + #endif // WINDOWS_STOREAPP + #endif // !(UNITY) public SkeletonData ReadSkeletonData (Stream input) { if (input == null) throw new ArgumentNullException("input cannot be null."); From 81eb9c49b436f803394b055a733c7129c0a9471f Mon Sep 17 00:00:00 2001 From: John Date: Wed, 30 Dec 2015 14:58:09 +0800 Subject: [PATCH 24/40] [Unity] Fix for Unity UWP/Windows Phone --- spine-csharp/src/SkeletonJson.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index ba530941b..17d2523f9 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -53,7 +53,9 @@ namespace Spine { Scale = 1; } -#if WINDOWS_STOREAPP + #if !(UNITY_5 || UNITY_4 || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1) + #if WINDOWS_STOREAPP + private async Task ReadFile(string path) { var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false); @@ -67,20 +69,22 @@ namespace Spine { public SkeletonData ReadSkeletonData (String path) { return this.ReadFile(path).Result; } -#else + #else public SkeletonData ReadSkeletonData (String path) { -#if WINDOWS_PHONE + #if WINDOWS_PHONE Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path); using (StreamReader reader = new StreamReader(stream)) { -#else + #else using (StreamReader reader = new StreamReader(path)) { -#endif + #endif // WINDOWS_PHONE SkeletonData skeletonData = ReadSkeletonData(reader); skeletonData.name = Path.GetFileNameWithoutExtension(path); return skeletonData; } } -#endif + + #endif // WINDOWS_STOREAPP + #endif // !UNITY public SkeletonData ReadSkeletonData (TextReader reader) { if (reader == null) throw new ArgumentNullException("reader cannot be null."); From 727c242e6f22196a98cfe9d97a7ac874a983a063 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 30 Dec 2015 14:59:23 +0800 Subject: [PATCH 25/40] [Unity] Fix for Unity UWP/Windows Phone https://github.com/EsotericSoftware/spine-runtimes/issues/495 --- spine-csharp/src/Atlas.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine-csharp/src/Atlas.cs b/spine-csharp/src/Atlas.cs index 6fa83425b..c3aeb43bd 100644 --- a/spine-csharp/src/Atlas.cs +++ b/spine-csharp/src/Atlas.cs @@ -45,7 +45,7 @@ namespace Spine { List regions = new List(); TextureLoader textureLoader; - #if !(UNITY_5 || UNITY_4 || UNITY_3) // Code inside this is not used by Unity and breaks when building for Unity-UWP + #if !(UNITY_5 || UNITY_4 || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1) // !UNITY #if WINDOWS_STOREAPP private async Task ReadFile(string path, TextureLoader textureLoader) { var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; From 7d002f99e5610cc32489cc411d65321cf8fa2aae Mon Sep 17 00:00:00 2001 From: John Date: Wed, 30 Dec 2015 15:31:00 +0800 Subject: [PATCH 26/40] [Unity] SpineAttributeBase & EventDataAttribute --- .../Assets/spine-unity/SpineAttributes.cs | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) 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 +} From 6d386c544d908f6d9ff409a9ecde66719be47756 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 30 Dec 2015 15:36:49 +0800 Subject: [PATCH 27/40] [Unity] SpineAttributeDrawer Update --- .../Editor/SpineAttributeDrawers.cs | 522 +++++------------- 1 file changed, 143 insertions(+), 379 deletions(-) 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 + +} From 914419d5fafe610849d3e7eee00a51d0a9972858 Mon Sep 17 00:00:00 2001 From: pharan Date: Wed, 30 Dec 2015 17:16:28 +0800 Subject: [PATCH 28/40] [Unity] New Spine yield returns for coroutines. --- .../WaitForSpineAnimationComplete.cs | 87 +++++++++++ .../WaitForSpineAnimationComplete.cs.meta | 12 ++ .../YieldInstructions/WaitForSpineEvent.cs | 144 ++++++++++++++++++ .../WaitForSpineEvent.cs.meta | 12 ++ 4 files changed, 255 insertions(+) create mode 100644 spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineAnimationComplete.cs create mode 100644 spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineAnimationComplete.cs.meta create mode 100644 spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs create mode 100644 spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs.meta diff --git a/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineAnimationComplete.cs b/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineAnimationComplete.cs new file mode 100644 index 000000000..a2c385bb4 --- /dev/null +++ b/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineAnimationComplete.cs @@ -0,0 +1,87 @@ +/****************************************************************************** + * 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. + *****************************************************************************/ + +//using UnityEngine; +using System.Collections; +using Spine; + +namespace Spine { + /// + /// Use this as a condition-blocking yield instruction for Unity Coroutines. + /// The routine will pause until the AnimationState.TrackEntry fires its Complete event. + public class WaitForSpineAnimationComplete : IEnumerator { + + bool m_WasFired = false; + + public WaitForSpineAnimationComplete (Spine.TrackEntry trackEntry) { + SafeSubscribe(trackEntry); + } + + void HandleComplete (AnimationState state, int trackIndex, int loopCount) { + m_WasFired = true; + } + + void SafeSubscribe (Spine.TrackEntry trackEntry) { + if (trackEntry == null) { + // Break immediately if trackEntry is null. + m_WasFired = true; + } else { + // Function normally. + trackEntry.Complete += HandleComplete; + } + } + + #region Reuse + /// + /// One optimization high-frequency YieldInstruction returns is to cache instances to minimize pressure. + /// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete. + public WaitForSpineAnimationComplete NowWaitFor (Spine.TrackEntry trackEntry) { + SafeSubscribe(trackEntry); + return this; + } + #endregion + + #region IEnumerator + bool IEnumerator.MoveNext () { + if (m_WasFired) { + ((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse + return false; + } + + return true; + } + void IEnumerator.Reset () { m_WasFired = false; } + object IEnumerator.Current { get { return null; } } + #endregion + + } + +} diff --git a/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineAnimationComplete.cs.meta b/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineAnimationComplete.cs.meta new file mode 100644 index 000000000..0aabc3b4f --- /dev/null +++ b/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineAnimationComplete.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a807dd9fb79db3545b6c2859a2bbfc0b +timeCreated: 1449704018 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs b/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs new file mode 100644 index 000000000..1cbddda00 --- /dev/null +++ b/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs @@ -0,0 +1,144 @@ +/****************************************************************************** + * 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. + *****************************************************************************/ + +//using UnityEngine; +using System.Collections; +using Spine; + +namespace Spine { + /// + /// Use this as a condition-blocking yield instruction for Unity Coroutines. + /// The routine will pause until the AnimationState fires an event matching the given event name or EventData reference. + public class WaitForSpineEvent : IEnumerator { + + Spine.EventData m_TargetEvent; + string m_EventName; + Spine.AnimationState m_AnimationState; + + bool m_WasFired = false; + bool m_unsubscribeAfterFiring = false; + + #region Constructors + void Subscribe (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribe) { + if (state == null || eventDataReference == null) { + m_WasFired = true; + } else { + m_AnimationState = state; + m_TargetEvent = eventDataReference; + state.Event += HandleAnimationStateEvent; + + m_unsubscribeAfterFiring = unsubscribe; + } + } + + void SubscribeByName (Spine.AnimationState state, string eventName, bool unsubscribe) { + if (state == null || string.IsNullOrEmpty(eventName)) { + m_WasFired = true; + } else { + m_AnimationState = state; + m_EventName = eventName; + state.Event += HandleAnimationStateEventByName; + + m_unsubscribeAfterFiring = unsubscribe; + } + } + + public WaitForSpineEvent (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) { + Subscribe(state, eventDataReference, unsubscribeAfterFiring); + } + + public WaitForSpineEvent (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) { + SubscribeByName(state, eventName, unsubscribeAfterFiring); + } + #endregion + + #region Event Handlers + void HandleAnimationStateEventByName (AnimationState state, int trackIndex, Spine.Event e) { + if (state != m_AnimationState) return; + + m_WasFired |= (e.Data.Name == m_EventName); // Check event name string match. + if (m_WasFired && m_unsubscribeAfterFiring) + state.Event -= HandleAnimationStateEventByName; // Unsubscribe after correct event fires. + } + + void HandleAnimationStateEvent (AnimationState state, int trackIndex, Spine.Event e) { + if (state != m_AnimationState) return; + + m_WasFired |= (e.Data == m_TargetEvent); // Check event data reference match. + if (m_WasFired && m_unsubscribeAfterFiring) + state.Event -= HandleAnimationStateEvent; // Usubscribe after correct event fires. + } + #endregion + + #region Reuse + /// + /// By default, WaitForSpineEvent will unsubscribe from the event immediately after it fires a correct matching event. + /// If you want to reuse this WaitForSpineEvent instance on the same event, you can set this to false. + public bool WillUnsubscribeAfterFiring { get { return m_unsubscribeAfterFiring; } set { m_unsubscribeAfterFiring = value; } } + + public WaitForSpineEvent NowWaitFor (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) { + ((IEnumerator)this).Reset(); + Clear(state); + Subscribe(state, eventDataReference, unsubscribeAfterFiring); + + return this; + } + + public WaitForSpineEvent NowWaitFor (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) { + ((IEnumerator)this).Reset(); + Clear(state); + SubscribeByName(state, eventName, unsubscribeAfterFiring); + + return this; + } + + void Clear (Spine.AnimationState state) { + state.Event -= HandleAnimationStateEvent; + state.Event -= HandleAnimationStateEventByName; + } + #endregion + + #region IEnumerator + bool IEnumerator.MoveNext () { + if (m_WasFired) { + ((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse + return false; + } + + return true; + } + void IEnumerator.Reset () { m_WasFired = false; } + object IEnumerator.Current { get { return null; } } + #endregion + + + } +} diff --git a/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs.meta b/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs.meta new file mode 100644 index 000000000..72bbef736 --- /dev/null +++ b/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fc166d883db083e469872998172f2d38 +timeCreated: 1449701857 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 4a7d3ecf962bdb0c2614045353419683cb227b52 Mon Sep 17 00:00:00 2001 From: pharan Date: Thu, 31 Dec 2015 09:34:56 +0800 Subject: [PATCH 29/40] [Unity] Extra constructors for WaitForSpineEvent --- .../spine-unity/YieldInstructions/WaitForSpineEvent.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs b/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs index 1cbddda00..28280b5c7 100644 --- a/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs +++ b/spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs @@ -74,10 +74,20 @@ namespace Spine { public WaitForSpineEvent (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) { Subscribe(state, eventDataReference, unsubscribeAfterFiring); } + + public WaitForSpineEvent (SkeletonAnimation skeletonAnimation, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) { + // If skeletonAnimation is invalid, its state will be null. Subscribe handles null states just fine. + Subscribe(skeletonAnimation.state, eventDataReference, unsubscribeAfterFiring); + } public WaitForSpineEvent (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) { SubscribeByName(state, eventName, unsubscribeAfterFiring); } + + public WaitForSpineEvent (SkeletonAnimation skeletonAnimation, string eventName, bool unsubscribeAfterFiring = true) { + // If skeletonAnimation is invalid, its state will be null. Subscribe handles null states just fine. + SubscribeByName(skeletonAnimation.state, eventName, unsubscribeAfterFiring); + } #endregion #region Event Handlers From fb9f53ee236d7e9611d525d44a179a6cdbf40138 Mon Sep 17 00:00:00 2001 From: pharan Date: Thu, 31 Dec 2015 09:36:54 +0800 Subject: [PATCH 30/40] [Unity] Shortened new [SpineEvent] property drawer attribute. --- .../Assets/spine-unity/Editor/SpineAttributeDrawers.cs | 6 +++--- spine-unity/Assets/spine-unity/SpineAttributes.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs index 6bb995da9..da4123d92 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs @@ -159,9 +159,9 @@ public class SpineAnimationDrawer : SpineTreeItemDrawerBase { } -[CustomPropertyDrawer(typeof(SpineEventData))] -public class SpineEventDataDrawer : SpineTreeItemDrawerBase { - protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEventData targetAttribute, SkeletonData data) { +[CustomPropertyDrawer(typeof(SpineEvent))] +public class SpineEventNameDrawer : SpineTreeItemDrawerBase { + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEvent targetAttribute, SkeletonData data) { var events = skeletonDataAsset.GetSkeletonData(false).Events; for (int i = 0; i < events.Count; i++) { string name = events.Items[i].Name; diff --git a/spine-unity/Assets/spine-unity/SpineAttributes.cs b/spine-unity/Assets/spine-unity/SpineAttributes.cs index fb60f2447..73eeebf4d 100644 --- a/spine-unity/Assets/spine-unity/SpineAttributes.cs +++ b/spine-unity/Assets/spine-unity/SpineAttributes.cs @@ -31,7 +31,7 @@ public class SpineSlot : SpineAttributeBase { } } -public class SpineEventData : SpineAttributeBase { +public class SpineEvent : SpineAttributeBase { /// /// Smart popup menu for Spine Events (Spine.EventData) /// @@ -40,7 +40,7 @@ public class SpineEventData : SpineAttributeBase { /// 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 = "") { + public SpineEvent(string startsWith = "", string dataField = "") { this.startsWith = startsWith; this.dataField = dataField; } From e116c15ffb9693e89a274058357a1431cd73faf7 Mon Sep 17 00:00:00 2001 From: pharan Date: Thu, 31 Dec 2015 09:45:14 +0800 Subject: [PATCH 31/40] [Unity] Minor update to sample scene scripts. --- .../Examples/Scripts/BasicPlatformerController.cs | 1 + spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs | 6 +++--- .../Assets/Examples/Scripts/SpineboyController.cs | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs b/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs index f8b40c06e..a50c1949f 100644 --- a/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs +++ b/spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs @@ -62,6 +62,7 @@ public class BasicPlatformerController : MonoBehaviour { public AudioSource jumpAudioSource; public AudioSource hardfallAudioSource; public AudioSource footstepAudioSource; + [SpineEvent] public string footstepEventName = "Footstep"; CharacterController controller; Vector2 velocity = Vector2.zero; diff --git a/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs b/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs index 8599c9598..2010c636a 100644 --- a/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs +++ b/spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs @@ -18,11 +18,11 @@ public class RaggedySpineboy : MonoBehaviour { void AddRigidbody () { var rb = gameObject.AddComponent(); -#if UNITY_5_1 + #if UNITY_5_1 || UNITY_5_2 || UNITY_5_3 || UNITY_5_4 || UNITY_5_5 rb.freezeRotation = true; -#else + #else rb.fixedAngle = true; -#endif + #endif naturalCollider.enabled = true; } diff --git a/spine-unity/Assets/Examples/Scripts/SpineboyController.cs b/spine-unity/Assets/Examples/Scripts/SpineboyController.cs index 2a6b4878e..d548bf34b 100644 --- a/spine-unity/Assets/Examples/Scripts/SpineboyController.cs +++ b/spine-unity/Assets/Examples/Scripts/SpineboyController.cs @@ -11,11 +11,11 @@ using System.Collections; public class SpineboyController : MonoBehaviour { SkeletonAnimation skeletonAnimation; - public string idleAnimation = "idle"; - public string walkAnimation = "walk"; - public string runAnimation = "run"; - public string hitAnimation = "hit"; - public string deathAnimation = "death"; + [SpineAnimation] public string idleAnimation = "idle"; + [SpineAnimation] public string walkAnimation = "walk"; + [SpineAnimation] public string runAnimation = "run"; + [SpineAnimation] public string hitAnimation = "hit"; + [SpineAnimation] public string deathAnimation = "death"; public float walkVelocity = 1; public float runVelocity = 3; public int hp = 10; From 3d19e22064d43cb8201cd535ff14a93b68e6fddf Mon Sep 17 00:00:00 2001 From: pharan Date: Thu, 31 Dec 2015 11:58:04 +0800 Subject: [PATCH 32/40] [XNA] Fixed compatibility with spine-csharp --- spine-xna/src/SkeletonMeshRenderer.cs | 5 +++-- spine-xna/src/SkeletonRegionRenderer.cs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/spine-xna/src/SkeletonMeshRenderer.cs b/spine-xna/src/SkeletonMeshRenderer.cs index bf60c2e0b..6d2047824 100644 --- a/spine-xna/src/SkeletonMeshRenderer.cs +++ b/spine-xna/src/SkeletonMeshRenderer.cs @@ -90,10 +90,11 @@ namespace Spine { public void Draw (Skeleton skeleton) { float[] vertices = this.vertices; - List drawOrder = skeleton.DrawOrder; + var drawOrder = skeleton.DrawOrder; + var drawOrderItems = skeleton.DrawOrder.Items; float skeletonR = skeleton.R, skeletonG = skeleton.G, skeletonB = skeleton.B, skeletonA = skeleton.A; for (int i = 0, n = drawOrder.Count; i < n; i++) { - Slot slot = drawOrder[i]; + Slot slot = drawOrderItems[i]; Attachment attachment = slot.Attachment; if (attachment is RegionAttachment) { RegionAttachment regionAttachment = (RegionAttachment)attachment; diff --git a/spine-xna/src/SkeletonRegionRenderer.cs b/spine-xna/src/SkeletonRegionRenderer.cs index 2189b01a3..c20c6b410 100644 --- a/spine-xna/src/SkeletonRegionRenderer.cs +++ b/spine-xna/src/SkeletonRegionRenderer.cs @@ -83,10 +83,11 @@ namespace Spine { } public void Draw (Skeleton skeleton) { - List drawOrder = skeleton.DrawOrder; + var drawOrder = skeleton.DrawOrder; + var drawOrderItems = skeleton.DrawOrder.Items; float skeletonR = skeleton.R, skeletonG = skeleton.G, skeletonB = skeleton.B, skeletonA = skeleton.A; for (int i = 0, n = drawOrder.Count; i < n; i++) { - Slot slot = drawOrder[i]; + Slot slot = drawOrderItems[i]; RegionAttachment regionAttachment = slot.Attachment as RegionAttachment; if (regionAttachment != null) { BlendState blend = slot.Data.BlendMode == BlendMode.additive ? BlendState.Additive : defaultBlendState; From 119e19c212d3c5ecd133fad1ec32d58f42756d23 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 31 Dec 2015 15:34:11 +0800 Subject: [PATCH 33/40] [Monogame] Fixed compatibility with spine-csharp --- .../src/spine-monogame-xamarinstudio-ios.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spine-monogame/xamarinstudio-ios/src/spine-monogame-xamarinstudio-ios.csproj b/spine-monogame/xamarinstudio-ios/src/spine-monogame-xamarinstudio-ios.csproj index 352d10bbc..69dd3597c 100644 --- a/spine-monogame/xamarinstudio-ios/src/spine-monogame-xamarinstudio-ios.csproj +++ b/spine-monogame/xamarinstudio-ios/src/spine-monogame-xamarinstudio-ios.csproj @@ -85,6 +85,9 @@ spine-csharp\BoneData.cs + + spine-csharp\ExposedList.cs + spine-csharp\Json.cs From b7f2f526ac08233242b43c963b75f2a4841fcbd4 Mon Sep 17 00:00:00 2001 From: pharan Date: Sat, 2 Jan 2016 06:06:31 +0800 Subject: [PATCH 34/40] [Unity] Fixed SkeletonDataAsset Inspector when drawing empty BoundingBoxAttachment --- .../Editor/SkeletonDataAssetInspector.cs | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs index 3f18858dc..e5f99040d 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(); @@ -61,7 +61,7 @@ public class SkeletonDataAssetInspector : Editor { EditorApplication.update += Update; } catch { - + // TODO: WARNING: empty catch block supresses errors. } @@ -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 () { @@ -162,7 +162,7 @@ public class SkeletonDataAssetInspector : Editor { EditorGUILayout.HelpBox("WARNING!\n\nBaking is NOT the same as SkeletonAnimator!\nDoes not support the following:\n\tFlipX or Y\n\tInheritScale\n\tColor Keys\n\tDraw Order Keys\n\tIK and Curves are sampled at 60fps and are not realtime.\n\tPlease read SkeletonBaker.cs comments for full details.\n\nThe main use of Baking is to export Spine projects to be used without the Spine Runtime (ie: for sale on the Asset Store, or background objects that are animated only with a wind noise generator)", MessageType.Warning, true); EditorGUI.indentLevel++; bakeAnimations = EditorGUILayout.Toggle("Bake Animations", bakeAnimations); - EditorGUI.BeginDisabledGroup(bakeAnimations == false); + EditorGUI.BeginDisabledGroup(!bakeAnimations); { EditorGUI.indentLevel++; bakeIK = EditorGUILayout.Toggle("Bake IK", bakeIK); @@ -195,7 +195,7 @@ public class SkeletonDataAssetInspector : Editor { try { GUILayout.BeginVertical(); if (GUILayout.Button(new GUIContent("Bake " + skinName, SpineEditorUtilities.Icons.unityIcon), GUILayout.Height(32), GUILayout.Width(250))) - SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, new ExposedList(new Skin[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions); + SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, new ExposedList(new [] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions); GUILayout.BeginHorizontal(); GUILayout.Label(new GUIContent("Skins", SpineEditorUtilities.Icons.skinsRoot), GUILayout.Width(50)); @@ -257,7 +257,7 @@ public class SkeletonDataAssetInspector : Editor { EditorGUILayout.PropertyField(defaultMix); // Animation names - String[] animations = new String[m_skeletonData.Animations.Count]; + var animations = new string[m_skeletonData.Animations.Count]; for (int i = 0; i < animations.Length; i++) animations[i] = m_skeletonData.Animations.Items[i].Name; @@ -292,7 +292,7 @@ public class SkeletonDataAssetInspector : Editor { serializedObject.ApplyModifiedProperties(); needToSerialize = true; } - + } void DrawAnimationList () { showAnimationList = EditorGUILayout.Foldout(showAnimationList, new GUIContent("Animations", SpineEditorUtilities.Icons.animationRoot)); @@ -445,7 +445,7 @@ public class SkeletonDataAssetInspector : Editor { warnings.Add("Skeleton data file is not a valid JSON or binary file."); } else { bool detectedNullAtlasEntry = false; - List atlasList = new List(); + var atlasList = new List(); for (int i = 0; i < atlasAssets.arraySize; i++) { if (atlasAssets.GetArrayElementAtIndex(i).objectReferenceValue == null) { detectedNullAtlasEntry = true; @@ -676,7 +676,7 @@ public class SkeletonDataAssetInspector : Editor { - if (drawHandles) { + if (drawHandles) { Handles.SetCamera(m_previewUtility.m_Camera); Handles.color = m_originColor; @@ -689,9 +689,10 @@ public class SkeletonDataAssetInspector : Editor { if (drawHandles) { Handles.SetCamera(m_previewUtility.m_Camera); foreach (var slot in m_skeletonAnimation.skeleton.Slots) { - if (slot.Attachment is BoundingBoxAttachment) { + var boundingBoxAttachment = slot.Attachment as BoundingBoxAttachment; - DrawBoundingBox(slot.Bone, (BoundingBoxAttachment)slot.Attachment); + if (boundingBoxAttachment != null) { + DrawBoundingBox (slot.Bone, boundingBoxAttachment); } } } @@ -702,8 +703,10 @@ public class SkeletonDataAssetInspector : Editor { } - void DrawBoundingBox (Bone bone, BoundingBoxAttachment box) { - float[] worldVerts = new float[box.Vertices.Length]; + static void DrawBoundingBox (Bone bone, BoundingBoxAttachment box) { + if (box.Vertices.Length <= 0) return; // Handle cases where user creates a BoundingBoxAttachment but doesn't actually define it. + + var worldVerts = new float[box.Vertices.Length]; box.ComputeWorldVertices(bone, worldVerts); Handles.color = Color.green; @@ -717,15 +720,12 @@ public class SkeletonDataAssetInspector : Editor { if (i > 0) { Handles.DrawLine(lastVert, vert); } - - + lastVert = vert; } Handles.DrawLine(lastVert, firstVert); - - - + } void Update () { @@ -824,7 +824,7 @@ public class SkeletonDataAssetInspector : Editor { float fr = m_animEventFrames[i]; - Rect evRect = new Rect(barRect); + var evRect = new Rect(barRect); evRect.x = Mathf.Clamp(((fr / t.Animation.Duration) * width) - (SpineEditorUtilities.Icons._event.width / 2), barRect.x, float.MaxValue); evRect.width = SpineEditorUtilities.Icons._event.width; evRect.height = SpineEditorUtilities.Icons._event.height; @@ -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) { @@ -930,7 +930,7 @@ public class SkeletonDataAssetInspector : Editor { //TODO: Fix first-import error //TODO: Update preview without thumbnail public override Texture2D RenderStaticPreview (string assetPath, UnityEngine.Object[] subAssets, int width, int height) { - Texture2D tex = new Texture2D(width, height, TextureFormat.ARGB32, false); + var tex = new Texture2D(width, height, TextureFormat.ARGB32, false); this.InitPreview(); @@ -956,4 +956,4 @@ public class SkeletonDataAssetInspector : Editor { tex = this.m_previewUtility.EndStaticPreview(); return tex; } -} +} \ No newline at end of file From 83a7ecc3b0598a96c315f311ac69e3164000ed97 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 2 Jan 2016 06:36:02 +0800 Subject: [PATCH 35/40] [TK2D] Fixed null array reference in some cases. from this PR: https://github.com/EsotericSoftware/spine-runtimes/pull/400 The other half of this PR was already applied in a previous commit. Thanks @YiDuwon --- spine-unity/Assets/spine-unity/SkeletonDataAsset.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs b/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs index b573681cb..72fb9d38a 100644 --- a/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs +++ b/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs @@ -50,6 +50,11 @@ public class SkeletonDataAsset : ScriptableObject { private SkeletonData skeletonData; private AnimationStateData stateData; + void OnEnable () { + if (atlasAssets == null) + atlasAssets = new AtlasAsset[0]; + } + public void Reset () { skeletonData = null; stateData = null; From bfb58cbb1951df5bfebd175dc94865ee53bba276 Mon Sep 17 00:00:00 2001 From: pharan Date: Sat, 2 Jan 2016 17:50:49 +0800 Subject: [PATCH 36/40] [Unity] Some ExposedList use optimization within loops. --- .../Assets/spine-unity/SkeletonRenderer.cs | 87 +++++++++++-------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 6cbac024c..aad0dcaf8 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -192,6 +192,7 @@ public class SkeletonRenderer : MonoBehaviour { int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0; Material lastMaterial = null; ExposedList drawOrder = skeleton.drawOrder; + var drawOrderItems = drawOrder.Items; int drawOrderCount = drawOrder.Count; int submeshSeparatorSlotsCount = submeshSeparatorSlots.Count; bool renderMeshes = this.renderMeshes; @@ -199,22 +200,29 @@ public class SkeletonRenderer : MonoBehaviour { // Clear last state of attachments and submeshes MeshState.SingleMeshState workingState = meshState.buffer; var workingAttachments = workingState.attachments; - var workingFlips = workingState.attachmentsFlipState; - var workingSubmeshArguments = workingState.addSubmeshArguments; workingAttachments.Clear(true); workingState.UpdateAttachmentCount(drawOrderCount); + var workingAttachmentsItems = workingAttachments.Items; // Make sure to not add to or remove from ExposedList inside the loop below + + var workingFlips = workingState.attachmentsFlipState; + var workingFlipsItems = workingState.attachmentsFlipState.Items; // Make sure to not add to or remove from ExposedList inside the loop below + + var workingSubmeshArguments = workingState.addSubmeshArguments; // Items array should not be cached. There is dynamic writing to this object. workingSubmeshArguments.Clear(false); MeshState.SingleMeshState storedState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; var storedAttachments = storedState.attachments; - var storedFlips = storedState.attachmentsFlipState; + var storedAttachmentsItems = storedAttachments.Items; // Make sure to not add to or remove from ExposedList inside the loop below - bool mustUpdateMeshStructure = storedState.requiresUpdate || // Force update if the mesh was cleared. (prevents flickering due to incorrect state) + var storedFlips = storedState.attachmentsFlipState; + var storedFlipsItems = storedFlips.Items; // Make sure to not add to or remove from ExposedList inside the loop below + + 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. for (int i = 0; i < drawOrderCount; i++) { - Slot slot = drawOrder.Items[i]; + Slot slot = drawOrderItems[i]; Bone bone = slot.bone; Attachment attachment = slot.attachment; @@ -225,14 +233,14 @@ public class SkeletonRenderer : MonoBehaviour { 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; + workingFlipsItems[i] = flip; + workingAttachmentsItems[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. + (attachment != storedAttachmentsItems[i]) || // Attachment order changed. // This relies on the drawOrder.Count != storedAttachments.Count check above as a bounds check. + (flip != storedFlipsItems[i]); // Flip states changed. - RegionAttachment regionAttachment = attachment as RegionAttachment; + var regionAttachment = attachment as RegionAttachment; if (regionAttachment != null) { rendererObject = regionAttachment.RendererObject; attachmentVertexCount = 4; @@ -240,13 +248,13 @@ public class SkeletonRenderer : MonoBehaviour { } else { if (!renderMeshes) continue; - MeshAttachment meshAttachment = attachment as MeshAttachment; + var meshAttachment = attachment as MeshAttachment; if (meshAttachment != null) { rendererObject = meshAttachment.RendererObject; attachmentVertexCount = meshAttachment.vertices.Length >> 1; attachmentTriangleCount = meshAttachment.triangles.Length; } else { - SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment; + var skinnedMeshAttachment = attachment as SkinnedMeshAttachment; if (skinnedMeshAttachment != null) { rendererObject = skinnedMeshAttachment.RendererObject; attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; @@ -301,7 +309,7 @@ public class SkeletonRenderer : MonoBehaviour { mustUpdateMeshStructure = mustUpdateMeshStructure || this.sharedMaterials.Length != workingSubmeshArguments.Count || // Material array changed in size - CheckIfMustUpdateMeshStructure(workingSubmeshArguments); // Submesh Argument Array changed. + CheckIfMustUpdateMeshStructure(workingSubmeshArguments); // Submesh Argument Array changed. // CheckIfMustUpdateMaterialArray (workingMaterials, sharedMaterials) if (!mustUpdateMeshStructure) { @@ -319,8 +327,10 @@ public class SkeletonRenderer : MonoBehaviour { if (mustUpdateMeshStructure) { this.submeshMaterials.Clear(); + + var workingSubmeshArgumentsItems = workingSubmeshArguments.Items; for (int i = 0, n = workingSubmeshArguments.Count; i < n; i++) { - AddSubmesh(workingSubmeshArguments.Items[i], workingFlips); + AddSubmesh(workingSubmeshArgumentsItems[i], workingFlips); } // Set materials. @@ -383,7 +393,7 @@ public class SkeletonRenderer : MonoBehaviour { } int i = 0; do { - Slot slot = drawOrder.Items[i]; + Slot slot = drawOrderItems[i]; Attachment attachment = slot.attachment; RegionAttachment regionAttachment = attachment as RegionAttachment; if (regionAttachment != null) { @@ -636,7 +646,7 @@ public class SkeletonRenderer : MonoBehaviour { return false; } - private void AddSubmesh (MeshState.AddSubmeshArguments submeshArguments, ExposedList flipStates) { + private void AddSubmesh (MeshState.AddSubmeshArguments submeshArguments, ExposedList flipStates) { //submeshArguments is a struct, so it's ok. int submeshIndex = submeshMaterials.Count; submeshMaterials.Add(submeshArguments.material); @@ -645,8 +655,8 @@ public class SkeletonRenderer : MonoBehaviour { else if (immutableTriangles) return; - Submesh submesh = submeshes.Items[submeshIndex]; - int[] triangles = submesh.triangles; + Submesh currentSubmesh = submeshes.Items[submeshIndex]; + int[] triangles = currentSubmesh.triangles; int triangleCount = submeshArguments.triangleCount; int firstVertex = submeshArguments.firstVertex; @@ -654,22 +664,24 @@ public class SkeletonRenderer : MonoBehaviour { int trianglesCapacity = triangles.Length; 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++) + for (int i = triangleCount; i < trianglesCapacity; i++) { triangles[i] = 0; - submesh.triangleCount = triangleCount; + } + currentSubmesh.triangleCount = triangleCount; + } else if (trianglesCapacity != triangleCount) { // Reallocate triangles when not the exact size needed. - submesh.triangles = triangles = new int[triangleCount]; - submesh.triangleCount = 0; + currentSubmesh.triangles = triangles = new int[triangleCount]; + currentSubmesh.triangleCount = 0; } - if (!renderMeshes && !frontFacing) { + if (!this.renderMeshes && !this.frontFacing) { // Use stored triangles if possible. - 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++*/) { + if (currentSubmesh.firstVertex != firstVertex || currentSubmesh.triangleCount < triangleCount) { //|| currentSubmesh.triangleCount == 0 + currentSubmesh.triangleCount = triangleCount; + currentSubmesh.firstVertex = firstVertex; + + for (int i = 0; i < triangleCount; i += 6, firstVertex += 4) { triangles[i] = firstVertex; triangles[i + 1] = firstVertex + 2; triangles[i + 2] = firstVertex + 1; @@ -677,18 +689,21 @@ public class SkeletonRenderer : MonoBehaviour { triangles[i + 4] = firstVertex + 3; triangles[i + 5] = firstVertex + 1; } + } return; } // Iterate through all slots and store their triangles. - ExposedList drawOrder = skeleton.DrawOrder; - 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]; + var drawOrderItems = skeleton.DrawOrder.Items; // Make sure to not modify ExposedList inside the loop below + var flipStatesItems = flipStates.Items; // Make sure to not modify ExposedList inside the loop below + + int triangleIndex = 0; // Modified by loop + for (int i = submeshArguments.startSlot, n = submeshArguments.endSlot; i < n; i++) { + Attachment attachment = drawOrderItems[i].attachment; + + bool flip = flipStatesItems[i]; // Add RegionAttachment triangles if (attachment is RegionAttachment) { @@ -716,12 +731,12 @@ public class SkeletonRenderer : MonoBehaviour { // Add (Skinned)MeshAttachment triangles int[] attachmentTriangles; int attachmentVertexCount; - MeshAttachment meshAttachment = attachment as MeshAttachment; + var meshAttachment = attachment as MeshAttachment; if (meshAttachment != null) { attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2 attachmentTriangles = meshAttachment.triangles; } else { - SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment; + var skinnedMeshAttachment = attachment as SkinnedMeshAttachment; if (skinnedMeshAttachment != null) { attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; // length/2 attachmentTriangles = skinnedMeshAttachment.triangles; From 64f69343a2d23f1cc1ba0397fd1522b5ee1d6778 Mon Sep 17 00:00:00 2001 From: pharan Date: Sat, 2 Jan 2016 18:55:45 +0800 Subject: [PATCH 37/40] [Unity] Allow SkeletonGhost use in camera distance sorting setup. --- .../Assets/spine-unity/Ghost/SkeletonGhost.cs | 20 ++++++++++++------- .../Ghost/SkeletonGhostRenderer.cs | 3 ++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs index fe827aebe..3717bc9a6 100644 --- a/spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs +++ b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs @@ -11,7 +11,7 @@ using System.Collections.Generic; public class SkeletonGhost : MonoBehaviour { public bool ghostingEnabled = true; public float spawnRate = 0.05f; - public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); + public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); // default for additive. [Tooltip("Remember to set color alpha to 0 if Additive is true")] public bool additive = true; public int maximumGhosts = 10; @@ -21,6 +21,10 @@ public class SkeletonGhost : MonoBehaviour { [Range(0, 1)] public float textureFade = 1; + [Header("Sorting")] + public bool sortWithDistanceOnly; + public float zOffset = 0f; + float nextSpawnTime; SkeletonGhostRenderer[] pool; int poolIndex = 0; @@ -100,14 +104,16 @@ public class SkeletonGhost : MonoBehaviour { materials[i] = ghostMat; } - pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingOrder - 1); - go.transform.parent = transform; + var goTransform = go.transform; + goTransform.parent = transform; - go.transform.localPosition = Vector3.zero; - go.transform.localRotation = Quaternion.identity; - go.transform.localScale = Vector3.one; + pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingLayerID, (sortWithDistanceOnly) ? meshRenderer.sortingOrder : meshRenderer.sortingOrder - 1); - go.transform.parent = null; + goTransform.localPosition = new Vector3(0f, 0f, zOffset); + goTransform.localRotation = Quaternion.identity; + goTransform.localScale = Vector3.one; + + goTransform.parent = null; poolIndex++; diff --git a/spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs index 6ce7dfefb..dbf4b0e3d 100644 --- a/spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs +++ b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs @@ -20,13 +20,14 @@ public class SkeletonGhostRenderer : MonoBehaviour { meshFilter = gameObject.AddComponent(); } - public void Initialize (Mesh mesh, Material[] materials, Color32 color, bool additive, float speed, int sortingOrder) { + public void Initialize (Mesh mesh, Material[] materials, Color32 color, bool additive, float speed, int sortingLayerID, int sortingOrder) { StopAllCoroutines(); gameObject.SetActive(true); meshRenderer.sharedMaterials = materials; + meshRenderer.sortingLayerID = sortingLayerID; meshRenderer.sortingOrder = sortingOrder; meshFilter.sharedMesh = (Mesh)Instantiate(mesh); From ff5b7e67e4208c048ddcb33e2e50a598ba395b1d Mon Sep 17 00:00:00 2001 From: pharan Date: Mon, 4 Jan 2016 09:10:18 +0800 Subject: [PATCH 38/40] [Unity] Updated Readme. --- spine-unity/README.md | 57 ++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/spine-unity/README.md b/spine-unity/README.md index f68b5a015..f75863b4f 100644 --- a/spine-unity/README.md +++ b/spine-unity/README.md @@ -1,8 +1,10 @@ # spine-unity -The spine-unity runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Unity](http://unity3d.com/). spine-unity is based on [spine-csharp](https://github.com/EsotericSoftware/spine-runtimes/tree/master/spine-csharp). +The **Spine-Unity** runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Unity](http://unity3d.com/). spine-unity is based on [spine-csharp](https://github.com/EsotericSoftware/spine-runtimes/tree/master/spine-csharp). -While spine-unity can render directly with Unity, without the need for any other plugins, it also works with [2D Toolkit](http://www.unikronsoftware.com/2dtoolkit/) and can render skeletons from a TK2D texture atlas. +For more documentation, see [Spine-Unity Documentation](https://github.com/pharan/spine-unity-docs/blob/master/README.md). + +While spine-unity can render directly with Unity, without the need for any other plugins, it also works with [2D Toolkit](http://www.unikronsoftware.com/2dtoolkit/) and can render skeletons using a TK2D texture atlas. ## Licensing @@ -12,12 +14,16 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Documentation -A Spine skeleton is a GameObject and can be used throughout Unity like any other GameObject. It depends only on Unity's `MeshRenderer`, so it is close to the metal. `SkeletonUtility` allows other GameObjects to interact with the Spine skeleton, to control bones in the skeleton, be controlled by the skeleton, attach colliders, etc. +A Spine skeleton GameObject (a GameObject with a SkeletonAnimation component on it) can be used throughout Unity like any other GameObject. It renders through `MeshRenderer`, so it is close to the metal. -Spine skeletons can be "baked" into native Unity animations for use with Mecanim. Baking is an advanced feature with a number of limitations, so in most cases baking should not be used. +`SkeletonUtility` allows other GameObjects to interact with the Spine skeleton, to control bones in the skeleton, be controlled by the skeleton, attach colliders, etc. + +For advanced uses and specific optimization cases, Spine skeletons can be "baked" into native Unity animation assets. Since Unity's animation feature-set does not overlap with Spine's perfectly, baked assets have many limitations and removed features. For most uses, baking is not necessary. The [Spine Unity Features Tutorial](http://esotericsoftware.com/forum/Unity-Feature-Tutorials-4839) forum thread has many videos on how to use spine-unity. +For more documentation, see [Spine-Unity Documentation](https://github.com/pharan/spine-unity-docs/blob/master/README.md). + ## Quick installation Download and run this Unity package: @@ -31,31 +37,36 @@ In the `Assets/Examples/Scenes` folder you will find many example scenes that de You can also choose to setup and run from the Git files: 1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip). -1. Copy the contents of `spine-csharp/src` to `Assets/spine-csharp` in your Unity project directory. -1. Copy the `spine-unity/Assets/spine-unity` to `Assets/spine-unity` in your Unity project directory. +2. Spine-Unity requires both `spine-csharp` and `spine-unity`. + - Copy the contents of `spine-csharp/src` to `Assets/spine-csharp` in your Unity project directory. + - Copy the contents of `spine-unity/Assets/` to `Assets/` in your Unity project directory. Including `Gizmos` and `spine-unity` and `Examples` if you want them. + + +> - `Gizmos` is a [special folder](http://docs.unity3d.com/Manual/SpecialFolders.html) in Unity. It needs to be at the root of your assets folder to function correctly. (ie. `Assets/Gizmos` +- `spine-csharp` and `spine-unity` can be placed in any subfolder you want. + +---------- ## Importing skeleton data -There are a few options for importing Spine skeletons into your Unity project: +1. Add your `.json`, `.atlas.txt` and `.png` into your Unity project. + - You can do this through Unity's Project View: Drag and drop a folder containing the `.json`, `.atlas.txt` and `.png` files exported from Spine directly into the Unity Project view. + - ... or you can opt to do this through Windows File Explorer or OSX Finder. Move or copy your `.json`, `.atlas.txt` and `.png` files into your Unity project's `Assets` folder, ideally in its own subfolder. +2. Spine-Unity will automatically detect the `.json` and `.atlas.txt` and attempt to generate the necessary Spine-Unity assets. +3. To start using your Spine assets, right-click on the SkeletonDataAsset (the asset with the orange Spine logo on it) and choose `Spine > Instantiate(SkeletonAnimation)`. This will add a GameObject with a `SkeletonAnimation` component on it. + - If you are more familiar with Mecanim, you may choose `Spine > Instantiate(Mecanim)` instead. +4. For more info on how to control the animation, see the [Spine-Unity Animation Control documentation](https://github.com/pharan/spine-unity-docs/blob/master/Animation.md). -### Drag and drop import -1. Drag and drop a folder containing the JSON, atlas and PNG files exported from Spine directly into Unity. -1. Drag the prefab that was created into your scene. -The [drag and drop video](http://www.youtube.com/watch?v=-Gk_zJsY1Ms) shows how this works. +> The original [manual setup video](https://www.youtube.com/watch?v=-V84OIvZdQc) to shows which assets belong where and what Spine-Unity's automatic import actually does for you under the hood. In case you have a specialized asset setup, this video will be useful for understanding how assets fit together. -### Automated import +> More resources: +[Drag and drop video](http://www.youtube.com/watch?v=-Gk_zJsY1Ms) +[readme PDF](https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/master/spine-unity/README.pdf) -1. Place the JSON, atlas and PNG files exported from Spine into your Unity project directory. -1. Right click the JSON file and click `Spine`, `Ingest` to create the Unity assets. -1. Right click the SkeletonData asset that was created and click `Spine`, `Spawn`. +---------- -The [readme PDF](https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/master/spine-unity/README.pdf) illustrates these steps. - -### Manual import - -1. Follow the [setup video](https://www.youtube.com/watch?v=-V84OIvZdQc) to manually set up the assets for a skeleton in Unity. This video may prove useful to understand how the pieces fit together but the other import methods are much easier. ## Examples @@ -71,7 +82,7 @@ To run the examples: ## Notes -- Atlas images should use premultiplied alpha when using the shaders that come with spine-unity. - This slightly outdated [spine-unity tutorial video](http://www.youtube.com/watch?v=x1umSQulghA) may still be useful. -- Unity scales large images down by default if they exceed 1024x1024, which causes atlas coordinates to be incorrect. To fix this, override the import settings in the Inspector for any large atlas image you have so Unity does not scale it down. -- Unity 4.3+'s 2D project defaults cause atlas images added to the project to be imported with the Texture Type "Sprite", which may cause artifacts when using Spine's Skeleton shader. To avoid these artifacts, make sure the Texture Type is set to "Texture". +- Atlas images should use **Premultiplied Alpha** when using the shaders that come with spine-unity (`Spine/Skeleton` or `Spine/SkeletonLit`). +- **TEXTURE SIZES.** Unity scales large images down by default if they exceed 1024x1024. This can cause atlas coordinates to be incorrect. To fix this, make sure to set import settings in the Inspector for any large atlas image you have so Unity does not scale it down. +- **TEXTURE ARTIFACTS FROM COMPRESSION.** Unity's 2D project defaults import new images added to the project with the Texture Type "Sprite". This can cause artifacts when using the `Spine/Skeleton` shader. To avoid these artifacts, make sure the Texture Type is set to "Texture". Spine-Unity's automatic import will attempt to apply these settings but in the process of updating your textures, these settings may be reverted. From f0d1b757322a1960f0a62ea88b8315462c7164ec Mon Sep 17 00:00:00 2001 From: pharan Date: Mon, 4 Jan 2016 09:39:38 +0800 Subject: [PATCH 39/40] [Unity] Disabled a false error message. --- .../spine-unity/Editor/SpineAttributeDrawers.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs index da4123d92..7cdc9080e 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs @@ -178,14 +178,11 @@ public class SpineAttachmentDrawer : SpineTreeItemDrawerBase { List validSkins = new List(); SkeletonRenderer skeletonRenderer = null; - if (property.serializedObject.targetObject is Component) { - var component = (Component)property.serializedObject.targetObject; + var component = property.serializedObject.targetObject as Component; + if (component != null) { 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."); - } - + //if (skeletonDataAsset != skeletonRenderer.skeletonDataAsset) Debug.LogWarning("DataField SkeletonDataAsset and SkeletonRenderer/SkeletonAnimation's SkeletonDataAsset do not match. Remove the explicit dataField parameter of your [SpineAttachment] field."); skeletonDataAsset = skeletonRenderer.skeletonDataAsset; } } @@ -212,9 +209,11 @@ public class SpineAttachmentDrawer : SpineTreeItemDrawerBase { 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]; From 8c7038c0b9938375b991993a2e187ec71762c05b Mon Sep 17 00:00:00 2001 From: pharan Date: Tue, 5 Jan 2016 15:41:10 +0800 Subject: [PATCH 40/40] [Unity] Autoreset will be a separate script until we clear up mixing stuff. Sorry for the confusion! --- .../Editor/SkeletonAnimationInspector.cs | 3 --- .../Assets/spine-unity/SkeletonAnimation.cs | 18 +++++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs index 80efaa998..c009dca73 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs @@ -45,8 +45,6 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector { 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) m_isPrefab = true; @@ -97,7 +95,6 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector { EditorGUILayout.PropertyField(loop); EditorGUILayout.PropertyField(timeScale); - EditorGUILayout.PropertyField(autoReset, autoResetLabel); component.timeScale = Math.Max(component.timeScale, 0); EditorGUILayout.Space(); diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index 1d2162b73..00e37f4a0 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs @@ -111,9 +111,9 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { #endif public float timeScale = 1; - #if UNITY_5 + #region AutoReset + /** [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; @@ -132,6 +132,13 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { } } + protected virtual void HandleNewAnimationAutoreset (Spine.AnimationState state, int trackIndex) { + if (!autoReset) return; + if (skeleton != null) skeleton.SetToSetupPose(); + } + */ + #endregion + public override void Reset () { base.Reset(); if (!valid) @@ -139,20 +146,17 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { 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);