From 34b08bceec5f27027e48fbc2b9e9610867698c49 Mon Sep 17 00:00:00 2001 From: Fenrisul Date: Sun, 15 Mar 2015 10:59:30 -0700 Subject: [PATCH] Added TK2D support to main spine-unity runtime with compiler directive. spine-tk2d now obsolete Added PreferenceItem GUI to SpineEditorUtilities (Default Mix and TK2D) --- .../Editor/SkeletonDataAssetInspector.cs | 14 ++ .../Editor/SpineEditorUtilities.cs | 81 +++++++++ .../Assets/spine-unity/SkeletonDataAsset.cs | 34 +++- .../Assets/spine-unity/SkeletonRenderer.cs | 4 + .../SpriteCollectionAttachmentLoader.cs | 164 ++++++++++++++++++ .../SpriteCollectionAttachmentLoader.cs.meta | 8 + 6 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 spine-unity/Assets/spine-unity/SpriteCollectionAttachmentLoader.cs create mode 100644 spine-unity/Assets/spine-unity/SpriteCollectionAttachmentLoader.cs.meta diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs index 83bbef004..5774d09be 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs @@ -55,6 +55,10 @@ public class SkeletonDataAssetInspector : Editor { private SerializedProperty atlasAssets, skeletonJSON, scale, fromAnimation, toAnimation, duration, defaultMix, controller; +#if SPINE_TK2D + private SerializedProperty spriteCollection; +#endif + private bool m_initialized = false; private SkeletonDataAsset m_skeletonDataAsset; private SkeletonData m_skeletonData; @@ -76,6 +80,9 @@ public class SkeletonDataAssetInspector : Editor { duration = serializedObject.FindProperty("duration"); defaultMix = serializedObject.FindProperty("defaultMix"); controller = serializedObject.FindProperty("controller"); +#if SPINE_TK2D + spriteCollection = serializedObject.FindProperty("spriteCollection"); +#endif m_skeletonDataAsset = (SkeletonDataAsset)target; m_skeletonDataAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_skeletonDataAsset)); @@ -107,7 +114,14 @@ public class SkeletonDataAssetInspector : Editor { serializedObject.Update(); EditorGUI.BeginChangeCheck(); +#if !SPINE_TK2D EditorGUILayout.PropertyField(atlasAssets, true); +#else + EditorGUI.BeginDisabledGroup(spriteCollection.objectReferenceValue != null); + EditorGUILayout.PropertyField(atlasAssets, true); + EditorGUI.EndDisabledGroup(); + EditorGUILayout.PropertyField(spriteCollection, true); +#endif EditorGUILayout.PropertyField(skeletonJSON); EditorGUILayout.PropertyField(scale); if (EditorGUI.EndChangeCheck()) { diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs index c1ebb804f..6bc92ec0b 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs @@ -154,11 +154,15 @@ public class SpineEditorUtilities : AssetPostprocessor { 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")); @@ -878,4 +882,81 @@ public class SpineEditorUtilities : AssetPostprocessor { return anim; } + + static bool preferencesLoaded = false; + + [PreferenceItem("Spine")] + static void PreferencesGUI () { + if (!preferencesLoaded) { + preferencesLoaded = true; + defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f); + } + + + EditorGUI.BeginChangeCheck(); + defaultMix = EditorGUILayout.FloatField("Default Mix", defaultMix); + if (EditorGUI.EndChangeCheck()) + EditorPrefs.SetFloat(DEFAULT_MIX_KEY, defaultMix); + + 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); + } + } + } \ 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 327ba4640..71b006b93 100644 --- a/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs +++ b/spine-unity/Assets/spine-unity/SkeletonDataAsset.cs @@ -36,6 +36,9 @@ using Spine; public class SkeletonDataAsset : ScriptableObject { public AtlasAsset[] atlasAssets; +#if SPINE_TK2D + public tk2dSpriteCollectionData spriteCollection; +#endif public TextAsset skeletonJSON; public float scale = 1; public String[] fromAnimation; @@ -67,12 +70,17 @@ public class SkeletonDataAsset : ScriptableObject { return null; } - - +#if !SPINE_TK2D if (atlasAssets.Length == 0) { Reset(); return null; } +#else + if (atlasAssets.Length == 0 && spriteCollection == null) { + Reset(); + return null; + } +#endif Atlas[] atlasArr = new Atlas[atlasAssets.Length]; for (int i = 0; i < atlasAssets.Length; i++) { @@ -90,8 +98,28 @@ public class SkeletonDataAsset : ScriptableObject { if (skeletonData != null) return skeletonData; - SkeletonJson json = new SkeletonJson(atlasArr); + SkeletonJson json; + +#if !SPINE_TK2D + json = new SkeletonJson(atlasArr); json.Scale = scale; +#else + if (spriteCollection != null) { + json = new SkeletonJson(new SpriteCollectionAttachmentLoader(spriteCollection)); + json.Scale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale) * 100f; + } else { + if (atlasArr.Length == 0) { + Reset(); + if (!quiet) + Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); + return null; + } + json = new SkeletonJson(atlasArr); + json.Scale = scale; + } +#endif + + try { skeletonData = json.ReadSkeletonData(new StringReader(skeletonJSON.text)); } catch (Exception ex) { diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 48b2c745e..6758defd9 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -205,7 +205,11 @@ public class SkeletonRenderer : MonoBehaviour { } // Populate submesh when material changes. +#if !SPINE_TK2D Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; +#else + Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; +#endif if ((lastMaterial != material && lastMaterial != null) || submeshSeparatorSlots.Contains(slot)) { AddSubmesh(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false); diff --git a/spine-unity/Assets/spine-unity/SpriteCollectionAttachmentLoader.cs b/spine-unity/Assets/spine-unity/SpriteCollectionAttachmentLoader.cs new file mode 100644 index 000000000..718edbab4 --- /dev/null +++ b/spine-unity/Assets/spine-unity/SpriteCollectionAttachmentLoader.cs @@ -0,0 +1,164 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), 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 SOFTARE 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. + *****************************************************************************/ + +#if SPINE_TK2D +using System; +using UnityEngine; +using Spine; + +// TODO: handle TPackerCW flip mode (probably not swap uv horizontaly) + +public class SpriteCollectionAttachmentLoader : AttachmentLoader { + private tk2dSpriteCollectionData sprites; + private float u, v, u2, v2; + private bool regionRotated; + private float regionOriginalWidth, regionOriginalHeight; + private float regionWidth, regionHeight; + private float regionOffsetX, regionOffsetY; + private Material material; + + public SpriteCollectionAttachmentLoader (tk2dSpriteCollectionData sprites) { + if (sprites == null) + throw new ArgumentNullException("sprites cannot be null."); + this.sprites = sprites; + } + + private void ProcessSpriteDefinition (String name) { + // Strip folder names. + int index = name.LastIndexOfAny(new char[] {'/', '\\'}); + if (index != -1) + name = name.Substring(index + 1); + + tk2dSpriteDefinition def = sprites.inst.GetSpriteDefinition(name); + + if (def == null) { + Debug.Log("Sprite not found in atlas: " + name, sprites); + throw new Exception("Sprite not found in atlas: " + name); + } + if (def.complexGeometry) + throw new NotImplementedException("Complex geometry is not supported: " + name); + if (def.flipped == tk2dSpriteDefinition.FlipMode.TPackerCW) + throw new NotImplementedException("Only 2D Toolkit atlases are supported: " + name); + + Vector2 minTexCoords = Vector2.one, maxTexCoords = Vector2.zero; + for (int i = 0; i < def.uvs.Length; ++i) { + Vector2 uv = def.uvs[i]; + minTexCoords = Vector2.Min(minTexCoords, uv); + maxTexCoords = Vector2.Max(maxTexCoords, uv); + } + regionRotated = def.flipped == tk2dSpriteDefinition.FlipMode.Tk2d; + if (regionRotated) { + float temp = minTexCoords.x; + minTexCoords.x = maxTexCoords.x; + maxTexCoords.x = temp; + } + u = minTexCoords.x; + v = maxTexCoords.y; + u2 = maxTexCoords.x; + v2 = minTexCoords.y; + + regionOriginalWidth = (int)(def.untrimmedBoundsData[1].x / def.texelSize.x); + regionOriginalHeight = (int)(def.untrimmedBoundsData[1].y / def.texelSize.y); + + regionWidth = (int)(def.boundsData[1].x / def.texelSize.x); + regionHeight = (int)(def.boundsData[1].y / def.texelSize.y); + + float x0 = def.untrimmedBoundsData[0].x - def.untrimmedBoundsData[1].x / 2; + float x1 = def.boundsData[0].x - def.boundsData[1].x / 2; + regionOffsetX = (int)((x1 - x0) / def.texelSize.x); + + float y0 = def.untrimmedBoundsData[0].y - def.untrimmedBoundsData[1].y / 2; + float y1 = def.boundsData[0].y - def.boundsData[1].y / 2; + regionOffsetY = (int)((y1 - y0) / def.texelSize.y); + + material = def.materialInst; + } + + public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) { + ProcessSpriteDefinition(path); + + RegionAttachment region = new RegionAttachment(name); + region.Path = path; + region.RendererObject = material; + region.SetUVs(u, v, u2, v2, regionRotated); + region.RegionOriginalWidth = regionOriginalWidth; + region.RegionOriginalHeight = regionOriginalHeight; + region.RegionWidth = regionWidth; + region.RegionHeight = regionHeight; + region.RegionOffsetX = regionOffsetX; + region.RegionOffsetY = regionOffsetY; + return region; + } + + public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) { + ProcessSpriteDefinition(path); + + MeshAttachment mesh = new MeshAttachment(name); + mesh.Path = path; + mesh.RendererObject = material; + mesh.RegionU = u; + mesh.RegionV = v; + mesh.RegionU2 = u2; + mesh.RegionV2 = v2; + mesh.RegionRotate = regionRotated; + mesh.RegionOriginalWidth = regionOriginalWidth; + mesh.RegionOriginalHeight = regionOriginalHeight; + mesh.RegionWidth = regionWidth; + mesh.RegionHeight = regionHeight; + mesh.RegionOffsetX = regionOffsetX; + mesh.RegionOffsetY = regionOffsetY; + return mesh; + } + + public SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, String name, String path) { + ProcessSpriteDefinition(path); + + SkinnedMeshAttachment mesh = new SkinnedMeshAttachment(name); + mesh.Path = path; + mesh.RendererObject = material; + mesh.RegionU = u; + mesh.RegionV = v; + mesh.RegionU2 = u2; + mesh.RegionV2 = v2; + mesh.RegionRotate = regionRotated; + mesh.RegionOriginalWidth = regionOriginalWidth; + mesh.RegionOriginalHeight = regionOriginalHeight; + mesh.RegionWidth = regionWidth; + mesh.RegionHeight = regionHeight; + mesh.RegionOffsetX = regionOffsetX; + mesh.RegionOffsetY = regionOffsetY; + return mesh; + } + + public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) { + return new BoundingBoxAttachment(name); + } +} +#endif diff --git a/spine-unity/Assets/spine-unity/SpriteCollectionAttachmentLoader.cs.meta b/spine-unity/Assets/spine-unity/SpriteCollectionAttachmentLoader.cs.meta new file mode 100644 index 000000000..671d5c768 --- /dev/null +++ b/spine-unity/Assets/spine-unity/SpriteCollectionAttachmentLoader.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 03238e4a73953c045a6cb289162532f3 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: