From baa6ed3e60ab27ff79c3c7c5d4242678d1852da5 Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 15 Dec 2017 09:39:40 +0800 Subject: [PATCH 1/6] [unity] Fix clipper skipping non-rendered slot. --- spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs b/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs index 68f1db3f0..0d57c2925 100644 --- a/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs +++ b/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs @@ -501,6 +501,7 @@ public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = t Color c = default(Color); + // Identify and prepare values. var region = attachment as RegionAttachment; if (region != null) { region.ComputeWorldVertices(slot.bone, workingVerts, 0); @@ -531,6 +532,9 @@ public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = t continue; } } + + // If not any renderable attachment. + clipper.ClipEnd(slot); continue; } } @@ -557,6 +561,7 @@ public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = t uvs = clipper.clippedUVs.Items; } + // Actually add slot/attachment data into buffers. if (attachmentVertexCount != 0 && attachmentIndexCount != 0) { if (tintBlack) AddAttachmentTintBlack(slot.r2, slot.g2, slot.b2, attachmentVertexCount); @@ -633,6 +638,7 @@ public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = t submeshItems[oldTriangleCount + i] = attachmentTriangleIndices[i] + ovc; } } + clipper.ClipEnd(slot); } clipper.ClipEnd(); From b06dc2134dee3d22339cc27133be215d1b31abe3 Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 15 Dec 2017 09:40:34 +0800 Subject: [PATCH 2/6] [unity] Fix method name typo. --- spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs | 2 +- .../Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs | 4 ++-- spine-unity/Assets/spine-unity/SkeletonRenderer.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs b/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs index 0d57c2925..40be3a896 100644 --- a/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs +++ b/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs @@ -1161,7 +1161,7 @@ public void Initialize () { doubleBufferedMesh = new DoubleBuffered(); } - public Material[] GetUpdatedShaderdMaterialsArray () { + public Material[] GetUpdatedSharedMaterialsArray () { if (submeshMaterials.Count == sharedMaterials.Length) submeshMaterials.CopyTo(sharedMaterials); else diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs index e89f2c5c1..f1fc26685 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs @@ -112,9 +112,9 @@ public void RenderParts (ExposedList instructions, int start meshGenerator.FillVertexData(mesh); if (updateTriangles) { meshGenerator.FillTriangles(mesh); - meshRenderer.sharedMaterials = buffers.GetUpdatedShaderdMaterialsArray(); + meshRenderer.sharedMaterials = buffers.GetUpdatedSharedMaterialsArray(); } else if (buffers.MaterialsChangedInLastUpdate()) { - meshRenderer.sharedMaterials = buffers.GetUpdatedShaderdMaterialsArray(); + meshRenderer.sharedMaterials = buffers.GetUpdatedSharedMaterialsArray(); } } diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index e12b0b0d0..3f33ba263 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -305,9 +305,9 @@ public virtual void LateUpdate () { rendererBuffers.UpdateSharedMaterials(workingSubmeshInstructions); if (updateTriangles) { // Check if the triangles should also be updated. meshGenerator.FillTriangles(currentMesh); - meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedShaderdMaterialsArray(); + meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedSharedMaterialsArray(); } else if (rendererBuffers.MaterialsChangedInLastUpdate()) { - meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedShaderdMaterialsArray(); + meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedSharedMaterialsArray(); } From 21d56f4b983e47edfcb7d3294a96cc4fb0890173 Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 15 Dec 2017 09:41:59 +0800 Subject: [PATCH 3/6] [unity] Move asset inspector scripts to the main Editor folder. --- .../spine-unity/Asset Types/Editor.meta | 9 - .../Editor/AtlasAssetInspector.cs | 732 +++++++++--------- .../Editor/AtlasAssetInspector.cs.meta | 0 .../Editor/SkeletonDataAssetInspector.cs | 0 .../Editor/SkeletonDataAssetInspector.cs.meta | 0 5 files changed, 366 insertions(+), 375 deletions(-) delete mode 100644 spine-unity/Assets/spine-unity/Asset Types/Editor.meta rename spine-unity/Assets/spine-unity/{Asset Types => }/Editor/AtlasAssetInspector.cs (96%) rename spine-unity/Assets/spine-unity/{Asset Types => }/Editor/AtlasAssetInspector.cs.meta (100%) rename spine-unity/Assets/spine-unity/{Asset Types => }/Editor/SkeletonDataAssetInspector.cs (100%) rename spine-unity/Assets/spine-unity/{Asset Types => }/Editor/SkeletonDataAssetInspector.cs.meta (100%) diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor.meta b/spine-unity/Assets/spine-unity/Asset Types/Editor.meta deleted file mode 100644 index ad248b556..000000000 --- a/spine-unity/Assets/spine-unity/Asset Types/Editor.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: cbe5d97ed1d75964cab2e2882a52a200 -folderAsset: yes -timeCreated: 1455489536 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs similarity index 96% rename from spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs rename to spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs index 2ea42ca3b..7b7349351 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs @@ -1,366 +1,366 @@ -/****************************************************************************** - * Spine Runtimes Software License v2.5 - * - * Copyright (c) 2013-2016, 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 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 develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes 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, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) 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. - *****************************************************************************/ - -//#define BAKE_ALL_BUTTON -//#define REGION_BAKING_MESH - -using System; -using System.Collections.Generic; -using System.Reflection; -using UnityEditor; -using UnityEngine; -using Spine; - -namespace Spine.Unity.Editor { - using Event = UnityEngine.Event; - - [CustomEditor(typeof(AtlasAsset)), CanEditMultipleObjects] - public class AtlasAssetInspector : UnityEditor.Editor { - SerializedProperty atlasFile, materials; - AtlasAsset atlasAsset; - - GUIContent spriteSlicesLabel; - GUIContent SpriteSlicesLabel { - get { - if (spriteSlicesLabel == null) { - spriteSlicesLabel = new GUIContent( - "Apply Regions as Texture Sprite Slices", - SpineEditorUtilities.Icons.unity, - "Adds Sprite slices to atlas texture(s). " + - "Updates existing slices if ones with matching names exist. \n\n" + - "If your atlas was exported with Premultiply Alpha, " + - "your SpriteRenderer should use the generated Spine _Material asset (or any Material with a PMA shader) instead of Sprites-Default."); - } - return spriteSlicesLabel; - } - } - - static List GetRegions (Atlas atlas) { - FieldInfo regionsField = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.NonPublic); - return (List)regionsField.GetValue(atlas); - } - - void OnEnable () { - SpineEditorUtilities.ConfirmInitialization(); - atlasFile = serializedObject.FindProperty("atlasFile"); - materials = serializedObject.FindProperty("materials"); - materials.isExpanded = true; - atlasAsset = (AtlasAsset)target; - #if REGION_BAKING_MESH - UpdateBakedList(); - #endif - } - - #if REGION_BAKING_MESH - private List baked; - private List bakedObjects; - - void UpdateBakedList () { - AtlasAsset asset = (AtlasAsset)target; - baked = new List(); - bakedObjects = new List(); - if (atlasFile.objectReferenceValue != null) { - List regions = this.Regions; - 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)); - baked.Add(prefab != null); - bakedObjects.Add(prefab); - } - } - } - #endif - - override public void OnInspectorGUI () { - if (serializedObject.isEditingMultipleObjects) { - DrawDefaultInspector(); - return; - } - - serializedObject.Update(); - atlasAsset = atlasAsset ?? (AtlasAsset)target; - EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(atlasFile); - EditorGUILayout.PropertyField(materials, true); - if (EditorGUI.EndChangeCheck()) { - serializedObject.ApplyModifiedProperties(); - atlasAsset.Clear(); - atlasAsset.GetAtlas(); - } - - if (materials.arraySize == 0) { - EditorGUILayout.HelpBox("No materials", MessageType.Error); - return; - } - - for (int i = 0; i < materials.arraySize; i++) { - SerializedProperty prop = materials.GetArrayElementAtIndex(i); - Material mat = (Material)prop.objectReferenceValue; - if (mat == null) { - EditorGUILayout.HelpBox("Materials cannot be null.", MessageType.Error); - return; - } - } - - EditorGUILayout.Space(); - if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Set Mipmap Bias to " + SpineEditorUtilities.DEFAULT_MIPMAPBIAS))) { - foreach (var m in atlasAsset.materials) { - var texture = m.mainTexture; - texture.mipMapBias = SpineEditorUtilities.DEFAULT_MIPMAPBIAS; - } - Debug.Log("Texture mipmap bias set to " + SpineEditorUtilities.DEFAULT_MIPMAPBIAS); - } - - EditorGUILayout.Space(); - if (atlasFile.objectReferenceValue != null) { - if (SpineInspectorUtility.LargeCenteredButton(SpriteSlicesLabel)) { - var atlas = atlasAsset.GetAtlas(); - foreach (var m in atlasAsset.materials) - UpdateSpriteSlices(m.mainTexture, atlas); - } - } - - EditorGUILayout.Space(); - - #if REGION_BAKING_MESH - if (atlasFile.objectReferenceValue != null) { - Atlas atlas = asset.GetAtlas(); - FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); - List regions = (List)field.GetValue(atlas); - EditorGUILayout.LabelField(new GUIContent("Region Baking", SpineEditorUtilities.Icons.unityIcon)); - EditorGUI.indentLevel++; - AtlasPage lastPage = null; - for (int i = 0; i < regions.Count; i++) { - if (lastPage != regions[i].page) { - if (lastPage != null) { - EditorGUILayout.Separator(); - EditorGUILayout.Separator(); - } - lastPage = regions[i].page; - Material mat = ((Material)lastPage.rendererObject); - if (mat != null) { - GUILayout.BeginHorizontal(); - { - EditorGUI.BeginDisabledGroup(true); - EditorGUILayout.ObjectField(mat, typeof(Material), false, GUILayout.Width(250)); - EditorGUI.EndDisabledGroup(); - } - GUILayout.EndHorizontal(); - - } else { - EditorGUILayout.LabelField(new GUIContent("Page missing material!", SpineEditorUtilities.Icons.warning)); - } - } - GUILayout.BeginHorizontal(); - { - //EditorGUILayout.ToggleLeft(baked[i] ? "" : regions[i].name, baked[i]); - bool result = baked[i] ? EditorGUILayout.ToggleLeft("", baked[i], GUILayout.Width(24)) : EditorGUILayout.ToggleLeft(" " + regions[i].name, baked[i]); - if(baked[i]){ - EditorGUILayout.ObjectField(bakedObjects[i], typeof(GameObject), false, GUILayout.Width(250)); - } - if (result && !baked[i]) { - //bake - baked[i] = true; - bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]); - EditorGUIUtility.PingObject(bakedObjects[i]); - } else if (!result && baked[i]) { - //unbake - bool unbakeResult = EditorUtility.DisplayDialog("Delete Baked Region", "Do you want to delete the prefab for " + regions[i].name, "Yes", "Cancel"); - switch (unbakeResult) { - case true: - //delete - string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); - string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); - string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); - string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(regions[i]) + ".prefab").Replace("\\", "/"); - AssetDatabase.DeleteAsset(bakedPrefabPath); - baked[i] = false; - break; - case false: - //do nothing - break; - } - } - } - GUILayout.EndHorizontal(); - } - EditorGUI.indentLevel--; - - #if BAKE_ALL_BUTTON - // Check state - bool allBaked = true; - bool allUnbaked = true; - for (int i = 0; i < regions.Count; i++) { - allBaked &= baked[i]; - allUnbaked &= !baked[i]; - } - - if (!allBaked && GUILayout.Button("Bake All")) { - for (int i = 0; i < regions.Count; i++) { - if (!baked[i]) { - baked[i] = true; - bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]); - } - } - - } else if (!allUnbaked && GUILayout.Button("Unbake All")) { - bool unbakeResult = EditorUtility.DisplayDialog("Delete All Baked Regions", "Are you sure you want to unbake all region prefabs? This cannot be undone.", "Yes", "Cancel"); - switch (unbakeResult) { - case true: - //delete - for (int i = 0; i < regions.Count; i++) { - if (baked[i]) { - string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); - string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); - string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); - string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(regions[i]) + ".prefab").Replace("\\", "/"); - AssetDatabase.DeleteAsset(bakedPrefabPath); - baked[i] = false; - } - } - break; - case false: - //do nothing - break; - } - - } - #endif - - } - #else - if (atlasFile.objectReferenceValue != null) { - EditorGUILayout.LabelField("Atlas Regions", EditorStyles.boldLabel); - int baseIndent = EditorGUI.indentLevel; - - var regions = AtlasAssetInspector.GetRegions(atlasAsset.GetAtlas()); - AtlasPage lastPage = null; - for (int i = 0; i < regions.Count; i++) { - if (lastPage != regions[i].page) { - if (lastPage != null) { - EditorGUILayout.Separator(); - EditorGUILayout.Separator(); - } - lastPage = regions[i].page; - Material mat = ((Material)lastPage.rendererObject); - if (mat != null) { - EditorGUI.indentLevel = baseIndent; - using (new GUILayout.HorizontalScope()) - using (new EditorGUI.DisabledGroupScope(true)) - EditorGUILayout.ObjectField(mat, typeof(Material), false, GUILayout.Width(250)); - EditorGUI.indentLevel = baseIndent + 1; - } else { - EditorGUILayout.HelpBox("Page missing material!", MessageType.Warning); - } - } - - EditorGUILayout.LabelField(new GUIContent(regions[i].name, SpineEditorUtilities.Icons.image)); - } - EditorGUI.indentLevel = baseIndent; - } - #endif - - if (serializedObject.ApplyModifiedProperties() || SpineInspectorUtility.UndoRedoPerformed(Event.current)) - atlasAsset.Clear(); - } - - static public void UpdateSpriteSlices (Texture texture, Atlas atlas) { - string texturePath = AssetDatabase.GetAssetPath(texture.GetInstanceID()); - var t = (TextureImporter)TextureImporter.GetAtPath(texturePath); - t.spriteImportMode = SpriteImportMode.Multiple; - var spriteSheet = t.spritesheet; - var sprites = new List(spriteSheet); - - var regions = AtlasAssetInspector.GetRegions(atlas); - char[] FilenameDelimiter = {'.'}; - int updatedCount = 0; - int addedCount = 0; - - foreach (var r in regions) { - string pageName = r.page.name.Split(FilenameDelimiter, StringSplitOptions.RemoveEmptyEntries)[0]; - string textureName = texture.name; - bool pageMatch = string.Equals(pageName, textureName, StringComparison.Ordinal); - -// if (pageMatch) { -// int pw = r.page.width; -// int ph = r.page.height; -// bool mismatchSize = pw != texture.width || pw > t.maxTextureSize || ph != texture.height || ph > t.maxTextureSize; -// if (mismatchSize) -// Debug.LogWarningFormat("Size mismatch found.\nExpected atlas size is {0}x{1}. Texture Import Max Size of texture '{2}'({4}x{5}) is currently set to {3}.", pw, ph, texture.name, t.maxTextureSize, texture.width, texture.height); -// } - - int spriteIndex = pageMatch ? sprites.FindIndex( - (s) => string.Equals(s.name, r.name, StringComparison.Ordinal) - ) : -1; - bool spriteNameMatchExists = spriteIndex >= 0; - - if (pageMatch) { - Rect spriteRect = new Rect(); - - if (r.rotate) { - spriteRect.width = r.height; - spriteRect.height = r.width; - } else { - spriteRect.width = r.width; - spriteRect.height = r.height; - } - spriteRect.x = r.x; - spriteRect.y = r.page.height - spriteRect.height - r.y; - - if (spriteNameMatchExists) { - var s = sprites[spriteIndex]; - s.rect = spriteRect; - sprites[spriteIndex] = s; - updatedCount++; - } else { - sprites.Add(new SpriteMetaData { - name = r.name, - pivot = new Vector2(0.5f, 0.5f), - rect = spriteRect - }); - addedCount++; - } - } - - } - - t.spritesheet = sprites.ToArray(); - EditorUtility.SetDirty(t); - AssetDatabase.ImportAsset(texturePath, ImportAssetOptions.ForceUpdate); - EditorGUIUtility.PingObject(texture); - Debug.Log(string.Format("Applied sprite slices to {2}. {0} added. {1} updated.", addedCount, updatedCount, texture.name)); - } - } - -} +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, 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 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 develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes 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, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) 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. + *****************************************************************************/ + +//#define BAKE_ALL_BUTTON +//#define REGION_BAKING_MESH + +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; +using UnityEngine; +using Spine; + +namespace Spine.Unity.Editor { + using Event = UnityEngine.Event; + + [CustomEditor(typeof(AtlasAsset)), CanEditMultipleObjects] + public class AtlasAssetInspector : UnityEditor.Editor { + SerializedProperty atlasFile, materials; + AtlasAsset atlasAsset; + + GUIContent spriteSlicesLabel; + GUIContent SpriteSlicesLabel { + get { + if (spriteSlicesLabel == null) { + spriteSlicesLabel = new GUIContent( + "Apply Regions as Texture Sprite Slices", + SpineEditorUtilities.Icons.unity, + "Adds Sprite slices to atlas texture(s). " + + "Updates existing slices if ones with matching names exist. \n\n" + + "If your atlas was exported with Premultiply Alpha, " + + "your SpriteRenderer should use the generated Spine _Material asset (or any Material with a PMA shader) instead of Sprites-Default."); + } + return spriteSlicesLabel; + } + } + + static List GetRegions (Atlas atlas) { + FieldInfo regionsField = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.NonPublic); + return (List)regionsField.GetValue(atlas); + } + + void OnEnable () { + SpineEditorUtilities.ConfirmInitialization(); + atlasFile = serializedObject.FindProperty("atlasFile"); + materials = serializedObject.FindProperty("materials"); + materials.isExpanded = true; + atlasAsset = (AtlasAsset)target; + #if REGION_BAKING_MESH + UpdateBakedList(); + #endif + } + + #if REGION_BAKING_MESH + private List baked; + private List bakedObjects; + + void UpdateBakedList () { + AtlasAsset asset = (AtlasAsset)target; + baked = new List(); + bakedObjects = new List(); + if (atlasFile.objectReferenceValue != null) { + List regions = this.Regions; + 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)); + baked.Add(prefab != null); + bakedObjects.Add(prefab); + } + } + } + #endif + + override public void OnInspectorGUI () { + if (serializedObject.isEditingMultipleObjects) { + DrawDefaultInspector(); + return; + } + + serializedObject.Update(); + atlasAsset = atlasAsset ?? (AtlasAsset)target; + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(atlasFile); + EditorGUILayout.PropertyField(materials, true); + if (EditorGUI.EndChangeCheck()) { + serializedObject.ApplyModifiedProperties(); + atlasAsset.Clear(); + atlasAsset.GetAtlas(); + } + + if (materials.arraySize == 0) { + EditorGUILayout.HelpBox("No materials", MessageType.Error); + return; + } + + for (int i = 0; i < materials.arraySize; i++) { + SerializedProperty prop = materials.GetArrayElementAtIndex(i); + var material = (Material)prop.objectReferenceValue; + if (material == null) { + EditorGUILayout.HelpBox("Materials cannot be null.", MessageType.Error); + return; + } + } + + EditorGUILayout.Space(); + if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Set Mipmap Bias to " + SpineEditorUtilities.DEFAULT_MIPMAPBIAS))) { + foreach (var m in atlasAsset.materials) { + var texture = m.mainTexture; + texture.mipMapBias = SpineEditorUtilities.DEFAULT_MIPMAPBIAS; + } + Debug.Log("Texture mipmap bias set to " + SpineEditorUtilities.DEFAULT_MIPMAPBIAS); + } + + EditorGUILayout.Space(); + if (atlasFile.objectReferenceValue != null) { + if (SpineInspectorUtility.LargeCenteredButton(SpriteSlicesLabel)) { + var atlas = atlasAsset.GetAtlas(); + foreach (var m in atlasAsset.materials) + UpdateSpriteSlices(m.mainTexture, atlas); + } + } + + EditorGUILayout.Space(); + + #if REGION_BAKING_MESH + if (atlasFile.objectReferenceValue != null) { + Atlas atlas = asset.GetAtlas(); + FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); + List regions = (List)field.GetValue(atlas); + EditorGUILayout.LabelField(new GUIContent("Region Baking", SpineEditorUtilities.Icons.unityIcon)); + EditorGUI.indentLevel++; + AtlasPage lastPage = null; + for (int i = 0; i < regions.Count; i++) { + if (lastPage != regions[i].page) { + if (lastPage != null) { + EditorGUILayout.Separator(); + EditorGUILayout.Separator(); + } + lastPage = regions[i].page; + Material mat = ((Material)lastPage.rendererObject); + if (mat != null) { + GUILayout.BeginHorizontal(); + { + EditorGUI.BeginDisabledGroup(true); + EditorGUILayout.ObjectField(mat, typeof(Material), false, GUILayout.Width(250)); + EditorGUI.EndDisabledGroup(); + } + GUILayout.EndHorizontal(); + + } else { + EditorGUILayout.LabelField(new GUIContent("Page missing material!", SpineEditorUtilities.Icons.warning)); + } + } + GUILayout.BeginHorizontal(); + { + //EditorGUILayout.ToggleLeft(baked[i] ? "" : regions[i].name, baked[i]); + bool result = baked[i] ? EditorGUILayout.ToggleLeft("", baked[i], GUILayout.Width(24)) : EditorGUILayout.ToggleLeft(" " + regions[i].name, baked[i]); + if(baked[i]){ + EditorGUILayout.ObjectField(bakedObjects[i], typeof(GameObject), false, GUILayout.Width(250)); + } + if (result && !baked[i]) { + //bake + baked[i] = true; + bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]); + EditorGUIUtility.PingObject(bakedObjects[i]); + } else if (!result && baked[i]) { + //unbake + bool unbakeResult = EditorUtility.DisplayDialog("Delete Baked Region", "Do you want to delete the prefab for " + regions[i].name, "Yes", "Cancel"); + switch (unbakeResult) { + case true: + //delete + string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); + string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(regions[i]) + ".prefab").Replace("\\", "/"); + AssetDatabase.DeleteAsset(bakedPrefabPath); + baked[i] = false; + break; + case false: + //do nothing + break; + } + } + } + GUILayout.EndHorizontal(); + } + EditorGUI.indentLevel--; + + #if BAKE_ALL_BUTTON + // Check state + bool allBaked = true; + bool allUnbaked = true; + for (int i = 0; i < regions.Count; i++) { + allBaked &= baked[i]; + allUnbaked &= !baked[i]; + } + + if (!allBaked && GUILayout.Button("Bake All")) { + for (int i = 0; i < regions.Count; i++) { + if (!baked[i]) { + baked[i] = true; + bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]); + } + } + + } else if (!allUnbaked && GUILayout.Button("Unbake All")) { + bool unbakeResult = EditorUtility.DisplayDialog("Delete All Baked Regions", "Are you sure you want to unbake all region prefabs? This cannot be undone.", "Yes", "Cancel"); + switch (unbakeResult) { + case true: + //delete + for (int i = 0; i < regions.Count; i++) { + if (baked[i]) { + string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); + string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(regions[i]) + ".prefab").Replace("\\", "/"); + AssetDatabase.DeleteAsset(bakedPrefabPath); + baked[i] = false; + } + } + break; + case false: + //do nothing + break; + } + + } + #endif + + } + #else + if (atlasFile.objectReferenceValue != null) { + EditorGUILayout.LabelField("Atlas Regions", EditorStyles.boldLabel); + int baseIndent = EditorGUI.indentLevel; + + var regions = AtlasAssetInspector.GetRegions(atlasAsset.GetAtlas()); + AtlasPage lastPage = null; + for (int i = 0; i < regions.Count; i++) { + if (lastPage != regions[i].page) { + if (lastPage != null) { + EditorGUILayout.Separator(); + EditorGUILayout.Separator(); + } + lastPage = regions[i].page; + Material mat = ((Material)lastPage.rendererObject); + if (mat != null) { + EditorGUI.indentLevel = baseIndent; + using (new GUILayout.HorizontalScope()) + using (new EditorGUI.DisabledGroupScope(true)) + EditorGUILayout.ObjectField(mat, typeof(Material), false, GUILayout.Width(250)); + EditorGUI.indentLevel = baseIndent + 1; + } else { + EditorGUILayout.HelpBox("Page missing material!", MessageType.Warning); + } + } + + EditorGUILayout.LabelField(new GUIContent(regions[i].name, SpineEditorUtilities.Icons.image)); + } + EditorGUI.indentLevel = baseIndent; + } + #endif + + if (serializedObject.ApplyModifiedProperties() || SpineInspectorUtility.UndoRedoPerformed(Event.current)) + atlasAsset.Clear(); + } + + static public void UpdateSpriteSlices (Texture texture, Atlas atlas) { + string texturePath = AssetDatabase.GetAssetPath(texture.GetInstanceID()); + var t = (TextureImporter)TextureImporter.GetAtPath(texturePath); + t.spriteImportMode = SpriteImportMode.Multiple; + var spriteSheet = t.spritesheet; + var sprites = new List(spriteSheet); + + var regions = AtlasAssetInspector.GetRegions(atlas); + char[] FilenameDelimiter = {'.'}; + int updatedCount = 0; + int addedCount = 0; + + foreach (var r in regions) { + string pageName = r.page.name.Split(FilenameDelimiter, StringSplitOptions.RemoveEmptyEntries)[0]; + string textureName = texture.name; + bool pageMatch = string.Equals(pageName, textureName, StringComparison.Ordinal); + +// if (pageMatch) { +// int pw = r.page.width; +// int ph = r.page.height; +// bool mismatchSize = pw != texture.width || pw > t.maxTextureSize || ph != texture.height || ph > t.maxTextureSize; +// if (mismatchSize) +// Debug.LogWarningFormat("Size mismatch found.\nExpected atlas size is {0}x{1}. Texture Import Max Size of texture '{2}'({4}x{5}) is currently set to {3}.", pw, ph, texture.name, t.maxTextureSize, texture.width, texture.height); +// } + + int spriteIndex = pageMatch ? sprites.FindIndex( + (s) => string.Equals(s.name, r.name, StringComparison.Ordinal) + ) : -1; + bool spriteNameMatchExists = spriteIndex >= 0; + + if (pageMatch) { + Rect spriteRect = new Rect(); + + if (r.rotate) { + spriteRect.width = r.height; + spriteRect.height = r.width; + } else { + spriteRect.width = r.width; + spriteRect.height = r.height; + } + spriteRect.x = r.x; + spriteRect.y = r.page.height - spriteRect.height - r.y; + + if (spriteNameMatchExists) { + var s = sprites[spriteIndex]; + s.rect = spriteRect; + sprites[spriteIndex] = s; + updatedCount++; + } else { + sprites.Add(new SpriteMetaData { + name = r.name, + pivot = new Vector2(0.5f, 0.5f), + rect = spriteRect + }); + addedCount++; + } + } + + } + + t.spritesheet = sprites.ToArray(); + EditorUtility.SetDirty(t); + AssetDatabase.ImportAsset(texturePath, ImportAssetOptions.ForceUpdate); + EditorGUIUtility.PingObject(texture); + Debug.Log(string.Format("Applied sprite slices to {2}. {0} added. {1} updated.", addedCount, updatedCount, texture.name)); + } + } + +} diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs.meta b/spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs.meta similarity index 100% rename from spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs.meta rename to spine-unity/Assets/spine-unity/Editor/AtlasAssetInspector.cs.meta diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs similarity index 100% rename from spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs rename to spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs.meta b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs.meta similarity index 100% rename from spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs.meta rename to spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs.meta From ad8964380e3322980acb72c94a149e090e1ca682 Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 15 Dec 2017 09:43:16 +0800 Subject: [PATCH 4/6] [unity] ISkeletonDataAssetComponent --- spine-unity/Assets/spine-unity/ISkeletonAnimation.cs | 8 +++++++- .../Modules/SkeletonGraphic/SkeletonGraphic.cs | 2 +- spine-unity/Assets/spine-unity/SkeletonRenderer.cs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/spine-unity/Assets/spine-unity/ISkeletonAnimation.cs b/spine-unity/Assets/spine-unity/ISkeletonAnimation.cs index 5e49db79a..f5e423adf 100644 --- a/spine-unity/Assets/spine-unity/ISkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/ISkeletonAnimation.cs @@ -37,10 +37,16 @@ public interface ISkeletonAnimation { event UpdateBonesDelegate UpdateWorld; event UpdateBonesDelegate UpdateComplete; - void LateUpdate (); + //void LateUpdate (); Skeleton Skeleton { get; } } + /// Holds a reference to a SkeletonDataAsset. + public interface ISkeletonDataAssetComponent { + /// Gets the SkeletonDataAsset of the Spine Component. + SkeletonDataAsset SkeletonDataAsset { get; } + } + /// A Spine-Unity Component that manages a Spine.Skeleton instance, instantiated from a SkeletonDataAsset. public interface ISkeletonComponent { /// Gets the SkeletonDataAsset of the Spine Component. diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs index f19a0ab22..25e4d688c 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs @@ -35,7 +35,7 @@ namespace Spine.Unity { [ExecuteInEditMode, RequireComponent(typeof(CanvasRenderer), typeof(RectTransform)), DisallowMultipleComponent] [AddComponentMenu("Spine/SkeletonGraphic (Unity UI Canvas)")] - public class SkeletonGraphic : MaskableGraphic, ISkeletonComponent, IAnimationStateComponent, ISkeletonAnimation { + public class SkeletonGraphic : MaskableGraphic, ISkeletonComponent, IAnimationStateComponent, ISkeletonAnimation, ISkeletonDataAssetComponent { #region Inspector public SkeletonDataAsset skeletonDataAsset; diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 3f33ba263..cf5429402 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -38,7 +38,7 @@ namespace Spine.Unity { /// Renders a skeleton. [ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer)), DisallowMultipleComponent] [HelpURL("http://esotericsoftware.com/spine-unity-documentation#Rendering")] - public class SkeletonRenderer : MonoBehaviour, ISkeletonComponent { + public class SkeletonRenderer : MonoBehaviour, ISkeletonComponent, ISkeletonDataAssetComponent { public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer); public event SkeletonRendererDelegate OnRebuild; From d7eff923755ebe6c0084b127723510fd48177d29 Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 15 Dec 2017 09:43:43 +0800 Subject: [PATCH 5/6] [unity] Updated docs. --- spine-unity/Assets/spine-unity/BoneFollower.cs | 2 +- spine-unity/Assets/spine-unity/SkeletonAnimation.cs | 5 +++++ spine-unity/Assets/spine-unity/SkeletonRenderer.cs | 7 +++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/spine-unity/Assets/spine-unity/BoneFollower.cs b/spine-unity/Assets/spine-unity/BoneFollower.cs index 2681c4ff2..c7d3ea1a2 100644 --- a/spine-unity/Assets/spine-unity/BoneFollower.cs +++ b/spine-unity/Assets/spine-unity/BoneFollower.cs @@ -73,7 +73,7 @@ public SkeletonRenderer SkeletonRenderer { bool skeletonTransformIsParent; /// - /// Sets the target bone by its bone name. Returns false if no bone was found. + /// Sets the target bone by its bone name. Returns false if no bone was found. To set the bone by reference, use BoneFollower.bone directly. public bool SetBone (string name) { bone = skeletonRenderer.skeleton.FindBone(name); if (bone == null) { diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index f69546429..28463c371 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs @@ -126,11 +126,16 @@ public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsse } #endregion + /// + /// Clears the previously generated mesh, resets the skeleton's pose, and clears all previously active animations. public override void ClearState () { base.ClearState(); if (state != null) state.ClearTracks(); } + /// + /// Initialize this component. Attempts to load the SkeletonData and creates the internal Spine objects and buffers. + /// If set to true, force overwrite an already initialized object. public override void Initialize (bool overwrite) { if (valid && !overwrite) return; diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index cf5429402..356cbd21f 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -163,12 +163,17 @@ void OnDestroy () { valid = false; } + /// + /// Clears the previously generated mesh and resets the skeleton's pose. public virtual void ClearState () { meshFilter.sharedMesh = null; currentInstructions.Clear(); if (skeleton != null) skeleton.SetToSetupPose(); } + /// + /// Initialize this component. Attempts to load the SkeletonData and creates the internal Skeleton object and buffers. + /// If set to true, it will overwrite internal objects if they were already generated. Otherwise, the initialized component will ignore subsequent calls to initialize. public virtual void Initialize (bool overwrite) { if (valid && !overwrite) return; @@ -219,6 +224,8 @@ public virtual void Initialize (bool overwrite) { OnRebuild(this); } + /// + /// Generates a new UnityEngine.Mesh from the internal Skeleton. public virtual void LateUpdate () { if (!valid) return; From ab4fcb48ec0fe59f3668c64723f3574451476eb0 Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 15 Dec 2017 09:45:05 +0800 Subject: [PATCH 6/6] [unity] GetRepackedSkin NullReferenceException. --- .../spine-unity/Modules/AttachmentTools/AttachmentTools.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs b/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs index 6a7934333..511a874fd 100644 --- a/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs +++ b/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs @@ -486,6 +486,7 @@ public static Skin GetRepackedSkin (this Skin o, string newName, Material materi /// Creates and populates a duplicate skin with cloned attachments that are backed by a new packed texture atlas comprised of all the regions from the original skin. /// No Spine.Atlas object is created so there is no way to find AtlasRegions except through the Attachments using them. public static Skin GetRepackedSkin (this Skin o, string newName, Shader shader, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, Material materialPropertySource = null, bool clearCache = false) { + if (o == null) throw new System.NullReferenceException("Skin was null"); var skinAttachments = o.Attachments; var newSkin = new Skin(newName);