diff --git a/spine-unity/Assets/spine-unity/Asset Types/AtlasAsset.cs b/spine-unity/Assets/spine-unity/Asset Types/AtlasAsset.cs index 19dadb509..44b178fb9 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/AtlasAsset.cs +++ b/spine-unity/Assets/spine-unity/Asset Types/AtlasAsset.cs @@ -34,148 +34,150 @@ using System.IO; using UnityEngine; using Spine; -/// Loads and stores a Spine atlas and list of materials. -public class AtlasAsset : ScriptableObject { - public TextAsset atlasFile; - public Material[] materials; - private Atlas atlas; +namespace Spine.Unity { + /// Loads and stores a Spine atlas and list of materials. + public class AtlasAsset : ScriptableObject { + public TextAsset atlasFile; + public Material[] materials; + private Atlas atlas; - public void Reset () { - atlas = null; - } - - /// The atlas or null if it could not be loaded. - public Atlas GetAtlas () { - if (atlasFile == null) { - Debug.LogError("Atlas file not set for atlas asset: " + name, this); - Reset(); - return null; + public void Reset () { + atlas = null; } - if (materials == null || materials.Length == 0) { - Debug.LogError("Materials not set for atlas asset: " + name, this); - Reset(); - return null; - } - - if (atlas != null) - return atlas; - - try { - atlas = new Atlas(new StringReader(atlasFile.text), "", new MaterialsTextureLoader(this)); - atlas.FlipV(); - return atlas; - } catch (Exception ex) { - Debug.LogError("Error reading atlas file for atlas asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); - return null; - } - } - - public Sprite GenerateSprite (string name, out Material material) { - AtlasRegion region = atlas.FindRegion(name); - - Sprite sprite = null; - material = null; - - if (region != null) { - //sprite.rect - } - - return sprite; - } - - public Mesh GenerateMesh (string name, Mesh mesh, out Material material, float scale = 0.01f) { - AtlasRegion region = atlas.FindRegion(name); - material = null; - if (region != null) { - if (mesh == null) { - mesh = new Mesh(); - mesh.name = name; + /// The atlas or null if it could not be loaded. + public Atlas GetAtlas () { + if (atlasFile == null) { + Debug.LogError("Atlas file not set for atlas asset: " + name, this); + Reset(); + return null; } - Vector3[] verts = new Vector3[4]; - Vector2[] uvs = new Vector2[4]; - Color[] colors = new Color[4] { Color.white, Color.white, Color.white, Color.white }; - int[] triangles = new int[6] { 0, 1, 2, 2, 3, 0 }; + if (materials == null || materials.Length == 0) { + Debug.LogError("Materials not set for atlas asset: " + name, this); + Reset(); + return null; + } - float left, right, top, bottom; - left = region.width / -2f; - right = left * -1f; - top = region.height / 2f; - bottom = top * -1; + if (atlas != null) + return atlas; - verts[0] = new Vector3(left, bottom, 0) * scale; - verts[1] = new Vector3(left, top, 0) * scale; - verts[2] = new Vector3(right, top, 0) * scale; - verts[3] = new Vector3(right, bottom, 0) * scale; - float u, v, u2, v2; - u = region.u; - v = region.v; - u2 = region.u2; - v2 = region.v2; + try { + atlas = new Atlas(new StringReader(atlasFile.text), "", new MaterialsTextureLoader(this)); + atlas.FlipV(); + return atlas; + } catch (Exception ex) { + Debug.LogError("Error reading atlas file for atlas asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); + return null; + } + } - if (!region.rotate) { - uvs[0] = new Vector2(u, v2); - uvs[1] = new Vector2(u, v); - uvs[2] = new Vector2(u2, v); - uvs[3] = new Vector2(u2, v2); + public Sprite GenerateSprite (string name, out Material material) { + AtlasRegion region = atlas.FindRegion(name); + + Sprite sprite = null; + material = null; + + if (region != null) { + //sprite.rect + } + + return sprite; + } + + public Mesh GenerateMesh (string name, Mesh mesh, out Material material, float scale = 0.01f) { + AtlasRegion region = atlas.FindRegion(name); + material = null; + if (region != null) { + if (mesh == null) { + mesh = new Mesh(); + mesh.name = name; + } + + Vector3[] verts = new Vector3[4]; + Vector2[] uvs = new Vector2[4]; + Color[] colors = new Color[4] { Color.white, Color.white, Color.white, Color.white }; + int[] triangles = new int[6] { 0, 1, 2, 2, 3, 0 }; + + float left, right, top, bottom; + left = region.width / -2f; + right = left * -1f; + top = region.height / 2f; + bottom = top * -1; + + verts[0] = new Vector3(left, bottom, 0) * scale; + verts[1] = new Vector3(left, top, 0) * scale; + verts[2] = new Vector3(right, top, 0) * scale; + verts[3] = new Vector3(right, bottom, 0) * scale; + float u, v, u2, v2; + u = region.u; + v = region.v; + u2 = region.u2; + v2 = region.v2; + + if (!region.rotate) { + uvs[0] = new Vector2(u, v2); + uvs[1] = new Vector2(u, v); + uvs[2] = new Vector2(u2, v); + uvs[3] = new Vector2(u2, v2); + } else { + uvs[0] = new Vector2(u2, v2); + uvs[1] = new Vector2(u, v2); + uvs[2] = new Vector2(u, v); + uvs[3] = new Vector2(u2, v); + } + + mesh.triangles = new int[0]; + mesh.vertices = verts; + mesh.uv = uvs; + mesh.colors = colors; + mesh.triangles = triangles; + mesh.RecalculateNormals(); + mesh.RecalculateBounds(); + + material = (Material)region.page.rendererObject; } else { - uvs[0] = new Vector2(u2, v2); - uvs[1] = new Vector2(u, v2); - uvs[2] = new Vector2(u, v); - uvs[3] = new Vector2(u2, v); + mesh = null; } - mesh.triangles = new int[0]; - mesh.vertices = verts; - mesh.uv = uvs; - mesh.colors = colors; - mesh.triangles = triangles; - mesh.RecalculateNormals(); - mesh.RecalculateBounds(); + return mesh; + } + } - material = (Material)region.page.rendererObject; - } else { - mesh = null; + public class MaterialsTextureLoader : TextureLoader { + AtlasAsset atlasAsset; + + public MaterialsTextureLoader (AtlasAsset atlasAsset) { + this.atlasAsset = atlasAsset; } - return mesh; - } -} - -public class MaterialsTextureLoader : TextureLoader { - AtlasAsset atlasAsset; - - public MaterialsTextureLoader (AtlasAsset atlasAsset) { - this.atlasAsset = atlasAsset; - } - - public void Load (AtlasPage page, String path) { - String name = Path.GetFileNameWithoutExtension(path); - Material material = null; - foreach (Material other in atlasAsset.materials) { - if (other.mainTexture == null) { - Debug.LogError("Material is missing texture: " + other.name, other); + public void Load (AtlasPage page, String path) { + String name = Path.GetFileNameWithoutExtension(path); + Material material = null; + foreach (Material other in atlasAsset.materials) { + if (other.mainTexture == null) { + Debug.LogError("Material is missing texture: " + other.name, other); + return; + } + if (other.mainTexture.name == name) { + material = other; + break; + } + } + if (material == null) { + Debug.LogError("Material with texture name \"" + name + "\" not found for atlas asset: " + atlasAsset.name, atlasAsset); return; } - if (other.mainTexture.name == name) { - material = other; - break; + page.rendererObject = material; + + // Very old atlas files expected the texture's actual size to be used at runtime. + if (page.width == 0 || page.height == 0) { + page.width = material.mainTexture.width; + page.height = material.mainTexture.height; } } - if (material == null) { - Debug.LogError("Material with texture name \"" + name + "\" not found for atlas asset: " + atlasAsset.name, atlasAsset); - return; - } - page.rendererObject = material; - // Very old atlas files expected the texture's actual size to be used at runtime. - if (page.width == 0 || page.height == 0) { - page.width = material.mainTexture.width; - page.height = material.mainTexture.height; + public void Unload (object texture) { } } - - public void Unload (object texture) { - } } diff --git a/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs index 661f7336e..503b99e22 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs @@ -182,10 +182,7 @@ namespace Spine.Unity.Editor { EditorGUI.EndDisabledGroup(); EditorGUI.indentLevel++; - GUILayout.BeginHorizontal(); - { - - + using (new EditorGUILayout.HorizontalScope()) { if (GUILayout.Button(new GUIContent("Bake All Skins", SpineEditorUtilities.Icons.unityIcon), GUILayout.Height(32), GUILayout.Width(150))) SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, m_skeletonData.Skins, "", bakeAnimations, bakeIK, bakeEventOptions); @@ -200,35 +197,20 @@ namespace Spine.Unity.Editor { } else skinName = m_skeletonAnimation.skeleton.Skin.Name; - bool oops = false; - - try { - GUILayout.BeginVertical(); + using (var m = new EditorGUILayout.VerticalScope()) { if (GUILayout.Button(new GUIContent("Bake " + skinName, SpineEditorUtilities.Icons.unityIcon), GUILayout.Height(32), GUILayout.Width(250))) SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, new ExposedList(new [] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions); - - GUILayout.BeginHorizontal(); - GUILayout.Label(new GUIContent("Skins", SpineEditorUtilities.Icons.skinsRoot), GUILayout.Width(50)); - if (GUILayout.Button(skinName, EditorStyles.popup, GUILayout.Width(196))) { - SelectSkinContext(); + using (new EditorGUILayout.HorizontalScope()) { + GUILayout.Label(new GUIContent("Skins", SpineEditorUtilities.Icons.skinsRoot), GUILayout.Width(50)); + if (GUILayout.Button(skinName, EditorStyles.popup, GUILayout.Width(196))) { + SelectSkinContext(); + } } - GUILayout.EndHorizontal(); - - - } catch { - oops = true; - //GUILayout.BeginVertical(); } - - - - if (!oops) - GUILayout.EndVertical(); } } - GUILayout.EndHorizontal(); EditorGUI.indentLevel--; EditorGUI.indentLevel--; } diff --git a/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs b/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs index 3912a3461..499ec80f2 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs +++ b/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs @@ -31,145 +31,147 @@ using System; using System.IO; -using System.Collections.Generic; using UnityEngine; using Spine; -public class SkeletonDataAsset : ScriptableObject { - public AtlasAsset[] atlasAssets; -#if SPINE_TK2D - public tk2dSpriteCollectionData spriteCollection; -#endif - public TextAsset skeletonJSON; - public float scale = 0.01f; - public String[] fromAnimation; - public String[] toAnimation; - public float[] duration; - public float defaultMix; - public RuntimeAnimatorController controller; - private SkeletonData skeletonData; - private AnimationStateData stateData; +namespace Spine.Unity { + public class SkeletonDataAsset : ScriptableObject { + public AtlasAsset[] atlasAssets; + #if SPINE_TK2D + public tk2dSpriteCollectionData spriteCollection; + #endif + public TextAsset skeletonJSON; + public float scale = 0.01f; + public String[] fromAnimation; + public String[] toAnimation; + public float[] duration; + public float defaultMix; + public RuntimeAnimatorController controller; + private SkeletonData skeletonData; + private AnimationStateData stateData; - void OnEnable () { - if (atlasAssets == null) - atlasAssets = new AtlasAsset[0]; - } - - public void Reset () { - skeletonData = null; - stateData = null; - } - - public SkeletonData GetSkeletonData (bool quiet) { - if (atlasAssets == null) { - atlasAssets = new AtlasAsset[0]; - if (!quiet) - Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); - Reset(); - return null; + void OnEnable () { + if (atlasAssets == null) + atlasAssets = new AtlasAsset[0]; } - if (skeletonJSON == null) { - if (!quiet) - Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this); - Reset(); - return null; + public void Reset () { + skeletonData = null; + stateData = 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++) { - if (atlasAssets[i] == null) { + public SkeletonData GetSkeletonData (bool quiet) { + if (atlasAssets == null) { + atlasAssets = new AtlasAsset[0]; + if (!quiet) + Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); Reset(); return null; } - atlasArr[i] = atlasAssets[i].GetAtlas(); - if (atlasArr[i] == null) { + + if (skeletonJSON == null) { + if (!quiet) + Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this); Reset(); return null; } - } - if (skeletonData != null) - return skeletonData; - - AttachmentLoader attachmentLoader; - float skeletonDataScale; - -#if !SPINE_TK2D - attachmentLoader = new AtlasAttachmentLoader(atlasArr); - skeletonDataScale = scale; -#else - if (spriteCollection != null) { - attachmentLoader = new SpriteCollectionAttachmentLoader(spriteCollection); - skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale); - } else { - if (atlasArr.Length == 0) { + #if !SPINE_TK2D + if (atlasAssets.Length == 0) { Reset(); - if (!quiet) Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); 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++) { + if (atlasAssets[i] == null) { + Reset(); + return null; + } + atlasArr[i] = atlasAssets[i].GetAtlas(); + if (atlasArr[i] == null) { + Reset(); + return null; + } + } + + if (skeletonData != null) + return skeletonData; + + AttachmentLoader attachmentLoader; + float skeletonDataScale; + + #if !SPINE_TK2D attachmentLoader = new AtlasAttachmentLoader(atlasArr); skeletonDataScale = scale; - } -#endif - - try { - //var stopwatch = new System.Diagnostics.Stopwatch(); - if (skeletonJSON.name.ToLower().Contains(".skel")) { - var input = new MemoryStream(skeletonJSON.bytes); - var binary = new SkeletonBinary(attachmentLoader); - binary.Scale = skeletonDataScale; - //stopwatch.Start(); - skeletonData = binary.ReadSkeletonData(input); + #else + if (spriteCollection != null) { + attachmentLoader = new Spine.Unity.TK2D.SpriteCollectionAttachmentLoader(spriteCollection); + skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale); } else { - var input = new StringReader(skeletonJSON.text); - var json = new SkeletonJson(attachmentLoader); - json.Scale = skeletonDataScale; - //stopwatch.Start(); - skeletonData = json.ReadSkeletonData(input); + if (atlasArr.Length == 0) { + Reset(); + if (!quiet) Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); + return null; + } + attachmentLoader = new AtlasAttachmentLoader(atlasArr); + skeletonDataScale = scale; } - //stopwatch.Stop(); - //Debug.Log(stopwatch.Elapsed); - } catch (Exception ex) { - if (!quiet) - Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); - return null; + #endif + + try { + //var stopwatch = new System.Diagnostics.Stopwatch(); + if (skeletonJSON.name.ToLower().Contains(".skel")) { + var input = new MemoryStream(skeletonJSON.bytes); + var binary = new SkeletonBinary(attachmentLoader); + binary.Scale = skeletonDataScale; + //stopwatch.Start(); + skeletonData = binary.ReadSkeletonData(input); + } else { + var input = new StringReader(skeletonJSON.text); + var json = new SkeletonJson(attachmentLoader); + json.Scale = skeletonDataScale; + //stopwatch.Start(); + skeletonData = json.ReadSkeletonData(input); + } + //stopwatch.Stop(); + //Debug.Log(stopwatch.Elapsed); + } catch (Exception ex) { + if (!quiet) + Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); + return null; + } + + stateData = new AnimationStateData(skeletonData); + FillStateData(); + + return skeletonData; } - stateData = new AnimationStateData(skeletonData); - FillStateData(); + public void FillStateData () { + if (stateData == null) + return; - return skeletonData; - } - - public void FillStateData () { - if (stateData == null) - return; - - stateData.DefaultMix = defaultMix; - for (int i = 0, n = fromAnimation.Length; i < n; i++) { - if (fromAnimation[i].Length == 0 || toAnimation[i].Length == 0) - continue; - stateData.SetMix(fromAnimation[i], toAnimation[i], duration[i]); + stateData.DefaultMix = defaultMix; + for (int i = 0, n = fromAnimation.Length; i < n; i++) { + if (fromAnimation[i].Length == 0 || toAnimation[i].Length == 0) + continue; + stateData.SetMix(fromAnimation[i], toAnimation[i], duration[i]); + } } - } - public AnimationStateData GetAnimationStateData () { - if (stateData != null) + public AnimationStateData GetAnimationStateData () { + if (stateData != null) + return stateData; + GetSkeletonData(false); return stateData; - GetSkeletonData(false); - return stateData; + } } + } diff --git a/spine-unity/Assets/spine-unity/BoneFollower.cs b/spine-unity/Assets/spine-unity/BoneFollower.cs index 43c18fac8..e43622c3f 100644 --- a/spine-unity/Assets/spine-unity/BoneFollower.cs +++ b/spine-unity/Assets/spine-unity/BoneFollower.cs @@ -28,119 +28,118 @@ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ - using System; -using System.IO; -using System.Collections.Generic; using UnityEngine; -using Spine; -/// Sets a GameObject's transform to match a bone on a Spine skeleton. -[ExecuteInEditMode] -[AddComponentMenu("Spine/BoneFollower")] -public class BoneFollower : MonoBehaviour { +namespace Spine.Unity { + /// Sets a GameObject's transform to match a bone on a Spine skeleton. + [ExecuteInEditMode] + [AddComponentMenu("Spine/BoneFollower")] + public class BoneFollower : MonoBehaviour { - #region Inspector - public SkeletonRenderer skeletonRenderer; - public SkeletonRenderer SkeletonRenderer { - get { return skeletonRenderer; } - set { - skeletonRenderer = value; + #region Inspector + public SkeletonRenderer skeletonRenderer; + public SkeletonRenderer SkeletonRenderer { + get { return skeletonRenderer; } + set { + skeletonRenderer = value; + Reset(); + } + } + /// If a bone isn't set, boneName is used to find the bone. + [SpineBone(dataField: "skeletonRenderer")] + public String boneName; + + public bool followZPosition = true; + public bool followBoneRotation = true; + public bool resetOnAwake = true; + #endregion + + [NonSerialized] + public bool valid; + + [NonSerialized] + public Bone bone; + Transform skeletonTransform; + + public void HandleResetRenderer (SkeletonRenderer skeletonRenderer) { Reset(); } - } - /// If a bone isn't set, boneName is used to find the bone. - [SpineBone(dataField: "skeletonRenderer")] - public String boneName; - public bool followZPosition = true; - public bool followBoneRotation = true; - public bool resetOnAwake = true; - #endregion + public void Reset () { + bone = null; + valid = skeletonRenderer != null && skeletonRenderer.valid; - [NonSerialized] - public bool valid; + if (!valid) return; - [NonSerialized] - public Bone bone; - Transform skeletonTransform; - - public void HandleResetRenderer (SkeletonRenderer skeletonRenderer) { - Reset(); - } - - public void Reset () { - bone = null; - valid = skeletonRenderer != null && skeletonRenderer.valid; - - if (!valid) return; - - skeletonTransform = skeletonRenderer.transform; - skeletonRenderer.OnRebuild -= HandleResetRenderer; - skeletonRenderer.OnRebuild += HandleResetRenderer; - - #if UNITY_EDITOR - if (Application.isEditor) - DoUpdate(); - #endif - } - - void OnDestroy () { - if (skeletonRenderer != null) + skeletonTransform = skeletonRenderer.transform; skeletonRenderer.OnRebuild -= HandleResetRenderer; - } + skeletonRenderer.OnRebuild += HandleResetRenderer; - public void Awake () { - if (resetOnAwake) - Reset(); - } - - void LateUpdate () { - DoUpdate(); - } - - public void DoUpdate () { - if (!valid) { - Reset(); - return; + #if UNITY_EDITOR + if (Application.isEditor) + DoUpdate(); + #endif } - if (bone == null) { - if (boneName == null || boneName.Length == 0) + void OnDestroy () { + if (skeletonRenderer != null) + skeletonRenderer.OnRebuild -= HandleResetRenderer; + } + + public void Awake () { + if (resetOnAwake) + Reset(); + } + + void LateUpdate () { + DoUpdate(); + } + + public void DoUpdate () { + if (!valid) { + Reset(); return; - bone = skeletonRenderer.skeleton.FindBone(boneName); + } + if (bone == null) { - Debug.LogError("Bone not found: " + boneName, this); - return; + if (boneName == null || boneName.Length == 0) + return; + bone = skeletonRenderer.skeleton.FindBone(boneName); + if (bone == null) { + Debug.LogError("Bone not found: " + boneName, this); + return; + } } + + Skeleton skeleton = skeletonRenderer.skeleton; + float flipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f; + Transform thisTransform = this.transform; + + // Recommended setup: Use local transform properties if Spine GameObject is parent + if (thisTransform.parent == skeletonTransform) { + thisTransform.localPosition = new Vector3(bone.worldX, bone.worldY, followZPosition ? 0f : thisTransform.localPosition.z); + + if (followBoneRotation) { + Vector3 rotation = thisTransform.localRotation.eulerAngles; + thisTransform.localRotation = Quaternion.Euler(rotation.x, rotation.y, bone.WorldRotationX * flipRotation); + } + + // For special cases: Use transform world properties if transform relationship is complicated + } else { + Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, 0f)); + if (!followZPosition) + targetWorldPosition.z = thisTransform.position.z; + + thisTransform.position = targetWorldPosition; + + if (followBoneRotation) { + Vector3 worldRotation = skeletonTransform.rotation.eulerAngles; + thisTransform.rotation = Quaternion.Euler(worldRotation.x, worldRotation.y, skeletonTransform.rotation.eulerAngles.z + (bone.WorldRotationX * flipRotation)); + } + } + } - - Skeleton skeleton = skeletonRenderer.skeleton; - float flipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f; - Transform thisTransform = this.transform; - - // Recommended setup: Use local transform properties if Spine GameObject is parent - if (thisTransform.parent == skeletonTransform) { - thisTransform.localPosition = new Vector3(bone.worldX, bone.worldY, followZPosition ? 0f : thisTransform.localPosition.z); - - if (followBoneRotation) { - Vector3 rotation = thisTransform.localRotation.eulerAngles; - thisTransform.localRotation = Quaternion.Euler(rotation.x, rotation.y, bone.WorldRotationX * flipRotation); - } - - // For special cases: Use transform world properties if transform relationship is complicated - } else { - Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, 0f)); - if (!followZPosition) - targetWorldPosition.z = thisTransform.position.z; - - thisTransform.position = targetWorldPosition; - - if (followBoneRotation) { - Vector3 worldRotation = skeletonTransform.rotation.eulerAngles; - thisTransform.rotation = Quaternion.Euler(worldRotation.x, worldRotation.y, skeletonTransform.rotation.eulerAngles.z + (bone.WorldRotationX * flipRotation)); - } - } - } + } diff --git a/spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs b/spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs index 7bcf31343..129ba97b6 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs @@ -30,10 +30,8 @@ *****************************************************************************/ using UnityEngine; -using System.Collections; using UnityEditor; using System.Reflection; -using System; namespace Spine.Unity.Editor { public static class SpineInspectorUtility { diff --git a/spine-unity/Assets/spine-unity/Modules/AtlasRegionAttacher.cs b/spine-unity/Assets/spine-unity/Modules/AtlasRegionAttacher.cs index cc95706b6..3b2866094 100644 --- a/spine-unity/Assets/spine-unity/Modules/AtlasRegionAttacher.cs +++ b/spine-unity/Assets/spine-unity/Modules/AtlasRegionAttacher.cs @@ -28,53 +28,53 @@ * 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.Unity.Modules { + public class AtlasRegionAttacher : MonoBehaviour { -public class AtlasRegionAttacher : MonoBehaviour { + [System.Serializable] + public class SlotRegionPair { + [SpineSlot] + public string slot; - [System.Serializable] - public class SlotRegionPair { - [SpineSlot] - public string slot; - - [SpineAtlasRegion] - public string region; - } - - public AtlasAsset atlasAsset; - public SlotRegionPair[] attachments; - - Atlas atlas; - - void Awake () { - GetComponent().OnRebuild += Apply; - } - - - void Apply (SkeletonRenderer skeletonRenderer) { - atlas = atlasAsset.GetAtlas(); - - AtlasAttachmentLoader loader = new AtlasAttachmentLoader(atlas); - - float scaleMultiplier = skeletonRenderer.skeletonDataAsset.scale; - - var enumerator = attachments.GetEnumerator(); - while (enumerator.MoveNext()) { - var entry = (SlotRegionPair)enumerator.Current; - var regionAttachment = loader.NewRegionAttachment(null, entry.region, entry.region); - regionAttachment.Width = regionAttachment.RegionOriginalWidth * scaleMultiplier; - regionAttachment.Height = regionAttachment.RegionOriginalHeight * scaleMultiplier; - - regionAttachment.SetColor(new Color(1, 1, 1, 1)); - regionAttachment.UpdateOffset(); - - var slot = skeletonRenderer.skeleton.FindSlot(entry.slot); - slot.Attachment = regionAttachment; + [SpineAtlasRegion] + public string region; } - } + public AtlasAsset atlasAsset; + public SlotRegionPair[] attachments; + + Atlas atlas; + + void Awake () { + GetComponent().OnRebuild += Apply; + } + + + void Apply (SkeletonRenderer skeletonRenderer) { + atlas = atlasAsset.GetAtlas(); + + AtlasAttachmentLoader loader = new AtlasAttachmentLoader(atlas); + + float scaleMultiplier = skeletonRenderer.skeletonDataAsset.scale; + + var enumerator = attachments.GetEnumerator(); + while (enumerator.MoveNext()) { + var entry = (SlotRegionPair)enumerator.Current; + var regionAttachment = loader.NewRegionAttachment(null, entry.region, entry.region); + regionAttachment.Width = regionAttachment.RegionOriginalWidth * scaleMultiplier; + regionAttachment.Height = regionAttachment.RegionOriginalHeight * scaleMultiplier; + + regionAttachment.SetColor(new Color(1, 1, 1, 1)); + regionAttachment.UpdateOffset(); + + var slot = skeletonRenderer.skeleton.FindSlot(entry.slot); + slot.Attachment = regionAttachment; + } + } + + } } diff --git a/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs b/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs index a3c71fc8b..c3269e13a 100644 --- a/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs +++ b/spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs @@ -28,167 +28,159 @@ * 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 System.Collections.Generic; -using Spine; -[ExecuteInEditMode] -public class BoundingBoxFollower : MonoBehaviour { +namespace Spine.Unity { + [ExecuteInEditMode] + public class BoundingBoxFollower : MonoBehaviour { + + public SkeletonRenderer skeletonRenderer; - public SkeletonRenderer skeletonRenderer; + [SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)] + public string slotName; - [SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)] - public string slotName; + //TODO: not this + [Tooltip("LOL JK, Someone else do it!")] + public bool use3DMeshCollider; - //TODO: not this - [Tooltip("LOL JK, Someone else do it!")] - public bool use3DMeshCollider; + private Slot slot; + private BoundingBoxAttachment currentAttachment; + private PolygonCollider2D currentCollider; + private string currentAttachmentName; + private bool valid = false; + private bool hasReset; - private Slot slot; - private BoundingBoxAttachment currentAttachment; - private PolygonCollider2D currentCollider; - private string currentAttachmentName; - private bool valid = false; - private bool hasReset; + public Dictionary colliderTable = new Dictionary(); + public Dictionary attachmentNameTable = new Dictionary(); - public Dictionary colliderTable = new Dictionary(); - public Dictionary attachmentNameTable = new Dictionary(); - - public string CurrentAttachmentName { - get { - return currentAttachmentName; + public string CurrentAttachmentName { + get { return currentAttachmentName; } } - } - public BoundingBoxAttachment CurrentAttachment { - get { - return currentAttachment; + public BoundingBoxAttachment CurrentAttachment { + get { return currentAttachment; } } - } - public PolygonCollider2D CurrentCollider { - get { - return currentCollider; + public PolygonCollider2D CurrentCollider { + get { return currentCollider; } } - } - public Slot Slot { - get { - return slot; + public Slot Slot { + get { return slot; } } - } - void OnEnable () { - ClearColliders(); + void OnEnable () { + ClearColliders(); - if (skeletonRenderer == null) - skeletonRenderer = GetComponentInParent(); + if (skeletonRenderer == null) + skeletonRenderer = GetComponentInParent(); - if (skeletonRenderer != null) { + if (skeletonRenderer != null) { + skeletonRenderer.OnRebuild -= HandleReset; + skeletonRenderer.OnRebuild += HandleReset; + + if (hasReset) + HandleReset(skeletonRenderer); + } + } + + void OnDisable () { skeletonRenderer.OnRebuild -= HandleReset; - skeletonRenderer.OnRebuild += HandleReset; + } - if (hasReset) + void Start () { + if (!hasReset && skeletonRenderer != null) HandleReset(skeletonRenderer); } - } - void OnDisable () { - skeletonRenderer.OnRebuild -= HandleReset; - } + public void HandleReset (SkeletonRenderer renderer) { + if (slotName == null || slotName == "") + return; - void Start () { - if (!hasReset && skeletonRenderer != null) - HandleReset(skeletonRenderer); - } + hasReset = true; - public void HandleReset (SkeletonRenderer renderer) { - if (slotName == null || slotName == "") - return; + ClearColliders(); + colliderTable.Clear(); - hasReset = true; - - ClearColliders(); - colliderTable.Clear(); - - if (skeletonRenderer.skeleton == null) { - skeletonRenderer.OnRebuild -= HandleReset; - skeletonRenderer.Initialize(false); - skeletonRenderer.OnRebuild += HandleReset; - } + if (skeletonRenderer.skeleton == null) { + skeletonRenderer.OnRebuild -= HandleReset; + skeletonRenderer.Initialize(false); + skeletonRenderer.OnRebuild += HandleReset; + } - var skeleton = skeletonRenderer.skeleton; - slot = skeleton.FindSlot(slotName); - int slotIndex = skeleton.FindSlotIndex(slotName); + var skeleton = skeletonRenderer.skeleton; + slot = skeleton.FindSlot(slotName); + int slotIndex = skeleton.FindSlotIndex(slotName); - foreach (var skin in skeleton.Data.Skins) { - List attachmentNames = new List(); - skin.FindNamesForSlot(slotIndex, attachmentNames); + foreach (var skin in skeleton.Data.Skins) { + List attachmentNames = new List(); + skin.FindNamesForSlot(slotIndex, attachmentNames); - foreach (var name in attachmentNames) { - var attachment = skin.GetAttachment(slotIndex, name); - if (attachment is BoundingBoxAttachment) { - var collider = SkeletonUtility.AddBoundingBoxAsComponent((BoundingBoxAttachment)attachment, gameObject, true); - collider.enabled = false; - collider.hideFlags = HideFlags.HideInInspector; - colliderTable.Add((BoundingBoxAttachment)attachment, collider); - attachmentNameTable.Add((BoundingBoxAttachment)attachment, name); + foreach (var name in attachmentNames) { + var attachment = skin.GetAttachment(slotIndex, name); + if (attachment is BoundingBoxAttachment) { + var collider = SkeletonUtility.AddBoundingBoxAsComponent((BoundingBoxAttachment)attachment, gameObject, true); + collider.enabled = false; + collider.hideFlags = HideFlags.HideInInspector; + colliderTable.Add((BoundingBoxAttachment)attachment, collider); + attachmentNameTable.Add((BoundingBoxAttachment)attachment, name); + } } } + + if (colliderTable.Count == 0) + valid = false; + else + valid = true; + + if (!valid) + Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!"); } - if (colliderTable.Count == 0) - valid = false; - else - valid = true; - - if (!valid) - Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!"); - } - - void ClearColliders () { - var colliders = GetComponents(); - if (Application.isPlaying) { - foreach (var c in colliders) { - Destroy(c); + void ClearColliders () { + var colliders = GetComponents(); + if (Application.isPlaying) { + foreach (var c in colliders) { + Destroy(c); + } + } else { + foreach (var c in colliders) { + DestroyImmediate(c); + } } - } else { - foreach (var c in colliders) { - DestroyImmediate(c); + + colliderTable.Clear(); + attachmentNameTable.Clear(); + } + + void LateUpdate () { + if (!skeletonRenderer.valid) + return; + + if (slot != null) { + if (slot.Attachment != currentAttachment) + SetCurrent((BoundingBoxAttachment)slot.Attachment); } } - colliderTable.Clear(); - attachmentNameTable.Clear(); - } + void SetCurrent (BoundingBoxAttachment attachment) { + if (currentCollider) + currentCollider.enabled = false; - void LateUpdate () { - if (!skeletonRenderer.valid) - return; + if (attachment != null) { + currentCollider = colliderTable[attachment]; + currentCollider.enabled = true; + } else { + currentCollider = null; + } - if (slot != null) { - if (slot.Attachment != currentAttachment) - SetCurrent((BoundingBoxAttachment)slot.Attachment); + currentAttachment = attachment; + + currentAttachmentName = currentAttachment == null ? null : attachmentNameTable[attachment]; } } - void SetCurrent (BoundingBoxAttachment attachment) { - if (currentCollider) - currentCollider.enabled = false; - - if (attachment != null) { - currentCollider = colliderTable[attachment]; - currentCollider.enabled = true; - } else { - currentCollider = null; - } - - currentAttachment = attachment; - - currentAttachmentName = currentAttachment == null ? null : attachmentNameTable[attachment]; - } } diff --git a/spine-unity/Assets/spine-unity/Modules/CustomSkin.cs b/spine-unity/Assets/spine-unity/Modules/CustomSkin.cs index fab223b14..70f514def 100644 --- a/spine-unity/Assets/spine-unity/Modules/CustomSkin.cs +++ b/spine-unity/Assets/spine-unity/Modules/CustomSkin.cs @@ -28,54 +28,56 @@ * 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; +using Spine.Unity; -public class CustomSkin : MonoBehaviour { +namespace Spine.Unity.Modules { + public class CustomSkin : MonoBehaviour { - [System.Serializable] - public class SkinPair { - /// SpineAttachment attachment path to help find the attachment. - /// This use of SpineAttachment generates an attachment path string that can only be used by SpineAttachment.GetAttachment. - [SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")] - [UnityEngine.Serialization.FormerlySerializedAs("sourceAttachment")] - public string sourceAttachmentPath; + [System.Serializable] + public class SkinPair { + /// SpineAttachment attachment path to help find the attachment. + /// This use of SpineAttachment generates an attachment path string that can only be used by SpineAttachment.GetAttachment. + [SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")] + [UnityEngine.Serialization.FormerlySerializedAs("sourceAttachment")] + public string sourceAttachmentPath; - [SpineSlot] - public string targetSlot; + [SpineSlot] + public string targetSlot; - /// The name of the skin placeholder/skin dictionary entry this attachment should be associated with. - /// This name is used by the skin dictionary, used in the method Skin.AddAttachment as well as setting a slot attachment - [SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)] - public string targetAttachment; - } - - #region Inspector - public SkeletonDataAsset skinSource; - - [UnityEngine.Serialization.FormerlySerializedAs("skinning")] - public SkinPair[] skinItems; - - public Skin customSkin; - #endregion - - SkeletonRenderer skeletonRenderer; - - void Start () { - skeletonRenderer = GetComponent(); - Skeleton skeleton = skeletonRenderer.skeleton; - - customSkin = new Skin("CustomSkin"); - - foreach (var pair in skinItems) { - var attachment = SpineAttachment.GetAttachment(pair.sourceAttachmentPath, skinSource); - customSkin.AddAttachment(skeleton.FindSlotIndex(pair.targetSlot), pair.targetAttachment, attachment); + /// The name of the skin placeholder/skin dictionary entry this attachment should be associated with. + /// This name is used by the skin dictionary, used in the method Skin.AddAttachment as well as setting a slot attachment + [SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)] + public string targetAttachment; } - // The custom skin does not need to be added to the skeleton data for it to work. - // But it's useful for your script to keep a reference to it. - skeleton.SetSkin(customSkin); + #region Inspector + public SkeletonDataAsset skinSource; + + [UnityEngine.Serialization.FormerlySerializedAs("skinning")] + public SkinPair[] skinItems; + + public Skin customSkin; + #endregion + + SkeletonRenderer skeletonRenderer; + + void Start () { + skeletonRenderer = GetComponent(); + Skeleton skeleton = skeletonRenderer.skeleton; + + customSkin = new Skin("CustomSkin"); + + foreach (var pair in skinItems) { + var attachment = SpineAttachment.GetAttachment(pair.sourceAttachmentPath, skinSource); + customSkin.AddAttachment(skeleton.FindSlotIndex(pair.targetSlot), pair.targetAttachment, attachment); + } + + // The custom skin does not need to be added to the skeleton data for it to work. + // But it's useful for your script to keep a reference to it. + skeleton.SetSkin(customSkin); + } } + } diff --git a/spine-unity/Assets/spine-unity/Modules/Ghost/SkeletonGhost.cs b/spine-unity/Assets/spine-unity/Modules/Ghost/SkeletonGhost.cs index 3717bc9a6..3de748487 100644 --- a/spine-unity/Assets/spine-unity/Modules/Ghost/SkeletonGhost.cs +++ b/spine-unity/Assets/spine-unity/Modules/Ghost/SkeletonGhost.cs @@ -4,150 +4,153 @@ *****************************************************************************/ using UnityEngine; -using System.Collections; using System.Collections.Generic; -[RequireComponent(typeof(SkeletonRenderer))] -public class SkeletonGhost : MonoBehaviour { - public bool ghostingEnabled = true; - public float spawnRate = 0.05f; - 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; - public float fadeSpeed = 10; - public Shader ghostShader; - [Tooltip("0 is Color and Alpha, 1 is Alpha only.")] - [Range(0, 1)] - public float textureFade = 1; +namespace Spine.Unity.Modules { + + [RequireComponent(typeof(SkeletonRenderer))] + public class SkeletonGhost : MonoBehaviour { + public bool ghostingEnabled = true; + public float spawnRate = 0.05f; + 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; + public float fadeSpeed = 10; + public Shader ghostShader; + [Tooltip("0 is Color and Alpha, 1 is Alpha only.")] + [Range(0, 1)] + public float textureFade = 1; - [Header("Sorting")] - public bool sortWithDistanceOnly; - public float zOffset = 0f; + [Header("Sorting")] + public bool sortWithDistanceOnly; + public float zOffset = 0f; - float nextSpawnTime; - SkeletonGhostRenderer[] pool; - int poolIndex = 0; - SkeletonRenderer skeletonRenderer; - MeshRenderer meshRenderer; - MeshFilter meshFilter; + float nextSpawnTime; + SkeletonGhostRenderer[] pool; + int poolIndex = 0; + SkeletonRenderer skeletonRenderer; + MeshRenderer meshRenderer; + MeshFilter meshFilter; - Dictionary materialTable = new Dictionary(); + Dictionary materialTable = new Dictionary(); - void Start () { - if (ghostShader == null) - ghostShader = Shader.Find("Spine/SkeletonGhost"); + void Start () { + if (ghostShader == null) + ghostShader = Shader.Find("Spine/SkeletonGhost"); + + skeletonRenderer = GetComponent(); + meshFilter = GetComponent(); + meshRenderer = GetComponent(); + nextSpawnTime = Time.time + spawnRate; + pool = new SkeletonGhostRenderer[maximumGhosts]; + for (int i = 0; i < maximumGhosts; i++) { + GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer)); + pool[i] = go.GetComponent(); + go.SetActive(false); + go.hideFlags = HideFlags.HideInHierarchy; + } + + if (skeletonRenderer is SkeletonAnimation) + ((SkeletonAnimation)skeletonRenderer).state.Event += OnEvent; - skeletonRenderer = GetComponent(); - meshFilter = GetComponent(); - meshRenderer = GetComponent(); - nextSpawnTime = Time.time + spawnRate; - pool = new SkeletonGhostRenderer[maximumGhosts]; - for (int i = 0; i < maximumGhosts; i++) { - GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer)); - pool[i] = go.GetComponent(); - go.SetActive(false); - go.hideFlags = HideFlags.HideInHierarchy; } - if (skeletonRenderer is SkeletonAnimation) - ((SkeletonAnimation)skeletonRenderer).state.Event += OnEvent; - - } - - //SkeletonAnimation - /* + //SkeletonAnimation + /* * Int Value: 0 sets ghostingEnabled to false, 1 sets ghostingEnabled to true * Float Value: Values greater than 0 set the spawnRate equal the float value * String Value: Pass RGBA hex color values in to set the color property. IE: "A0FF8BFF" */ - void OnEvent (Spine.AnimationState state, int trackIndex, Spine.Event e) { - if (e.Data.Name == "Ghosting") { - ghostingEnabled = e.Int > 0; - if (e.Float > 0) - spawnRate = e.Float; - if (e.String != null) { - this.color = HexToColor(e.String); + void OnEvent (Spine.AnimationState state, int trackIndex, Spine.Event e) { + if (e.Data.Name == "Ghosting") { + ghostingEnabled = e.Int > 0; + if (e.Float > 0) + spawnRate = e.Float; + if (e.String != null) { + this.color = HexToColor(e.String); + } } } - } - //SkeletonAnimator - //SkeletonAnimator or Mecanim based animations only support toggling ghostingEnabled. Be sure not to set anything other than the Int param in Spine or String will take priority. - void Ghosting (float val) { - ghostingEnabled = val > 0; - } + //SkeletonAnimator + //SkeletonAnimator or Mecanim based animations only support toggling ghostingEnabled. Be sure not to set anything other than the Int param in Spine or String will take priority. + void Ghosting (float val) { + ghostingEnabled = val > 0; + } - void Update () { - if (!ghostingEnabled) - return; + void Update () { + if (!ghostingEnabled) + return; - if (Time.time >= nextSpawnTime) { - GameObject go = pool[poolIndex].gameObject; + if (Time.time >= nextSpawnTime) { + GameObject go = pool[poolIndex].gameObject; - Material[] materials = meshRenderer.sharedMaterials; - for (int i = 0; i < materials.Length; i++) { - var originalMat = materials[i]; - Material ghostMat; - if (!materialTable.ContainsKey(originalMat)) { - ghostMat = new Material(originalMat); - ghostMat.shader = ghostShader; - ghostMat.color = Color.white; - if (ghostMat.HasProperty("_TextureFade")) - ghostMat.SetFloat("_TextureFade", textureFade); - materialTable.Add(originalMat, ghostMat); - } else { - ghostMat = materialTable[originalMat]; + Material[] materials = meshRenderer.sharedMaterials; + for (int i = 0; i < materials.Length; i++) { + var originalMat = materials[i]; + Material ghostMat; + if (!materialTable.ContainsKey(originalMat)) { + ghostMat = new Material(originalMat); + ghostMat.shader = ghostShader; + ghostMat.color = Color.white; + if (ghostMat.HasProperty("_TextureFade")) + ghostMat.SetFloat("_TextureFade", textureFade); + materialTable.Add(originalMat, ghostMat); + } else { + ghostMat = materialTable[originalMat]; + } + + materials[i] = ghostMat; } - materials[i] = ghostMat; + var goTransform = go.transform; + goTransform.parent = transform; + + pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingLayerID, (sortWithDistanceOnly) ? meshRenderer.sortingOrder : meshRenderer.sortingOrder - 1); + + goTransform.localPosition = new Vector3(0f, 0f, zOffset); + goTransform.localRotation = Quaternion.identity; + goTransform.localScale = Vector3.one; + + goTransform.parent = null; + + poolIndex++; + + if (poolIndex == pool.Length) + poolIndex = 0; + + nextSpawnTime = Time.time + spawnRate; + } + } + + void OnDestroy () { + for (int i = 0; i < maximumGhosts; i++) { + if (pool[i] != null) + pool[i].Cleanup(); } - var goTransform = go.transform; - goTransform.parent = transform; + foreach (var mat in materialTable.Values) + Destroy(mat); + } - pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingLayerID, (sortWithDistanceOnly) ? meshRenderer.sortingOrder : meshRenderer.sortingOrder - 1); - goTransform.localPosition = new Vector3(0f, 0f, zOffset); - goTransform.localRotation = Quaternion.identity; - goTransform.localScale = Vector3.one; + //based on UnifyWiki http://wiki.unity3d.com/index.php?title=HexConverter + static Color32 HexToColor (string hex) { + if (hex.Length < 6) + return Color.magenta; - goTransform.parent = null; + hex = hex.Replace("#", ""); + byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); + byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); + byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); + byte a = 0xFF; + if (hex.Length == 8) + a = byte.Parse(hex.Substring(6, 2), System.Globalization.NumberStyles.HexNumber); - poolIndex++; - - if (poolIndex == pool.Length) - poolIndex = 0; - - nextSpawnTime = Time.time + spawnRate; + return new Color32(r, g, b, a); } } - void OnDestroy () { - for (int i = 0; i < maximumGhosts; i++) { - if (pool[i] != null) - pool[i].Cleanup(); - } - - foreach (var mat in materialTable.Values) - Destroy(mat); - } - - - //based on UnifyWiki http://wiki.unity3d.com/index.php?title=HexConverter - static Color32 HexToColor (string hex) { - if (hex.Length < 6) - return Color.magenta; - - hex = hex.Replace("#", ""); - byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); - byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); - byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); - byte a = 0xFF; - if (hex.Length == 8) - a = byte.Parse(hex.Substring(6, 2), System.Globalization.NumberStyles.HexNumber); - - return new Color32(r, g, b, a); - } } diff --git a/spine-unity/Assets/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs b/spine-unity/Assets/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs index 87c3da693..649345d0e 100644 --- a/spine-unity/Assets/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs +++ b/spine-unity/Assets/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs @@ -5,48 +5,49 @@ using UnityEngine; using UnityEditor; -using System.Collections; -using System.Collections.Generic; -[CustomEditor(typeof(SkeletonRagdoll2D))] -public class SkeletonRagdoll2DInspector : Editor { - SerializedProperty startingBoneName, stopBoneNames, applyOnStart, pinStartBone, enableJointCollision, gravityScale, disableIK, thickness, rotationLimit, colliderLayer, mix, rootMass, massFalloffFactor; +namespace Spine.Unity.Modules { + [CustomEditor(typeof(SkeletonRagdoll2D))] + public class SkeletonRagdoll2DInspector : UnityEditor.Editor { + SerializedProperty startingBoneName, stopBoneNames, applyOnStart, pinStartBone, enableJointCollision, gravityScale, disableIK, thickness, rotationLimit, colliderLayer, mix, rootMass, massFalloffFactor; - void OnEnable () { - startingBoneName = serializedObject.FindProperty("startingBoneName"); - stopBoneNames = serializedObject.FindProperty("stopBoneNames"); - applyOnStart = serializedObject.FindProperty("applyOnStart"); - pinStartBone = serializedObject.FindProperty("pinStartBone"); - gravityScale = serializedObject.FindProperty("gravityScale"); - disableIK = serializedObject.FindProperty("disableIK"); - thickness = serializedObject.FindProperty("thickness"); - rotationLimit = serializedObject.FindProperty("rotationLimit"); - colliderLayer = serializedObject.FindProperty("colliderLayer"); - mix = serializedObject.FindProperty("mix"); - rootMass = serializedObject.FindProperty("rootMass"); - massFalloffFactor = serializedObject.FindProperty("massFalloffFactor"); + void OnEnable () { + startingBoneName = serializedObject.FindProperty("startingBoneName"); + stopBoneNames = serializedObject.FindProperty("stopBoneNames"); + applyOnStart = serializedObject.FindProperty("applyOnStart"); + pinStartBone = serializedObject.FindProperty("pinStartBone"); + gravityScale = serializedObject.FindProperty("gravityScale"); + disableIK = serializedObject.FindProperty("disableIK"); + thickness = serializedObject.FindProperty("thickness"); + rotationLimit = serializedObject.FindProperty("rotationLimit"); + colliderLayer = serializedObject.FindProperty("colliderLayer"); + mix = serializedObject.FindProperty("mix"); + rootMass = serializedObject.FindProperty("rootMass"); + massFalloffFactor = serializedObject.FindProperty("massFalloffFactor"); + } + + public override void OnInspectorGUI () { + EditorGUILayout.PropertyField(startingBoneName); + EditorGUILayout.PropertyField(stopBoneNames, true); + EditorGUILayout.PropertyField(applyOnStart); + EditorGUILayout.PropertyField(pinStartBone); + EditorGUILayout.PropertyField(gravityScale); + EditorGUILayout.PropertyField(disableIK); + EditorGUILayout.PropertyField(thickness); + EditorGUILayout.PropertyField(rotationLimit); + EditorGUILayout.PropertyField(rootMass); + EditorGUILayout.PropertyField(massFalloffFactor); + colliderLayer.intValue = EditorGUILayout.LayerField(colliderLayer.displayName, colliderLayer.intValue); + EditorGUILayout.PropertyField(mix); + + + serializedObject.ApplyModifiedProperties(); + } + + void Header (string name) { + GUILayout.Space(20); + EditorGUILayout.LabelField(name, EditorStyles.boldLabel); + } } - public override void OnInspectorGUI () { - EditorGUILayout.PropertyField(startingBoneName); - EditorGUILayout.PropertyField(stopBoneNames, true); - EditorGUILayout.PropertyField(applyOnStart); - EditorGUILayout.PropertyField(pinStartBone); - EditorGUILayout.PropertyField(gravityScale); - EditorGUILayout.PropertyField(disableIK); - EditorGUILayout.PropertyField(thickness); - EditorGUILayout.PropertyField(rotationLimit); - EditorGUILayout.PropertyField(rootMass); - EditorGUILayout.PropertyField(massFalloffFactor); - colliderLayer.intValue = EditorGUILayout.LayerField(colliderLayer.displayName, colliderLayer.intValue); - EditorGUILayout.PropertyField(mix); - - - serializedObject.ApplyModifiedProperties(); - } - - void Header (string name) { - GUILayout.Space(20); - EditorGUILayout.LabelField(name, EditorStyles.boldLabel); - } } diff --git a/spine-unity/Assets/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs b/spine-unity/Assets/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs index af1627af4..bac20be1b 100644 --- a/spine-unity/Assets/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs +++ b/spine-unity/Assets/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs @@ -8,46 +8,49 @@ using UnityEditor; using System.Collections; using System.Collections.Generic; -[CustomEditor(typeof(SkeletonRagdoll))] -public class SkeletonRagdollInspector : Editor { - SerializedProperty startingBoneName, stopBoneNames, applyOnStart, pinStartBone, enableJointCollision, useGravity, disableIK, thickness, rotationLimit, colliderLayer, mix, rootMass, massFalloffFactor; +namespace Spine.Unity.Modules { + [CustomEditor(typeof(SkeletonRagdoll))] + public class SkeletonRagdollInspector : UnityEditor.Editor { + SerializedProperty startingBoneName, stopBoneNames, applyOnStart, pinStartBone, enableJointCollision, useGravity, disableIK, thickness, rotationLimit, colliderLayer, mix, rootMass, massFalloffFactor; - void OnEnable () { - startingBoneName = serializedObject.FindProperty("startingBoneName"); - stopBoneNames = serializedObject.FindProperty("stopBoneNames"); - applyOnStart = serializedObject.FindProperty("applyOnStart"); - pinStartBone = serializedObject.FindProperty("pinStartBone"); - enableJointCollision = serializedObject.FindProperty("enableJointCollision"); - useGravity = serializedObject.FindProperty("useGravity"); - disableIK = serializedObject.FindProperty("disableIK"); - thickness = serializedObject.FindProperty("thickness"); - rotationLimit = serializedObject.FindProperty("rotationLimit"); - colliderLayer = serializedObject.FindProperty("colliderLayer"); - mix = serializedObject.FindProperty("mix"); - rootMass = serializedObject.FindProperty("rootMass"); - massFalloffFactor = serializedObject.FindProperty("massFalloffFactor"); + void OnEnable () { + startingBoneName = serializedObject.FindProperty("startingBoneName"); + stopBoneNames = serializedObject.FindProperty("stopBoneNames"); + applyOnStart = serializedObject.FindProperty("applyOnStart"); + pinStartBone = serializedObject.FindProperty("pinStartBone"); + enableJointCollision = serializedObject.FindProperty("enableJointCollision"); + useGravity = serializedObject.FindProperty("useGravity"); + disableIK = serializedObject.FindProperty("disableIK"); + thickness = serializedObject.FindProperty("thickness"); + rotationLimit = serializedObject.FindProperty("rotationLimit"); + colliderLayer = serializedObject.FindProperty("colliderLayer"); + mix = serializedObject.FindProperty("mix"); + rootMass = serializedObject.FindProperty("rootMass"); + massFalloffFactor = serializedObject.FindProperty("massFalloffFactor"); + } + + public override void OnInspectorGUI () { + EditorGUILayout.PropertyField(startingBoneName); + EditorGUILayout.PropertyField(stopBoneNames, true); + EditorGUILayout.PropertyField(applyOnStart); + EditorGUILayout.PropertyField(pinStartBone); + EditorGUILayout.PropertyField(enableJointCollision); + EditorGUILayout.PropertyField(useGravity); + EditorGUILayout.PropertyField(disableIK); + EditorGUILayout.PropertyField(thickness); + EditorGUILayout.PropertyField(rotationLimit); + EditorGUILayout.PropertyField(rootMass); + EditorGUILayout.PropertyField(massFalloffFactor); + colliderLayer.intValue = EditorGUILayout.LayerField(colliderLayer.displayName, colliderLayer.intValue); + EditorGUILayout.PropertyField(mix); + + serializedObject.ApplyModifiedProperties(); + } + + void Header (string name) { + GUILayout.Space(20); + EditorGUILayout.LabelField(name, EditorStyles.boldLabel); + } } - public override void OnInspectorGUI () { - EditorGUILayout.PropertyField(startingBoneName); - EditorGUILayout.PropertyField(stopBoneNames, true); - EditorGUILayout.PropertyField(applyOnStart); - EditorGUILayout.PropertyField(pinStartBone); - EditorGUILayout.PropertyField(enableJointCollision); - EditorGUILayout.PropertyField(useGravity); - EditorGUILayout.PropertyField(disableIK); - EditorGUILayout.PropertyField(thickness); - EditorGUILayout.PropertyField(rotationLimit); - EditorGUILayout.PropertyField(rootMass); - EditorGUILayout.PropertyField(massFalloffFactor); - colliderLayer.intValue = EditorGUILayout.LayerField(colliderLayer.displayName, colliderLayer.intValue); - EditorGUILayout.PropertyField(mix); - - serializedObject.ApplyModifiedProperties(); - } - - void Header (string name) { - GUILayout.Space(20); - EditorGUILayout.LabelField(name, EditorStyles.boldLabel); - } } diff --git a/spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs b/spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs index 9e5f2f5bb..1275edb25 100644 --- a/spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs +++ b/spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs @@ -6,421 +6,423 @@ using UnityEngine; using System.Collections; using System.Collections.Generic; -using Spine; -[RequireComponent(typeof(SkeletonRenderer))] -public class SkeletonRagdoll : MonoBehaviour { - private static Transform helper; +namespace Spine.Unity.Modules { + [RequireComponent(typeof(SkeletonRenderer))] + public class SkeletonRagdoll : MonoBehaviour { + private static Transform helper; - [Header("Hierarchy")] - [SpineBone] - public string startingBoneName = ""; - [SpineBone] - public List stopBoneNames = new List(); + [Header("Hierarchy")] + [SpineBone] + public string startingBoneName = ""; + [SpineBone] + public List stopBoneNames = new List(); - [Header("Parameters")] - public bool applyOnStart; - [Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")] - public bool pinStartBone; - [Tooltip("Enable Collision between adjacent ragdoll elements (IE: Neck and Head)")] - public bool enableJointCollision; - public bool useGravity = true; - [Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")] - public bool disableIK = true; - [Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")] - public float thickness = 0.125f; - [Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")] - public float rotationLimit = 20; - public float rootMass = 20; - [Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")] - [Range(0.01f, 1f)] - public float massFalloffFactor = 0.4f; - [Tooltip("The layer assigned to all of the rigidbody parts.")] - public int colliderLayer = 0; - [Range(0, 1)] - public float mix = 1; + [Header("Parameters")] + public bool applyOnStart; + [Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")] + public bool pinStartBone; + [Tooltip("Enable Collision between adjacent ragdoll elements (IE: Neck and Head)")] + public bool enableJointCollision; + public bool useGravity = true; + [Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")] + public bool disableIK = true; + [Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")] + public float thickness = 0.125f; + [Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")] + public float rotationLimit = 20; + public float rootMass = 20; + [Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")] + [Range(0.01f, 1f)] + public float massFalloffFactor = 0.4f; + [Tooltip("The layer assigned to all of the rigidbody parts.")] + public int colliderLayer = 0; + [Range(0, 1)] + public float mix = 1; - public Rigidbody RootRigidbody { - get { - return this.rootRigidbody; - } - } - - public Vector3 RootOffset { - get { - return this.rootOffset; - } - } - - public Vector3 EstimatedSkeletonPosition { - get { - return rootRigidbody.position - rootOffset; - } - } - - public bool IsActive { - get { - return this.isActive; - } - } - - private Rigidbody rootRigidbody; - private ISkeletonAnimation skeletonAnim; - private Skeleton skeleton; - private Dictionary boneTable = new Dictionary(); - private Bone startingBone; - private Transform ragdollRoot; - private Vector3 rootOffset; - private bool isActive; - - IEnumerator Start () { - skeletonAnim = (ISkeletonAnimation)GetComponent(); - if (helper == null) { - helper = (Transform)(new GameObject("Helper")).transform; - helper.hideFlags = HideFlags.HideInHierarchy; + public Rigidbody RootRigidbody { + get { + return this.rootRigidbody; + } } - if (applyOnStart) { - yield return null; - Apply(); - } - } - - public Coroutine SmoothMix (float target, float duration) { - return StartCoroutine(SmoothMixCoroutine(target, duration)); - } - - IEnumerator SmoothMixCoroutine (float target, float duration) { - float startTime = Time.time; - float startMix = mix; - while (mix > 0) { - mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration); - yield return null; - } - } - - public void SetSkeletonPosition (Vector3 worldPosition) { - if (!isActive) { - Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!"); - return; + public Vector3 RootOffset { + get { + return this.rootOffset; + } } - Vector3 offset = worldPosition - transform.position; - transform.position = worldPosition; - foreach (Transform t in boneTable.Values) { - t.position -= offset; + public Vector3 EstimatedSkeletonPosition { + get { + return rootRigidbody.position - rootOffset; + } } - UpdateWorld(null); - skeleton.UpdateWorldTransform(); - } - - public Rigidbody[] GetRigidbodyArray () { - if (!isActive) - return new Rigidbody[0]; - - Rigidbody[] arr = new Rigidbody[boneTable.Count]; - int i = 0; - foreach (Transform t in boneTable.Values) { - arr[i] = t.GetComponent(); - i++; + public bool IsActive { + get { + return this.isActive; + } } - return arr; - } + private Rigidbody rootRigidbody; + private ISkeletonAnimation skeletonAnim; + private Skeleton skeleton; + private Dictionary boneTable = new Dictionary(); + private Bone startingBone; + private Transform ragdollRoot; + private Vector3 rootOffset; + private bool isActive; + + IEnumerator Start () { + skeletonAnim = (ISkeletonAnimation)GetComponent(); + if (helper == null) { + helper = (Transform)(new GameObject("Helper")).transform; + helper.hideFlags = HideFlags.HideInHierarchy; + } + + if (applyOnStart) { + yield return null; + Apply(); + } + } + + public Coroutine SmoothMix (float target, float duration) { + return StartCoroutine(SmoothMixCoroutine(target, duration)); + } + + IEnumerator SmoothMixCoroutine (float target, float duration) { + float startTime = Time.time; + float startMix = mix; + while (mix > 0) { + mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration); + yield return null; + } + } + + public void SetSkeletonPosition (Vector3 worldPosition) { + if (!isActive) { + Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!"); + return; + } + + Vector3 offset = worldPosition - transform.position; + transform.position = worldPosition; + foreach (Transform t in boneTable.Values) { + t.position -= offset; + } + + UpdateWorld(null); + skeleton.UpdateWorldTransform(); + } + + public Rigidbody[] GetRigidbodyArray () { + if (!isActive) + return new Rigidbody[0]; + + Rigidbody[] arr = new Rigidbody[boneTable.Count]; + int i = 0; + foreach (Transform t in boneTable.Values) { + arr[i] = t.GetComponent(); + i++; + } + + return arr; + } + + public Rigidbody GetRigidbody (string boneName) { + var bone = skeleton.FindBone(boneName); + if (bone == null) + return null; + + if (boneTable.ContainsKey(bone)) + return boneTable[bone].GetComponent(); - public Rigidbody GetRigidbody (string boneName) { - var bone = skeleton.FindBone(boneName); - if (bone == null) return null; - - if (boneTable.ContainsKey(bone)) - return boneTable[bone].GetComponent(); - - return null; - } - - public void Remove () { - isActive = false; - foreach (var t in boneTable.Values) { - Destroy(t.gameObject); } - Destroy(ragdollRoot.gameObject); - boneTable.Clear(); - skeletonAnim.UpdateWorld -= UpdateWorld; - } + public void Remove () { + isActive = false; + foreach (var t in boneTable.Values) { + Destroy(t.gameObject); + } + Destroy(ragdollRoot.gameObject); - public void Apply () { - isActive = true; - skeleton = skeletonAnim.Skeleton; - mix = 1; + boneTable.Clear(); + skeletonAnim.UpdateWorld -= UpdateWorld; + } - var ragdollRootBone = skeleton.FindBone(startingBoneName); - startingBone = ragdollRootBone; - RecursivelyCreateBoneProxies(ragdollRootBone); + public void Apply () { + isActive = true; + skeleton = skeletonAnim.Skeleton; + mix = 1; - rootRigidbody = boneTable[ragdollRootBone].GetComponent(); - rootRigidbody.isKinematic = pinStartBone; + var ragdollRootBone = skeleton.FindBone(startingBoneName); + startingBone = ragdollRootBone; + RecursivelyCreateBoneProxies(ragdollRootBone); - rootRigidbody.mass = rootMass; + rootRigidbody = boneTable[ragdollRootBone].GetComponent(); + rootRigidbody.isKinematic = pinStartBone; - List boneColliders = new List(); + rootRigidbody.mass = rootMass; - foreach (var pair in boneTable) { - var b = pair.Key; - var t = pair.Value; - Bone parentBone = null; - Transform parentTransform = transform; + List boneColliders = new List(); - boneColliders.Add(t.GetComponent()); + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + Bone parentBone = null; + Transform parentTransform = transform; - if (b != startingBone) { - parentBone = b.Parent; - parentTransform = boneTable[parentBone]; - } else { - ragdollRoot = new GameObject("RagdollRoot").transform; - ragdollRoot.parent = transform; + boneColliders.Add(t.GetComponent()); - if (b == skeleton.RootBone) { - ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0); - ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b)); - parentTransform = ragdollRoot; + if (b != startingBone) { + parentBone = b.Parent; + parentTransform = boneTable[parentBone]; } else { - ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0); - ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b.Parent)); - parentTransform = ragdollRoot; + ragdollRoot = new GameObject("RagdollRoot").transform; + ragdollRoot.parent = transform; + + if (b == skeleton.RootBone) { + ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b)); + parentTransform = ragdollRoot; + } else { + ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b.Parent)); + parentTransform = ragdollRoot; + } + + rootOffset = t.position - transform.position; } - rootOffset = t.position - transform.position; - } + var rbParent = parentTransform.GetComponent(); - var rbParent = parentTransform.GetComponent(); - - if (rbParent != null) { - var joint = t.gameObject.AddComponent(); - joint.connectedBody = rbParent; - Vector3 localPos = parentTransform.InverseTransformPoint(t.position); - localPos.x *= 1; - joint.connectedAnchor = localPos; - joint.axis = Vector3.forward; - joint.GetComponent().mass = joint.connectedBody.mass * massFalloffFactor; - JointLimits limits = new JointLimits(); - limits.min = -rotationLimit; - limits.max = rotationLimit; - joint.limits = limits; - joint.useLimits = true; - joint.enableCollision = enableJointCollision; - } - } - - for (int x = 0; x < boneColliders.Count; x++) { - for (int y = 0; y < boneColliders.Count; y++) { - if (x == y) continue; - Physics.IgnoreCollision(boneColliders[x], boneColliders[y]); - } - } - - var utilityBones = GetComponentsInChildren(); - if (utilityBones.Length > 0) { - List destroyedUtilityBoneNames = new List(); - foreach (var ub in utilityBones) { - if (ub.mode == SkeletonUtilityBone.Mode.Override) { - destroyedUtilityBoneNames.Add(ub.gameObject.name); - Destroy(ub.gameObject); + if (rbParent != null) { + var joint = t.gameObject.AddComponent(); + joint.connectedBody = rbParent; + Vector3 localPos = parentTransform.InverseTransformPoint(t.position); + localPos.x *= 1; + joint.connectedAnchor = localPos; + joint.axis = Vector3.forward; + joint.GetComponent().mass = joint.connectedBody.mass * massFalloffFactor; + JointLimits limits = new JointLimits(); + limits.min = -rotationLimit; + limits.max = rotationLimit; + joint.limits = limits; + joint.useLimits = true; + joint.enableCollision = enableJointCollision; } } - if (destroyedUtilityBoneNames.Count > 0) { - string msg = "Destroyed Utility Bones: "; - for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) { - msg += destroyedUtilityBoneNames[i]; - if (i != destroyedUtilityBoneNames.Count - 1) { - msg += ","; + for (int x = 0; x < boneColliders.Count; x++) { + for (int y = 0; y < boneColliders.Count; y++) { + if (x == y) continue; + Physics.IgnoreCollision(boneColliders[x], boneColliders[y]); + } + } + + var utilityBones = GetComponentsInChildren(); + if (utilityBones.Length > 0) { + List destroyedUtilityBoneNames = new List(); + foreach (var ub in utilityBones) { + if (ub.mode == SkeletonUtilityBone.Mode.Override) { + destroyedUtilityBoneNames.Add(ub.gameObject.name); + Destroy(ub.gameObject); } } - Debug.LogWarning(msg); - } - } - if (disableIK) { - foreach (IkConstraint ik in skeleton.IkConstraints) { - ik.Mix = 0; - } - } - - skeletonAnim.UpdateWorld += UpdateWorld; - } - - void RecursivelyCreateBoneProxies (Bone b) { - if (stopBoneNames.Contains(b.Data.Name)) - return; - - GameObject go = new GameObject(b.Data.Name); - go.layer = colliderLayer; - Transform t = go.transform; - boneTable.Add(b, t); - - t.parent = transform; - - t.localPosition = new Vector3(b.WorldX, b.WorldY, 0); - // MITCH - // t.localRotation = Quaternion.Euler(0, 0, b.WorldFlipX ^ b.WorldFlipY ? -b.WorldRotation : b.WorldRotation); - t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX); - t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1); - - float length = b.Data.Length; - - var colliders = AttachBoundingBoxRagdollColliders(b); - - if (length == 0) { - //physics - if (colliders.Count == 0) { - var ball = go.AddComponent(); - ball.radius = thickness / 2f; - } - } else { - //physics - if (colliders.Count == 0) { - var box = go.AddComponent(); - box.size = new Vector3(length, thickness, thickness); - // MITCH - // box.center = new Vector3((b.WorldFlipX ? -length : length) / 2, 0); - box.center = new Vector3(length / 2, 0); - } - } - - var rb = go.AddComponent(); - rb.constraints = RigidbodyConstraints.FreezePositionZ; - foreach (Bone child in b.Children) { - RecursivelyCreateBoneProxies(child); - } - } - - List AttachBoundingBoxRagdollColliders (Bone b) { - List colliders = new List(); - - Transform t = boneTable[b]; - GameObject go = t.gameObject; - var skin = skeleton.Skin; - if (skin == null) - skin = skeleton.Data.DefaultSkin; - - // MITCH - // bool flipX = b.WorldFlipX; - // bool flipY = b.WorldFlipY; - bool flipX = false; - bool flipY = false; - - List attachments = new List(); - foreach (Slot s in skeleton.Slots) { - if (s.Bone == b) { - skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments); - foreach (var a in attachments) { - if (a is BoundingBoxAttachment) { - if (!a.Name.ToLower().Contains("ragdoll")) - continue; - - var collider = go.AddComponent(); - var bounds = SkeletonUtility.GetBoundingBoxBounds((BoundingBoxAttachment)a, thickness); - - collider.center = bounds.center; - collider.size = bounds.size; - - if (flipX || flipY) { - Vector3 center = collider.center; - - if (flipX) - center.x *= -1; - - if (flipY) - center.y *= -1; - - collider.center = center; + if (destroyedUtilityBoneNames.Count > 0) { + string msg = "Destroyed Utility Bones: "; + for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) { + msg += destroyedUtilityBoneNames[i]; + if (i != destroyedUtilityBoneNames.Count - 1) { + msg += ","; } + } + Debug.LogWarning(msg); + } + } - colliders.Add(collider); + if (disableIK) { + foreach (IkConstraint ik in skeleton.IkConstraints) { + ik.Mix = 0; + } + } + + skeletonAnim.UpdateWorld += UpdateWorld; + } + + void RecursivelyCreateBoneProxies (Bone b) { + if (stopBoneNames.Contains(b.Data.Name)) + return; + + GameObject go = new GameObject(b.Data.Name); + go.layer = colliderLayer; + Transform t = go.transform; + boneTable.Add(b, t); + + t.parent = transform; + + t.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + // MITCH + // t.localRotation = Quaternion.Euler(0, 0, b.WorldFlipX ^ b.WorldFlipY ? -b.WorldRotation : b.WorldRotation); + t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX); + t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1); + + float length = b.Data.Length; + + var colliders = AttachBoundingBoxRagdollColliders(b); + + if (length == 0) { + //physics + if (colliders.Count == 0) { + var ball = go.AddComponent(); + ball.radius = thickness / 2f; + } + } else { + //physics + if (colliders.Count == 0) { + var box = go.AddComponent(); + box.size = new Vector3(length, thickness, thickness); + // MITCH + // box.center = new Vector3((b.WorldFlipX ? -length : length) / 2, 0); + box.center = new Vector3(length / 2, 0); + } + } + + var rb = go.AddComponent(); + rb.constraints = RigidbodyConstraints.FreezePositionZ; + foreach (Bone child in b.Children) { + RecursivelyCreateBoneProxies(child); + } + } + + List AttachBoundingBoxRagdollColliders (Bone b) { + List colliders = new List(); + + Transform t = boneTable[b]; + GameObject go = t.gameObject; + var skin = skeleton.Skin; + if (skin == null) + skin = skeleton.Data.DefaultSkin; + + // MITCH + // bool flipX = b.WorldFlipX; + // bool flipY = b.WorldFlipY; + bool flipX = false; + bool flipY = false; + + List attachments = new List(); + foreach (Slot s in skeleton.Slots) { + if (s.Bone == b) { + skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments); + foreach (var a in attachments) { + if (a is BoundingBoxAttachment) { + if (!a.Name.ToLower().Contains("ragdoll")) + continue; + + var collider = go.AddComponent(); + var bounds = SkeletonUtility.GetBoundingBoxBounds((BoundingBoxAttachment)a, thickness); + + collider.center = bounds.center; + collider.size = bounds.size; + + if (flipX || flipY) { + Vector3 center = collider.center; + + if (flipX) + center.x *= -1; + + if (flipY) + center.y *= -1; + + collider.center = center; + } + + colliders.Add(collider); + } } } } + + return colliders; } - return colliders; - } + void UpdateWorld (ISkeletonAnimation skeletonRenderer) { + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + // bool flip = false; + bool flipX = false; //TODO: deal with negative scale instead of Flip Key for Spine 3.0 + bool flipY = false; //TODO: deal with negative scale instead of Flip Key for Spine 3.0 + Bone parentBone = null; + Transform parentTransform = transform; - void UpdateWorld (ISkeletonAnimation skeletonRenderer) { - foreach (var pair in boneTable) { - var b = pair.Key; - var t = pair.Value; - // bool flip = false; - bool flipX = false; //TODO: deal with negative scale instead of Flip Key for Spine 3.0 - bool flipY = false; //TODO: deal with negative scale instead of Flip Key for Spine 3.0 - Bone parentBone = null; - Transform parentTransform = transform; + if (b != startingBone) { + parentBone = b.Parent; + parentTransform = boneTable[parentBone]; + // MITCH + // flipX = parentBone.WorldFlipX; + // flipY = parentBone.WorldFlipY; - if (b != startingBone) { - parentBone = b.Parent; - parentTransform = boneTable[parentBone]; - // MITCH - // flipX = parentBone.WorldFlipX; - // flipY = parentBone.WorldFlipY; - - } else { - parentBone = b.Parent; - parentTransform = ragdollRoot; - if (b.Parent != null) { - // MITCH - // flipX = b.worldFlipX; - // flipY = b.WorldFlipY; - } else { - flipX = b.Skeleton.FlipX; - flipY = b.Skeleton.FlipY; + } else { + parentBone = b.Parent; + parentTransform = ragdollRoot; + if (b.Parent != null) { + // MITCH + // flipX = b.worldFlipX; + // flipY = b.WorldFlipY; + } else { + flipX = b.Skeleton.FlipX; + flipY = b.Skeleton.FlipY; + } } - } - //flip = flipX ^ flipY; + //flip = flipX ^ flipY; - helper.position = parentTransform.position; - helper.rotation = parentTransform.rotation; - helper.localScale = new Vector3(flipX ? -parentTransform.localScale.x : parentTransform.localScale.x, flipY ? -parentTransform.localScale.y : parentTransform.localScale.y, 1); + helper.position = parentTransform.position; + helper.rotation = parentTransform.rotation; + helper.localScale = new Vector3(flipX ? -parentTransform.localScale.x : parentTransform.localScale.x, flipY ? -parentTransform.localScale.y : parentTransform.localScale.y, 1); - Vector3 pos = t.position; - pos = helper.InverseTransformPoint(pos); - b.X = Mathf.Lerp(b.X, pos.x, mix); - b.Y = Mathf.Lerp(b.Y, pos.y, mix); + Vector3 pos = t.position; + pos = helper.InverseTransformPoint(pos); + b.X = Mathf.Lerp(b.X, pos.x, mix); + b.Y = Mathf.Lerp(b.Y, pos.y, mix); - Vector3 right = helper.InverseTransformDirection(t.right); + Vector3 right = helper.InverseTransformDirection(t.right); - float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg; + float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg; - // MITCH - //if (b.WorldFlipX ^ b.WorldFlipY) { - // a *= -1; - //} - - if (parentBone != null) { - // MITCH - //if ((b.WorldFlipX ^ b.WorldFlipY) != flip) { - // a -= GetCompensatedRotationIK(parentBone) * 2; + // MITCH + //if (b.WorldFlipX ^ b.WorldFlipY) { + // a *= -1; //} + + if (parentBone != null) { + // MITCH + //if ((b.WorldFlipX ^ b.WorldFlipY) != flip) { + // a -= GetCompensatedRotationIK(parentBone) * 2; + //} + } + + b.Rotation = Mathf.Lerp(b.Rotation, a, mix); + // MITCH + // b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix); + } + } + + float GetCompensatedRotationIK (Bone b) { + Bone parent = b.Parent; + // MITCH + float a = b.AppliedRotation; + while (parent != null) { + a += parent.AppliedRotation; + parent = parent.parent; } - b.Rotation = Mathf.Lerp(b.Rotation, a, mix); - // MITCH - // b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix); + return a; } } - float GetCompensatedRotationIK (Bone b) { - Bone parent = b.Parent; - // MITCH - float a = b.AppliedRotation; - while (parent != null) { - a += parent.AppliedRotation; - parent = parent.parent; - } - - return a; - } } diff --git a/spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs b/spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs index b0dca43b0..79a3d6f6a 100644 --- a/spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs +++ b/spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs @@ -6,432 +6,427 @@ using UnityEngine; using System.Collections; using System.Collections.Generic; -using Spine; +using Spine.Unity; -[RequireComponent(typeof(SkeletonRenderer))] -public class SkeletonRagdoll2D : MonoBehaviour { - private static Transform helper; +namespace Spine.Unity.Modules { + [RequireComponent(typeof(SkeletonRenderer))] + public class SkeletonRagdoll2D : MonoBehaviour { + private static Transform helper; - [Header("Hierarchy")] - [SpineBone] - public string startingBoneName = ""; - [SpineBone] - public List stopBoneNames = new List(); + [Header("Hierarchy")] + [SpineBone] + public string startingBoneName = ""; + [SpineBone] + public List stopBoneNames = new List(); - [Header("Parameters")] - public bool applyOnStart; - [Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")] - public bool pinStartBone; - public float gravityScale = 1; - [Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")] - public bool disableIK = true; - [Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")] - public float thickness = 0.125f; - [Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")] - public float rotationLimit = 20; - public float rootMass = 20; - [Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")] - [Range(0.01f, 1f)] - public float massFalloffFactor = 0.4f; - [Tooltip("The layer assigned to all of the rigidbody parts.")] - public int colliderLayer = 0; - [Range(0, 1)] - public float mix = 1; + [Header("Parameters")] + public bool applyOnStart; + [Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")] + public bool pinStartBone; + public float gravityScale = 1; + [Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")] + public bool disableIK = true; + [Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")] + public float thickness = 0.125f; + [Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")] + public float rotationLimit = 20; + public float rootMass = 20; + [Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")] + [Range(0.01f, 1f)] + public float massFalloffFactor = 0.4f; + [Tooltip("The layer assigned to all of the rigidbody parts.")] + public int colliderLayer = 0; + [Range(0, 1)] + public float mix = 1; - public Rigidbody2D RootRigidbody { - get { - return this.rootRigidbody; - } - } - - public Vector3 RootOffset { - get { - return this.rootOffset; - } - } - - public Vector3 EstimatedSkeletonPosition { - get { - return this.rootRigidbody.position - rootOffset; - } - } - - public bool IsActive { - get { - return this.isActive; - } - } - - private Rigidbody2D rootRigidbody; - private ISkeletonAnimation skeletonAnim; - private Skeleton skeleton; - private Dictionary boneTable = new Dictionary(); - private Bone startingBone; - private Transform ragdollRoot; - private Vector2 rootOffset; - private bool isActive; - - - IEnumerator Start () { - skeletonAnim = (ISkeletonAnimation)GetComponent(); - if (helper == null) { - helper = (Transform)(new GameObject("Helper")).transform; - helper.hideFlags = HideFlags.HideInHierarchy; + public Rigidbody2D RootRigidbody { + get { return this.rootRigidbody; } } - if (applyOnStart) { - yield return null; - Apply(); - } - } - - public Coroutine SmoothMix (float target, float duration) { - return StartCoroutine(SmoothMixCoroutine(target, duration)); - } - - IEnumerator SmoothMixCoroutine (float target, float duration) { - float startTime = Time.time; - float startMix = mix; - while (mix > 0) { - mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration); - yield return null; - } - } - - public void SetSkeletonPosition (Vector3 worldPosition) { - if (!isActive) { - Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!"); - return; + public Vector3 RootOffset { + get { return this.rootOffset; } } - Vector3 offset = worldPosition - transform.position; - transform.position = worldPosition; - foreach (Transform t in boneTable.Values) { - t.position -= offset; + public Vector3 EstimatedSkeletonPosition { + get { return this.rootRigidbody.position - rootOffset; } } - UpdateWorld(null); - skeleton.UpdateWorldTransform(); - } - - public Rigidbody2D[] GetRigidbodyArray () { - if (!isActive) - return new Rigidbody2D[0]; - - Rigidbody2D[] arr = new Rigidbody2D[boneTable.Count]; - int i = 0; - foreach (Transform t in boneTable.Values) { - arr[i] = t.GetComponent(); - i++; + public bool IsActive { + get { return this.isActive; } } - return arr; - } + private Rigidbody2D rootRigidbody; + private ISkeletonAnimation skeletonAnim; + private Skeleton skeleton; + private Dictionary boneTable = new Dictionary(); + private Bone startingBone; + private Transform ragdollRoot; + private Vector2 rootOffset; + private bool isActive; + + + IEnumerator Start () { + skeletonAnim = (ISkeletonAnimation)GetComponent(); + if (helper == null) { + helper = (Transform)(new GameObject("Helper")).transform; + helper.hideFlags = HideFlags.HideInHierarchy; + } + + if (applyOnStart) { + yield return null; + Apply(); + } + } + + public Coroutine SmoothMix (float target, float duration) { + return StartCoroutine(SmoothMixCoroutine(target, duration)); + } + + IEnumerator SmoothMixCoroutine (float target, float duration) { + float startTime = Time.time; + float startMix = mix; + while (mix > 0) { + mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration); + yield return null; + } + } + + public void SetSkeletonPosition (Vector3 worldPosition) { + if (!isActive) { + Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!"); + return; + } + + Vector3 offset = worldPosition - transform.position; + transform.position = worldPosition; + foreach (Transform t in boneTable.Values) { + t.position -= offset; + } + + UpdateWorld(null); + skeleton.UpdateWorldTransform(); + } + + public Rigidbody2D[] GetRigidbodyArray () { + if (!isActive) + return new Rigidbody2D[0]; + + Rigidbody2D[] arr = new Rigidbody2D[boneTable.Count]; + int i = 0; + foreach (Transform t in boneTable.Values) { + arr[i] = t.GetComponent(); + i++; + } + + return arr; + } + + public Rigidbody2D GetRigidbody (string boneName) { + var bone = skeleton.FindBone(boneName); + if (bone == null) + return null; + + if (boneTable.ContainsKey(bone)) + return boneTable[bone].GetComponent(); - public Rigidbody2D GetRigidbody (string boneName) { - var bone = skeleton.FindBone(boneName); - if (bone == null) return null; - - if (boneTable.ContainsKey(bone)) - return boneTable[bone].GetComponent(); - - return null; - } - - public void Remove () { - isActive = false; - foreach (var t in boneTable.Values) { - Destroy(t.gameObject); } - Destroy(ragdollRoot.gameObject); - boneTable.Clear(); - skeletonAnim.UpdateWorld -= UpdateWorld; - } + public void Remove () { + isActive = false; + foreach (var t in boneTable.Values) { + Destroy(t.gameObject); + } + Destroy(ragdollRoot.gameObject); - public void Apply () { - isActive = true; - skeleton = skeletonAnim.Skeleton; - mix = 1; + boneTable.Clear(); + skeletonAnim.UpdateWorld -= UpdateWorld; + } - var ragdollRootBone = skeleton.FindBone(startingBoneName); - startingBone = ragdollRootBone; - RecursivelyCreateBoneProxies(ragdollRootBone); + public void Apply () { + isActive = true; + skeleton = skeletonAnim.Skeleton; + mix = 1; - rootRigidbody = boneTable[ragdollRootBone].GetComponent(); - rootRigidbody.isKinematic = pinStartBone; - rootRigidbody.mass = rootMass; + var ragdollRootBone = skeleton.FindBone(startingBoneName); + startingBone = ragdollRootBone; + RecursivelyCreateBoneProxies(ragdollRootBone); - List boneColliders = new List(); + rootRigidbody = boneTable[ragdollRootBone].GetComponent(); + rootRigidbody.isKinematic = pinStartBone; + rootRigidbody.mass = rootMass; - foreach (var pair in boneTable) { - var b = pair.Key; - var t = pair.Value; - Bone parentBone = null; - Transform parentTransform = transform; + List boneColliders = new List(); - boneColliders.Add(t.GetComponent()); + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + Bone parentBone = null; + Transform parentTransform = transform; - if (b != startingBone) { - parentBone = b.Parent; - parentTransform = boneTable[parentBone]; - } else { - ragdollRoot = new GameObject("RagdollRoot").transform; - ragdollRoot.parent = transform; + boneColliders.Add(t.GetComponent()); - if (b == skeleton.RootBone) { - ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0); - ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b)); - parentTransform = ragdollRoot; + if (b != startingBone) { + parentBone = b.Parent; + parentTransform = boneTable[parentBone]; } else { - ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0); - ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b.Parent)); - parentTransform = ragdollRoot; + ragdollRoot = new GameObject("RagdollRoot").transform; + ragdollRoot.parent = transform; + + if (b == skeleton.RootBone) { + ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b)); + parentTransform = ragdollRoot; + } else { + ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetCompensatedRotationIK(b.Parent)); + parentTransform = ragdollRoot; + } + + rootOffset = t.position - transform.position; } - rootOffset = t.position - transform.position; - } + var rbParent = parentTransform.GetComponent(); - var rbParent = parentTransform.GetComponent(); - - if (rbParent != null) { - var joint = t.gameObject.AddComponent(); - joint.connectedBody = rbParent; - Vector3 localPos = parentTransform.InverseTransformPoint(t.position); - localPos.x *= 1; - joint.connectedAnchor = localPos; - joint.GetComponent().mass = joint.connectedBody.mass * massFalloffFactor; - JointAngleLimits2D limits = new JointAngleLimits2D(); - limits.min = -rotationLimit; - limits.max = rotationLimit; - joint.limits = limits; - joint.useLimits = true; - } - } - - for (int x = 0; x < boneColliders.Count; x++) { - for (int y = 0; y < boneColliders.Count; y++) { - if (x == y) continue; - Physics2D.IgnoreCollision(boneColliders[x], boneColliders[y]); - } - } - - var utilityBones = GetComponentsInChildren(); - if (utilityBones.Length > 0) { - List destroyedUtilityBoneNames = new List(); - foreach (var ub in utilityBones) { - if (ub.mode == SkeletonUtilityBone.Mode.Override) { - destroyedUtilityBoneNames.Add(ub.gameObject.name); - Destroy(ub.gameObject); + if (rbParent != null) { + var joint = t.gameObject.AddComponent(); + joint.connectedBody = rbParent; + Vector3 localPos = parentTransform.InverseTransformPoint(t.position); + localPos.x *= 1; + joint.connectedAnchor = localPos; + joint.GetComponent().mass = joint.connectedBody.mass * massFalloffFactor; + JointAngleLimits2D limits = new JointAngleLimits2D(); + limits.min = -rotationLimit; + limits.max = rotationLimit; + joint.limits = limits; + joint.useLimits = true; } } - if (destroyedUtilityBoneNames.Count > 0) { - string msg = "Destroyed Utility Bones: "; - for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) { - msg += destroyedUtilityBoneNames[i]; - if (i != destroyedUtilityBoneNames.Count - 1) { - msg += ","; + for (int x = 0; x < boneColliders.Count; x++) { + for (int y = 0; y < boneColliders.Count; y++) { + if (x == y) continue; + Physics2D.IgnoreCollision(boneColliders[x], boneColliders[y]); + } + } + + var utilityBones = GetComponentsInChildren(); + if (utilityBones.Length > 0) { + List destroyedUtilityBoneNames = new List(); + foreach (var ub in utilityBones) { + if (ub.mode == SkeletonUtilityBone.Mode.Override) { + destroyedUtilityBoneNames.Add(ub.gameObject.name); + Destroy(ub.gameObject); } } - Debug.LogWarning(msg); + + if (destroyedUtilityBoneNames.Count > 0) { + string msg = "Destroyed Utility Bones: "; + for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) { + msg += destroyedUtilityBoneNames[i]; + if (i != destroyedUtilityBoneNames.Count - 1) { + msg += ","; + } + } + Debug.LogWarning(msg); + } + } + + if (disableIK) { + foreach (IkConstraint ik in skeleton.IkConstraints) { + ik.Mix = 0; + } + } + + skeletonAnim.UpdateWorld += UpdateWorld; + } + + void RecursivelyCreateBoneProxies (Bone b) { + if (stopBoneNames.Contains(b.Data.Name)) + return; + + GameObject go = new GameObject(b.Data.Name); + go.layer = colliderLayer; + Transform t = go.transform; + boneTable.Add(b, t); + + t.parent = transform; + + t.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + //TODO: deal with WorldFlipY + // MITCH + // t.localRotation = Quaternion.Euler(0, 0, b.WorldFlipX ? -b.WorldRotation : b.WorldRotation); + t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX); + t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 0); + + float length = b.Data.Length; + + //TODO proper ragdoll branching + var colliders = AttachBoundingBoxRagdollColliders(b); + + if (length == 0) { + //physics + if (colliders.Count == 0) { + var circle = go.AddComponent(); + circle.radius = thickness / 2f; + } + } else { + //physics + if (colliders.Count == 0) { + var box = go.AddComponent(); + box.size = new Vector2(length, thickness); + #if UNITY_5 + // MITCH + // box.offset = new Vector2((b.WorldFlipX ? -length : length) / 2, 0); + box.offset = new Vector2(length / 2, 0); + #else + //box.center = new Vector2((b.WorldFlipX ? -length : length) / 2, 0); + box.center = new Vector2(length/2, 0); + #endif + } + } + + var rb = go.AddComponent(); + rb.gravityScale = gravityScale; + + foreach (Bone child in b.Children) { + RecursivelyCreateBoneProxies(child); } } - if (disableIK) { - foreach (IkConstraint ik in skeleton.IkConstraints) { - ik.Mix = 0; - } - } + List AttachBoundingBoxRagdollColliders (Bone b) { + List colliders = new List(); + Transform t = boneTable[b]; + GameObject go = t.gameObject; + var skin = skeleton.Skin; + if (skin == null) + skin = skeleton.Data.DefaultSkin; - skeletonAnim.UpdateWorld += UpdateWorld; - } + // MITCH + // bool flipX = b.WorldFlipX; + // bool flipY = b.WorldFlipY; + bool flipX = false; + bool flipY = false; - void RecursivelyCreateBoneProxies (Bone b) { - if (stopBoneNames.Contains(b.Data.Name)) - return; + List attachments = new List(); + foreach (Slot s in skeleton.Slots) { + if (s.Bone == b) { + skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments); + foreach (var a in attachments) { + if (a is BoundingBoxAttachment) { + if (!a.Name.ToLower().Contains("ragdoll")) + continue; - GameObject go = new GameObject(b.Data.Name); - go.layer = colliderLayer; - Transform t = go.transform; - boneTable.Add(b, t); + var collider = SkeletonUtility.AddBoundingBoxAsComponent((BoundingBoxAttachment)a, go, false); - t.parent = transform; + if (flipX || flipY) { + Vector2[] points = collider.points; - t.localPosition = new Vector3(b.WorldX, b.WorldY, 0); - //TODO: deal with WorldFlipY - // MITCH - // t.localRotation = Quaternion.Euler(0, 0, b.WorldFlipX ? -b.WorldRotation : b.WorldRotation); - t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX); - t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 0); + for (int i = 0; i < points.Length; i++) { + if (flipX) + points[i].x *= -1; - float length = b.Data.Length; + if (flipY) + points[i].y *= -1; + } - //TODO proper ragdoll branching - var colliders = AttachBoundingBoxRagdollColliders(b); - - if (length == 0) { - //physics - if (colliders.Count == 0) { - var circle = go.AddComponent(); - circle.radius = thickness / 2f; - } - } else { - //physics - if (colliders.Count == 0) { - var box = go.AddComponent(); - box.size = new Vector2(length, thickness); -#if UNITY_5 - // MITCH - // box.offset = new Vector2((b.WorldFlipX ? -length : length) / 2, 0); - box.offset = new Vector2(length / 2, 0); -#else - //box.center = new Vector2((b.WorldFlipX ? -length : length) / 2, 0); - box.center = new Vector2(length/2, 0); -#endif - } - } - - var rb = go.AddComponent(); - rb.gravityScale = gravityScale; - - foreach (Bone child in b.Children) { - RecursivelyCreateBoneProxies(child); - } - } - - List AttachBoundingBoxRagdollColliders (Bone b) { - List colliders = new List(); - Transform t = boneTable[b]; - GameObject go = t.gameObject; - var skin = skeleton.Skin; - if (skin == null) - skin = skeleton.Data.DefaultSkin; - - // MITCH - // bool flipX = b.WorldFlipX; - // bool flipY = b.WorldFlipY; - bool flipX = false; - bool flipY = false; - - List attachments = new List(); - foreach (Slot s in skeleton.Slots) { - if (s.Bone == b) { - skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments); - foreach (var a in attachments) { - if (a is BoundingBoxAttachment) { - if (!a.Name.ToLower().Contains("ragdoll")) - continue; - - var collider = SkeletonUtility.AddBoundingBoxAsComponent((BoundingBoxAttachment)a, go, false); - - if (flipX || flipY) { - Vector2[] points = collider.points; - - for (int i = 0; i < points.Length; i++) { - if (flipX) - points[i].x *= -1; - - if (flipY) - points[i].y *= -1; + collider.points = points; } - collider.points = points; + colliders.Add(collider); } - - colliders.Add(collider); } } } + + return colliders; } - return colliders; - } + void UpdateWorld (ISkeletonAnimation skeletonRenderer) { + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + //bool flip = false; + bool flipX = false; //TODO: deal with negative scale instead of Flip Key + bool flipY = false; //TODO: deal with negative scale instead of Flip Key + Bone parentBone = null; + Transform parentTransform = transform; - void UpdateWorld (ISkeletonAnimation skeletonRenderer) { - foreach (var pair in boneTable) { - var b = pair.Key; - var t = pair.Value; - //bool flip = false; - bool flipX = false; //TODO: deal with negative scale instead of Flip Key - bool flipY = false; //TODO: deal with negative scale instead of Flip Key - Bone parentBone = null; - Transform parentTransform = transform; + if (b != startingBone) { + parentBone = b.Parent; + parentTransform = boneTable[parentBone]; + // MITCH + // flipX = parentBone.WorldFlipX; + // flipY = parentBone.WorldFlipY; - if (b != startingBone) { - parentBone = b.Parent; - parentTransform = boneTable[parentBone]; - // MITCH - // flipX = parentBone.WorldFlipX; - // flipY = parentBone.WorldFlipY; - - } else { - parentBone = b.Parent; - parentTransform = ragdollRoot; - if (b.Parent != null) { - // MITCH - // flipX = b.worldFlipX; - // flipY = b.WorldFlipY; } else { - flipX = b.Skeleton.FlipX; - flipY = b.Skeleton.FlipY; + parentBone = b.Parent; + parentTransform = ragdollRoot; + if (b.Parent != null) { + // MITCH + // flipX = b.worldFlipX; + // flipY = b.WorldFlipY; + } else { + flipX = b.Skeleton.FlipX; + flipY = b.Skeleton.FlipY; + } } - } - //flip = flipX ^ flipY; + //flip = flipX ^ flipY; - helper.position = parentTransform.position; - helper.rotation = parentTransform.rotation; - helper.localScale = new Vector3(flipX ? -parentTransform.localScale.x : parentTransform.localScale.x, flipY ? -parentTransform.localScale.y : parentTransform.localScale.y, 1); + helper.position = parentTransform.position; + helper.rotation = parentTransform.rotation; + helper.localScale = new Vector3(flipX ? -parentTransform.localScale.x : parentTransform.localScale.x, flipY ? -parentTransform.localScale.y : parentTransform.localScale.y, 1); - Vector3 pos = t.position; - pos = helper.InverseTransformPoint(pos); - b.X = Mathf.Lerp(b.X, pos.x, mix); - b.Y = Mathf.Lerp(b.Y, pos.y, mix); + Vector3 pos = t.position; + pos = helper.InverseTransformPoint(pos); + b.X = Mathf.Lerp(b.X, pos.x, mix); + b.Y = Mathf.Lerp(b.Y, pos.y, mix); - Vector3 right = helper.InverseTransformDirection(t.right); + Vector3 right = helper.InverseTransformDirection(t.right); - float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg; + float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg; - // MITCH - //if (b.WorldFlipX ^ b.WorldFlipY) { - // a *= -1; - //} - - if (parentBone != null) { - // MITCH - //if ((b.WorldFlipX ^ b.WorldFlipY) != flip) { - // a -= GetCompensatedRotationIK(parentBone) * 2; + // MITCH + //if (b.WorldFlipX ^ b.WorldFlipY) { + // a *= -1; //} + + if (parentBone != null) { + // MITCH + //if ((b.WorldFlipX ^ b.WorldFlipY) != flip) { + // a -= GetCompensatedRotationIK(parentBone) * 2; + //} + } + + b.Rotation = Mathf.Lerp(b.Rotation, a, mix); + // MITCH + // b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix); + } + } + + float GetCompensatedRotationIK (Bone b) { + Bone parent = b.Parent; + // MITCH + float a = b.AppliedRotation; + while (parent != null) { + a += parent.AppliedRotation; + parent = parent.parent; } - b.Rotation = Mathf.Lerp(b.Rotation, a, mix); - // MITCH - // b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix); - } - } - - float GetCompensatedRotationIK (Bone b) { - Bone parent = b.Parent; - // MITCH - float a = b.AppliedRotation; - while (parent != null) { - a += parent.AppliedRotation; - parent = parent.parent; + return a; } - return a; - } - - void OnDrawGizmosSelected () { - if (isActive) { - Gizmos.DrawWireSphere(transform.position, thickness * 1.2f); - Vector3 newTransformPos = rootRigidbody.position - rootOffset; - Gizmos.DrawLine(transform.position, newTransformPos); - Gizmos.DrawWireSphere(newTransformPos, thickness * 1.2f); + void OnDrawGizmosSelected () { + if (isActive) { + Gizmos.DrawWireSphere(transform.position, thickness * 1.2f); + Vector3 newTransformPos = rootRigidbody.position - rootOffset; + Gizmos.DrawLine(transform.position, newTransformPos); + Gizmos.DrawWireSphere(newTransformPos, thickness * 1.2f); + } } + } } diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs index 15db2ef77..e2b531a5c 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs @@ -1,9 +1,38 @@ -using UnityEngine; -using System.Collections; +/****************************************************************************** + * 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 UnityEditor; -using Spine.Unity; +using Spine.Unity.Editor; -namespace Spine.Unity.Editor { +namespace Spine.Unity.Modules { [CustomEditor(typeof(SkeletonPartsRenderer))] public class SkeletonRenderPartInspector : UnityEditor.Editor { SpineInspectorUtility.SerializedSortingProperties sortingProperties; @@ -13,7 +42,6 @@ namespace Spine.Unity.Editor { } public override void OnInspectorGUI () { - DrawDefaultInspector(); SpineInspectorUtility.SortingPropertyFields(sortingProperties, true); } } diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs index 95eb165f4..a8e75cf7f 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs @@ -1,9 +1,40 @@ -using UnityEngine; +/****************************************************************************** + * 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 UnityEditor; using Spine.Unity; +using Spine.Unity.Editor; -namespace Spine.Unity.Editor { +namespace Spine.Unity.Modules { [CustomEditor(typeof(SkeletonRenderSeparator))] public class SkeletonRenderSeparatorInspector : UnityEditor.Editor { diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs index 273121195..d1822fd5a 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs @@ -1,8 +1,37 @@ -using UnityEngine; -using System.Collections; +/****************************************************************************** + * 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 Spine.Unity.MeshGeneration; -namespace Spine.Unity { +namespace Spine.Unity.Modules { [RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))] public class SkeletonPartsRenderer : MonoBehaviour { diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs index 29bee2a67..6852f7a77 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs @@ -1,9 +1,38 @@ -using UnityEngine; -using System.Collections; +/****************************************************************************** + * 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.Generic; using Spine.Unity; -namespace Spine.Unity { +namespace Spine.Unity.Modules { [ExecuteInEditMode] [HelpURL("https://github.com/pharan/spine-unity-docs/blob/master/SkeletonRenderSeparator.md")] @@ -28,7 +57,7 @@ namespace Spine.Unity { public bool copyPropertyBlock = false; [Tooltip("Copies MeshRenderer flags into ")] public bool copyMeshRendererFlags = false; - public List partsRenderers = new List(); + public List partsRenderers = new List(); #if UNITY_EDITOR void Reset () { diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs index 7b0e81430..f7c2714c5 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs @@ -32,56 +32,57 @@ using UnityEngine; using System.Collections; -public class SkeletonUtilityEyeConstraint : SkeletonUtilityConstraint { +namespace Spine.Unity.Modules { + public class SkeletonUtilityEyeConstraint : SkeletonUtilityConstraint { + public Transform[] eyes; + public float radius = 0.5f; + public Transform target; + public Vector3 targetPosition; + public float speed = 10; + Vector3[] origins; + Vector3 centerPoint; - public Transform[] eyes; - public float radius = 0.5f; - public Transform target; - public Vector3 targetPosition; - public float speed = 10; - Vector3[] origins; - Vector3 centerPoint; + protected override void OnEnable () { + if (!Application.isPlaying) + return; - protected override void OnEnable () { - if (!Application.isPlaying) - return; + base.OnEnable(); - base.OnEnable(); + Bounds centerBounds = new Bounds(eyes[0].localPosition, Vector3.zero); + origins = new Vector3[eyes.Length]; + for (int i = 0; i < eyes.Length; i++) { + origins[i] = eyes[i].localPosition; + centerBounds.Encapsulate(origins[i]); + } - Bounds centerBounds = new Bounds(eyes[0].localPosition, Vector3.zero); - origins = new Vector3[eyes.Length]; - for (int i = 0; i < eyes.Length; i++) { - origins[i] = eyes[i].localPosition; - centerBounds.Encapsulate(origins[i]); + centerPoint = centerBounds.center; } - centerPoint = centerBounds.center; - } + protected override void OnDisable () { + if (!Application.isPlaying) + return; - protected override void OnDisable () { - if (!Application.isPlaying) - return; - - base.OnDisable(); - } - - public override void DoUpdate () { - - if (target != null) - targetPosition = target.position; - - Vector3 goal = targetPosition; - - Vector3 center = transform.TransformPoint(centerPoint); - Vector3 dir = goal - center; - - if (dir.magnitude > 1) - dir.Normalize(); - - for (int i = 0; i < eyes.Length; i++) { - center = transform.TransformPoint(origins[i]); - eyes[i].position = Vector3.MoveTowards(eyes[i].position, center + (dir * radius), speed * Time.deltaTime); + base.OnDisable(); } + public override void DoUpdate () { + + if (target != null) + targetPosition = target.position; + + Vector3 goal = targetPosition; + + Vector3 center = transform.TransformPoint(centerPoint); + Vector3 dir = goal - center; + + if (dir.magnitude > 1) + dir.Normalize(); + + for (int i = 0; i < eyes.Length; i++) { + center = transform.TransformPoint(origins[i]); + eyes[i].position = Vector3.MoveTowards(eyes[i].position, center + (dir * radius), speed * Time.deltaTime); + } + + } } } diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs index 2b8837eac..a60182e3f 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs @@ -32,121 +32,124 @@ using UnityEngine; using System.Collections; -[RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode] -public class SkeletonUtilityGroundConstraint : SkeletonUtilityConstraint { +namespace Spine.Unity.Modules { + [RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode] + public class SkeletonUtilityGroundConstraint : SkeletonUtilityConstraint { -#if UNITY_4_3 - public LayerMask groundMask; - public bool use2D = false; - public bool useRadius = false; - public float castRadius = 0.1f; - public float castDistance = 5f; - public float castOffset = 0; - public float groundOffset = 0; - public float adjustSpeed = 5; -#else - [Tooltip("LayerMask for what objects to raycast against")] - public LayerMask groundMask; - [Tooltip("The 2D")] - public bool use2D = false; - [Tooltip("Uses SphereCast for 3D mode and CircleCast for 2D mode")] - public bool useRadius = false; - [Tooltip("The Radius")] - public float castRadius = 0.1f; - [Tooltip("How high above the target bone to begin casting from")] - public float castDistance = 5f; - [Tooltip("X-Axis adjustment")] - public float castOffset = 0; - [Tooltip("Y-Axis adjustment")] - public float groundOffset = 0; - [Tooltip("How fast the target IK position adjusts to the ground. Use smaller values to prevent snapping")] - public float adjustSpeed = 5; -#endif + #if UNITY_4_3 + public LayerMask groundMask; + public bool use2D = false; + public bool useRadius = false; + public float castRadius = 0.1f; + public float castDistance = 5f; + public float castOffset = 0; + public float groundOffset = 0; + public float adjustSpeed = 5; + #else + [Tooltip("LayerMask for what objects to raycast against")] + public LayerMask groundMask; + [Tooltip("The 2D")] + public bool use2D = false; + [Tooltip("Uses SphereCast for 3D mode and CircleCast for 2D mode")] + public bool useRadius = false; + [Tooltip("The Radius")] + public float castRadius = 0.1f; + [Tooltip("How high above the target bone to begin casting from")] + public float castDistance = 5f; + [Tooltip("X-Axis adjustment")] + public float castOffset = 0; + [Tooltip("Y-Axis adjustment")] + public float groundOffset = 0; + [Tooltip("How fast the target IK position adjusts to the ground. Use smaller values to prevent snapping")] + public float adjustSpeed = 5; + #endif - Vector3 rayOrigin; - Vector3 rayDir = new Vector3(0, -1, 0); - float hitY; - float lastHitY; + Vector3 rayOrigin; + Vector3 rayDir = new Vector3(0, -1, 0); + float hitY; + float lastHitY; - protected override void OnEnable () { - base.OnEnable(); - lastHitY = transform.position.y; - } - - protected override void OnDisable () { - base.OnDisable(); - } - - public override void DoUpdate () { - rayOrigin = transform.position + new Vector3(castOffset, castDistance, 0); - - hitY = float.MinValue; - if (use2D) { - RaycastHit2D hit; - - if (useRadius) { -#if UNITY_4_3 - //NOTE: Unity 4.3.x does not have CircleCast - hit = Physics2D.Raycast(rayOrigin , rayDir, castDistance + groundOffset, groundMask); -#else - hit = Physics2D.CircleCast(rayOrigin, castRadius, rayDir, castDistance + groundOffset, groundMask); -#endif - } else { - hit = Physics2D.Raycast(rayOrigin, rayDir, castDistance + groundOffset, groundMask); - } - - if (hit.collider != null) { - hitY = hit.point.y + groundOffset; - if (Application.isPlaying) { - hitY = Mathf.MoveTowards(lastHitY, hitY, adjustSpeed * Time.deltaTime); - } - } else { - if (Application.isPlaying) - hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime); - } - } else { - RaycastHit hit; - bool validHit = false; - - if (useRadius) { - validHit = Physics.SphereCast(rayOrigin, castRadius, rayDir, out hit, castDistance + groundOffset, groundMask); - } else { - validHit = Physics.Raycast(rayOrigin, rayDir, out hit, castDistance + groundOffset, groundMask); - } - - if (validHit) { - hitY = hit.point.y + groundOffset; - if (Application.isPlaying) { - hitY = Mathf.MoveTowards(lastHitY, hitY, adjustSpeed * Time.deltaTime); - } - } else { - if (Application.isPlaying) - hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime); - } + protected override void OnEnable () { + base.OnEnable(); + lastHitY = transform.position.y; } - Vector3 v = transform.position; - v.y = Mathf.Clamp(v.y, Mathf.Min(lastHitY, hitY), float.MaxValue); - transform.position = v; - - utilBone.bone.X = transform.localPosition.x; - utilBone.bone.Y = transform.localPosition.y; - - lastHitY = hitY; - } - - void OnDrawGizmos () { - Vector3 hitEnd = rayOrigin + (rayDir * Mathf.Min(castDistance, rayOrigin.y - hitY)); - Vector3 clearEnd = rayOrigin + (rayDir * castDistance); - Gizmos.DrawLine(rayOrigin, hitEnd); - - if (useRadius) { - Gizmos.DrawLine(new Vector3(hitEnd.x - castRadius, hitEnd.y - groundOffset, hitEnd.z), new Vector3(hitEnd.x + castRadius, hitEnd.y - groundOffset, hitEnd.z)); - Gizmos.DrawLine(new Vector3(clearEnd.x - castRadius, clearEnd.y, clearEnd.z), new Vector3(clearEnd.x + castRadius, clearEnd.y, clearEnd.z)); + protected override void OnDisable () { + base.OnDisable(); } - Gizmos.color = Color.red; - Gizmos.DrawLine(hitEnd, clearEnd); + public override void DoUpdate () { + rayOrigin = transform.position + new Vector3(castOffset, castDistance, 0); + + hitY = float.MinValue; + if (use2D) { + RaycastHit2D hit; + + if (useRadius) { + #if UNITY_4_3 + //NOTE: Unity 4.3.x does not have CircleCast + hit = Physics2D.Raycast(rayOrigin , rayDir, castDistance + groundOffset, groundMask); + #else + hit = Physics2D.CircleCast(rayOrigin, castRadius, rayDir, castDistance + groundOffset, groundMask); + #endif + } else { + hit = Physics2D.Raycast(rayOrigin, rayDir, castDistance + groundOffset, groundMask); + } + + if (hit.collider != null) { + hitY = hit.point.y + groundOffset; + if (Application.isPlaying) { + hitY = Mathf.MoveTowards(lastHitY, hitY, adjustSpeed * Time.deltaTime); + } + } else { + if (Application.isPlaying) + hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime); + } + } else { + RaycastHit hit; + bool validHit = false; + + if (useRadius) { + validHit = Physics.SphereCast(rayOrigin, castRadius, rayDir, out hit, castDistance + groundOffset, groundMask); + } else { + validHit = Physics.Raycast(rayOrigin, rayDir, out hit, castDistance + groundOffset, groundMask); + } + + if (validHit) { + hitY = hit.point.y + groundOffset; + if (Application.isPlaying) { + hitY = Mathf.MoveTowards(lastHitY, hitY, adjustSpeed * Time.deltaTime); + } + } else { + if (Application.isPlaying) + hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime); + } + } + + Vector3 v = transform.position; + v.y = Mathf.Clamp(v.y, Mathf.Min(lastHitY, hitY), float.MaxValue); + transform.position = v; + + utilBone.bone.X = transform.localPosition.x; + utilBone.bone.Y = transform.localPosition.y; + + lastHitY = hitY; + } + + void OnDrawGizmos () { + Vector3 hitEnd = rayOrigin + (rayDir * Mathf.Min(castDistance, rayOrigin.y - hitY)); + Vector3 clearEnd = rayOrigin + (rayDir * castDistance); + Gizmos.DrawLine(rayOrigin, hitEnd); + + if (useRadius) { + Gizmos.DrawLine(new Vector3(hitEnd.x - castRadius, hitEnd.y - groundOffset, hitEnd.z), new Vector3(hitEnd.x + castRadius, hitEnd.y - groundOffset, hitEnd.z)); + Gizmos.DrawLine(new Vector3(clearEnd.x - castRadius, clearEnd.y, clearEnd.z), new Vector3(clearEnd.x + castRadius, clearEnd.y, clearEnd.z)); + } + + Gizmos.color = Color.red; + Gizmos.DrawLine(hitEnd, clearEnd); + } } + } diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs index 8f88297be..f1398b58e 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs @@ -30,85 +30,86 @@ *****************************************************************************/ using UnityEngine; -using System.Collections; using System.Collections.Generic; -public class SkeletonUtilityKinematicShadow : MonoBehaviour { - public bool hideShadow = true; - public Transform parent; - Dictionary shadowTable; - GameObject shadowRoot; +namespace Spine.Unity.Modules { + public class SkeletonUtilityKinematicShadow : MonoBehaviour { + public bool hideShadow = true; + public Transform parent; + Dictionary shadowTable; + GameObject shadowRoot; - void Start () { - shadowRoot = (GameObject)Instantiate(gameObject); - if (hideShadow) - shadowRoot.hideFlags = HideFlags.HideInHierarchy; + void Start () { + shadowRoot = (GameObject)Instantiate(gameObject); + if (hideShadow) + shadowRoot.hideFlags = HideFlags.HideInHierarchy; - if (parent == null) - shadowRoot.transform.parent = transform.root; - else - shadowRoot.transform.parent = parent; + if (parent == null) + shadowRoot.transform.parent = transform.root; + else + shadowRoot.transform.parent = parent; - shadowTable = new Dictionary(); + shadowTable = new Dictionary(); - Destroy(shadowRoot.GetComponent()); + Destroy(shadowRoot.GetComponent()); - shadowRoot.transform.position = transform.position; - shadowRoot.transform.rotation = transform.rotation; + shadowRoot.transform.position = transform.position; + shadowRoot.transform.rotation = transform.rotation; - Vector3 scaleRef = transform.TransformPoint(Vector3.right); - float scale = Vector3.Distance(transform.position, scaleRef); - shadowRoot.transform.localScale = Vector3.one; + Vector3 scaleRef = transform.TransformPoint(Vector3.right); + float scale = Vector3.Distance(transform.position, scaleRef); + shadowRoot.transform.localScale = Vector3.one; - var shadowJoints = shadowRoot.GetComponentsInChildren(); - foreach (Joint j in shadowJoints) { - j.connectedAnchor *= scale; - } + var shadowJoints = shadowRoot.GetComponentsInChildren(); + foreach (Joint j in shadowJoints) { + j.connectedAnchor *= scale; + } - var joints = GetComponentsInChildren(); - foreach (var j in joints) - Destroy(j); + var joints = GetComponentsInChildren(); + foreach (var j in joints) + Destroy(j); - var rbs = GetComponentsInChildren(); - foreach (var rb in rbs) - Destroy(rb); + var rbs = GetComponentsInChildren(); + foreach (var rb in rbs) + Destroy(rb); - var colliders = GetComponentsInChildren(); - foreach (var c in colliders) - Destroy(c); + var colliders = GetComponentsInChildren(); + foreach (var c in colliders) + Destroy(c); - //match by bone name - var shadowBones = shadowRoot.GetComponentsInChildren(); - var bones = GetComponentsInChildren(); + //match by bone name + var shadowBones = shadowRoot.GetComponentsInChildren(); + var bones = GetComponentsInChildren(); - //build bone lookup - foreach (var b in bones) { - if (b.gameObject == gameObject) - continue; - - foreach (var sb in shadowBones) { - if (sb.GetComponent() == null) + //build bone lookup + foreach (var b in bones) { + if (b.gameObject == gameObject) continue; - if (sb.boneName == b.boneName) { - shadowTable.Add(sb.transform, b.transform); - break; + foreach (var sb in shadowBones) { + if (sb.GetComponent() == null) + continue; + + if (sb.boneName == b.boneName) { + shadowTable.Add(sb.transform, b.transform); + break; + } } } + + foreach (var b in shadowBones) + Destroy(b); } - foreach (var b in shadowBones) - Destroy(b); - } + void FixedUpdate () { + shadowRoot.GetComponent().MovePosition(transform.position); + shadowRoot.GetComponent().MoveRotation(transform.rotation); - void FixedUpdate () { - shadowRoot.GetComponent().MovePosition(transform.position); - shadowRoot.GetComponent().MoveRotation(transform.rotation); - - foreach (var pair in shadowTable) { - pair.Value.localPosition = pair.Key.localPosition; - pair.Value.localRotation = pair.Key.localRotation; + foreach (var pair in shadowTable) { + pair.Value.localPosition = pair.Key.localPosition; + pair.Value.localRotation = pair.Key.localRotation; + } } } } diff --git a/spine-unity/Assets/spine-unity/Modules/SpriteAttacher.cs b/spine-unity/Assets/spine-unity/Modules/SpriteAttacher.cs index 8a6faed44..5f44696ca 100644 --- a/spine-unity/Assets/spine-unity/Modules/SpriteAttacher.cs +++ b/spine-unity/Assets/spine-unity/Modules/SpriteAttacher.cs @@ -30,170 +30,198 @@ *****************************************************************************/ using UnityEngine; -using System.Collections; using System.Collections.Generic; using Spine; -public class SpriteAttacher : MonoBehaviour { +namespace Spine.Unity.Modules { + public class SpriteAttacher : MonoBehaviour { + public bool attachOnStart = true; + public bool keepLoaderInMemory = true; + public Sprite sprite; - public bool attachOnStart = true; - public bool keepLoaderInMemory = true; + [SpineSlot] + public string slot; - public Sprite sprite; + private SpriteAttachmentLoader loader; + private RegionAttachment attachment; - [SpineSlot] - public string slot; - - private SpriteAttachmentLoader loader; - private RegionAttachment attachment; - - void Start () { - if (attachOnStart) - Attach(); - } - - public void Attach () { - var skeletonRenderer = GetComponent(); - - if (loader == null) - //create loader instance, tell it what sprite and shader to use - loader = new SpriteAttachmentLoader(sprite, Shader.Find("Spine/Skeleton")); - - if (attachment == null) - attachment = loader.NewRegionAttachment(null, sprite.name, ""); - - skeletonRenderer.skeleton.FindSlot(slot).Attachment = attachment; - - if (!keepLoaderInMemory) - loader = null; - } -} - -public class SpriteAttachmentLoader : AttachmentLoader { - - //TODO: Memory cleanup functions - - //IMPORTANT: Make sure you clear this when you don't need it anymore. Goodluck. - public static Dictionary atlasTable = new Dictionary(); - - //Shouldn't need to clear this, should just prevent redoing premultiply alpha pass on packed atlases - public static List premultipliedAtlasIds = new List(); - - Sprite sprite; - Shader shader; - - public SpriteAttachmentLoader (Sprite sprite, Shader shader) { - - if (sprite.packed && sprite.packingMode == SpritePackingMode.Tight) { - Debug.LogError("Tight Packer Policy not supported yet!"); - return; + void Start () { + if (attachOnStart) + Attach(); } - this.sprite = sprite; - this.shader = shader; + public void Attach () { + var skeletonRenderer = GetComponent(); - Texture2D tex = sprite.texture; - //premultiply texture if it hasn't been yet - int instanceId = tex.GetInstanceID(); - if (!premultipliedAtlasIds.Contains(instanceId)) { - try { - var colors = tex.GetPixels(); - Color c; - float a; - for (int i = 0; i < colors.Length; i++) { - c = colors[i]; - a = c.a; - c.r *= a; - c.g *= a; - c.b *= a; - colors[i] = c; + if (loader == null) + //create loader instance, tell it what sprite and shader to use + loader = new SpriteAttachmentLoader(sprite, Shader.Find("Spine/Skeleton")); + + if (attachment == null) + attachment = loader.NewRegionAttachment(null, sprite.name, ""); + + skeletonRenderer.skeleton.FindSlot(slot).Attachment = attachment; + + if (!keepLoaderInMemory) + loader = null; + } + } + + public class SpriteAttachmentLoader : AttachmentLoader { + + //TODO: Memory cleanup functions + + //IMPORTANT: Make sure you clear this when you don't need it anymore. Goodluck. + public static Dictionary atlasTable = new Dictionary(); + + //Shouldn't need to clear this, should just prevent redoing premultiply alpha pass on packed atlases + public static List premultipliedAtlasIds = new List(); + + Sprite sprite; + Shader shader; + + public SpriteAttachmentLoader (Sprite sprite, Shader shader) { + + if (sprite.packed && sprite.packingMode == SpritePackingMode.Tight) { + Debug.LogError("Tight Packer Policy not supported yet!"); + return; + } + + this.sprite = sprite; + this.shader = shader; + + Texture2D tex = sprite.texture; + //premultiply texture if it hasn't been yet + int instanceId = tex.GetInstanceID(); + if (!premultipliedAtlasIds.Contains(instanceId)) { + try { + var colors = tex.GetPixels(); + Color c; + float a; + for (int i = 0; i < colors.Length; i++) { + c = colors[i]; + a = c.a; + c.r *= a; + c.g *= a; + c.b *= a; + colors[i] = c; + } + + tex.SetPixels(colors); + tex.Apply(); + + premultipliedAtlasIds.Add(instanceId); + } catch { + //texture is not readable! Can't pre-multiply it, you're on your own. } - - tex.SetPixels(colors); - tex.Apply(); - - premultipliedAtlasIds.Add(instanceId); - } catch { - //texture is not readable! Can't pre-multiply it, you're on your own. } } - } - public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) { - RegionAttachment attachment = new RegionAttachment(name); + public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) { + RegionAttachment attachment = new RegionAttachment(name); - Texture2D tex = sprite.texture; - int instanceId = tex.GetInstanceID(); - AtlasRegion atlasRegion; + Texture2D tex = sprite.texture; + int instanceId = tex.GetInstanceID(); + AtlasRegion atlasRegion; - //check cache first - if (atlasTable.ContainsKey(instanceId)) { - atlasRegion = atlasTable[instanceId]; - } else { - //Setup new material - Material mat = new Material(shader); + //check cache first + if (atlasTable.ContainsKey(instanceId)) { + atlasRegion = atlasTable[instanceId]; + } else { + //Setup new material + Material mat = new Material(shader); + if (sprite.packed) + mat.name = "Unity Packed Sprite Material"; + else + mat.name = sprite.name + " Sprite Material"; + mat.mainTexture = tex; + + //create faux-region to play nice with SkeletonRenderer + atlasRegion = new AtlasRegion(); + AtlasPage page = new AtlasPage(); + page.rendererObject = mat; + atlasRegion.page = page; + + //cache it + atlasTable[instanceId] = atlasRegion; + } + + Rect texRect = sprite.textureRect; + + //normalize rect to UV space of packed atlas + texRect.x = Mathf.InverseLerp(0, tex.width, texRect.x); + texRect.y = Mathf.InverseLerp(0, tex.height, texRect.y); + texRect.width = Mathf.InverseLerp(0, tex.width, texRect.width); + texRect.height = Mathf.InverseLerp(0, tex.height, texRect.height); + + Bounds bounds = sprite.bounds; + Vector3 size = bounds.size; + + //TODO: make sure this rotation thing actually works + bool rotated = false; if (sprite.packed) - mat.name = "Unity Packed Sprite Material"; - else - mat.name = sprite.name + " Sprite Material"; - mat.mainTexture = tex; + rotated = sprite.packingRotation == SpritePackingRotation.Any; - //create faux-region to play nice with SkeletonRenderer - atlasRegion = new AtlasRegion(); - AtlasPage page = new AtlasPage(); - page.rendererObject = mat; - atlasRegion.page = page; + //do some math and assign UVs and sizes + attachment.SetUVs(texRect.xMin, texRect.yMax, texRect.xMax, texRect.yMin, rotated); + attachment.RendererObject = atlasRegion; + attachment.SetColor(Color.white); + attachment.ScaleX = 1; + attachment.ScaleY = 1; + attachment.RegionOffsetX = sprite.rect.width * (0.5f - Mathf.InverseLerp(bounds.min.x, bounds.max.x, 0)) / sprite.pixelsPerUnit; + attachment.RegionOffsetY = sprite.rect.height * (0.5f - Mathf.InverseLerp(bounds.min.y, bounds.max.y, 0)) / sprite.pixelsPerUnit; + attachment.Width = size.x; + attachment.Height = size.y; + attachment.RegionWidth = size.x; + attachment.RegionHeight = size.y; + attachment.RegionOriginalWidth = size.x; + attachment.RegionOriginalHeight = size.y; + attachment.UpdateOffset(); - //cache it - atlasTable[instanceId] = atlasRegion; + return attachment; } - Rect texRect = sprite.textureRect; + public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) { + //TODO: Unity 5 only + throw new System.NotImplementedException(); + } - //normalize rect to UV space of packed atlas - texRect.x = Mathf.InverseLerp(0, tex.width, texRect.x); - texRect.y = Mathf.InverseLerp(0, tex.height, texRect.y); - texRect.width = Mathf.InverseLerp(0, tex.width, texRect.width); - texRect.height = Mathf.InverseLerp(0, tex.height, texRect.height); + public WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, string name, string path) { + throw new System.NotImplementedException(); + } - Bounds bounds = sprite.bounds; - Vector3 size = bounds.size; - - //TODO: make sure this rotation thing actually works - bool rotated = false; - if (sprite.packed) - rotated = sprite.packingRotation == SpritePackingRotation.Any; - - //do some math and assign UVs and sizes - attachment.SetUVs(texRect.xMin, texRect.yMax, texRect.xMax, texRect.yMin, rotated); - attachment.RendererObject = atlasRegion; - attachment.SetColor(Color.white); - attachment.ScaleX = 1; - attachment.ScaleY = 1; - attachment.RegionOffsetX = sprite.rect.width * (0.5f - Mathf.InverseLerp(bounds.min.x, bounds.max.x, 0)) / sprite.pixelsPerUnit; - attachment.RegionOffsetY = sprite.rect.height * (0.5f - Mathf.InverseLerp(bounds.min.y, bounds.max.y, 0)) / sprite.pixelsPerUnit; - attachment.Width = size.x; - attachment.Height = size.y; - attachment.RegionWidth = size.x; - attachment.RegionHeight = size.y; - attachment.RegionOriginalWidth = size.x; - attachment.RegionOriginalHeight = size.y; - attachment.UpdateOffset(); - - return attachment; + public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { + throw new System.NotImplementedException(); + } } - public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) { - //TODO: Unity 5 only - throw new System.NotImplementedException(); - } + public static class SpriteAttachmentExtensions { + public static Attachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, string shaderName = "Spine/Skeleton") { + var att = sprite.ToRegionAttachment(shaderName); + skeleton.FindSlot(slotName).Attachment = att; + return att; + } - public WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, string name, string path) { - throw new System.NotImplementedException(); - } + public static Attachment AddUnitySprite (this SkeletonData skeletonData, string slotName, Sprite sprite, string skinName = "", string shaderName = "Spine/Skeleton") { + var att = sprite.ToRegionAttachment(shaderName); - public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { - throw new System.NotImplementedException(); + var slotIndex = skeletonData.FindSlotIndex(slotName); + Skin skin = skeletonData.defaultSkin; + if (skinName != "") + skin = skeletonData.FindSkin(skinName); + + skin.AddAttachment(slotIndex, att.Name, att); + + return att; + } + + public static RegionAttachment ToRegionAttachment (this Sprite sprite, string shaderName = "Spine/Skeleton") { + var loader = new SpriteAttachmentLoader(sprite, Shader.Find(shaderName)); + var att = loader.NewRegionAttachment(null, sprite.name, ""); + loader = null; + return att; + } } } + diff --git a/spine-unity/Assets/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs b/spine-unity/Assets/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs index b9c10613a..3c6097de6 100644 --- a/spine-unity/Assets/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs +++ b/spine-unity/Assets/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs @@ -35,131 +35,132 @@ using UnityEngine; using Spine; // TODO: handle TPackerCW flip mode (probably not swap uv horizontaly) +namespace Spine.Unity.TK2D { + 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 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); + public SpriteCollectionAttachmentLoader (tk2dSpriteCollectionData sprites) { + if (sprites == null) + throw new ArgumentNullException("sprites cannot be null."); + this.sprites = sprites; } - 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); + 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; } - regionRotated = def.flipped == tk2dSpriteDefinition.FlipMode.Tk2d; - if (regionRotated) { - float temp = minTexCoords.x; - minTexCoords.x = maxTexCoords.x; - maxTexCoords.x = temp; + + 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; } - 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); + public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) { + ProcessSpriteDefinition(path); - material = def.materialInst; - } + 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 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 WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path) { - ProcessSpriteDefinition(path); - - WeightedMeshAttachment mesh = new WeightedMeshAttachment(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 WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path) { + ProcessSpriteDefinition(path); - public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) { - return new BoundingBoxAttachment(name); + WeightedMeshAttachment mesh = new WeightedMeshAttachment(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/SkeletonAnimation.cs b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs index 05cee27b1..5608ec762 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimation.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimation.cs @@ -30,163 +30,165 @@ *****************************************************************************/ using System; -using System.Collections.Generic; using UnityEngine; -using Spine; -[ExecuteInEditMode] -[AddComponentMenu("Spine/SkeletonAnimation")] -[HelpURL("http://esotericsoftware.com/spine-unity-documentation#Controlling-Animation")] -public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { +namespace Spine.Unity { + + [ExecuteInEditMode] + [AddComponentMenu("Spine/SkeletonAnimation")] + [HelpURL("http://esotericsoftware.com/spine-unity-documentation#Controlling-Animation")] + public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation { - /// - /// 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; + /// + /// 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; } - } - - public event UpdateBonesDelegate UpdateWorld { - add { _UpdateWorld += value; } - remove { _UpdateWorld -= value; } - } - - public event UpdateBonesDelegate UpdateComplete { - add { _UpdateComplete += value; } - remove { _UpdateComplete -= value; } - } - - protected event UpdateBonesDelegate _UpdateLocal; - protected event UpdateBonesDelegate _UpdateWorld; - protected event UpdateBonesDelegate _UpdateComplete; - - /// Gets the skeleton. - public Skeleton Skeleton { - get { - this.Initialize(false); - return this.skeleton; + public event UpdateBonesDelegate UpdateLocal { + add { _UpdateLocal += value; } + remove { _UpdateLocal -= value; } } - } - [SerializeField] - [SpineAnimation] - private String _animationName; + public event UpdateBonesDelegate UpdateWorld { + add { _UpdateWorld += value; } + remove { _UpdateWorld -= value; } + } - 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; + public event UpdateBonesDelegate UpdateComplete { + add { _UpdateComplete += value; } + remove { _UpdateComplete -= value; } + } + + protected event UpdateBonesDelegate _UpdateLocal; + protected event UpdateBonesDelegate _UpdateWorld; + protected event UpdateBonesDelegate _UpdateComplete; + + /// Gets the skeleton. + public Skeleton Skeleton { + get { + this.Initialize(false); + return this.skeleton; } - - TrackEntry entry = state.GetCurrent(0); - return entry == null ? null : entry.Animation.Name; } - set { - if (_animationName == value) + + [SerializeField] + [SpineAnimation] + private String _animationName; + + public String AnimationName { + get { + if (!valid) { + Debug.LogWarning("You tried access AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors."); + return null; + } + + TrackEntry entry = state.GetCurrent(0); + return entry == null ? null : entry.Animation.Name; + } + set { + 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 + state.SetAnimation(0, value, loop); + } + } + + /// 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; + + #region Runtime Instantiation + /// Adds and prepares a SkeletonAnimation component to a GameObject at runtime. + /// The newly instantiated SkeletonAnimation + public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) { + return SkeletonRenderer.AddSpineComponent(gameObject, skeletonDataAsset); + } + + /// Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it. + /// The newly instantiated SkeletonAnimation component. + public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) { + return SkeletonRenderer.NewSpineGameObject(skeletonDataAsset); + } + #endregion + + public override void Initialize (bool overwrite) { + if (valid && !overwrite) 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."); + + base.Initialize(overwrite); + + if (!valid) return; + + state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData()); + + #if UNITY_EDITOR + if (!string.IsNullOrEmpty(_animationName)) { + if (Application.isPlaying) { + state.SetAnimation(0, _animationName, loop); + } else { + // Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above. + var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName); + if (animationObject != null) + animationObject.Apply(skeleton, 0f, 0f, false, null); + } + Update(0); } - - if (value == null || value.Length == 0) - state.ClearTrack(0); - else - state.SetAnimation(0, value, loop); - } - } - - /// 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; - - #region Runtime Instantiation - /// Adds and prepares a SkeletonAnimation component to a GameObject at runtime. - /// The newly instantiated SkeletonAnimation - public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) { - return SkeletonRenderer.AddSpineComponent(gameObject, skeletonDataAsset); - } - - /// Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it. - /// The newly instantiated SkeletonAnimation component. - public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) { - return SkeletonRenderer.NewSpineGameObject(skeletonDataAsset); - } - #endregion - - public override void Initialize (bool overwrite) { - if (valid && !overwrite) - return; - - base.Initialize(overwrite); - - if (!valid) - return; - - state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData()); - - #if UNITY_EDITOR - if (!string.IsNullOrEmpty(_animationName)) { - if (Application.isPlaying) { - state.SetAnimation(0, _animationName, loop); - } else { - // Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above. - var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName); - if (animationObject != null) - animationObject.Apply(skeleton, 0f, 0f, false, null); - } - Update(0); - } - #else - if (!string.IsNullOrEmpty(_animationName)) { + #else + if (!string.IsNullOrEmpty(_animationName)) { state.SetAnimation(0, _animationName, loop); Update(0); + } + #endif } - #endif - } - public virtual void Update () { - Update(Time.deltaTime); - } + public virtual void Update () { + Update(Time.deltaTime); + } - public virtual void Update (float deltaTime) { - if (!valid) - return; + public virtual void Update (float deltaTime) { + if (!valid) + return; - deltaTime *= timeScale; - skeleton.Update(deltaTime); - state.Update(deltaTime); - state.Apply(skeleton); + deltaTime *= timeScale; + skeleton.Update(deltaTime); + state.Update(deltaTime); + state.Apply(skeleton); - if (_UpdateLocal != null) - _UpdateLocal(this); + if (_UpdateLocal != null) + _UpdateLocal(this); - skeleton.UpdateWorldTransform(); - - if (_UpdateWorld != null) { - _UpdateWorld(this); skeleton.UpdateWorldTransform(); + + if (_UpdateWorld != null) { + _UpdateWorld(this); + skeleton.UpdateWorldTransform(); + } + + if (_UpdateComplete != null) { + _UpdateComplete(this); + } } - if (_UpdateComplete != null) { - _UpdateComplete(this); - } } - + } diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs index 9cab006f6..5a8600907 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs @@ -5,161 +5,173 @@ * Full irrevocable rights and permissions granted to Esoteric Software *****************************************************************************/ using UnityEngine; -using System.Collections; using System.Collections.Generic; -using Spine; -[RequireComponent(typeof(Animator))] -public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { +namespace Spine.Unity { + [RequireComponent(typeof(Animator))] + public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { - public enum MixMode { AlwaysMix, MixNext, SpineStyle } - public MixMode[] layerMixModes = new MixMode[0]; + public enum MixMode { AlwaysMix, MixNext, SpineStyle } + public MixMode[] layerMixModes = new MixMode[0]; - public event UpdateBonesDelegate UpdateLocal { - add { _UpdateLocal += value; } - remove { _UpdateLocal -= value; } - } - - public event UpdateBonesDelegate UpdateWorld { - add { _UpdateWorld += value; } - remove { _UpdateWorld -= value; } - } - - public event UpdateBonesDelegate UpdateComplete { - add { _UpdateComplete += value; } - remove { _UpdateComplete -= value; } - } - - protected event UpdateBonesDelegate _UpdateLocal; - protected event UpdateBonesDelegate _UpdateWorld; - protected event UpdateBonesDelegate _UpdateComplete; - - public Skeleton Skeleton { - get { - return this.skeleton; - } - } - - readonly Dictionary animationTable = new Dictionary(); - readonly Dictionary clipNameHashCodeTable = new Dictionary(); - Animator animator; - float lastTime; - - public override void Initialize (bool overwrite) { - if (valid && !overwrite) - return; - - base.Initialize(overwrite); - - if (!valid) - return; - - animationTable.Clear(); - clipNameHashCodeTable.Clear(); - - var data = skeletonDataAsset.GetSkeletonData(true); - - foreach (var a in data.Animations) { - animationTable.Add(a.Name.GetHashCode(), a); + public event UpdateBonesDelegate UpdateLocal { + add { _UpdateLocal += value; } + remove { _UpdateLocal -= value; } } - animator = GetComponent(); - - lastTime = Time.time; - } - - void Update () { - if (!valid) - return; - - if (layerMixModes.Length != animator.layerCount) { - System.Array.Resize(ref layerMixModes, animator.layerCount); + public event UpdateBonesDelegate UpdateWorld { + add { _UpdateWorld += value; } + remove { _UpdateWorld -= value; } } - float deltaTime = Time.time - lastTime; - skeleton.Update(Time.deltaTime); + public event UpdateBonesDelegate UpdateComplete { + add { _UpdateComplete += value; } + remove { _UpdateComplete -= value; } + } - //apply - int layerCount = animator.layerCount; + protected event UpdateBonesDelegate _UpdateLocal; + protected event UpdateBonesDelegate _UpdateWorld; + protected event UpdateBonesDelegate _UpdateComplete; - for (int i = 0; i < layerCount; i++) { + public Skeleton Skeleton { + get { + return this.skeleton; + } + } - float layerWeight = animator.GetLayerWeight(i); - if (i == 0) - layerWeight = 1; + readonly Dictionary animationTable = new Dictionary(); + readonly Dictionary clipNameHashCodeTable = new Dictionary(); + Animator animator; + float lastTime; - var stateInfo = animator.GetCurrentAnimatorStateInfo(i); - var nextStateInfo = animator.GetNextAnimatorStateInfo(i); + public override void Initialize (bool overwrite) { + if (valid && !overwrite) + return; -#if UNITY_5 - var clipInfo = animator.GetCurrentAnimatorClipInfo(i); - var nextClipInfo = animator.GetNextAnimatorClipInfo(i); -#else - var clipInfo = animator.GetCurrentAnimationClipState(i); - var nextClipInfo = animator.GetNextAnimationClipState(i); -#endif - MixMode mode = layerMixModes[i]; + base.Initialize(overwrite); - if (mode == MixMode.AlwaysMix) { - //always use Mix instead of Applying the first non-zero weighted clip - for (int c = 0; c < clipInfo.Length; c++) { - var info = clipInfo[c]; - float weight = info.weight * layerWeight; - if (weight == 0) - continue; + if (!valid) + return; - float time = stateInfo.normalizedTime * info.clip.length; - animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight); - } -#if UNITY_5 - if (nextStateInfo.fullPathHash != 0) { -#else - if (nextStateInfo.nameHash != 0) { -#endif - for (int c = 0; c < nextClipInfo.Length; c++) { - var info = nextClipInfo[c]; + animationTable.Clear(); + clipNameHashCodeTable.Clear(); + + var data = skeletonDataAsset.GetSkeletonData(true); + + foreach (var a in data.Animations) { + animationTable.Add(a.Name.GetHashCode(), a); + } + + animator = GetComponent(); + + lastTime = Time.time; + } + + void Update () { + if (!valid) + return; + + if (layerMixModes.Length != animator.layerCount) { + System.Array.Resize(ref layerMixModes, animator.layerCount); + } + float deltaTime = Time.time - lastTime; + + skeleton.Update(Time.deltaTime); + + //apply + int layerCount = animator.layerCount; + + for (int i = 0; i < layerCount; i++) { + + float layerWeight = animator.GetLayerWeight(i); + if (i == 0) + layerWeight = 1; + + var stateInfo = animator.GetCurrentAnimatorStateInfo(i); + var nextStateInfo = animator.GetNextAnimatorStateInfo(i); + + #if UNITY_5 + var clipInfo = animator.GetCurrentAnimatorClipInfo(i); + var nextClipInfo = animator.GetNextAnimatorClipInfo(i); + #else + var clipInfo = animator.GetCurrentAnimationClipState(i); + var nextClipInfo = animator.GetNextAnimationClipState(i); + #endif + MixMode mode = layerMixModes[i]; + + if (mode == MixMode.AlwaysMix) { + //always use Mix instead of Applying the first non-zero weighted clip + for (int c = 0; c < clipInfo.Length; c++) { + var info = clipInfo[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 = stateInfo.normalizedTime * info.clip.length; + animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight); } - } - } else if (mode >= MixMode.MixNext) { - //apply first non-zero weighted clip - int c = 0; + #if UNITY_5 + if (nextStateInfo.fullPathHash != 0) { + #else + if (nextStateInfo.nameHash != 0) { + #endif + for (int c = 0; c < nextClipInfo.Length; c++) { + var info = nextClipInfo[c]; + float weight = info.weight * layerWeight; + if (weight == 0) + continue; - for (; c < clipInfo.Length; c++) { - var info = clipInfo[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); + } + } + } else if (mode >= MixMode.MixNext) { + //apply first non-zero weighted clip + int c = 0; - float time = stateInfo.normalizedTime * info.clip.length; - animationTable[GetAnimationClipNameHashCode(info.clip)].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null); - break; - } + for (; c < clipInfo.Length; c++) { + var info = clipInfo[c]; + float weight = info.weight * layerWeight; + if (weight == 0) + continue; - //mix the rest - for (; c < clipInfo.Length; c++) { - var info = clipInfo[c]; - float weight = info.weight * layerWeight; - if (weight == 0) - continue; + float time = stateInfo.normalizedTime * info.clip.length; + animationTable[GetAnimationClipNameHashCode(info.clip)].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null); + break; + } - float time = stateInfo.normalizedTime * info.clip.length; - animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight); - } + //mix the rest + for (; c < clipInfo.Length; c++) { + var info = clipInfo[c]; + float weight = info.weight * layerWeight; + if (weight == 0) + continue; - c = 0; -#if UNITY_5 - if (nextStateInfo.fullPathHash != 0) { -#else - if (nextStateInfo.nameHash != 0) { -#endif - //apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights) - if (mode == MixMode.SpineStyle) { + float time = stateInfo.normalizedTime * info.clip.length; + animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight); + } + + c = 0; + #if UNITY_5 + if (nextStateInfo.fullPathHash != 0) { + #else + if (nextStateInfo.nameHash != 0) { + #endif + //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; @@ -167,49 +179,37 @@ 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); - } } } - } - if (_UpdateLocal != null) - _UpdateLocal(this); + if (_UpdateLocal != null) + _UpdateLocal(this); - skeleton.UpdateWorldTransform(); - - if (_UpdateWorld != null) { - _UpdateWorld(this); skeleton.UpdateWorldTransform(); + + if (_UpdateWorld != null) { + _UpdateWorld(this); + skeleton.UpdateWorldTransform(); + } + + if (_UpdateComplete != null) { + _UpdateComplete(this); + } + + lastTime = Time.time; } - if (_UpdateComplete != null) { - _UpdateComplete(this); + private int GetAnimationClipNameHashCode (AnimationClip clip) { + int clipNameHashCode; + if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) { + clipNameHashCode = clip.name.GetHashCode(); + clipNameHashCodeTable.Add(clip, clipNameHashCode); + } + + return clipNameHashCode; } - - lastTime = Time.time; - } - - private int GetAnimationClipNameHashCode (AnimationClip clip) { - int clipNameHashCode; - if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) { - clipNameHashCode = clip.name.GetHashCode(); - clipNameHashCodeTable.Add(clip, clipNameHashCode); - } - - return clipNameHashCode; } } diff --git a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs index 0db50e09d..525edbff9 100644 --- a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs +++ b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs @@ -6,153 +6,125 @@ *****************************************************************************/ using UnityEngine; -using System.Collections; using Spine; +using Spine.Unity; -public static class SkeletonExtensions { +namespace Spine.Unity { + public static class SkeletonExtensions { - const float ByteToFloat = 1f / 255f; + const float ByteToFloat = 1f / 255f; - #region Colors - public static Color GetColor (this Skeleton s) { return new Color(s.r, s.g, s.b, s.a); } - public static Color GetColor (this RegionAttachment a) { return new Color(a.r, a.g, a.b, a.a); } - public static Color GetColor (this MeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); } - public static Color GetColor (this WeightedMeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); } + #region Colors + public static Color GetColor (this Skeleton s) { return new Color(s.r, s.g, s.b, s.a); } + public static Color GetColor (this RegionAttachment a) { return new Color(a.r, a.g, a.b, a.a); } + public static Color GetColor (this MeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); } + public static Color GetColor (this WeightedMeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); } - public static void SetColor (this Skeleton skeleton, Color color) { - skeleton.A = color.a; - skeleton.R = color.r; - skeleton.G = color.g; - skeleton.B = color.b; + public static void SetColor (this Skeleton skeleton, Color color) { + skeleton.A = color.a; + skeleton.R = color.r; + skeleton.G = color.g; + skeleton.B = color.b; + } + + public static void SetColor (this Skeleton skeleton, Color32 color) { + skeleton.A = color.a * ByteToFloat; + skeleton.R = color.r * ByteToFloat; + skeleton.G = color.g * ByteToFloat; + skeleton.B = color.b * ByteToFloat; + } + + public static void SetColor (this Slot slot, Color color) { + slot.A = color.a; + slot.R = color.r; + slot.G = color.g; + slot.B = color.b; + } + + public static void SetColor (this Slot slot, Color32 color) { + slot.A = color.a * ByteToFloat; + slot.R = color.r * ByteToFloat; + slot.G = color.g * ByteToFloat; + slot.B = color.b * ByteToFloat; + } + + public static void SetColor (this RegionAttachment attachment, Color color) { + attachment.A = color.a; + attachment.R = color.r; + attachment.G = color.g; + attachment.B = color.b; + } + + public static void SetColor (this RegionAttachment attachment, Color32 color) { + attachment.A = color.a * ByteToFloat; + attachment.R = color.r * ByteToFloat; + attachment.G = color.g * ByteToFloat; + attachment.B = color.b * ByteToFloat; + } + + public static void SetColor (this MeshAttachment attachment, Color color) { + attachment.A = color.a; + attachment.R = color.r; + attachment.G = color.g; + attachment.B = color.b; + } + + public static void SetColor (this MeshAttachment attachment, Color32 color) { + attachment.A = color.a * ByteToFloat; + attachment.R = color.r * ByteToFloat; + attachment.G = color.g * ByteToFloat; + attachment.B = color.b * ByteToFloat; + } + + public static void SetColor (this WeightedMeshAttachment attachment, Color color) { + attachment.A = color.a; + attachment.R = color.r; + attachment.G = color.g; + attachment.B = color.b; + } + + public static void SetColor (this WeightedMeshAttachment attachment, Color32 color) { + attachment.A = color.a * ByteToFloat; + attachment.R = color.r * ByteToFloat; + attachment.G = color.g * ByteToFloat; + attachment.B = color.b * ByteToFloat; + } + #endregion + + #region Bone Position + public static void SetPosition (this Bone bone, Vector2 position) { + bone.X = position.x; + bone.Y = position.y; + } + + public static void SetPosition (this Bone bone, Vector3 position) { + bone.X = position.x; + bone.Y = position.y; + } + + public static Vector2 GetSkeletonSpacePosition (this Bone bone) { + return new Vector2(bone.worldX, bone.worldY); + } + + public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform parentTransform) { + return parentTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY)); + } + #endregion + + #region Posing + /// + /// Shortcut for posing a skeleton at a specific time. Time is in seconds. (frameNumber / 30f) will give you seconds. + /// If you need to do this often, you should get the Animation object yourself using skeleton.data.FindAnimation. and call Apply on that. + /// The skeleton to pose. + /// The name of the animation to use. + /// The time of the pose within the animation. + /// Wraps the time around if it is longer than the duration of the animation. + public static void PoseWithAnimation (this Skeleton skeleton, string animationName, float time, bool loop) { + // Fail loud when skeleton.data is null. + Spine.Animation animation = skeleton.data.FindAnimation(animationName); + if (animation == null) return; + animation.Apply(skeleton, 0, time, loop, null); + } + #endregion } - - public static void SetColor (this Skeleton skeleton, Color32 color) { - skeleton.A = color.a * ByteToFloat; - skeleton.R = color.r * ByteToFloat; - skeleton.G = color.g * ByteToFloat; - skeleton.B = color.b * ByteToFloat; - } - - public static void SetColor (this Slot slot, Color color) { - slot.A = color.a; - slot.R = color.r; - slot.G = color.g; - slot.B = color.b; - } - - public static void SetColor (this Slot slot, Color32 color) { - slot.A = color.a * ByteToFloat; - slot.R = color.r * ByteToFloat; - slot.G = color.g * ByteToFloat; - slot.B = color.b * ByteToFloat; - } - - public static void SetColor (this RegionAttachment attachment, Color color) { - attachment.A = color.a; - attachment.R = color.r; - attachment.G = color.g; - attachment.B = color.b; - } - - public static void SetColor (this RegionAttachment attachment, Color32 color) { - attachment.A = color.a * ByteToFloat; - attachment.R = color.r * ByteToFloat; - attachment.G = color.g * ByteToFloat; - attachment.B = color.b * ByteToFloat; - } - - public static void SetColor (this MeshAttachment attachment, Color color) { - attachment.A = color.a; - attachment.R = color.r; - attachment.G = color.g; - attachment.B = color.b; - } - - public static void SetColor (this MeshAttachment attachment, Color32 color) { - attachment.A = color.a * ByteToFloat; - attachment.R = color.r * ByteToFloat; - attachment.G = color.g * ByteToFloat; - attachment.B = color.b * ByteToFloat; - } - - public static void SetColor (this WeightedMeshAttachment attachment, Color color) { - attachment.A = color.a; - attachment.R = color.r; - attachment.G = color.g; - attachment.B = color.b; - } - - public static void SetColor (this WeightedMeshAttachment attachment, Color32 color) { - attachment.A = color.a * ByteToFloat; - attachment.R = color.r * ByteToFloat; - attachment.G = color.g * ByteToFloat; - attachment.B = color.b * ByteToFloat; - } - #endregion - - #region Bone Position - public static void SetPosition (this Bone bone, Vector2 position) { - bone.X = position.x; - bone.Y = position.y; - } - - public static void SetPosition (this Bone bone, Vector3 position) { - bone.X = position.x; - bone.Y = position.y; - } - - public static Vector2 GetSkeletonSpacePosition (this Bone bone) { - // TODO: This changes in v3.0 - return new Vector2(bone.worldX, bone.worldY); - } - - public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform parentTransform) { - return parentTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY)); - } - #endregion - - #region Posing - /// - /// Shortcut for posing a skeleton at a specific time. Time is in seconds. (frameNumber / 30f) will give you seconds. - /// If you need to do this often, you should get the Animation object yourself using skeleton.data.FindAnimation. and call Apply on that. - /// The skeleton to pose. - /// The name of the animation to use. - /// The time of the pose within the animation. - /// Wraps the time around if it is longer than the duration of the animation. - public static void PoseWithAnimation (this Skeleton skeleton, string animationName, float time, bool loop) { - // Fail loud when skeleton.data is null. - Spine.Animation animation = skeleton.data.FindAnimation(animationName); - if (animation == null) return; - animation.Apply(skeleton, 0, time, loop, null); - } - #endregion - - #region Unity Sprite To Attachments - public static Attachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, string shaderName = "Spine/Skeleton") { - var att = sprite.ToRegionAttachment(shaderName); - skeleton.FindSlot(slotName).Attachment = att; - return att; - } - - public static Attachment AddUnitySprite (this SkeletonData skeletonData, string slotName, Sprite sprite, string skinName = "", string shaderName = "Spine/Skeleton") { - var att = sprite.ToRegionAttachment(shaderName); - - var slotIndex = skeletonData.FindSlotIndex(slotName); - Skin skin = skeletonData.defaultSkin; - if (skinName != "") - skin = skeletonData.FindSkin(skinName); - - skin.AddAttachment(slotIndex, att.Name, att); - - return att; - } - - public static RegionAttachment ToRegionAttachment (this Sprite sprite, string shaderName = "Spine/Skeleton") { - var loader = new SpriteAttachmentLoader(sprite, Shader.Find(shaderName)); - var att = loader.NewRegionAttachment(null, sprite.name, ""); - loader = null; - return att; - } - #endregion - -} \ No newline at end of file +} diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 35b2d9969..4cf20e743 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -31,549 +31,518 @@ #define SPINE_OPTIONAL_NORMALS #define SPINE_OPTIONAL_FRONTFACING #define SPINE_OPTIONAL_RENDEROVERRIDE +//#define SPINE_OPTIONAL_SUBMESHRENDERER // Deprecated using System; using System.Collections.Generic; using UnityEngine; -using Spine; -using Spine.Unity; using Spine.Unity.MeshGeneration; -/// Renders a skeleton. -[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer)), DisallowMultipleComponent] -[HelpURL("http://esotericsoftware.com/spine-unity-documentation#Rendering")] -public class SkeletonRenderer : MonoBehaviour { +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 { - public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer); - public SkeletonRendererDelegate OnRebuild; + public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer); + public SkeletonRendererDelegate OnRebuild; - public SkeletonDataAsset skeletonDataAsset; - public String initialSkinName; + public SkeletonDataAsset skeletonDataAsset; + public String initialSkinName; -#region Advanced - // Submesh Separation - [UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")] - [SpineSlot] - public string[] separatorSlotNames = new string[0]; - [System.NonSerialized] - public readonly List separatorSlots = new List(); - - public float zSpacing; - public bool renderMeshes = true, immutableTriangles; - public bool pmaVertexColors = true; - #if SPINE_OPTIONAL_NORMALS - public bool calculateNormals, calculateTangents; - #endif - #if SPINE_OPTIONAL_FRONTFACING - public bool frontFacing; - #endif - - public bool logErrors = false; - - #if SPINE_OPTIONAL_RENDEROVERRIDE - public bool disableRenderingOnOverride = true; - public delegate void InstructionDelegate (SkeletonRenderer.SmartMesh.Instruction instruction); - event InstructionDelegate generateMeshOverride; - public event InstructionDelegate GenerateMeshOverride { - add { - generateMeshOverride += value; - if (disableRenderingOnOverride && generateMeshOverride != null) { - Initialize(false); - meshRenderer.enabled = false; - } - } - remove { - generateMeshOverride -= value; - if (disableRenderingOnOverride && generateMeshOverride == null) { - Initialize(false); - meshRenderer.enabled = true; - } - } - } - - #endif - - // Custom Slot Material - [System.NonSerialized] readonly Dictionary customSlotMaterials = new Dictionary(); - public Dictionary CustomSlotMaterials { get { return customSlotMaterials; } } -#endregion - - [System.NonSerialized] public bool valid; - [System.NonSerialized] public Skeleton skeleton; - - MeshRenderer meshRenderer; - MeshFilter meshFilter; - - Spine.Unity.DoubleBuffered doubleBufferedMesh; - readonly SmartMesh.Instruction currentInstructions = new SmartMesh.Instruction(); - readonly ExposedList submeshes = new ExposedList(); - - float[] tempVertices = new float[8]; - Vector3[] vertices; - Color32[] colors; - Vector2[] uvs; - - readonly ExposedList submeshMaterials = new ExposedList(); - Material[] sharedMaterials = new Material[0]; - - #if SPINE_OPTIONAL_NORMALS - Vector3[] normals; - Vector4[] tangents; - #endif - - #region Runtime Instantiation - public static T NewSpineGameObject (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { - return SkeletonRenderer.AddSpineComponent(new GameObject("New Spine GameObject"), skeletonDataAsset); - } - - /// Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime. - /// T should be SkeletonRenderer or any of its derived classes. - public static T AddSpineComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { - var c = gameObject.AddComponent(); - if (skeletonDataAsset != null) { - c.skeletonDataAsset = skeletonDataAsset; - c.Initialize(false); - } - return c; - } - #endregion - - public virtual void Awake () { - Initialize(false); - } - - public virtual void Initialize (bool overwrite) { - if (valid && !overwrite) - return; - - // Clear - { - if (meshFilter != null) - meshFilter.sharedMesh = null; - - meshRenderer = GetComponent(); - if (meshRenderer != null) meshRenderer.sharedMaterial = null; - - currentInstructions.Clear(); - vertices = null; - colors = null; - uvs = null; - sharedMaterials = new Material[0]; - submeshMaterials.Clear(); - submeshes.Clear(); - skeleton = null; - - valid = false; - } - - if (!skeletonDataAsset) { - if (logErrors) - Debug.LogError("Missing SkeletonData asset.", this); - - return; - } - SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false); - if (skeletonData == null) - return; - valid = true; - - meshFilter = GetComponent(); - meshRenderer = GetComponent(); - doubleBufferedMesh = new DoubleBuffered(); - vertices = new Vector3[0]; - - skeleton = new Skeleton(skeletonData); - if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default") - skeleton.SetSkin(initialSkinName); - - separatorSlots.Clear(); - for (int i = 0; i < separatorSlotNames.Length; i++) { - separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i])); - } - - LateUpdate(); - - if (OnRebuild != null) - OnRebuild(this); - } - - public virtual void LateUpdate () { - if (!valid) - return; - - if ( - ( - !meshRenderer.enabled - - ) - #if SPINE_OPTIONAL_RENDEROVERRIDE - && this.generateMeshOverride == null - #endif - - ) - return; - - - - // STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. - - // This method caches several .Items arrays. - // Never mutate their overlying ExposedList objects. - ExposedList drawOrder = skeleton.drawOrder; - var drawOrderItems = drawOrder.Items; - int drawOrderCount = drawOrder.Count; - int separatorSlotCount = separatorSlots.Count; - bool renderMeshes = this.renderMeshes; - - // Clear last state of attachments and submeshes - var workingInstruction = this.currentInstructions; - var workingAttachments = workingInstruction.attachments; - workingAttachments.Clear(false); - workingAttachments.GrowIfNeeded(drawOrderCount); - workingAttachments.Count = drawOrderCount; - var workingAttachmentsItems = workingInstruction.attachments.Items; + #region Advanced + // Submesh Separation + [UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")] + [SpineSlot] + public string[] separatorSlotNames = new string[0]; + [System.NonSerialized] + public readonly List separatorSlots = new List(); + public float zSpacing; + public bool renderMeshes = true, immutableTriangles; + public bool pmaVertexColors = true; + #if SPINE_OPTIONAL_NORMALS + public bool calculateNormals, calculateTangents; + #endif #if SPINE_OPTIONAL_FRONTFACING - var workingFlips = workingInstruction.attachmentFlips; - workingFlips.Clear(false); - workingFlips.GrowIfNeeded(drawOrderCount); - workingFlips.Count = drawOrderCount; - var workingFlipsItems = workingFlips.Items; + public bool frontFacing; #endif - var workingSubmeshInstructions = workingInstruction.submeshInstructions; // Items array should not be cached. There is dynamic writing to this list. - workingSubmeshInstructions.Clear(false); - - bool isCustomMaterialsPopulated = customSlotMaterials.Count > 0; - - int vertexCount = 0; - int submeshVertexCount = 0; - int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0; - Material lastMaterial = null; - for (int i = 0; i < drawOrderCount; i++) { - Slot slot = drawOrderItems[i]; - Attachment attachment = slot.attachment; - - workingAttachmentsItems[i] = attachment; - - #if SPINE_OPTIONAL_FRONTFACING - bool flip = frontFacing && (slot.bone.WorldSignX != slot.bone.WorldSignY); - workingFlipsItems[i] = flip; - #endif - - object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object. - int attachmentVertexCount, attachmentTriangleCount; - - var regionAttachment = attachment as RegionAttachment; - if (regionAttachment != null) { - rendererObject = regionAttachment.RendererObject; - attachmentVertexCount = 4; - attachmentTriangleCount = 6; - } else { - if (!renderMeshes) - continue; - var meshAttachment = attachment as MeshAttachment; - if (meshAttachment != null) { - rendererObject = meshAttachment.RendererObject; - attachmentVertexCount = meshAttachment.vertices.Length >> 1; - attachmentTriangleCount = meshAttachment.triangles.Length; - } else { - var skinnedMeshAttachment = attachment as WeightedMeshAttachment; - if (skinnedMeshAttachment != null) { - rendererObject = skinnedMeshAttachment.RendererObject; - attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; - attachmentTriangleCount = skinnedMeshAttachment.triangles.Length; - } else - continue; - } - } - - #if !SPINE_TK2D - // Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; // For no customSlotMaterials - - Material material; - if (isCustomMaterialsPopulated) { - if (!customSlotMaterials.TryGetValue(slot, out material)) { - material = (Material)((AtlasRegion)rendererObject).page.rendererObject; - } - } else { - material = (Material)((AtlasRegion)rendererObject).page.rendererObject; - } - #else - Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; - #endif - - // Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator) - bool forceSeparate = (separatorSlotCount > 0 && separatorSlots.Contains(slot)); - if ((vertexCount > 0 && lastMaterial.GetInstanceID() != material.GetInstanceID()) || forceSeparate) { - workingSubmeshInstructions.Add( - new Spine.Unity.MeshGeneration.SubmeshInstruction { - skeleton = this.skeleton, - material = lastMaterial, - startSlot = submeshStartSlotIndex, - endSlot = i, - triangleCount = submeshTriangleCount, - firstVertexIndex = submeshFirstVertex, - vertexCount = submeshVertexCount, - forceSeparate = forceSeparate - } - ); - - submeshTriangleCount = 0; - submeshVertexCount = 0; - submeshFirstVertex = vertexCount; - submeshStartSlotIndex = i; - } - lastMaterial = material; - - submeshTriangleCount += attachmentTriangleCount; - vertexCount += attachmentVertexCount; - submeshVertexCount += attachmentVertexCount; - } - - workingSubmeshInstructions.Add( - new Spine.Unity.MeshGeneration.SubmeshInstruction { - skeleton = this.skeleton, - material = lastMaterial, - startSlot = submeshStartSlotIndex, - endSlot = drawOrderCount, - triangleCount = submeshTriangleCount, - firstVertexIndex = submeshFirstVertex, - vertexCount = submeshVertexCount, - forceSeparate = false - } - ); - - workingInstruction.vertexCount = vertexCount; - workingInstruction.immutableTriangles = this.immutableTriangles; - #if SPINE_OPTIONAL_FRONTFACING - workingInstruction.frontFacing = this.frontFacing; - #endif + public bool logErrors = false; #if SPINE_OPTIONAL_RENDEROVERRIDE - if (this.generateMeshOverride != null) { - this.generateMeshOverride(workingInstruction); - - if (disableRenderingOnOverride) { - return; + public bool disableRenderingOnOverride = true; + public delegate void InstructionDelegate (SkeletonRenderer.SmartMesh.Instruction instruction); + event InstructionDelegate generateMeshOverride; + public event InstructionDelegate GenerateMeshOverride { + add { + generateMeshOverride += value; + if (disableRenderingOnOverride && generateMeshOverride != null) { + Initialize(false); + meshRenderer.enabled = false; + } + } + remove { + generateMeshOverride -= value; + if (disableRenderingOnOverride && generateMeshOverride == null) { + Initialize(false); + meshRenderer.enabled = true; + } } } #endif - // STEP 2. Update vertex buffer based on verts from the attachments. - // Uses values that were also stored in workingInstruction. - Vector3[] vertices = this.vertices; - bool vertexCountIncreased = vertexCount > vertices.Length; + #if SPINE_OPTIONAL_SUBMESHRENDERER + private Spine.Unity.Modules.SkeletonUtilitySubmeshRenderer[] submeshRenderers; + #endif - if (vertexCountIncreased) { - this.vertices = vertices = new Vector3[vertexCount]; - this.colors = new Color32[vertexCount]; - this.uvs = new Vector2[vertexCount]; + // Custom Slot Material + [System.NonSerialized] readonly Dictionary customSlotMaterials = new Dictionary(); + public Dictionary CustomSlotMaterials { get { return customSlotMaterials; } } + #endregion - #if SPINE_OPTIONAL_NORMALS - if (calculateNormals) { - Vector3[] localNormals = this.normals = new Vector3[vertexCount]; - Vector3 normal = new Vector3(0, 0, -1); - for (int i = 0; i < vertexCount; i++) - localNormals[i] = normal; - } + [System.NonSerialized] public bool valid; + [System.NonSerialized] public Skeleton skeleton; - // For dynamic tangent calculation, you can remove the tangent-filling logic and add tangent calculation logic below. - if (calculateTangents) { - Vector4[] localTangents = this.tangents = new Vector4[vertexCount]; - Vector4 tangent = new Vector4(1, 0, 0, -1); - for (int i = 0; i < vertexCount; i++) - localTangents[i] = tangent; - } - #endif - } else { - Vector3 zero = Vector3.zero; - for (int i = vertexCount, n = vertices.Length; i < n; i++) - vertices[i] = zero; + MeshRenderer meshRenderer; + MeshFilter meshFilter; + + Spine.Unity.DoubleBuffered doubleBufferedMesh; + readonly SmartMesh.Instruction currentInstructions = new SmartMesh.Instruction(); + readonly ExposedList submeshes = new ExposedList(); + + float[] tempVertices = new float[8]; + Vector3[] vertices; + Color32[] colors; + Vector2[] uvs; + + readonly ExposedList submeshMaterials = new ExposedList(); + Material[] sharedMaterials = new Material[0]; + + #if SPINE_OPTIONAL_NORMALS + Vector3[] normals; + Vector4[] tangents; + #endif + + #region Runtime Instantiation + public static T NewSpineGameObject (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { + return SkeletonRenderer.AddSpineComponent(new GameObject("New Spine GameObject"), skeletonDataAsset); } - float zSpacing = this.zSpacing; - float[] tempVertices = this.tempVertices; - Vector2[] uvs = this.uvs; - Color32[] colors = this.colors; - int vertexIndex = 0; - bool pmaVertexColors = this.pmaVertexColors; - Color32 color; - float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b; - - Vector3 meshBoundsMin; - Vector3 meshBoundsMax; - if (vertexCount == 0) { - meshBoundsMin = new Vector3(0, 0, 0); - meshBoundsMax = new Vector3(0, 0, 0); - } else { - meshBoundsMin.x = int.MaxValue; - meshBoundsMin.y = int.MaxValue; - meshBoundsMax.x = int.MinValue; - meshBoundsMax.y = int.MinValue; - if (zSpacing > 0f) { - meshBoundsMin.z = 0f; - meshBoundsMax.z = zSpacing * (drawOrderCount - 1); - } else { - meshBoundsMin.z = zSpacing * (drawOrderCount - 1); - meshBoundsMax.z = 0f; + /// Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime. + /// T should be SkeletonRenderer or any of its derived classes. + public static T AddSpineComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { + var c = gameObject.AddComponent(); + if (skeletonDataAsset != null) { + c.skeletonDataAsset = skeletonDataAsset; + c.Initialize(false); } - int i = 0; - do { + return c; + } + #endregion + + public virtual void Awake () { + Initialize(false); + } + + public virtual void Initialize (bool overwrite) { + if (valid && !overwrite) + return; + + // Clear + { + if (meshFilter != null) + meshFilter.sharedMesh = null; + + meshRenderer = GetComponent(); + if (meshRenderer != null) meshRenderer.sharedMaterial = null; + + currentInstructions.Clear(); + vertices = null; + colors = null; + uvs = null; + sharedMaterials = new Material[0]; + submeshMaterials.Clear(); + submeshes.Clear(); + skeleton = null; + + valid = false; + } + + if (!skeletonDataAsset) { + if (logErrors) + Debug.LogError("Missing SkeletonData asset.", this); + + return; + } + SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false); + if (skeletonData == null) + return; + valid = true; + + meshFilter = GetComponent(); + meshRenderer = GetComponent(); + doubleBufferedMesh = new DoubleBuffered(); + vertices = new Vector3[0]; + + skeleton = new Skeleton(skeletonData); + if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default") + skeleton.SetSkin(initialSkinName); + + separatorSlots.Clear(); + for (int i = 0; i < separatorSlotNames.Length; i++) + separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i])); + + #if SPINE_OPTIONAL_SUBMESHRENDERER + submeshRenderers = GetComponentsInChildren(); + #endif + + LateUpdate(); + + if (OnRebuild != null) + OnRebuild(this); + } + + public virtual void LateUpdate () { + if (!valid) + return; + + if ( + ( + !meshRenderer.enabled + + ) + #if SPINE_OPTIONAL_RENDEROVERRIDE + && this.generateMeshOverride == null + #endif + + #if SPINE_OPTIONAL_SUBMESHRENDERER + && submeshRenderers.Length > 0 + #endif + + ) + return; + + + + // STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. + + // This method caches several .Items arrays. + // Never mutate their overlying ExposedList objects. + ExposedList drawOrder = skeleton.drawOrder; + var drawOrderItems = drawOrder.Items; + int drawOrderCount = drawOrder.Count; + int separatorSlotCount = separatorSlots.Count; + bool renderMeshes = this.renderMeshes; + + // Clear last state of attachments and submeshes + var workingInstruction = this.currentInstructions; + var workingAttachments = workingInstruction.attachments; + workingAttachments.Clear(false); + workingAttachments.GrowIfNeeded(drawOrderCount); + workingAttachments.Count = drawOrderCount; + var workingAttachmentsItems = workingInstruction.attachments.Items; + + #if SPINE_OPTIONAL_FRONTFACING + var workingFlips = workingInstruction.attachmentFlips; + workingFlips.Clear(false); + workingFlips.GrowIfNeeded(drawOrderCount); + workingFlips.Count = drawOrderCount; + var workingFlipsItems = workingFlips.Items; + #endif + + var workingSubmeshInstructions = workingInstruction.submeshInstructions; // Items array should not be cached. There is dynamic writing to this list. + workingSubmeshInstructions.Clear(false); + + bool isCustomMaterialsPopulated = customSlotMaterials.Count > 0; + + int vertexCount = 0; + int submeshVertexCount = 0; + int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0; + Material lastMaterial = null; + for (int i = 0; i < drawOrderCount; i++) { Slot slot = drawOrderItems[i]; Attachment attachment = slot.attachment; - RegionAttachment regionAttachment = attachment as RegionAttachment; + + workingAttachmentsItems[i] = attachment; + + #if SPINE_OPTIONAL_FRONTFACING + bool flip = frontFacing && (slot.bone.WorldSignX != slot.bone.WorldSignY); + workingFlipsItems[i] = flip; + #endif + + object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object. + int attachmentVertexCount, attachmentTriangleCount; + + var regionAttachment = attachment as RegionAttachment; if (regionAttachment != null) { - regionAttachment.ComputeWorldVertices(slot.bone, tempVertices); - - float z = i * zSpacing; - float x1 = tempVertices[RegionAttachment.X1], y1 = tempVertices[RegionAttachment.Y1]; - float x2 = tempVertices[RegionAttachment.X2], y2 = tempVertices[RegionAttachment.Y2]; - float x3 = tempVertices[RegionAttachment.X3], y3 = tempVertices[RegionAttachment.Y3]; - float x4 = tempVertices[RegionAttachment.X4], y4 = tempVertices[RegionAttachment.Y4]; - vertices[vertexIndex].x = x1; - vertices[vertexIndex].y = y1; - vertices[vertexIndex].z = z; - vertices[vertexIndex + 1].x = x4; - vertices[vertexIndex + 1].y = y4; - vertices[vertexIndex + 1].z = z; - vertices[vertexIndex + 2].x = x2; - vertices[vertexIndex + 2].y = y2; - vertices[vertexIndex + 2].z = z; - vertices[vertexIndex + 3].x = x3; - vertices[vertexIndex + 3].y = y3; - vertices[vertexIndex + 3].z = z; - - if (pmaVertexColors) { - color.a = (byte)(a * slot.a * regionAttachment.a); - color.r = (byte)(r * slot.r * regionAttachment.r * color.a); - color.g = (byte)(g * slot.g * regionAttachment.g * color.a); - color.b = (byte)(b * slot.b * regionAttachment.b * color.a); - if (slot.data.blendMode == BlendMode.additive) color.a = 0; - } else { - color.a = (byte)(a * slot.a * regionAttachment.a); - color.r = (byte)(r * slot.r * regionAttachment.r * 255); - color.g = (byte)(g * slot.g * regionAttachment.g * 255); - color.b = (byte)(b * slot.b * regionAttachment.b * 255); - } - - colors[vertexIndex] = color; - colors[vertexIndex + 1] = color; - colors[vertexIndex + 2] = color; - colors[vertexIndex + 3] = color; - - float[] regionUVs = regionAttachment.uvs; - uvs[vertexIndex].x = regionUVs[RegionAttachment.X1]; - uvs[vertexIndex].y = regionUVs[RegionAttachment.Y1]; - uvs[vertexIndex + 1].x = regionUVs[RegionAttachment.X4]; - uvs[vertexIndex + 1].y = regionUVs[RegionAttachment.Y4]; - uvs[vertexIndex + 2].x = regionUVs[RegionAttachment.X2]; - uvs[vertexIndex + 2].y = regionUVs[RegionAttachment.Y2]; - uvs[vertexIndex + 3].x = regionUVs[RegionAttachment.X3]; - uvs[vertexIndex + 3].y = regionUVs[RegionAttachment.Y3]; - - // Calculate min/max X - if (x1 < meshBoundsMin.x) - meshBoundsMin.x = x1; - else if (x1 > meshBoundsMax.x) - meshBoundsMax.x = x1; - if (x2 < meshBoundsMin.x) - meshBoundsMin.x = x2; - else if (x2 > meshBoundsMax.x) - meshBoundsMax.x = x2; - if (x3 < meshBoundsMin.x) - meshBoundsMin.x = x3; - else if (x3 > meshBoundsMax.x) - meshBoundsMax.x = x3; - if (x4 < meshBoundsMin.x) - meshBoundsMin.x = x4; - else if (x4 > meshBoundsMax.x) - meshBoundsMax.x = x4; - - // Calculate min/max Y - if (y1 < meshBoundsMin.y) - meshBoundsMin.y = y1; - else if (y1 > meshBoundsMax.y) - meshBoundsMax.y = y1; - if (y2 < meshBoundsMin.y) - meshBoundsMin.y = y2; - else if (y2 > meshBoundsMax.y) - meshBoundsMax.y = y2; - if (y3 < meshBoundsMin.y) - meshBoundsMin.y = y3; - else if (y3 > meshBoundsMax.y) - meshBoundsMax.y = y3; - if (y4 < meshBoundsMin.y) - meshBoundsMin.y = y4; - else if (y4 > meshBoundsMax.y) - meshBoundsMax.y = y4; - - vertexIndex += 4; + rendererObject = regionAttachment.RendererObject; + attachmentVertexCount = 4; + attachmentTriangleCount = 6; } else { if (!renderMeshes) continue; - MeshAttachment meshAttachment = attachment as MeshAttachment; + var meshAttachment = attachment as MeshAttachment; if (meshAttachment != null) { - int meshVertexCount = meshAttachment.vertices.Length; - if (tempVertices.Length < meshVertexCount) - this.tempVertices = tempVertices = new float[meshVertexCount]; - meshAttachment.ComputeWorldVertices(slot, tempVertices); + rendererObject = meshAttachment.RendererObject; + attachmentVertexCount = meshAttachment.vertices.Length >> 1; + attachmentTriangleCount = meshAttachment.triangles.Length; + } else { + var skinnedMeshAttachment = attachment as WeightedMeshAttachment; + if (skinnedMeshAttachment != null) { + rendererObject = skinnedMeshAttachment.RendererObject; + attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; + attachmentTriangleCount = skinnedMeshAttachment.triangles.Length; + } else + continue; + } + } + + #if !SPINE_TK2D + // Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; // For no customSlotMaterials + + Material material; + if (isCustomMaterialsPopulated) { + if (!customSlotMaterials.TryGetValue(slot, out material)) { + material = (Material)((AtlasRegion)rendererObject).page.rendererObject; + } + } else { + material = (Material)((AtlasRegion)rendererObject).page.rendererObject; + } + #else + Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; + #endif + + // Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator) + bool forceSeparate = (separatorSlotCount > 0 && separatorSlots.Contains(slot)); + if ((vertexCount > 0 && lastMaterial.GetInstanceID() != material.GetInstanceID()) || forceSeparate) { + workingSubmeshInstructions.Add( + new Spine.Unity.MeshGeneration.SubmeshInstruction { + skeleton = this.skeleton, + material = lastMaterial, + startSlot = submeshStartSlotIndex, + endSlot = i, + triangleCount = submeshTriangleCount, + firstVertexIndex = submeshFirstVertex, + vertexCount = submeshVertexCount, + forceSeparate = forceSeparate + } + ); + + submeshTriangleCount = 0; + submeshVertexCount = 0; + submeshFirstVertex = vertexCount; + submeshStartSlotIndex = i; + } + lastMaterial = material; + + submeshTriangleCount += attachmentTriangleCount; + vertexCount += attachmentVertexCount; + submeshVertexCount += attachmentVertexCount; + } + + workingSubmeshInstructions.Add( + new Spine.Unity.MeshGeneration.SubmeshInstruction { + skeleton = this.skeleton, + material = lastMaterial, + startSlot = submeshStartSlotIndex, + endSlot = drawOrderCount, + triangleCount = submeshTriangleCount, + firstVertexIndex = submeshFirstVertex, + vertexCount = submeshVertexCount, + forceSeparate = false + } + ); + + workingInstruction.vertexCount = vertexCount; + workingInstruction.immutableTriangles = this.immutableTriangles; + #if SPINE_OPTIONAL_FRONTFACING + workingInstruction.frontFacing = this.frontFacing; + #endif + + #if SPINE_OPTIONAL_RENDEROVERRIDE + if (this.generateMeshOverride != null) { + this.generateMeshOverride(workingInstruction); + + if (disableRenderingOnOverride) { + return; + } + } + #endif + + // STEP 2. Update vertex buffer based on verts from the attachments. + // Uses values that were also stored in workingInstruction. + Vector3[] vertices = this.vertices; + bool vertexCountIncreased = vertexCount > vertices.Length; + + if (vertexCountIncreased) { + this.vertices = vertices = new Vector3[vertexCount]; + this.colors = new Color32[vertexCount]; + this.uvs = new Vector2[vertexCount]; + + #if SPINE_OPTIONAL_NORMALS + if (calculateNormals) { + Vector3[] localNormals = this.normals = new Vector3[vertexCount]; + Vector3 normal = new Vector3(0, 0, -1); + for (int i = 0; i < vertexCount; i++) + localNormals[i] = normal; + } + + // For dynamic tangent calculation, you can remove the tangent-filling logic and add tangent calculation logic below. + if (calculateTangents) { + Vector4[] localTangents = this.tangents = new Vector4[vertexCount]; + Vector4 tangent = new Vector4(1, 0, 0, -1); + for (int i = 0; i < vertexCount; i++) + localTangents[i] = tangent; + } + #endif + } else { + Vector3 zero = Vector3.zero; + for (int i = vertexCount, n = vertices.Length; i < n; i++) + vertices[i] = zero; + } + + float zSpacing = this.zSpacing; + float[] tempVertices = this.tempVertices; + Vector2[] uvs = this.uvs; + Color32[] colors = this.colors; + int vertexIndex = 0; + bool pmaVertexColors = this.pmaVertexColors; + Color32 color; + float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b; + + Vector3 meshBoundsMin; + Vector3 meshBoundsMax; + if (vertexCount == 0) { + meshBoundsMin = new Vector3(0, 0, 0); + meshBoundsMax = new Vector3(0, 0, 0); + } else { + meshBoundsMin.x = int.MaxValue; + meshBoundsMin.y = int.MaxValue; + meshBoundsMax.x = int.MinValue; + meshBoundsMax.y = int.MinValue; + if (zSpacing > 0f) { + meshBoundsMin.z = 0f; + meshBoundsMax.z = zSpacing * (drawOrderCount - 1); + } else { + meshBoundsMin.z = zSpacing * (drawOrderCount - 1); + meshBoundsMax.z = 0f; + } + int i = 0; + do { + Slot slot = drawOrderItems[i]; + Attachment attachment = slot.attachment; + RegionAttachment regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) { + regionAttachment.ComputeWorldVertices(slot.bone, tempVertices); + + float z = i * zSpacing; + float x1 = tempVertices[RegionAttachment.X1], y1 = tempVertices[RegionAttachment.Y1]; + float x2 = tempVertices[RegionAttachment.X2], y2 = tempVertices[RegionAttachment.Y2]; + float x3 = tempVertices[RegionAttachment.X3], y3 = tempVertices[RegionAttachment.Y3]; + float x4 = tempVertices[RegionAttachment.X4], y4 = tempVertices[RegionAttachment.Y4]; + vertices[vertexIndex].x = x1; + vertices[vertexIndex].y = y1; + vertices[vertexIndex].z = z; + vertices[vertexIndex + 1].x = x4; + vertices[vertexIndex + 1].y = y4; + vertices[vertexIndex + 1].z = z; + vertices[vertexIndex + 2].x = x2; + vertices[vertexIndex + 2].y = y2; + vertices[vertexIndex + 2].z = z; + vertices[vertexIndex + 3].x = x3; + vertices[vertexIndex + 3].y = y3; + vertices[vertexIndex + 3].z = z; if (pmaVertexColors) { - color.a = (byte)(a * slot.a * meshAttachment.a); - color.r = (byte)(r * slot.r * meshAttachment.r * color.a); - color.g = (byte)(g * slot.g * meshAttachment.g * color.a); - color.b = (byte)(b * slot.b * meshAttachment.b * color.a); + color.a = (byte)(a * slot.a * regionAttachment.a); + color.r = (byte)(r * slot.r * regionAttachment.r * color.a); + color.g = (byte)(g * slot.g * regionAttachment.g * color.a); + color.b = (byte)(b * slot.b * regionAttachment.b * color.a); if (slot.data.blendMode == BlendMode.additive) color.a = 0; } else { - color.a = (byte)(a * slot.a * meshAttachment.a); - color.r = (byte)(r * slot.r * meshAttachment.r * 255); - color.g = (byte)(g * slot.g * meshAttachment.g * 255); - color.b = (byte)(b * slot.b * meshAttachment.b * 255); + color.a = (byte)(a * slot.a * regionAttachment.a); + color.r = (byte)(r * slot.r * regionAttachment.r * 255); + color.g = (byte)(g * slot.g * regionAttachment.g * 255); + color.b = (byte)(b * slot.b * regionAttachment.b * 255); } - float[] meshUVs = meshAttachment.uvs; - float z = i * zSpacing; - for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { - float x = tempVertices[ii], y = tempVertices[ii + 1]; - vertices[vertexIndex].x = x; - vertices[vertexIndex].y = y; - vertices[vertexIndex].z = z; - colors[vertexIndex] = color; - uvs[vertexIndex].x = meshUVs[ii]; - uvs[vertexIndex].y = meshUVs[ii + 1]; + colors[vertexIndex] = color; + colors[vertexIndex + 1] = color; + colors[vertexIndex + 2] = color; + colors[vertexIndex + 3] = color; - if (x < meshBoundsMin.x) - meshBoundsMin.x = x; - else if (x > meshBoundsMax.x) - meshBoundsMax.x = x; - if (y < meshBoundsMin.y) - meshBoundsMin.y = y; - else if (y > meshBoundsMax.y) - meshBoundsMax.y = y; - } + float[] regionUVs = regionAttachment.uvs; + uvs[vertexIndex].x = regionUVs[RegionAttachment.X1]; + uvs[vertexIndex].y = regionUVs[RegionAttachment.Y1]; + uvs[vertexIndex + 1].x = regionUVs[RegionAttachment.X4]; + uvs[vertexIndex + 1].y = regionUVs[RegionAttachment.Y4]; + uvs[vertexIndex + 2].x = regionUVs[RegionAttachment.X2]; + uvs[vertexIndex + 2].y = regionUVs[RegionAttachment.Y2]; + uvs[vertexIndex + 3].x = regionUVs[RegionAttachment.X3]; + uvs[vertexIndex + 3].y = regionUVs[RegionAttachment.Y3]; + + // Calculate min/max X + if (x1 < meshBoundsMin.x) + meshBoundsMin.x = x1; + else if (x1 > meshBoundsMax.x) + meshBoundsMax.x = x1; + if (x2 < meshBoundsMin.x) + meshBoundsMin.x = x2; + else if (x2 > meshBoundsMax.x) + meshBoundsMax.x = x2; + if (x3 < meshBoundsMin.x) + meshBoundsMin.x = x3; + else if (x3 > meshBoundsMax.x) + meshBoundsMax.x = x3; + if (x4 < meshBoundsMin.x) + meshBoundsMin.x = x4; + else if (x4 > meshBoundsMax.x) + meshBoundsMax.x = x4; + + // Calculate min/max Y + if (y1 < meshBoundsMin.y) + meshBoundsMin.y = y1; + else if (y1 > meshBoundsMax.y) + meshBoundsMax.y = y1; + if (y2 < meshBoundsMin.y) + meshBoundsMin.y = y2; + else if (y2 > meshBoundsMax.y) + meshBoundsMax.y = y2; + if (y3 < meshBoundsMin.y) + meshBoundsMin.y = y3; + else if (y3 > meshBoundsMax.y) + meshBoundsMax.y = y3; + if (y4 < meshBoundsMin.y) + meshBoundsMin.y = y4; + else if (y4 > meshBoundsMax.y) + meshBoundsMax.y = y4; + + vertexIndex += 4; } else { - WeightedMeshAttachment weightedMeshAttachment = attachment as WeightedMeshAttachment; - if (weightedMeshAttachment != null) { - int meshVertexCount = weightedMeshAttachment.uvs.Length; + if (!renderMeshes) + continue; + MeshAttachment meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + int meshVertexCount = meshAttachment.vertices.Length; if (tempVertices.Length < meshVertexCount) this.tempVertices = tempVertices = new float[meshVertexCount]; - weightedMeshAttachment.ComputeWorldVertices(slot, tempVertices); + meshAttachment.ComputeWorldVertices(slot, tempVertices); if (pmaVertexColors) { - color.a = (byte)(a * slot.a * weightedMeshAttachment.a); - color.r = (byte)(r * slot.r * weightedMeshAttachment.r * color.a); - color.g = (byte)(g * slot.g * weightedMeshAttachment.g * color.a); - color.b = (byte)(b * slot.b * weightedMeshAttachment.b * color.a); + color.a = (byte)(a * slot.a * meshAttachment.a); + color.r = (byte)(r * slot.r * meshAttachment.r * color.a); + color.g = (byte)(g * slot.g * meshAttachment.g * color.a); + color.b = (byte)(b * slot.b * meshAttachment.b * color.a); if (slot.data.blendMode == BlendMode.additive) color.a = 0; } else { - color.a = (byte)(a * slot.a * weightedMeshAttachment.a); - color.r = (byte)(r * slot.r * weightedMeshAttachment.r * 255); - color.g = (byte)(g * slot.g * weightedMeshAttachment.g * 255); - color.b = (byte)(b * slot.b * weightedMeshAttachment.b * 255); + color.a = (byte)(a * slot.a * meshAttachment.a); + color.r = (byte)(r * slot.r * meshAttachment.r * 255); + color.g = (byte)(g * slot.g * meshAttachment.g * 255); + color.b = (byte)(b * slot.b * meshAttachment.b * 255); } - float[] meshUVs = weightedMeshAttachment.uvs; + float[] meshUVs = meshAttachment.uvs; float z = i * zSpacing; for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { float x = tempVertices[ii], y = tempVertices[ii + 1]; @@ -593,252 +562,305 @@ public class SkeletonRenderer : MonoBehaviour { else if (y > meshBoundsMax.y) meshBoundsMax.y = y; } + } else { + WeightedMeshAttachment weightedMeshAttachment = attachment as WeightedMeshAttachment; + if (weightedMeshAttachment != null) { + int meshVertexCount = weightedMeshAttachment.uvs.Length; + if (tempVertices.Length < meshVertexCount) + this.tempVertices = tempVertices = new float[meshVertexCount]; + weightedMeshAttachment.ComputeWorldVertices(slot, tempVertices); + + if (pmaVertexColors) { + color.a = (byte)(a * slot.a * weightedMeshAttachment.a); + color.r = (byte)(r * slot.r * weightedMeshAttachment.r * color.a); + color.g = (byte)(g * slot.g * weightedMeshAttachment.g * color.a); + color.b = (byte)(b * slot.b * weightedMeshAttachment.b * color.a); + if (slot.data.blendMode == BlendMode.additive) color.a = 0; + } else { + color.a = (byte)(a * slot.a * weightedMeshAttachment.a); + color.r = (byte)(r * slot.r * weightedMeshAttachment.r * 255); + color.g = (byte)(g * slot.g * weightedMeshAttachment.g * 255); + color.b = (byte)(b * slot.b * weightedMeshAttachment.b * 255); + } + + float[] meshUVs = weightedMeshAttachment.uvs; + float z = i * zSpacing; + for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { + float x = tempVertices[ii], y = tempVertices[ii + 1]; + vertices[vertexIndex].x = x; + vertices[vertexIndex].y = y; + vertices[vertexIndex].z = z; + colors[vertexIndex] = color; + uvs[vertexIndex].x = meshUVs[ii]; + uvs[vertexIndex].y = meshUVs[ii + 1]; + + if (x < meshBoundsMin.x) + meshBoundsMin.x = x; + else if (x > meshBoundsMax.x) + meshBoundsMax.x = x; + if (y < meshBoundsMin.y) + meshBoundsMin.y = y; + else if (y > meshBoundsMax.y) + meshBoundsMax.y = y; + } + } + } + } + } while (++i < drawOrderCount); + } + + // Step 3. Move the mesh data into a UnityEngine.Mesh + var currentSmartMesh = doubleBufferedMesh.GetNext(); // Double-buffer for performance. + var currentMesh = currentSmartMesh.mesh; + + currentMesh.vertices = vertices; + currentMesh.colors32 = colors; + currentMesh.uv = uvs; + var currentSmartMeshInstructionUsed = currentSmartMesh.instructionUsed; + #if SPINE_OPTIONAL_NORMALS + if (currentSmartMeshInstructionUsed.vertexCount < vertexCount) { + if (calculateNormals) + currentMesh.normals = normals; + + // For dynamic calculated tangents, this needs to be moved out of the vertexCount check block when replacing the logic, also ensuring the size. + if (calculateTangents) + currentMesh.tangents = this.tangents; + } + #endif + + // Check if the triangles should also be updated. + // This thorough structure check is cheaper than updating triangles every frame. + bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(workingInstruction, currentSmartMeshInstructionUsed); + if (mustUpdateMeshStructure) { + var thisSubmeshMaterials = this.submeshMaterials; + thisSubmeshMaterials.Clear(false); + + int submeshCount = workingSubmeshInstructions.Count; + int oldSubmeshCount = submeshes.Count; + + submeshes.Capacity = submeshCount; + for (int i = oldSubmeshCount; i < submeshCount; i++) + submeshes.Items[i] = new SubmeshTriangleBuffer(); + + var mutableTriangles = !workingInstruction.immutableTriangles; + for (int i = 0, last = submeshCount - 1; i < submeshCount; i++) { + var submeshInstruction = workingSubmeshInstructions.Items[i]; + if (mutableTriangles || i >= oldSubmeshCount) + SetSubmesh(i, submeshInstruction, + #if SPINE_OPTIONAL_FRONTFACING + currentInstructions.attachmentFlips, + #endif + i == last); + thisSubmeshMaterials.Add(submeshInstruction.material); + } + + currentMesh.subMeshCount = submeshCount; + + for (int i = 0; i < submeshCount; ++i) + currentMesh.SetTriangles(submeshes.Items[i].triangles, i); + } + + Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin; + Vector3 meshBoundsCenter = meshBoundsMin + meshBoundsExtents * 0.5f; + currentMesh.bounds = new Bounds(meshBoundsCenter, meshBoundsExtents); + + // CheckIfMustUpdateMaterialArray (last pushed materials vs currently parsed materials) + // Needs to check against the Working Submesh Instructions Materials instead of the cached submeshMaterials. + { + var lastPushedMaterials = this.sharedMaterials; + bool mustUpdateRendererMaterials = mustUpdateMeshStructure || + (lastPushedMaterials.Length != workingSubmeshInstructions.Count); + + if (!mustUpdateRendererMaterials) { + var workingSubmeshInstructionsItems = workingSubmeshInstructions.Items; + for (int i = 0, n = lastPushedMaterials.Length; i < n; i++) { + if (lastPushedMaterials[i].GetInstanceID() != workingSubmeshInstructionsItems[i].material.GetInstanceID()) { // Bounds check is implied above. + mustUpdateRendererMaterials = true; + break; } } } - } while (++i < drawOrderCount); - } - // Step 3. Move the mesh data into a UnityEngine.Mesh - var currentSmartMesh = doubleBufferedMesh.GetNext(); // Double-buffer for performance. - var currentMesh = currentSmartMesh.mesh; + if (mustUpdateRendererMaterials) { + if (submeshMaterials.Count == sharedMaterials.Length) + submeshMaterials.CopyTo(sharedMaterials); + else + sharedMaterials = submeshMaterials.ToArray(); - currentMesh.vertices = vertices; - currentMesh.colors32 = colors; - currentMesh.uv = uvs; - var currentSmartMeshInstructionUsed = currentSmartMesh.instructionUsed; - #if SPINE_OPTIONAL_NORMALS - if (currentSmartMeshInstructionUsed.vertexCount < vertexCount) { - if (calculateNormals) - currentMesh.normals = normals; - - // For dynamic calculated tangents, this needs to be moved out of the vertexCount check block when replacing the logic, also ensuring the size. - if (calculateTangents) - currentMesh.tangents = this.tangents; - } - #endif - - // Check if the triangles should also be updated. - // This thorough structure check is cheaper than updating triangles every frame. - bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(workingInstruction, currentSmartMeshInstructionUsed); - if (mustUpdateMeshStructure) { - var thisSubmeshMaterials = this.submeshMaterials; - thisSubmeshMaterials.Clear(false); - - int submeshCount = workingSubmeshInstructions.Count; - int oldSubmeshCount = submeshes.Count; - - submeshes.Capacity = submeshCount; - for (int i = oldSubmeshCount; i < submeshCount; i++) - submeshes.Items[i] = new SubmeshTriangleBuffer(); - - var mutableTriangles = !workingInstruction.immutableTriangles; - for (int i = 0, last = submeshCount - 1; i < submeshCount; i++) { - var submeshInstruction = workingSubmeshInstructions.Items[i]; - if (mutableTriangles || i >= oldSubmeshCount) - SetSubmesh(i, submeshInstruction, - #if SPINE_OPTIONAL_FRONTFACING - currentInstructions.attachmentFlips, - #endif - i == last); - thisSubmeshMaterials.Add(submeshInstruction.material); - } - - currentMesh.subMeshCount = submeshCount; - - for (int i = 0; i < submeshCount; ++i) - currentMesh.SetTriangles(submeshes.Items[i].triangles, i); - } - - Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin; - Vector3 meshBoundsCenter = meshBoundsMin + meshBoundsExtents * 0.5f; - currentMesh.bounds = new Bounds(meshBoundsCenter, meshBoundsExtents); - - // CheckIfMustUpdateMaterialArray (last pushed materials vs currently parsed materials) - // Needs to check against the Working Submesh Instructions Materials instead of the cached submeshMaterials. - { - var lastPushedMaterials = this.sharedMaterials; - bool mustUpdateRendererMaterials = mustUpdateMeshStructure || - (lastPushedMaterials.Length != workingSubmeshInstructions.Count); - - if (!mustUpdateRendererMaterials) { - var workingSubmeshInstructionsItems = workingSubmeshInstructions.Items; - for (int i = 0, n = lastPushedMaterials.Length; i < n; i++) { - if (lastPushedMaterials[i].GetInstanceID() != workingSubmeshInstructionsItems[i].material.GetInstanceID()) { // Bounds check is implied above. - mustUpdateRendererMaterials = true; - break; - } + meshRenderer.sharedMaterials = sharedMaterials; } } - if (mustUpdateRendererMaterials) { - if (submeshMaterials.Count == sharedMaterials.Length) - submeshMaterials.CopyTo(sharedMaterials); - else - sharedMaterials = submeshMaterials.ToArray(); + // Step 4. The UnityEngine.Mesh is ready. Set it as the MeshFilter's mesh. Store the instructions used for that mesh. + meshFilter.sharedMesh = currentMesh; + currentSmartMesh.instructionUsed.Set(workingInstruction); - meshRenderer.sharedMaterials = sharedMaterials; + + // Step 5. Miscellaneous + // Add stuff here if you want + + #if SPINE_OPTIONAL_SUBMESHRENDERER + if (submeshRenderers.Length > 0) { + for (int i = 0; i < submeshRenderers.Length; i++) { + var submeshRenderer = submeshRenderers[i]; + if (submeshRenderer.submeshIndex < sharedMaterials.Length) + submeshRenderer.SetMesh(meshRenderer, currentMesh, sharedMaterials[submeshRenderer.submeshIndex]); + else + submeshRenderer.GetComponent().enabled = false; + } } + #endif } - // Step 4. The UnityEngine.Mesh is ready. Set it as the MeshFilter's mesh. Store the instructions used for that mesh. - meshFilter.sharedMesh = currentMesh; - currentSmartMesh.instructionUsed.Set(workingInstruction); + static bool CheckIfMustUpdateMeshStructure (SmartMesh.Instruction a, SmartMesh.Instruction b) { - - // Step 5. Miscellaneous - // Add stuff here if you want - } - - static bool CheckIfMustUpdateMeshStructure (SmartMesh.Instruction a, SmartMesh.Instruction b) { - - #if UNITY_EDITOR - if (!Application.isPlaying) - return true; - #endif - - if (a.vertexCount != b.vertexCount) - return true; - - if (a.immutableTriangles != b.immutableTriangles) - return true; - - int attachmentCountB = b.attachments.Count; - if (a.attachments.Count != attachmentCountB) // Bounds check for the looped storedAttachments count below. - return true; - - var attachmentsA = a.attachments.Items; - var attachmentsB = b.attachments.Items; - for (int i = 0; i < attachmentCountB; i++) { - if (attachmentsA[i] != attachmentsB[i]) + #if UNITY_EDITOR + if (!Application.isPlaying) return true; - } + #endif - #if SPINE_OPTIONAL_FRONTFACING - if (a.frontFacing != b.frontFacing) { // if settings changed - return true; - } else if (a.frontFacing) { // if settings matched, only need to check one. - var flipsA = a.attachmentFlips.Items; - var flipsB = b.attachmentFlips.Items; + if (a.vertexCount != b.vertexCount) + return true; + + if (a.immutableTriangles != b.immutableTriangles) + return true; + + int attachmentCountB = b.attachments.Count; + if (a.attachments.Count != attachmentCountB) // Bounds check for the looped storedAttachments count below. + return true; + + var attachmentsA = a.attachments.Items; + var attachmentsB = b.attachments.Items; for (int i = 0; i < attachmentCountB; i++) { - if (flipsA[i] != flipsB[i]) + if (attachmentsA[i] != attachmentsB[i]) return true; } - } - #endif - // Submesh count changed - int submeshCountA = a.submeshInstructions.Count; - int submeshCountB = b.submeshInstructions.Count; - if (submeshCountA != submeshCountB) - return true; - - // Submesh Instruction mismatch - var submeshInstructionsItemsA = a.submeshInstructions.Items; - var submeshInstructionsItemsB = b.submeshInstructions.Items; - for (int i = 0; i < submeshCountB; i++) { - var submeshA = submeshInstructionsItemsA[i]; - var submeshB = submeshInstructionsItemsB[i]; - - if (!( - submeshA.vertexCount == submeshB.vertexCount && - submeshA.startSlot == submeshB.startSlot && - submeshA.endSlot == submeshB.endSlot && - submeshA.triangleCount == submeshB.triangleCount && - submeshA.firstVertexIndex == submeshB.firstVertexIndex - )) - return true; - } - - return false; - } - - #if SPINE_OPTIONAL_FRONTFACING - void SetSubmesh (int submeshIndex, Spine.Unity.MeshGeneration.SubmeshInstruction submeshInstructions, ExposedList flipStates, bool isLastSubmesh) { - #else - void SetSubmesh (int submeshIndex, Spine.Unity.MeshGeneration.SubmeshInstruction submeshInstructions, bool isLastSubmesh) { - #endif - SubmeshTriangleBuffer currentSubmesh = submeshes.Items[submeshIndex]; - int[] triangles = currentSubmesh.triangles; - - int triangleCount = submeshInstructions.triangleCount; - int firstVertex = submeshInstructions.firstVertexIndex; - - int trianglesCapacity = triangles.Length; - if (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; - - currentSubmesh.triangleCount = triangleCount; - - } else if (trianglesCapacity != triangleCount) { - // Reallocate triangles when not the exact size needed. - currentSubmesh.triangles = triangles = new int[triangleCount]; - currentSubmesh.triangleCount = 0; - } - - #if SPINE_OPTIONAL_FRONTFACING - if (!this.renderMeshes && !this.frontFacing) { - #else - if (!this.renderMeshes) { - #endif - // Use stored triangles if possible. - 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; - triangles[i + 3] = firstVertex + 2; - triangles[i + 4] = firstVertex + 3; - triangles[i + 5] = firstVertex + 1; - } - } - return; - } - - // This method caches several .Items arrays. - // Never mutate their overlying ExposedList objects. - - #if SPINE_OPTIONAL_FRONTFACING - var flipStatesItems = flipStates.Items; - #endif - - // Iterate through all slots and store their triangles. - var drawOrderItems = skeleton.DrawOrder.Items; - int triangleIndex = 0; // Modified by loop - for (int i = submeshInstructions.startSlot, n = submeshInstructions.endSlot; i < n; i++) { - Attachment attachment = drawOrderItems[i].attachment; #if SPINE_OPTIONAL_FRONTFACING - bool flip = frontFacing && flipStatesItems[i]; - - // Add RegionAttachment triangles - if (attachment is RegionAttachment) { - if (!flip) { - triangles[triangleIndex] = firstVertex; - triangles[triangleIndex + 1] = firstVertex + 2; - triangles[triangleIndex + 2] = firstVertex + 1; - triangles[triangleIndex + 3] = firstVertex + 2; - triangles[triangleIndex + 4] = firstVertex + 3; - triangles[triangleIndex + 5] = firstVertex + 1; - } else { - triangles[triangleIndex] = firstVertex + 1; - triangles[triangleIndex + 1] = firstVertex + 2; - triangles[triangleIndex + 2] = firstVertex; - triangles[triangleIndex + 3] = firstVertex + 1; - triangles[triangleIndex + 4] = firstVertex + 3; - triangles[triangleIndex + 5] = firstVertex + 2; + if (a.frontFacing != b.frontFacing) { // if settings changed + return true; + } else if (a.frontFacing) { // if settings matched, only need to check one. + var flipsA = a.attachmentFlips.Items; + var flipsB = b.attachmentFlips.Items; + for (int i = 0; i < attachmentCountB; i++) { + if (flipsA[i] != flipsB[i]) + return true; } - - triangleIndex += 6; - firstVertex += 4; - continue; } + #endif + + // Submesh count changed + int submeshCountA = a.submeshInstructions.Count; + int submeshCountB = b.submeshInstructions.Count; + if (submeshCountA != submeshCountB) + return true; + + // Submesh Instruction mismatch + var submeshInstructionsItemsA = a.submeshInstructions.Items; + var submeshInstructionsItemsB = b.submeshInstructions.Items; + for (int i = 0; i < submeshCountB; i++) { + var submeshA = submeshInstructionsItemsA[i]; + var submeshB = submeshInstructionsItemsB[i]; + + if (!( + submeshA.vertexCount == submeshB.vertexCount && + submeshA.startSlot == submeshB.startSlot && + submeshA.endSlot == submeshB.endSlot && + submeshA.triangleCount == submeshB.triangleCount && + submeshA.firstVertexIndex == submeshB.firstVertexIndex + )) + return true; + } + + return false; + } + + #if SPINE_OPTIONAL_FRONTFACING + void SetSubmesh (int submeshIndex, Spine.Unity.MeshGeneration.SubmeshInstruction submeshInstructions, ExposedList flipStates, bool isLastSubmesh) { + #else + void SetSubmesh (int submeshIndex, Spine.Unity.MeshGeneration.SubmeshInstruction submeshInstructions, bool isLastSubmesh) { + #endif + SubmeshTriangleBuffer currentSubmesh = submeshes.Items[submeshIndex]; + int[] triangles = currentSubmesh.triangles; + + int triangleCount = submeshInstructions.triangleCount; + int firstVertex = submeshInstructions.firstVertexIndex; + + int trianglesCapacity = triangles.Length; + if (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; + + currentSubmesh.triangleCount = triangleCount; + + } else if (trianglesCapacity != triangleCount) { + // Reallocate triangles when not the exact size needed. + currentSubmesh.triangles = triangles = new int[triangleCount]; + currentSubmesh.triangleCount = 0; + } + + #if SPINE_OPTIONAL_FRONTFACING + if (!this.renderMeshes && !this.frontFacing) { #else - if (attachment is RegionAttachment) { + if (!this.renderMeshes) { + #endif + // Use stored triangles if possible. + 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; + triangles[i + 3] = firstVertex + 2; + triangles[i + 4] = firstVertex + 3; + triangles[i + 5] = firstVertex + 1; + } + } + return; + } + + // This method caches several .Items arrays. + // Never mutate their overlying ExposedList objects. + + #if SPINE_OPTIONAL_FRONTFACING + var flipStatesItems = flipStates.Items; + #endif + + // Iterate through all slots and store their triangles. + var drawOrderItems = skeleton.DrawOrder.Items; + int triangleIndex = 0; // Modified by loop + for (int i = submeshInstructions.startSlot, n = submeshInstructions.endSlot; i < n; i++) { + Attachment attachment = drawOrderItems[i].attachment; + #if SPINE_OPTIONAL_FRONTFACING + bool flip = frontFacing && flipStatesItems[i]; + + // Add RegionAttachment triangles + if (attachment is RegionAttachment) { + if (!flip) { + triangles[triangleIndex] = firstVertex; + triangles[triangleIndex + 1] = firstVertex + 2; + triangles[triangleIndex + 2] = firstVertex + 1; + triangles[triangleIndex + 3] = firstVertex + 2; + triangles[triangleIndex + 4] = firstVertex + 3; + triangles[triangleIndex + 5] = firstVertex + 1; + } else { + triangles[triangleIndex] = firstVertex + 1; + triangles[triangleIndex + 1] = firstVertex + 2; + triangles[triangleIndex + 2] = firstVertex; + triangles[triangleIndex + 3] = firstVertex + 1; + triangles[triangleIndex + 4] = firstVertex + 3; + triangles[triangleIndex + 5] = firstVertex + 2; + } + + triangleIndex += 6; + firstVertex += 4; + continue; + } + #else + if (attachment is RegionAttachment) { triangles[triangleIndex] = firstVertex; triangles[triangleIndex + 1] = firstVertex + 2; triangles[triangleIndex + 2] = firstVertex + 1; @@ -849,121 +871,120 @@ public class SkeletonRenderer : MonoBehaviour { triangleIndex += 6; firstVertex += 4; continue; - } - #endif - - // Add (Weighted)MeshAttachment triangles - int[] attachmentTriangles; - int attachmentVertexCount; - var meshAttachment = attachment as MeshAttachment; - if (meshAttachment != null) { - attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2 - attachmentTriangles = meshAttachment.triangles; - } else { - var weightedMeshAttachment = attachment as WeightedMeshAttachment; - if (weightedMeshAttachment != null) { - attachmentVertexCount = weightedMeshAttachment.uvs.Length >> 1; // length/2 - attachmentTriangles = weightedMeshAttachment.triangles; - } else - continue; - } - - #if SPINE_OPTIONAL_FRONTFACING - if (flip) { - for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii += 3, triangleIndex += 3) { - triangles[triangleIndex + 2] = firstVertex + attachmentTriangles[ii]; - triangles[triangleIndex + 1] = firstVertex + attachmentTriangles[ii + 1]; - triangles[triangleIndex] = firstVertex + attachmentTriangles[ii + 2]; } - } else { + #endif + + // Add (Weighted)MeshAttachment triangles + int[] attachmentTriangles; + int attachmentVertexCount; + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2 + attachmentTriangles = meshAttachment.triangles; + } else { + var weightedMeshAttachment = attachment as WeightedMeshAttachment; + if (weightedMeshAttachment != null) { + attachmentVertexCount = weightedMeshAttachment.uvs.Length >> 1; // length/2 + attachmentTriangles = weightedMeshAttachment.triangles; + } else + continue; + } + + #if SPINE_OPTIONAL_FRONTFACING + if (flip) { + for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii += 3, triangleIndex += 3) { + triangles[triangleIndex + 2] = firstVertex + attachmentTriangles[ii]; + triangles[triangleIndex + 1] = firstVertex + attachmentTriangles[ii + 1]; + triangles[triangleIndex] = firstVertex + attachmentTriangles[ii + 2]; + } + } else { + for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) { + triangles[triangleIndex] = firstVertex + attachmentTriangles[ii]; + } + } + #else for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) { - triangles[triangleIndex] = firstVertex + attachmentTriangles[ii]; + triangles[triangleIndex] = firstVertex + attachmentTriangles[ii]; + } + #endif + + firstVertex += attachmentVertexCount; + } + } + + #if UNITY_EDITOR + void OnDrawGizmos () { + // Make scene view selection easier by drawing a clear gizmo over the skeleton. + meshFilter = GetComponent(); + if (meshFilter == null) return; + + Mesh mesh = meshFilter.sharedMesh; + if (mesh == null) return; + + Bounds meshBounds = mesh.bounds; + Gizmos.color = Color.clear; + Gizmos.matrix = transform.localToWorldMatrix; + Gizmos.DrawCube(meshBounds.center, meshBounds.size); + } + #endif + + ///This is a Mesh that also stores the instructions SkeletonRenderer generated for it. + public class SmartMesh { + public Mesh mesh = Spine.Unity.SpineMesh.NewMesh(); + public SmartMesh.Instruction instructionUsed = new SmartMesh.Instruction(); + + public class Instruction { + public bool immutableTriangles; + public int vertexCount = -1; + public readonly ExposedList attachments = new ExposedList(); + public readonly ExposedList submeshInstructions = new ExposedList(); + + #if SPINE_OPTIONAL_FRONTFACING + public bool frontFacing; + public readonly ExposedList attachmentFlips = new ExposedList(); + #endif + + public void Clear () { + this.attachments.Clear(false); + this.submeshInstructions.Clear(false); + + #if SPINE_OPTIONAL_FRONTFACING + this.attachmentFlips.Clear(false); + #endif + } + + public void Set (Instruction other) { + this.immutableTriangles = other.immutableTriangles; + this.vertexCount = other.vertexCount; + + this.attachments.Clear(false); + this.attachments.GrowIfNeeded(other.attachments.Capacity); + this.attachments.Count = other.attachments.Count; + other.attachments.CopyTo(this.attachments.Items); + + #if SPINE_OPTIONAL_FRONTFACING + this.frontFacing = other.frontFacing; + this.attachmentFlips.Clear(false); + this.attachmentFlips.GrowIfNeeded(other.attachmentFlips.Capacity); + this.attachmentFlips.Count = other.attachmentFlips.Count; + if (this.frontFacing) + other.attachmentFlips.CopyTo(this.attachmentFlips.Items); + #endif + + this.submeshInstructions.Clear(false); + this.submeshInstructions.GrowIfNeeded(other.submeshInstructions.Capacity); + this.submeshInstructions.Count = other.submeshInstructions.Count; + other.submeshInstructions.CopyTo(this.submeshInstructions.Items); } } - #else - for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) { - triangles[triangleIndex] = firstVertex + attachmentTriangles[ii]; - } - #endif + } - firstVertex += attachmentVertexCount; + class SubmeshTriangleBuffer { + public int[] triangles = new int[0]; + + // These two fields are used when renderMeshes == false + public int triangleCount; + public int firstVertex = -1; } } - - #if UNITY_EDITOR - void OnDrawGizmos () { - // Make scene view selection easier by drawing a clear gizmo over the skeleton. - meshFilter = GetComponent(); - if (meshFilter == null) return; - - Mesh mesh = meshFilter.sharedMesh; - if (mesh == null) return; - - Bounds meshBounds = mesh.bounds; - Gizmos.color = Color.clear; - Gizmos.matrix = transform.localToWorldMatrix; - Gizmos.DrawCube(meshBounds.center, meshBounds.size); - } - #endif - - ///This is a Mesh that also stores the instructions SkeletonRenderer generated for it. - public class SmartMesh { - public Mesh mesh = Spine.Unity.SpineMesh.NewMesh(); - public SmartMesh.Instruction instructionUsed = new SmartMesh.Instruction(); - - public class Instruction { - public bool immutableTriangles; - public int vertexCount = -1; - public readonly ExposedList attachments = new ExposedList(); - public readonly ExposedList submeshInstructions = new ExposedList(); - - #if SPINE_OPTIONAL_FRONTFACING - public bool frontFacing; - public readonly ExposedList attachmentFlips = new ExposedList(); - #endif - - public void Clear () { - this.attachments.Clear(false); - this.submeshInstructions.Clear(false); - - #if SPINE_OPTIONAL_FRONTFACING - this.attachmentFlips.Clear(false); - #endif - } - - public void Set (Instruction other) { - this.immutableTriangles = other.immutableTriangles; - this.vertexCount = other.vertexCount; - - this.attachments.Clear(false); - this.attachments.GrowIfNeeded(other.attachments.Capacity); - this.attachments.Count = other.attachments.Count; - other.attachments.CopyTo(this.attachments.Items); - - #if SPINE_OPTIONAL_FRONTFACING - this.frontFacing = other.frontFacing; - this.attachmentFlips.Clear(false); - this.attachmentFlips.GrowIfNeeded(other.attachmentFlips.Capacity); - this.attachmentFlips.Count = other.attachmentFlips.Count; - if (this.frontFacing) - other.attachmentFlips.CopyTo(this.attachmentFlips.Items); - #endif - - this.submeshInstructions.Clear(false); - this.submeshInstructions.GrowIfNeeded(other.submeshInstructions.Capacity); - this.submeshInstructions.Count = other.submeshInstructions.Count; - other.submeshInstructions.CopyTo(this.submeshInstructions.Items); - } - } - } - - class SubmeshTriangleBuffer { - public int[] triangles = new int[0]; - - // These two fields are used when renderMeshes == false - public int triangleCount; - public int firstVertex = -1; - } -} - - +} \ No newline at end of file diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs index 613d4740b..b096efcad 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs @@ -10,345 +10,348 @@ using System.Collections; using System.Collections.Generic; using Spine; -[CustomEditor(typeof(SkeletonUtilityBone)), CanEditMultipleObjects] -public class SkeletonUtilityBoneInspector : Editor { - SerializedProperty mode, boneName, zPosition, position, rotation, scale, overrideAlpha, parentReference; - // MITCH -// SerializedProperty flip, flipX; - - //multi selected flags - bool containsFollows, containsOverrides, multiObject; - - //single selected helpers - SkeletonUtilityBone utilityBone; - SkeletonUtility skeletonUtility; - bool canCreateHingeChain = false; - - Dictionary> boundingBoxTable = new Dictionary>(); - string currentSkinName = ""; - - void OnEnable () { - mode = this.serializedObject.FindProperty("mode"); - boneName = this.serializedObject.FindProperty("boneName"); - zPosition = this.serializedObject.FindProperty("zPosition"); - position = this.serializedObject.FindProperty("position"); - rotation = this.serializedObject.FindProperty("rotation"); - scale = this.serializedObject.FindProperty("scale"); - overrideAlpha = this.serializedObject.FindProperty("overrideAlpha"); - parentReference = this.serializedObject.FindProperty("parentReference"); - +namespace Spine.Unity.Editor { + [CustomEditor(typeof(SkeletonUtilityBone)), CanEditMultipleObjects] + public class SkeletonUtilityBoneInspector : UnityEditor.Editor { + SerializedProperty mode, boneName, zPosition, position, rotation, scale, overrideAlpha, parentReference; // MITCH -// flip = this.serializedObject.FindProperty("flip"); -// flipX = this.serializedObject.FindProperty("flipX"); + // SerializedProperty flip, flipX; - EvaluateFlags(); + //multi selected flags + bool containsFollows, containsOverrides, multiObject; - if (utilityBone.valid == false && skeletonUtility != null && skeletonUtility.skeletonRenderer != null) { - skeletonUtility.skeletonRenderer.Initialize(false); - } + //single selected helpers + SkeletonUtilityBone utilityBone; + SkeletonUtility skeletonUtility; + bool canCreateHingeChain = false; - canCreateHingeChain = CanCreateHingeChain(); + Dictionary> boundingBoxTable = new Dictionary>(); + string currentSkinName = ""; - boundingBoxTable.Clear(); - - if (multiObject) - return; - - if (utilityBone.bone == null) - return; - - var skeleton = utilityBone.bone.Skeleton; - int slotCount = skeleton.Slots.Count; - Skin skin = skeleton.Skin; - if (skeleton.Skin == null) - skin = skeleton.Data.DefaultSkin; - - currentSkinName = skin.Name; - for(int i = 0; i < slotCount; i++){ - Slot slot = skeletonUtility.skeletonRenderer.skeleton.Slots.Items[i]; - if (slot.Bone == utilityBone.bone) { - List attachments = new List(); - - - skin.FindAttachmentsForSlot(skeleton.FindSlotIndex(slot.Data.Name), attachments); - - List boundingBoxes = new List(); - foreach (var att in attachments) { - if (att is BoundingBoxAttachment) { - boundingBoxes.Add((BoundingBoxAttachment)att); - } - } - - if (boundingBoxes.Count > 0) { - boundingBoxTable.Add(slot, boundingBoxes); - } - } - } - - } - - void EvaluateFlags () { - utilityBone = (SkeletonUtilityBone)target; - skeletonUtility = utilityBone.skeletonUtility; - - if (Selection.objects.Length == 1) { - containsFollows = utilityBone.mode == SkeletonUtilityBone.Mode.Follow; - containsOverrides = utilityBone.mode == SkeletonUtilityBone.Mode.Override; - } else { - int boneCount = 0; - foreach (Object o in Selection.objects) { - if (o is GameObject) { - GameObject go = (GameObject)o; - SkeletonUtilityBone sub = go.GetComponent(); - if (sub != null) { - boneCount++; - if (sub.mode == SkeletonUtilityBone.Mode.Follow) - containsFollows = true; - if (sub.mode == SkeletonUtilityBone.Mode.Override) - containsOverrides = true; - } - } - } - - if (boneCount > 1) - multiObject = true; - } - } - - public override void OnInspectorGUI () { - serializedObject.Update(); - - EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(mode); - if (EditorGUI.EndChangeCheck()) { - containsOverrides = mode.enumValueIndex == 1; - containsFollows = mode.enumValueIndex == 0; - } - - EditorGUI.BeginDisabledGroup(multiObject); - { - string str = boneName.stringValue; - if (str == "") - str = ""; - if (multiObject) - str = ""; - - GUILayout.BeginHorizontal(); - EditorGUILayout.PrefixLabel("Bone"); - - if (GUILayout.Button(str, EditorStyles.popup)) { - BoneSelectorContextMenu(str, ((SkeletonUtilityBone)target).skeletonUtility.skeletonRenderer.skeleton.Bones, "", TargetBoneSelected); - } - - GUILayout.EndHorizontal(); - } - EditorGUI.EndDisabledGroup(); - - EditorGUILayout.PropertyField(zPosition); - EditorGUILayout.PropertyField(position); - EditorGUILayout.PropertyField(rotation); - EditorGUILayout.PropertyField(scale); - // MITCH -// EditorGUILayout.PropertyField(flip); - - EditorGUI.BeginDisabledGroup(containsFollows); - { - EditorGUILayout.PropertyField(overrideAlpha); - EditorGUILayout.PropertyField(parentReference); + void OnEnable () { + mode = this.serializedObject.FindProperty("mode"); + boneName = this.serializedObject.FindProperty("boneName"); + zPosition = this.serializedObject.FindProperty("zPosition"); + position = this.serializedObject.FindProperty("position"); + rotation = this.serializedObject.FindProperty("rotation"); + scale = this.serializedObject.FindProperty("scale"); + overrideAlpha = this.serializedObject.FindProperty("overrideAlpha"); + parentReference = this.serializedObject.FindProperty("parentReference"); // MITCH -// EditorGUI.BeginDisabledGroup(multiObject || !flip.boolValue); -// { -// EditorGUI.BeginChangeCheck(); -// EditorGUILayout.PropertyField(flipX); -// if (EditorGUI.EndChangeCheck()) { -// FlipX(flipX.boolValue); -// } -// } -// EditorGUI.EndDisabledGroup(); + // flip = this.serializedObject.FindProperty("flip"); + // flipX = this.serializedObject.FindProperty("flipX"); - } - EditorGUI.EndDisabledGroup(); + EvaluateFlags(); - EditorGUILayout.Space(); - - GUILayout.BeginHorizontal(); - { - EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || utilityBone.bone == null || utilityBone.bone.Children.Count == 0); - { - if (GUILayout.Button(new GUIContent("Add Child", SpineEditorUtilities.Icons.bone), GUILayout.Width(150), GUILayout.Height(24))) - BoneSelectorContextMenu("", utilityBone.bone.Children, "", SpawnChildBoneSelected); + if (utilityBone.valid == false && skeletonUtility != null && skeletonUtility.skeletonRenderer != null) { + skeletonUtility.skeletonRenderer.Initialize(false); } - EditorGUI.EndDisabledGroup(); - EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || utilityBone.bone == null || containsOverrides); - { - if (GUILayout.Button(new GUIContent("Add Override", SpineEditorUtilities.Icons.poseBones), GUILayout.Width(150), GUILayout.Height(24))) - SpawnOverride(); - } - EditorGUI.EndDisabledGroup(); + canCreateHingeChain = CanCreateHingeChain(); - EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || !canCreateHingeChain); - { - if (GUILayout.Button(new GUIContent("Create Hinge Chain", SpineEditorUtilities.Icons.hingeChain), GUILayout.Width(150), GUILayout.Height(24))) - CreateHingeChain(); - } - EditorGUI.EndDisabledGroup(); + boundingBoxTable.Clear(); - } - GUILayout.EndHorizontal(); + if (multiObject) + return; - EditorGUI.BeginDisabledGroup(multiObject || boundingBoxTable.Count == 0); - EditorGUILayout.LabelField(new GUIContent("Bounding Boxes", SpineEditorUtilities.Icons.boundingBox), EditorStyles.boldLabel); + if (utilityBone.bone == null) + return; - foreach(var entry in boundingBoxTable){ - EditorGUI.indentLevel++; - EditorGUILayout.LabelField(entry.Key.Data.Name); - EditorGUI.indentLevel++; - foreach (var box in entry.Value) { - GUILayout.BeginHorizontal(); - GUILayout.Space(30); - if (GUILayout.Button(box.Name, GUILayout.Width(200))) { - var child = utilityBone.transform.FindChild("[BoundingBox]" + box.Name); - if (child != null) { - var originalCollider = child.GetComponent(); - var updatedCollider = SkeletonUtility.AddBoundingBoxAsComponent(box, child.gameObject, originalCollider.isTrigger); - originalCollider.points = updatedCollider.points; - if (EditorApplication.isPlaying) - Destroy(updatedCollider); - else - DestroyImmediate(updatedCollider); - } else { - utilityBone.AddBoundingBox(currentSkinName, entry.Key.Data.Name, box.Name); + var skeleton = utilityBone.bone.Skeleton; + int slotCount = skeleton.Slots.Count; + Skin skin = skeleton.Skin; + if (skeleton.Skin == null) + skin = skeleton.Data.DefaultSkin; + + currentSkinName = skin.Name; + for(int i = 0; i < slotCount; i++){ + Slot slot = skeletonUtility.skeletonRenderer.skeleton.Slots.Items[i]; + if (slot.Bone == utilityBone.bone) { + List attachments = new List(); + + + skin.FindAttachmentsForSlot(skeleton.FindSlotIndex(slot.Data.Name), attachments); + + List boundingBoxes = new List(); + foreach (var att in attachments) { + if (att is BoundingBoxAttachment) { + boundingBoxes.Add((BoundingBoxAttachment)att); + } + } + + if (boundingBoxes.Count > 0) { + boundingBoxTable.Add(slot, boundingBoxes); } - } + } + + } + + void EvaluateFlags () { + utilityBone = (SkeletonUtilityBone)target; + skeletonUtility = utilityBone.skeletonUtility; + + if (Selection.objects.Length == 1) { + containsFollows = utilityBone.mode == SkeletonUtilityBone.Mode.Follow; + containsOverrides = utilityBone.mode == SkeletonUtilityBone.Mode.Override; + } else { + int boneCount = 0; + foreach (Object o in Selection.objects) { + if (o is GameObject) { + GameObject go = (GameObject)o; + SkeletonUtilityBone sub = go.GetComponent(); + if (sub != null) { + boneCount++; + if (sub.mode == SkeletonUtilityBone.Mode.Follow) + containsFollows = true; + if (sub.mode == SkeletonUtilityBone.Mode.Override) + containsOverrides = true; + } + } + } + + if (boneCount > 1) + multiObject = true; + } + } + + public override void OnInspectorGUI () { + serializedObject.Update(); + + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(mode); + if (EditorGUI.EndChangeCheck()) { + containsOverrides = mode.enumValueIndex == 1; + containsFollows = mode.enumValueIndex == 0; + } + + EditorGUI.BeginDisabledGroup(multiObject); + { + string str = boneName.stringValue; + if (str == "") + str = ""; + if (multiObject) + str = ""; + + GUILayout.BeginHorizontal(); + EditorGUILayout.PrefixLabel("Bone"); + + if (GUILayout.Button(str, EditorStyles.popup)) { + BoneSelectorContextMenu(str, ((SkeletonUtilityBone)target).skeletonUtility.skeletonRenderer.skeleton.Bones, "", TargetBoneSelected); + } + GUILayout.EndHorizontal(); } - } + EditorGUI.EndDisabledGroup(); - EditorGUI.EndDisabledGroup(); + EditorGUILayout.PropertyField(zPosition); + EditorGUILayout.PropertyField(position); + EditorGUILayout.PropertyField(rotation); + EditorGUILayout.PropertyField(scale); + // MITCH + // EditorGUILayout.PropertyField(flip); - serializedObject.ApplyModifiedProperties(); - } + EditorGUI.BeginDisabledGroup(containsFollows); + { + EditorGUILayout.PropertyField(overrideAlpha); + EditorGUILayout.PropertyField(parentReference); - // MITCH -// void FlipX (bool state) { -// utilityBone.FlipX(state); -// if (Application.isPlaying == false) { -// skeletonUtility.skeletonAnimation.LateUpdate(); -// } -// } + // MITCH + // EditorGUI.BeginDisabledGroup(multiObject || !flip.boolValue); + // { + // EditorGUI.BeginChangeCheck(); + // EditorGUILayout.PropertyField(flipX); + // if (EditorGUI.EndChangeCheck()) { + // FlipX(flipX.boolValue); + // } + // } + // EditorGUI.EndDisabledGroup(); - void BoneSelectorContextMenu (string current, ExposedList bones, string topValue, GenericMenu.MenuFunction2 callback) { - GenericMenu menu = new GenericMenu(); - - if (topValue != "") - menu.AddItem(new GUIContent(topValue), current == topValue, callback, null); - - for (int i = 0; i < bones.Count; i++) { - menu.AddItem(new GUIContent(bones.Items[i].Data.Name), bones.Items[i].Data.Name == current, callback, bones.Items[i]); - } - - menu.ShowAsContext(); - - } - - void TargetBoneSelected (object obj) { - if (obj == null) { - boneName.stringValue = ""; - serializedObject.ApplyModifiedProperties(); - } else { - Bone bone = (Bone)obj; - boneName.stringValue = bone.Data.Name; - serializedObject.ApplyModifiedProperties(); - - utilityBone.Reset(); - } - } - - void SpawnChildBoneSelected (object obj) { - if (obj == null) { - //add recursively - foreach (var bone in utilityBone.bone.Children) { - GameObject go = skeletonUtility.SpawnBoneRecursively(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale); - SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren(); - foreach (SkeletonUtilityBone utilBone in newUtilityBones) - SkeletonUtilityInspector.AttachIcon(utilBone); } - } else { - Bone bone = (Bone)obj; - GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale); + EditorGUI.EndDisabledGroup(); + + EditorGUILayout.Space(); + + GUILayout.BeginHorizontal(); + { + EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || utilityBone.bone == null || utilityBone.bone.Children.Count == 0); + { + if (GUILayout.Button(new GUIContent("Add Child", SpineEditorUtilities.Icons.bone), GUILayout.Width(150), GUILayout.Height(24))) + BoneSelectorContextMenu("", utilityBone.bone.Children, "", SpawnChildBoneSelected); + } + EditorGUI.EndDisabledGroup(); + + EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || utilityBone.bone == null || containsOverrides); + { + if (GUILayout.Button(new GUIContent("Add Override", SpineEditorUtilities.Icons.poseBones), GUILayout.Width(150), GUILayout.Height(24))) + SpawnOverride(); + } + EditorGUI.EndDisabledGroup(); + + EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || !canCreateHingeChain); + { + if (GUILayout.Button(new GUIContent("Create Hinge Chain", SpineEditorUtilities.Icons.hingeChain), GUILayout.Width(150), GUILayout.Height(24))) + CreateHingeChain(); + } + EditorGUI.EndDisabledGroup(); + + } + GUILayout.EndHorizontal(); + + EditorGUI.BeginDisabledGroup(multiObject || boundingBoxTable.Count == 0); + EditorGUILayout.LabelField(new GUIContent("Bounding Boxes", SpineEditorUtilities.Icons.boundingBox), EditorStyles.boldLabel); + + foreach(var entry in boundingBoxTable){ + EditorGUI.indentLevel++; + EditorGUILayout.LabelField(entry.Key.Data.Name); + EditorGUI.indentLevel++; + foreach (var box in entry.Value) { + GUILayout.BeginHorizontal(); + GUILayout.Space(30); + if (GUILayout.Button(box.Name, GUILayout.Width(200))) { + var child = utilityBone.transform.FindChild("[BoundingBox]" + box.Name); + if (child != null) { + var originalCollider = child.GetComponent(); + var updatedCollider = SkeletonUtility.AddBoundingBoxAsComponent(box, child.gameObject, originalCollider.isTrigger); + originalCollider.points = updatedCollider.points; + if (EditorApplication.isPlaying) + Destroy(updatedCollider); + else + DestroyImmediate(updatedCollider); + } else { + utilityBone.AddBoundingBox(currentSkinName, entry.Key.Data.Name, box.Name); + } + + } + GUILayout.EndHorizontal(); + } + } + + EditorGUI.EndDisabledGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + // MITCH + // void FlipX (bool state) { + // utilityBone.FlipX(state); + // if (Application.isPlaying == false) { + // skeletonUtility.skeletonAnimation.LateUpdate(); + // } + // } + + void BoneSelectorContextMenu (string current, ExposedList bones, string topValue, GenericMenu.MenuFunction2 callback) { + GenericMenu menu = new GenericMenu(); + + if (topValue != "") + menu.AddItem(new GUIContent(topValue), current == topValue, callback, null); + + for (int i = 0; i < bones.Count; i++) { + menu.AddItem(new GUIContent(bones.Items[i].Data.Name), bones.Items[i].Data.Name == current, callback, bones.Items[i]); + } + + menu.ShowAsContext(); + + } + + void TargetBoneSelected (object obj) { + if (obj == null) { + boneName.stringValue = ""; + serializedObject.ApplyModifiedProperties(); + } else { + Bone bone = (Bone)obj; + boneName.stringValue = bone.Data.Name; + serializedObject.ApplyModifiedProperties(); + + utilityBone.Reset(); + } + } + + void SpawnChildBoneSelected (object obj) { + if (obj == null) { + //add recursively + foreach (var bone in utilityBone.bone.Children) { + GameObject go = skeletonUtility.SpawnBoneRecursively(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale); + SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren(); + foreach (SkeletonUtilityBone utilBone in newUtilityBones) + SkeletonUtilityInspector.AttachIcon(utilBone); + } + } else { + Bone bone = (Bone)obj; + GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale); + SkeletonUtilityInspector.AttachIcon(go.GetComponent()); + Selection.activeGameObject = go; + EditorGUIUtility.PingObject(go); + } + } + + void SpawnOverride () { + GameObject go = skeletonUtility.SpawnBone(utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale); + go.name = go.name + " [Override]"; SkeletonUtilityInspector.AttachIcon(go.GetComponent()); Selection.activeGameObject = go; EditorGUIUtility.PingObject(go); } - } - void SpawnOverride () { - GameObject go = skeletonUtility.SpawnBone(utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale); - go.name = go.name + " [Override]"; - SkeletonUtilityInspector.AttachIcon(go.GetComponent()); - Selection.activeGameObject = go; - EditorGUIUtility.PingObject(go); - } + bool CanCreateHingeChain () { + if (utilityBone == null) + return false; + if (utilityBone.GetComponent() != null) + return false; + if (utilityBone.bone != null && utilityBone.bone.Children.Count == 0) + return false; - bool CanCreateHingeChain () { - if (utilityBone == null) - return false; - if (utilityBone.GetComponent() != null) - return false; - if (utilityBone.bone != null && utilityBone.bone.Children.Count == 0) - return false; + Rigidbody[] rigidbodies = utilityBone.GetComponentsInChildren(); - Rigidbody[] rigidbodies = utilityBone.GetComponentsInChildren(); + if (rigidbodies.Length > 0) + return false; - if (rigidbodies.Length > 0) - return false; - - return true; - } - - void CreateHingeChain () { - var utilBoneArr = utilityBone.GetComponentsInChildren(); - - foreach (var utilBone in utilBoneArr) { - AttachRigidbody(utilBone); + return true; } - utilityBone.GetComponent().isKinematic = true; + void CreateHingeChain () { + var utilBoneArr = utilityBone.GetComponentsInChildren(); - foreach (var utilBone in utilBoneArr) { - if (utilBone == utilityBone) - continue; + foreach (var utilBone in utilBoneArr) { + AttachRigidbody(utilBone); + } - utilBone.mode = SkeletonUtilityBone.Mode.Override; + utilityBone.GetComponent().isKinematic = true; - HingeJoint joint = utilBone.gameObject.AddComponent(); - joint.axis = Vector3.forward; - joint.connectedBody = utilBone.transform.parent.GetComponent(); - joint.useLimits = true; - JointLimits limits = new JointLimits(); - limits.min = -20; - limits.max = 20; - joint.limits = limits; - utilBone.GetComponent().mass = utilBone.transform.parent.GetComponent().mass * 0.75f; - } - } - - static void AttachRigidbody (SkeletonUtilityBone utilBone) { - if (utilBone.GetComponent() == null) { - if (utilBone.bone.Data.Length == 0) { - SphereCollider sphere = utilBone.gameObject.AddComponent(); - sphere.radius = 0.1f; - } else { - float length = utilBone.bone.Data.Length; - BoxCollider box = utilBone.gameObject.AddComponent(); - box.size = new Vector3(length, length / 3, 0.2f); - box.center = new Vector3(length / 2, 0, 0); + foreach (var utilBone in utilBoneArr) { + if (utilBone == utilityBone) + continue; + + utilBone.mode = SkeletonUtilityBone.Mode.Override; + + HingeJoint joint = utilBone.gameObject.AddComponent(); + joint.axis = Vector3.forward; + joint.connectedBody = utilBone.transform.parent.GetComponent(); + joint.useLimits = true; + JointLimits limits = new JointLimits(); + limits.min = -20; + limits.max = 20; + joint.limits = limits; + utilBone.GetComponent().mass = utilBone.transform.parent.GetComponent().mass * 0.75f; } } - utilBone.gameObject.AddComponent(); + static void AttachRigidbody (SkeletonUtilityBone utilBone) { + if (utilBone.GetComponent() == null) { + if (utilBone.bone.Data.Length == 0) { + SphereCollider sphere = utilBone.gameObject.AddComponent(); + sphere.radius = 0.1f; + } else { + float length = utilBone.bone.Data.Length; + BoxCollider box = utilBone.gameObject.AddComponent(); + box.size = new Vector3(length, length / 3, 0.2f); + box.center = new Vector3(length / 2, 0, 0); + } + } + + utilBone.gameObject.AddComponent(); + } } + } diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs index 3fe90035f..f61f22b25 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs @@ -18,264 +18,268 @@ using Spine; using System.Reflection; -[CustomEditor(typeof(SkeletonUtility))] -public class SkeletonUtilityInspector : Editor { - public static void AttachIcon (SkeletonUtilityBone utilityBone) { - Skeleton skeleton = utilityBone.skeletonUtility.skeletonRenderer.skeleton; - Texture2D icon; - if (utilityBone.bone.Data.Length == 0) - icon = SpineEditorUtilities.Icons.nullBone; - else - icon = SpineEditorUtilities.Icons.boneNib; - - foreach (IkConstraint c in skeleton.IkConstraints) { - if (c.Target == utilityBone.bone) { - icon = SpineEditorUtilities.Icons.constraintNib; - break; +namespace Spine.Unity.Editor { + [CustomEditor(typeof(SkeletonUtility))] + public class SkeletonUtilityInspector : UnityEditor.Editor { + + public static void AttachIcon (SkeletonUtilityBone utilityBone) { + Skeleton skeleton = utilityBone.skeletonUtility.skeletonRenderer.skeleton; + Texture2D icon; + if (utilityBone.bone.Data.Length == 0) + icon = SpineEditorUtilities.Icons.nullBone; + else + icon = SpineEditorUtilities.Icons.boneNib; + + foreach (IkConstraint c in skeleton.IkConstraints) { + if (c.Target == utilityBone.bone) { + icon = SpineEditorUtilities.Icons.constraintNib; + break; + } + } + + typeof(EditorGUIUtility).InvokeMember("SetIconForObject", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new object[2] { + utilityBone.gameObject, + icon + }); + } + + static void AttachIconsToChildren (Transform root) { + if (root != null) { + var utilityBones = root.GetComponentsInChildren(); + foreach (var utilBone in utilityBones) { + AttachIcon(utilBone); + } } } - typeof(EditorGUIUtility).InvokeMember("SetIconForObject", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new object[2] { - utilityBone.gameObject, - icon - }); - } - - static void AttachIconsToChildren (Transform root) { - if (root != null) { - var utilityBones = root.GetComponentsInChildren(); - foreach (var utilBone in utilityBones) { - AttachIcon(utilBone); - } + static SkeletonUtilityInspector () { + #if UNITY_4_3 + showSlots = false; + #else + showSlots = new AnimBool(false); + #endif } - } - static SkeletonUtilityInspector () { + SkeletonUtility skeletonUtility; + Skeleton skeleton; + SkeletonRenderer skeletonRenderer; + Transform transform; + bool isPrefab; + Dictionary> attachmentTable = new Dictionary>(); + + + //GUI stuff #if UNITY_4_3 - showSlots = false; + static bool showSlots; #else - showSlots = new AnimBool(false); + static AnimBool showSlots; #endif - } - - SkeletonUtility skeletonUtility; - Skeleton skeleton; - SkeletonRenderer skeletonRenderer; - Transform transform; - bool isPrefab; - Dictionary> attachmentTable = new Dictionary>(); - - - //GUI stuff -#if UNITY_4_3 - static bool showSlots; -#else - static AnimBool showSlots; -#endif - - void OnEnable () { - skeletonUtility = (SkeletonUtility)target; - skeletonRenderer = skeletonUtility.GetComponent(); - skeleton = skeletonRenderer.skeleton; - transform = skeletonRenderer.transform; - - if (skeleton == null) { - skeletonRenderer.Initialize(false); - skeletonRenderer.LateUpdate(); + void OnEnable () { + skeletonUtility = (SkeletonUtility)target; + skeletonRenderer = skeletonUtility.GetComponent(); skeleton = skeletonRenderer.skeleton; + transform = skeletonRenderer.transform; + + if (skeleton == null) { + skeletonRenderer.Initialize(false); + skeletonRenderer.LateUpdate(); + + skeleton = skeletonRenderer.skeleton; + } + + UpdateAttachments(); + + if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab) + isPrefab = true; + } - UpdateAttachments(); - - if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab) - isPrefab = true; - - } - - void OnSceneGUI () { - if (skeleton == null) { - OnEnable(); - return; - } - - // MITCH - //float flipRotation = skeleton.FlipX ? -1 : 1; - const float flipRotation = 1; - - foreach (Bone b in skeleton.Bones) { - Vector3 vec = transform.TransformPoint(new Vector3(b.WorldX, b.WorldY, 0)); - - // MITCH - Quaternion rot = Quaternion.Euler(0, 0, b.WorldRotationX * flipRotation); - Vector3 forward = transform.TransformDirection(rot * Vector3.right); - forward *= flipRotation; - - SpineEditorUtilities.Icons.boneMaterial.SetPass(0); - Graphics.DrawMeshNow(SpineEditorUtilities.Icons.boneMesh, Matrix4x4.TRS(vec, Quaternion.LookRotation(transform.forward, forward), Vector3.one * b.Data.Length * b.WorldScaleX)); - } - } - - void UpdateAttachments () { - attachmentTable = new Dictionary>(); - Skin skin = skeleton.Skin; - - if (skin == null) { - skin = skeletonRenderer.skeletonDataAsset.GetSkeletonData(true).DefaultSkin; - } - - for (int i = skeleton.Slots.Count-1; i >= 0; i--) { - List attachments = new List(); - skin.FindAttachmentsForSlot(i, attachments); - - attachmentTable.Add(skeleton.Slots.Items[i], attachments); - } - } - - void SpawnHierarchyButton (string label, string tooltip, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca, params GUILayoutOption[] options) { - GUIContent content = new GUIContent(label, tooltip); - if (GUILayout.Button(content, options)) { - if (skeletonUtility.skeletonRenderer == null) - skeletonUtility.skeletonRenderer = skeletonUtility.GetComponent(); - - if (skeletonUtility.boneRoot != null) { + void OnSceneGUI () { + if (skeleton == null) { + OnEnable(); return; } - skeletonUtility.SpawnHierarchy(mode, pos, rot, sca); + // MITCH + //float flipRotation = skeleton.FlipX ? -1 : 1; + const float flipRotation = 1; - SkeletonUtilityBone[] boneComps = skeletonUtility.GetComponentsInChildren(); - foreach (SkeletonUtilityBone b in boneComps) - AttachIcon(b); - } - } + foreach (Bone b in skeleton.Bones) { + Vector3 vec = transform.TransformPoint(new Vector3(b.WorldX, b.WorldY, 0)); - public override void OnInspectorGUI () { - if (isPrefab) { - GUILayout.Label(new GUIContent("Cannot edit Prefabs", SpineEditorUtilities.Icons.warning)); - return; + // MITCH + Quaternion rot = Quaternion.Euler(0, 0, b.WorldRotationX * flipRotation); + Vector3 forward = transform.TransformDirection(rot * Vector3.right); + forward *= flipRotation; + + SpineEditorUtilities.Icons.boneMaterial.SetPass(0); + Graphics.DrawMeshNow(SpineEditorUtilities.Icons.boneMesh, Matrix4x4.TRS(vec, Quaternion.LookRotation(transform.forward, forward), Vector3.one * b.Data.Length * b.WorldScaleX)); + } } - skeletonUtility.boneRoot = (Transform)EditorGUILayout.ObjectField("Bone Root", skeletonUtility.boneRoot, typeof(Transform), true); + void UpdateAttachments () { + attachmentTable = new Dictionary>(); + Skin skin = skeleton.Skin; - GUILayout.BeginHorizontal(); - EditorGUI.BeginDisabledGroup(skeletonUtility.boneRoot != null); - { - if (GUILayout.Button(new GUIContent("Spawn Hierarchy", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(150), GUILayout.Height(24))) - SpawnHierarchyContextMenu(); - } - EditorGUI.EndDisabledGroup(); + if (skin == null) { + skin = skeletonRenderer.skeletonDataAsset.GetSkeletonData(true).DefaultSkin; + } -// if (GUILayout.Button(new GUIContent("Spawn Submeshes", SpineEditorUtilities.Icons.subMeshRenderer), GUILayout.Width(150), GUILayout.Height(24))) -// skeletonUtility.SpawnSubRenderers(true); + for (int i = skeleton.Slots.Count-1; i >= 0; i--) { + List attachments = new List(); + skin.FindAttachmentsForSlot(i, attachments); - GUILayout.EndHorizontal(); - - EditorGUI.BeginChangeCheck(); - skeleton.FlipX = EditorGUILayout.ToggleLeft("Flip X", skeleton.FlipX); - skeleton.FlipY = EditorGUILayout.ToggleLeft("Flip Y", skeleton.FlipY); - if (EditorGUI.EndChangeCheck()) { - skeletonRenderer.LateUpdate(); - SceneView.RepaintAll(); + attachmentTable.Add(skeleton.Slots.Items[i], attachments); + } } -#if UNITY_4_3 - showSlots = EditorGUILayout.Foldout(showSlots, "Slots"); -#else - showSlots.target = EditorGUILayout.Foldout(showSlots.target, "Slots"); - if (EditorGUILayout.BeginFadeGroup(showSlots.faded)) { -#endif - foreach (KeyValuePair> pair in attachmentTable) { + void SpawnHierarchyButton (string label, string tooltip, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca, params GUILayoutOption[] options) { + GUIContent content = new GUIContent(label, tooltip); + if (GUILayout.Button(content, options)) { + if (skeletonUtility.skeletonRenderer == null) + skeletonUtility.skeletonRenderer = skeletonUtility.GetComponent(); - Slot slot = pair.Key; - - EditorGUILayout.BeginHorizontal(); - EditorGUI.indentLevel = 1; - EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, SpineEditorUtilities.Icons.slot), GUILayout.ExpandWidth(false)); - - EditorGUI.BeginChangeCheck(); - Color c = EditorGUILayout.ColorField(new Color(slot.R, slot.G, slot.B, slot.A), GUILayout.Width(60)); - - if (EditorGUI.EndChangeCheck()) { - slot.SetColor(c); - skeletonRenderer.LateUpdate(); + if (skeletonUtility.boneRoot != null) { + return; } - EditorGUILayout.EndHorizontal(); + skeletonUtility.SpawnHierarchy(mode, pos, rot, sca); + SkeletonUtilityBone[] boneComps = skeletonUtility.GetComponentsInChildren(); + foreach (SkeletonUtilityBone b in boneComps) + AttachIcon(b); + } + } + public override void OnInspectorGUI () { + if (isPrefab) { + GUILayout.Label(new GUIContent("Cannot edit Prefabs", SpineEditorUtilities.Icons.warning)); + return; + } - foreach (Attachment attachment in pair.Value) { + skeletonUtility.boneRoot = (Transform)EditorGUILayout.ObjectField("Bone Root", skeletonUtility.boneRoot, typeof(Transform), true); - if (slot.Attachment == attachment) { - GUI.contentColor = Color.white; - } else { - GUI.contentColor = Color.grey; + GUILayout.BeginHorizontal(); + EditorGUI.BeginDisabledGroup(skeletonUtility.boneRoot != null); + { + if (GUILayout.Button(new GUIContent("Spawn Hierarchy", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(150), GUILayout.Height(24))) + SpawnHierarchyContextMenu(); + } + EditorGUI.EndDisabledGroup(); + + // if (GUILayout.Button(new GUIContent("Spawn Submeshes", SpineEditorUtilities.Icons.subMeshRenderer), GUILayout.Width(150), GUILayout.Height(24))) + // skeletonUtility.SpawnSubRenderers(true); + + GUILayout.EndHorizontal(); + + EditorGUI.BeginChangeCheck(); + skeleton.FlipX = EditorGUILayout.ToggleLeft("Flip X", skeleton.FlipX); + skeleton.FlipY = EditorGUILayout.ToggleLeft("Flip Y", skeleton.FlipY); + if (EditorGUI.EndChangeCheck()) { + skeletonRenderer.LateUpdate(); + SceneView.RepaintAll(); + } + + #if UNITY_4_3 + showSlots = EditorGUILayout.Foldout(showSlots, "Slots"); + #else + showSlots.target = EditorGUILayout.Foldout(showSlots.target, "Slots"); + if (EditorGUILayout.BeginFadeGroup(showSlots.faded)) { + #endif + foreach (KeyValuePair> pair in attachmentTable) { + + Slot slot = pair.Key; + + EditorGUILayout.BeginHorizontal(); + EditorGUI.indentLevel = 1; + EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, SpineEditorUtilities.Icons.slot), GUILayout.ExpandWidth(false)); + + EditorGUI.BeginChangeCheck(); + Color c = EditorGUILayout.ColorField(new Color(slot.R, slot.G, slot.B, slot.A), GUILayout.Width(60)); + + if (EditorGUI.EndChangeCheck()) { + slot.SetColor(c); + skeletonRenderer.LateUpdate(); } - EditorGUI.indentLevel = 2; - bool isAttached = attachment == slot.Attachment; + EditorGUILayout.EndHorizontal(); - Texture2D icon = null; - if (attachment is MeshAttachment || attachment is WeightedMeshAttachment) - icon = SpineEditorUtilities.Icons.mesh; - else - icon = SpineEditorUtilities.Icons.image; - bool swap = EditorGUILayout.ToggleLeft(new GUIContent(attachment.Name, icon), attachment == slot.Attachment); + foreach (Attachment attachment in pair.Value) { - if (!isAttached && swap) { - slot.Attachment = attachment; - skeletonRenderer.LateUpdate(); - } else if (isAttached && !swap) { + if (slot.Attachment == attachment) { + GUI.contentColor = Color.white; + } else { + GUI.contentColor = Color.grey; + } + + EditorGUI.indentLevel = 2; + bool isAttached = attachment == slot.Attachment; + + Texture2D icon = null; + + if (attachment is MeshAttachment || attachment is WeightedMeshAttachment) + icon = SpineEditorUtilities.Icons.mesh; + else + icon = SpineEditorUtilities.Icons.image; + + bool swap = EditorGUILayout.ToggleLeft(new GUIContent(attachment.Name, icon), attachment == slot.Attachment); + + if (!isAttached && swap) { + slot.Attachment = attachment; + skeletonRenderer.LateUpdate(); + } else if (isAttached && !swap) { slot.Attachment = null; skeletonRenderer.LateUpdate(); } - GUI.contentColor = Color.white; + GUI.contentColor = Color.white; + } } + #if UNITY_4_3 + + #else } - #if UNITY_4_3 - -#else + EditorGUILayout.EndFadeGroup(); + if (showSlots.isAnimating) + Repaint(); + #endif + } + + void SpawnHierarchyContextMenu () { + GenericMenu menu = new GenericMenu(); + + menu.AddItem(new GUIContent("Follow"), false, SpawnFollowHierarchy); + menu.AddItem(new GUIContent("Follow (Root Only)"), false, SpawnFollowHierarchyRootOnly); + menu.AddSeparator(""); + menu.AddItem(new GUIContent("Override"), false, SpawnOverrideHierarchy); + menu.AddItem(new GUIContent("Override (Root Only)"), false, SpawnOverrideHierarchyRootOnly); + + menu.ShowAsContext(); + } + + void SpawnFollowHierarchy () { + Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); + } + + void SpawnFollowHierarchyRootOnly () { + Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); + } + + void SpawnOverrideHierarchy () { + Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); + } + + void SpawnOverrideHierarchyRootOnly () { + Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); } - EditorGUILayout.EndFadeGroup(); - if (showSlots.isAnimating) - Repaint(); -#endif } - void SpawnHierarchyContextMenu () { - GenericMenu menu = new GenericMenu(); - - menu.AddItem(new GUIContent("Follow"), false, SpawnFollowHierarchy); - menu.AddItem(new GUIContent("Follow (Root Only)"), false, SpawnFollowHierarchyRootOnly); - menu.AddSeparator(""); - menu.AddItem(new GUIContent("Override"), false, SpawnOverrideHierarchy); - menu.AddItem(new GUIContent("Override (Root Only)"), false, SpawnOverrideHierarchyRootOnly); - - menu.ShowAsContext(); - } - - void SpawnFollowHierarchy () { - Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true); - AttachIconsToChildren(skeletonUtility.boneRoot); - } - - void SpawnFollowHierarchyRootOnly () { - Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true); - AttachIconsToChildren(skeletonUtility.boneRoot); - } - - void SpawnOverrideHierarchy () { - Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true); - AttachIconsToChildren(skeletonUtility.boneRoot); - } - - void SpawnOverrideHierarchyRootOnly () { - Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true); - AttachIconsToChildren(skeletonUtility.boneRoot); - } } diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilitySubmeshRendererInspector.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilitySubmeshRendererInspector.cs new file mode 100644 index 000000000..bf47ba50b --- /dev/null +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilitySubmeshRendererInspector.cs @@ -0,0 +1,49 @@ +/****************************************************************************** + * 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 UnityEditor; +using Spine.Unity.Editor; + +namespace Spine.Unity.Modules { + [CustomEditor(typeof(SkeletonUtilitySubmeshRenderer))] + public class SkeletonUtilitySubmeshRendererInspector : UnityEditor.Editor { + public SpineInspectorUtility.SerializedSortingProperties sorting; + + void OnEnable () { + sorting = new SpineInspectorUtility.SerializedSortingProperties((target as Component).GetComponent()); + } + + public override void OnInspectorGUI () { + EditorGUILayout.HelpBox("SkeletonUtilitySubmeshRenderer is now obsolete. We recommend using SkeletonRenderSeparator.", MessageType.Info); + SpineInspectorUtility.SortingPropertyFields(sorting, true); + } + } +} diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilitySubmeshRendererInspector.cs.meta b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilitySubmeshRendererInspector.cs.meta new file mode 100644 index 000000000..3b5bd3485 --- /dev/null +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilitySubmeshRendererInspector.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 67418e462bd4dc24e8c234b92f1d4d9b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs index 5b95dc74d..b242d5421 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs @@ -10,49 +10,76 @@ using System.Collections; using System.Collections.Generic; using Spine; -[RequireComponent(typeof(ISkeletonAnimation))] -[ExecuteInEditMode] -public class SkeletonUtility : MonoBehaviour { +namespace Spine.Unity { + [RequireComponent(typeof(ISkeletonAnimation))] + [ExecuteInEditMode] + public class SkeletonUtility : MonoBehaviour { - public static T GetInParent (Transform origin) where T : Component { -#if UNITY_4_3 - Transform parent = origin.parent; - while(parent.GetComponent() == null){ + public static T GetInParent (Transform origin) where T : Component { + #if UNITY_4_3 + Transform parent = origin.parent; + while(parent.GetComponent() == null){ parent = parent.parent; if(parent == null) - return default(T); + return default(T); + } + + return parent.GetComponent(); + #else + return origin.GetComponentInParent(); + #endif } - return parent.GetComponent(); -#else - return origin.GetComponentInParent(); -#endif - } + public static PolygonCollider2D AddBoundingBox (Skeleton skeleton, string skinName, string slotName, string attachmentName, Transform parent, bool isTrigger = true) { + // List attachments = new List(); + Skin skin; - public static PolygonCollider2D AddBoundingBox (Skeleton skeleton, string skinName, string slotName, string attachmentName, Transform parent, bool isTrigger = true) { - // List attachments = new List(); - Skin skin; + if (skinName == "") + skinName = skeleton.Data.DefaultSkin.Name; - if (skinName == "") - skinName = skeleton.Data.DefaultSkin.Name; + skin = skeleton.Data.FindSkin(skinName); - skin = skeleton.Data.FindSkin(skinName); + if (skin == null) { + Debug.LogError("Skin " + skinName + " not found!"); + return null; + } + + var attachment = skin.GetAttachment(skeleton.FindSlotIndex(slotName), attachmentName); + if (attachment is BoundingBoxAttachment) { + GameObject go = new GameObject("[BoundingBox]" + attachmentName); + go.transform.parent = parent; + go.transform.localPosition = Vector3.zero; + go.transform.localRotation = Quaternion.identity; + go.transform.localScale = Vector3.one; + var collider = go.AddComponent(); + collider.isTrigger = isTrigger; + var boundingBox = (BoundingBoxAttachment)attachment; + float[] floats = boundingBox.Vertices; + int floatCount = floats.Length; + int vertCount = floatCount / 2; + + Vector2[] verts = new Vector2[vertCount]; + int v = 0; + for (int i = 0; i < floatCount; i += 2, v++) { + verts[v].x = floats[i]; + verts[v].y = floats[i + 1]; + } + + collider.SetPath(0, verts); + + return collider; + + } - if (skin == null) { - Debug.LogError("Skin " + skinName + " not found!"); return null; } - var attachment = skin.GetAttachment(skeleton.FindSlotIndex(slotName), attachmentName); - if (attachment is BoundingBoxAttachment) { - GameObject go = new GameObject("[BoundingBox]" + attachmentName); - go.transform.parent = parent; - go.transform.localPosition = Vector3.zero; - go.transform.localRotation = Quaternion.identity; - go.transform.localScale = Vector3.one; - var collider = go.AddComponent(); + public static PolygonCollider2D AddBoundingBoxAsComponent (BoundingBoxAttachment boundingBox, GameObject gameObject, bool isTrigger = true) { + if (boundingBox == null) + return null; + + var collider = gameObject.AddComponent(); collider.isTrigger = isTrigger; - var boundingBox = (BoundingBoxAttachment)attachment; float[] floats = boundingBox.Vertices; int floatCount = floats.Length; int vertCount = floatCount / 2; @@ -67,318 +94,294 @@ public class SkeletonUtility : MonoBehaviour { collider.SetPath(0, verts); return collider; - } - return null; - } + public static Bounds GetBoundingBoxBounds (BoundingBoxAttachment boundingBox, float depth = 0) { + float[] floats = boundingBox.Vertices; + int floatCount = floats.Length; - public static PolygonCollider2D AddBoundingBoxAsComponent (BoundingBoxAttachment boundingBox, GameObject gameObject, bool isTrigger = true) { - if (boundingBox == null) - return null; + Bounds bounds = new Bounds(); - var collider = gameObject.AddComponent(); - collider.isTrigger = isTrigger; - float[] floats = boundingBox.Vertices; - int floatCount = floats.Length; - int vertCount = floatCount / 2; + bounds.center = new Vector3(floats[0], floats[1], 0); + for (int i = 2; i < floatCount; i += 2) { + bounds.Encapsulate(new Vector3(floats[i], floats[i + 1], 0)); + } + Vector3 size = bounds.size; + size.z = depth; + bounds.size = size; - Vector2[] verts = new Vector2[vertCount]; - int v = 0; - for (int i = 0; i < floatCount; i += 2, v++) { - verts[v].x = floats[i]; - verts[v].y = floats[i + 1]; + return bounds; } - collider.SetPath(0, verts); + public delegate void SkeletonUtilityDelegate (); - return collider; - } + public event SkeletonUtilityDelegate OnReset; - public static Bounds GetBoundingBoxBounds (BoundingBoxAttachment boundingBox, float depth = 0) { - float[] floats = boundingBox.Vertices; - int floatCount = floats.Length; + public Transform boneRoot; - Bounds bounds = new Bounds(); + void Update () { + if (boneRoot != null && skeletonRenderer.skeleton != null) { + Vector3 flipScale = Vector3.one; + if (skeletonRenderer.skeleton.FlipX) + flipScale.x = -1; - bounds.center = new Vector3(floats[0], floats[1], 0); - for (int i = 2; i < floatCount; i += 2) { - bounds.Encapsulate(new Vector3(floats[i], floats[i + 1], 0)); - } - Vector3 size = bounds.size; - size.z = depth; - bounds.size = size; + if (skeletonRenderer.skeleton.FlipY) + flipScale.y = -1; - return bounds; - } - - public delegate void SkeletonUtilityDelegate (); - - public event SkeletonUtilityDelegate OnReset; - - public Transform boneRoot; - - void Update () { - if (boneRoot != null && skeletonRenderer.skeleton != null) { - Vector3 flipScale = Vector3.one; - if (skeletonRenderer.skeleton.FlipX) - flipScale.x = -1; - - if (skeletonRenderer.skeleton.FlipY) - flipScale.y = -1; - - boneRoot.localScale = flipScale; - } - } - - [HideInInspector] - public SkeletonRenderer skeletonRenderer; - [HideInInspector] - public ISkeletonAnimation skeletonAnimation; - [System.NonSerialized] - public List utilityBones = new List(); - [System.NonSerialized] - public List utilityConstraints = new List(); - // Dictionary utilityBoneTable; - - protected bool hasTransformBones; - protected bool hasUtilityConstraints; - protected bool needToReprocessBones; - - void OnEnable () { - if (skeletonRenderer == null) { - skeletonRenderer = GetComponent(); + boneRoot.localScale = flipScale; + } } - if (skeletonAnimation == null) { - skeletonAnimation = GetComponent(); - if (skeletonAnimation == null) - skeletonAnimation = GetComponent(); - } + [HideInInspector] + public SkeletonRenderer skeletonRenderer; + [HideInInspector] + public ISkeletonAnimation skeletonAnimation; + [System.NonSerialized] + public List utilityBones = new List(); + [System.NonSerialized] + public List utilityConstraints = new List(); + // Dictionary utilityBoneTable; - skeletonRenderer.OnRebuild -= HandleRendererReset; - skeletonRenderer.OnRebuild += HandleRendererReset; + protected bool hasTransformBones; + protected bool hasUtilityConstraints; + protected bool needToReprocessBones; - if (skeletonAnimation != null) { - skeletonAnimation.UpdateLocal -= UpdateLocal; - skeletonAnimation.UpdateLocal += UpdateLocal; - } - - - CollectBones(); - } - - void Start () { - //recollect because order of operations failure when switching between game mode and edit mode... - // CollectBones(); - } - - void OnDisable () { - skeletonRenderer.OnRebuild -= HandleRendererReset; - - if (skeletonAnimation != null) { - skeletonAnimation.UpdateLocal -= UpdateLocal; - skeletonAnimation.UpdateWorld -= UpdateWorld; - skeletonAnimation.UpdateComplete -= UpdateComplete; - } - } - - void HandleRendererReset (SkeletonRenderer r) { - if (OnReset != null) - OnReset(); - - CollectBones(); - } - - public void RegisterBone (SkeletonUtilityBone bone) { - if (utilityBones.Contains(bone)) - return; - else { - utilityBones.Add(bone); - needToReprocessBones = true; - } - } - - public void UnregisterBone (SkeletonUtilityBone bone) { - utilityBones.Remove(bone); - } - - public void RegisterConstraint (SkeletonUtilityConstraint constraint) { - - if (utilityConstraints.Contains(constraint)) - return; - else { - utilityConstraints.Add(constraint); - needToReprocessBones = true; - } - } - - public void UnregisterConstraint (SkeletonUtilityConstraint constraint) { - utilityConstraints.Remove(constraint); - } - - public void CollectBones () { - if (skeletonRenderer.skeleton == null) - return; - - if (boneRoot != null) { - List constraintTargetNames = new List(); - - ExposedList ikConstraints = skeletonRenderer.skeleton.IkConstraints; - for (int i = 0, n = ikConstraints.Count; i < n; i++) - constraintTargetNames.Add(ikConstraints.Items[i].Target.Data.Name); - - foreach (var b in utilityBones) { - if (b.bone == null) { - return; - } - if (b.mode == SkeletonUtilityBone.Mode.Override) { - hasTransformBones = true; - } - - if (constraintTargetNames.Contains(b.bone.Data.Name)) { - hasUtilityConstraints = true; - } + void OnEnable () { + if (skeletonRenderer == null) { + skeletonRenderer = GetComponent(); } - if (utilityConstraints.Count > 0) - hasUtilityConstraints = true; + if (skeletonAnimation == null) { + skeletonAnimation = GetComponent(); + if (skeletonAnimation == null) + skeletonAnimation = GetComponent(); + } + + skeletonRenderer.OnRebuild -= HandleRendererReset; + skeletonRenderer.OnRebuild += HandleRendererReset; if (skeletonAnimation != null) { - skeletonAnimation.UpdateWorld -= UpdateWorld; - skeletonAnimation.UpdateComplete -= UpdateComplete; - - if (hasTransformBones || hasUtilityConstraints) { - skeletonAnimation.UpdateWorld += UpdateWorld; - } - - if (hasUtilityConstraints) { - skeletonAnimation.UpdateComplete += UpdateComplete; - } + skeletonAnimation.UpdateLocal -= UpdateLocal; + skeletonAnimation.UpdateLocal += UpdateLocal; } - needToReprocessBones = false; - } else { - utilityBones.Clear(); - utilityConstraints.Clear(); - } - } - - void UpdateLocal (ISkeletonAnimation anim) { - - if (needToReprocessBones) - CollectBones(); - - if (utilityBones == null) - return; - - foreach (SkeletonUtilityBone b in utilityBones) { - b.transformLerpComplete = false; - } - - UpdateAllBones(); - } - - void UpdateWorld (ISkeletonAnimation anim) { - UpdateAllBones(); - - foreach (SkeletonUtilityConstraint c in utilityConstraints) - c.DoUpdate(); - } - - void UpdateComplete (ISkeletonAnimation anim) { - UpdateAllBones(); - } - - void UpdateAllBones () { - if (boneRoot == null) { CollectBones(); } - if (utilityBones == null) - return; - - foreach (SkeletonUtilityBone b in utilityBones) { - b.DoUpdate(); + void Start () { + //recollect because order of operations failure when switching between game mode and edit mode... + // CollectBones(); } - } - public Transform GetBoneRoot () { - if (boneRoot != null) + void OnDisable () { + skeletonRenderer.OnRebuild -= HandleRendererReset; + + if (skeletonAnimation != null) { + skeletonAnimation.UpdateLocal -= UpdateLocal; + skeletonAnimation.UpdateWorld -= UpdateWorld; + skeletonAnimation.UpdateComplete -= UpdateComplete; + } + } + + void HandleRendererReset (SkeletonRenderer r) { + if (OnReset != null) + OnReset(); + + CollectBones(); + } + + public void RegisterBone (SkeletonUtilityBone bone) { + if (utilityBones.Contains(bone)) + return; + else { + utilityBones.Add(bone); + needToReprocessBones = true; + } + } + + public void UnregisterBone (SkeletonUtilityBone bone) { + utilityBones.Remove(bone); + } + + public void RegisterConstraint (SkeletonUtilityConstraint constraint) { + + if (utilityConstraints.Contains(constraint)) + return; + else { + utilityConstraints.Add(constraint); + needToReprocessBones = true; + } + } + + public void UnregisterConstraint (SkeletonUtilityConstraint constraint) { + utilityConstraints.Remove(constraint); + } + + public void CollectBones () { + if (skeletonRenderer.skeleton == null) + return; + + if (boneRoot != null) { + List constraintTargetNames = new List(); + + ExposedList ikConstraints = skeletonRenderer.skeleton.IkConstraints; + for (int i = 0, n = ikConstraints.Count; i < n; i++) + constraintTargetNames.Add(ikConstraints.Items[i].Target.Data.Name); + + foreach (var b in utilityBones) { + if (b.bone == null) { + return; + } + if (b.mode == SkeletonUtilityBone.Mode.Override) { + hasTransformBones = true; + } + + if (constraintTargetNames.Contains(b.bone.Data.Name)) { + hasUtilityConstraints = true; + } + } + + if (utilityConstraints.Count > 0) + hasUtilityConstraints = true; + + if (skeletonAnimation != null) { + skeletonAnimation.UpdateWorld -= UpdateWorld; + skeletonAnimation.UpdateComplete -= UpdateComplete; + + if (hasTransformBones || hasUtilityConstraints) { + skeletonAnimation.UpdateWorld += UpdateWorld; + } + + if (hasUtilityConstraints) { + skeletonAnimation.UpdateComplete += UpdateComplete; + } + } + + needToReprocessBones = false; + } else { + utilityBones.Clear(); + utilityConstraints.Clear(); + } + + } + + void UpdateLocal (ISkeletonAnimation anim) { + + if (needToReprocessBones) + CollectBones(); + + if (utilityBones == null) + return; + + foreach (SkeletonUtilityBone b in utilityBones) { + b.transformLerpComplete = false; + } + + UpdateAllBones(); + } + + void UpdateWorld (ISkeletonAnimation anim) { + UpdateAllBones(); + + foreach (SkeletonUtilityConstraint c in utilityConstraints) + c.DoUpdate(); + } + + void UpdateComplete (ISkeletonAnimation anim) { + UpdateAllBones(); + } + + void UpdateAllBones () { + if (boneRoot == null) { + CollectBones(); + } + + if (utilityBones == null) + return; + + foreach (SkeletonUtilityBone b in utilityBones) { + b.DoUpdate(); + } + } + + public Transform GetBoneRoot () { + if (boneRoot != null) + return boneRoot; + + boneRoot = new GameObject("SkeletonUtility-Root").transform; + boneRoot.parent = transform; + boneRoot.localPosition = Vector3.zero; + boneRoot.localRotation = Quaternion.identity; + boneRoot.localScale = Vector3.one; + return boneRoot; - - boneRoot = new GameObject("SkeletonUtility-Root").transform; - boneRoot.parent = transform; - boneRoot.localPosition = Vector3.zero; - boneRoot.localRotation = Quaternion.identity; - boneRoot.localScale = Vector3.one; - - return boneRoot; - } - - public GameObject SpawnRoot (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { - GetBoneRoot(); - Skeleton skeleton = this.skeletonRenderer.skeleton; - - GameObject go = SpawnBone(skeleton.RootBone, boneRoot, mode, pos, rot, sca); - - CollectBones(); - - return go; - } - - public GameObject SpawnHierarchy (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { - GetBoneRoot(); - - Skeleton skeleton = this.skeletonRenderer.skeleton; - - GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca); - - CollectBones(); - - return go; - } - - public GameObject SpawnBoneRecursively (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { - GameObject go = SpawnBone(bone, parent, mode, pos, rot, sca); - - ExposedList childrenBones = bone.Children; - for (int i = 0, n = childrenBones.Count; i < n; i++) { - Bone child = childrenBones.Items[i]; - SpawnBoneRecursively(child, go.transform, mode, pos, rot, sca); } - return go; - } + public GameObject SpawnRoot (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { + GetBoneRoot(); + Skeleton skeleton = this.skeletonRenderer.skeleton; - public GameObject SpawnBone (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { - GameObject go = new GameObject(bone.Data.Name); - go.transform.parent = parent; + GameObject go = SpawnBone(skeleton.RootBone, boneRoot, mode, pos, rot, sca); - SkeletonUtilityBone b = go.AddComponent(); - b.skeletonUtility = this; - b.position = pos; - b.rotation = rot; - b.scale = sca; - b.mode = mode; - b.zPosition = true; - b.Reset(); - b.bone = bone; - b.boneName = bone.Data.Name; - b.valid = true; + CollectBones(); - if (mode == SkeletonUtilityBone.Mode.Override) { - if (rot) - go.transform.localRotation = Quaternion.Euler(0, 0, b.bone.AppliedRotation); - - if (pos) - go.transform.localPosition = new Vector3(b.bone.X, b.bone.Y, 0); - - go.transform.localScale = new Vector3(b.bone.scaleX, b.bone.scaleY, 0); + return go; + } + + public GameObject SpawnHierarchy (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { + GetBoneRoot(); + + Skeleton skeleton = this.skeletonRenderer.skeleton; + + GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca); + + CollectBones(); + + return go; + } + + public GameObject SpawnBoneRecursively (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { + GameObject go = SpawnBone(bone, parent, mode, pos, rot, sca); + + ExposedList childrenBones = bone.Children; + for (int i = 0, n = childrenBones.Count; i < n; i++) { + Bone child = childrenBones.Items[i]; + SpawnBoneRecursively(child, go.transform, mode, pos, rot, sca); + } + + return go; + } + + public GameObject SpawnBone (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { + GameObject go = new GameObject(bone.Data.Name); + go.transform.parent = parent; + + SkeletonUtilityBone b = go.AddComponent(); + b.skeletonUtility = this; + b.position = pos; + b.rotation = rot; + b.scale = sca; + b.mode = mode; + b.zPosition = true; + b.Reset(); + b.bone = bone; + b.boneName = bone.Data.Name; + b.valid = true; + + if (mode == SkeletonUtilityBone.Mode.Override) { + if (rot) + go.transform.localRotation = Quaternion.Euler(0, 0, b.bone.AppliedRotation); + + if (pos) + go.transform.localPosition = new Vector3(b.bone.X, b.bone.Y, 0); + + go.transform.localScale = new Vector3(b.bone.scaleX, b.bone.scaleY, 0); + } + + return go; } - return go; } } diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs index 0a9f3cd4b..79bb0cdce 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs @@ -11,274 +11,276 @@ using System.Collections.Generic; using UnityEngine; using Spine; -/// Sets a GameObject's transform to match a bone on a Spine skeleton. -[ExecuteInEditMode] -[AddComponentMenu("Spine/SkeletonUtilityBone")] -public class SkeletonUtilityBone : MonoBehaviour { +namespace Spine.Unity { + /// Sets a GameObject's transform to match a bone on a Spine skeleton. + [ExecuteInEditMode] + [AddComponentMenu("Spine/SkeletonUtilityBone")] + public class SkeletonUtilityBone : MonoBehaviour { - public enum Mode { - Follow, - Override - } - - [System.NonSerialized] - public bool valid; - [System.NonSerialized] - public SkeletonUtility skeletonUtility; - [System.NonSerialized] - public Bone bone; - public Mode mode; - public bool zPosition = true; - public bool position; - public bool rotation; - public bool scale; - // MITCH : remove flipX - // Kept these fields public to retain serialization. Probably remove eventually? - public bool flip; - public bool flipX; - [Range(0f, 1f)] - public float overrideAlpha = 1; - - /// If a bone isn't set, boneName is used to find the bone. - public String boneName; - public Transform parentReference; - - [System.NonSerialized] - public bool transformLerpComplete; - - protected Transform cachedTransform; - protected Transform skeletonTransform; - - // MITCH : nonuniform scale -// private bool nonUniformScaleWarning; -// public bool NonUniformScaleWarning { -// get { return nonUniformScaleWarning; } -// } - - private bool disableInheritScaleWarning; - public bool DisableInheritScaleWarning { - get { return disableInheritScaleWarning; } - } - - public void Reset () { - bone = null; - cachedTransform = transform; - valid = skeletonUtility != null && skeletonUtility.skeletonRenderer != null && skeletonUtility.skeletonRenderer.valid; - if (!valid) - return; - skeletonTransform = skeletonUtility.transform; - skeletonUtility.OnReset -= HandleOnReset; - skeletonUtility.OnReset += HandleOnReset; - DoUpdate(); - } - - void OnEnable () { - skeletonUtility = SkeletonUtility.GetInParent(transform); - - if (skeletonUtility == null) - return; - - skeletonUtility.RegisterBone(this); - skeletonUtility.OnReset += HandleOnReset; - } - - void HandleOnReset () { - Reset(); - } - - void OnDisable () { - if (skeletonUtility != null) { - skeletonUtility.OnReset -= HandleOnReset; - skeletonUtility.UnregisterBone(this); - } - } - - public void DoUpdate () { - if (!valid) { - Reset(); - return; + public enum Mode { + Follow, + Override } - Spine.Skeleton skeleton = skeletonUtility.skeletonRenderer.skeleton; + [System.NonSerialized] + public bool valid; + [System.NonSerialized] + public SkeletonUtility skeletonUtility; + [System.NonSerialized] + public Bone bone; + public Mode mode; + public bool zPosition = true; + public bool position; + public bool rotation; + public bool scale; + // MITCH : remove flipX + // Kept these fields public to retain serialization. Probably remove eventually? + public bool flip; + public bool flipX; + [Range(0f, 1f)] + public float overrideAlpha = 1; - if (bone == null) { - if (boneName == null || boneName.Length == 0) + /// If a bone isn't set, boneName is used to find the bone. + public String boneName; + public Transform parentReference; + + [System.NonSerialized] + public bool transformLerpComplete; + + protected Transform cachedTransform; + protected Transform skeletonTransform; + + // MITCH : nonuniform scale + // private bool nonUniformScaleWarning; + // public bool NonUniformScaleWarning { + // get { return nonUniformScaleWarning; } + // } + + private bool disableInheritScaleWarning; + public bool DisableInheritScaleWarning { + get { return disableInheritScaleWarning; } + } + + public void Reset () { + bone = null; + cachedTransform = transform; + valid = skeletonUtility != null && skeletonUtility.skeletonRenderer != null && skeletonUtility.skeletonRenderer.valid; + if (!valid) return; - - bone = skeleton.FindBone(boneName); + skeletonTransform = skeletonUtility.transform; + skeletonUtility.OnReset -= HandleOnReset; + skeletonUtility.OnReset += HandleOnReset; + DoUpdate(); + } + + void OnEnable () { + skeletonUtility = SkeletonUtility.GetInParent(transform); + + if (skeletonUtility == null) + return; + + skeletonUtility.RegisterBone(this); + skeletonUtility.OnReset += HandleOnReset; + } + + void HandleOnReset () { + Reset(); + } + + void OnDisable () { + if (skeletonUtility != null) { + skeletonUtility.OnReset -= HandleOnReset; + skeletonUtility.UnregisterBone(this); + } + } + + public void DoUpdate () { + if (!valid) { + Reset(); + return; + } + + Spine.Skeleton skeleton = skeletonUtility.skeletonRenderer.skeleton; if (bone == null) { - Debug.LogError("Bone not found: " + boneName, this); - return; + if (boneName == null || boneName.Length == 0) + return; + + bone = skeleton.FindBone(boneName); + + if (bone == null) { + Debug.LogError("Bone not found: " + boneName, this); + return; + } } - } - float skeletonFlipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f; + float skeletonFlipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f; - // MITCH : remove flipX -// float flipCompensation = 0; -// if (flip && (flipX || (flipX != bone.flipX)) && bone.parent != null) { -// flipCompensation = bone.parent.WorldRotation * -2; -// } - - if (mode == Mode.Follow) { // MITCH : remove flipX -// if (flip) -// flipX = bone.flipX; - - if (position) - cachedTransform.localPosition = new Vector3(bone.x, bone.y, 0); + // float flipCompensation = 0; + // if (flip && (flipX || (flipX != bone.flipX)) && bone.parent != null) { + // flipCompensation = bone.parent.WorldRotation * -2; + // } - if (rotation) { - if (bone.Data.InheritRotation) { - // MITCH : remove flipX - //if (bone.FlipX) { - // cachedTransform.localRotation = Quaternion.Euler(0, 180, bone.rotationIK - flipCompensation); - //} else { - cachedTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation); - //} - } else { - Vector3 euler = skeletonTransform.rotation.eulerAngles; - cachedTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation)); - } - } + if (mode == Mode.Follow) { + // MITCH : remove flipX + // if (flip) + // flipX = bone.flipX; - if (scale) { - cachedTransform.localScale = new Vector3(bone.scaleX, bone.scaleY, bone.WorldSignX); - // MITCH : nonuniform scale - //nonUniformScaleWarning = (bone.scaleX != bone.scaleY); - disableInheritScaleWarning = !bone.data.inheritScale; - } - - } else if (mode == Mode.Override) { - if (transformLerpComplete) - return; - - if (parentReference == null) { - if (position) { - bone.x = Mathf.Lerp(bone.x, cachedTransform.localPosition.x, overrideAlpha); - bone.y = Mathf.Lerp(bone.y, cachedTransform.localPosition.y, overrideAlpha); - } + if (position) + cachedTransform.localPosition = new Vector3(bone.x, bone.y, 0); if (rotation) { - float angle = Mathf.LerpAngle(bone.Rotation, cachedTransform.localRotation.eulerAngles.z, overrideAlpha); - - // MITCH : remove flipX -// float angle = Mathf.LerpAngle(bone.Rotation, cachedTransform.localRotation.eulerAngles.z, overrideAlpha) + flipCompensation; -// if (flip) { -// -// if ((!flipX && bone.flipX)) { -// angle -= flipCompensation; -// } -// -// //TODO fix this... -// if (angle >= 360) -// angle -= 360; -// else if (angle <= -360) -// angle += 360; -// } - bone.Rotation = angle; - bone.AppliedRotation = angle; + if (bone.Data.InheritRotation) { + // MITCH : remove flipX + //if (bone.FlipX) { + // cachedTransform.localRotation = Quaternion.Euler(0, 180, bone.rotationIK - flipCompensation); + //} else { + cachedTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation); + //} + } else { + Vector3 euler = skeletonTransform.rotation.eulerAngles; + cachedTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation)); + } } if (scale) { - bone.scaleX = Mathf.Lerp(bone.scaleX, cachedTransform.localScale.x, overrideAlpha); - bone.scaleY = Mathf.Lerp(bone.scaleY, cachedTransform.localScale.y, overrideAlpha); + cachedTransform.localScale = new Vector3(bone.scaleX, bone.scaleY, bone.WorldSignX); // MITCH : nonuniform scale //nonUniformScaleWarning = (bone.scaleX != bone.scaleY); + disableInheritScaleWarning = !bone.data.inheritScale; } - // MITCH : remove flipX - //if (flip) - // bone.flipX = flipX; - } else { + } else if (mode == Mode.Override) { if (transformLerpComplete) return; - if (position) { - Vector3 pos = parentReference.InverseTransformPoint(cachedTransform.position); - bone.x = Mathf.Lerp(bone.x, pos.x, overrideAlpha); - bone.y = Mathf.Lerp(bone.y, pos.y, overrideAlpha); - } + if (parentReference == null) { + if (position) { + bone.x = Mathf.Lerp(bone.x, cachedTransform.localPosition.x, overrideAlpha); + bone.y = Mathf.Lerp(bone.y, cachedTransform.localPosition.y, overrideAlpha); + } - // MITCH - if (rotation) { - float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(flipX ? Vector3.forward * -1 : Vector3.forward, parentReference.InverseTransformDirection(cachedTransform.up)).eulerAngles.z, overrideAlpha); + if (rotation) { + float angle = Mathf.LerpAngle(bone.Rotation, cachedTransform.localRotation.eulerAngles.z, overrideAlpha); + + // MITCH : remove flipX + // float angle = Mathf.LerpAngle(bone.Rotation, cachedTransform.localRotation.eulerAngles.z, overrideAlpha) + flipCompensation; + // if (flip) { + // + // if ((!flipX && bone.flipX)) { + // angle -= flipCompensation; + // } + // + // //TODO fix this... + // if (angle >= 360) + // angle -= 360; + // else if (angle <= -360) + // angle += 360; + // } + bone.Rotation = angle; + bone.AppliedRotation = angle; + } + + if (scale) { + bone.scaleX = Mathf.Lerp(bone.scaleX, cachedTransform.localScale.x, overrideAlpha); + bone.scaleY = Mathf.Lerp(bone.scaleY, cachedTransform.localScale.y, overrideAlpha); + // MITCH : nonuniform scale + //nonUniformScaleWarning = (bone.scaleX != bone.scaleY); + } // MITCH : remove flipX -// float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(flipX ? Vector3.forward * -1 : Vector3.forward, parentReference.InverseTransformDirection(cachedTransform.up)).eulerAngles.z, overrideAlpha) + flipCompensation; -// if (flip) { -// -// if ((!flipX && bone.flipX)) { -// angle -= flipCompensation; -// } -// -// //TODO fix this... -// if (angle >= 360) -// angle -= 360; -// else if (angle <= -360) -// angle += 360; -// } - bone.Rotation = angle; - bone.AppliedRotation = angle; - } - - if (scale) { - bone.scaleX = Mathf.Lerp(bone.scaleX, cachedTransform.localScale.x, overrideAlpha); - bone.scaleY = Mathf.Lerp(bone.scaleY, cachedTransform.localScale.y, overrideAlpha); - // MITCH : nonuniform scale - //nonUniformScaleWarning = (bone.scaleX != bone.scaleY); + //if (flip) + // bone.flipX = flipX; + } else { + if (transformLerpComplete) + return; + + if (position) { + Vector3 pos = parentReference.InverseTransformPoint(cachedTransform.position); + bone.x = Mathf.Lerp(bone.x, pos.x, overrideAlpha); + bone.y = Mathf.Lerp(bone.y, pos.y, overrideAlpha); + } + + // MITCH + if (rotation) { + float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(flipX ? Vector3.forward * -1 : Vector3.forward, parentReference.InverseTransformDirection(cachedTransform.up)).eulerAngles.z, overrideAlpha); + + // MITCH : remove flipX + // float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(flipX ? Vector3.forward * -1 : Vector3.forward, parentReference.InverseTransformDirection(cachedTransform.up)).eulerAngles.z, overrideAlpha) + flipCompensation; + // if (flip) { + // + // if ((!flipX && bone.flipX)) { + // angle -= flipCompensation; + // } + // + // //TODO fix this... + // if (angle >= 360) + // angle -= 360; + // else if (angle <= -360) + // angle += 360; + // } + bone.Rotation = angle; + bone.AppliedRotation = angle; + } + + if (scale) { + bone.scaleX = Mathf.Lerp(bone.scaleX, cachedTransform.localScale.x, overrideAlpha); + bone.scaleY = Mathf.Lerp(bone.scaleY, cachedTransform.localScale.y, overrideAlpha); + // MITCH : nonuniform scale + //nonUniformScaleWarning = (bone.scaleX != bone.scaleY); + } + + disableInheritScaleWarning = !bone.data.inheritScale; + + // MITCH : remove flipX + //if (flip) + // bone.flipX = flipX; } - disableInheritScaleWarning = !bone.data.inheritScale; - - // MITCH : remove flipX - //if (flip) - // bone.flipX = flipX; + transformLerpComplete = true; } - - transformLerpComplete = true; } + + // MITCH : remove flipX + // public void FlipX (bool state) { + // if (state != flipX) { + // flipX = state; + // if (flipX && Mathf.Abs(transform.localRotation.eulerAngles.y) > 90) { + // skeletonUtility.skeletonAnimation.LateUpdate(); + // return; + // } else if (!flipX && Mathf.Abs(transform.localRotation.eulerAngles.y) < 90) { + // skeletonUtility.skeletonAnimation.LateUpdate(); + // return; + // } + // } + // + // + // bone.FlipX = state; + // transform.RotateAround(transform.position, skeletonUtility.transform.up, 180); + // Vector3 euler = transform.localRotation.eulerAngles; + // euler.x = 0; + // + // euler.y = bone.FlipX ? 180 : 0; + // euler.y = 0; + // transform.localRotation = Quaternion.Euler(euler); + // } + + public void AddBoundingBox (string skinName, string slotName, string attachmentName) { + SkeletonUtility.AddBoundingBox(bone.skeleton, skinName, slotName, attachmentName, transform); + } + + + #if UNITY_EDITOR + void OnDrawGizmos () { + // MITCH : nonuniform scale + // if (NonUniformScaleWarning) { + // Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning"); + // } + + if (DisableInheritScaleWarning) + Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning"); + } + #endif } - - // MITCH : remove flipX -// public void FlipX (bool state) { -// if (state != flipX) { -// flipX = state; -// if (flipX && Mathf.Abs(transform.localRotation.eulerAngles.y) > 90) { -// skeletonUtility.skeletonAnimation.LateUpdate(); -// return; -// } else if (!flipX && Mathf.Abs(transform.localRotation.eulerAngles.y) < 90) { -// skeletonUtility.skeletonAnimation.LateUpdate(); -// return; -// } -// } -// -// -// bone.FlipX = state; -// transform.RotateAround(transform.position, skeletonUtility.transform.up, 180); -// Vector3 euler = transform.localRotation.eulerAngles; -// euler.x = 0; -// -// euler.y = bone.FlipX ? 180 : 0; -// euler.y = 0; -// transform.localRotation = Quaternion.Euler(euler); -// } - - public void AddBoundingBox (string skinName, string slotName, string attachmentName) { - SkeletonUtility.AddBoundingBox(bone.skeleton, skinName, slotName, attachmentName, transform); - } - - - #if UNITY_EDITOR - void OnDrawGizmos () { - // MITCH : nonuniform scale -// if (NonUniformScaleWarning) { -// Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning"); -// } - - if (DisableInheritScaleWarning) - Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning"); - } - #endif -} \ No newline at end of file +} diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs index 4f2b1cd9b..1381fc816 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs @@ -32,22 +32,23 @@ using UnityEngine; using System.Collections; -[RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode] +namespace Spine.Unity { + [RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode] + public abstract class SkeletonUtilityConstraint : MonoBehaviour { -public abstract class SkeletonUtilityConstraint : MonoBehaviour { + protected SkeletonUtilityBone utilBone; + protected SkeletonUtility skeletonUtility; - protected SkeletonUtilityBone utilBone; - protected SkeletonUtility skeletonUtility; + protected virtual void OnEnable () { + utilBone = GetComponent(); + skeletonUtility = SkeletonUtility.GetInParent(transform); + skeletonUtility.RegisterConstraint(this); + } - protected virtual void OnEnable () { - utilBone = GetComponent(); - skeletonUtility = SkeletonUtility.GetInParent(transform); - skeletonUtility.RegisterConstraint(this); + protected virtual void OnDisable () { + skeletonUtility.UnregisterConstraint(this); + } + + public abstract void DoUpdate (); } - - protected virtual void OnDisable () { - skeletonUtility.UnregisterConstraint(this); - } - - public abstract void DoUpdate (); } diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilitySubmeshRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilitySubmeshRenderer.cs new file mode 100644 index 000000000..2320f86d8 --- /dev/null +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilitySubmeshRenderer.cs @@ -0,0 +1,71 @@ +/****************************************************************************** + * 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; + +namespace Spine.Unity.Modules { + [ExecuteInEditMode] + public class SkeletonUtilitySubmeshRenderer : MonoBehaviour { + [System.NonSerialized] + public Mesh mesh; + public int submeshIndex = 0; + public Material hiddenPassMaterial; + Renderer cachedRenderer; + MeshFilter filter; + Material[] sharedMaterials; + + void Awake () { + cachedRenderer = GetComponent(); + filter = GetComponent(); + sharedMaterials = new Material[0]; + } + + public void SetMesh (Renderer parentRenderer, Mesh mesh, Material mat) { + if (cachedRenderer == null) + return; + + cachedRenderer.enabled = true; + filter.sharedMesh = mesh; + if (cachedRenderer.sharedMaterials.Length != parentRenderer.sharedMaterials.Length) { + sharedMaterials = parentRenderer.sharedMaterials; + } + + for (int i = 0; i < sharedMaterials.Length; i++) { + if (i == submeshIndex) + sharedMaterials[i] = mat; + else + sharedMaterials[i] = hiddenPassMaterial; + } + + cachedRenderer.sharedMaterials = sharedMaterials; + } + } + +} diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilitySubmeshRenderer.cs.meta b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilitySubmeshRenderer.cs.meta new file mode 100644 index 000000000..a58d557e9 --- /dev/null +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilitySubmeshRenderer.cs.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 7820c1c2b0e52c6408de899d6939996e +MonoImporter: + serializedVersion: 2 + defaultReferences: + - parentRenderer: {instanceID: 0} + - mesh: {instanceID: 0} + - hiddenPassMaterial: {fileID: 2100000, guid: 43227e5adadc6f24bb4bf74b92a56fb4, + type: 2} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/SpineAttributes.cs b/spine-unity/Assets/spine-unity/SpineAttributes.cs index 667d8eb73..269f37ae9 100644 --- a/spine-unity/Assets/spine-unity/SpineAttributes.cs +++ b/spine-unity/Assets/spine-unity/SpineAttributes.cs @@ -7,168 +7,171 @@ using UnityEngine; using System.Collections; -public abstract class SpineAttributeBase : PropertyAttribute { - public string dataField = ""; - public string startsWith = ""; -} - -public class SpineSlot : SpineAttributeBase { - public bool containsBoundingBoxes = false; - - /// - /// Smart popup menu for Spine Slots - /// - /// 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. - /// - /// Disables popup results that don't contain bounding box attachments when true. - public SpineSlot(string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) { - this.startsWith = startsWith; - this.dataField = dataField; - this.containsBoundingBoxes = containsBoundingBoxes; - } -} - -public class SpineEvent : 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 SpineEvent(string startsWith = "", string dataField = "") { - this.startsWith = startsWith; - this.dataField = dataField; - } -} - -public class SpineSkin : SpineAttributeBase { - /// - /// Smart popup menu for Spine Skins - /// - /// 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 SpineSkin(string startsWith = "", string dataField = "") { - this.startsWith = startsWith; - this.dataField = dataField; - } -} -public class SpineAnimation : SpineAttributeBase { - /// - /// Smart popup menu for Spine Animations - /// - /// 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 SpineAnimation(string startsWith = "", string dataField = "") { - this.startsWith = startsWith; - this.dataField = dataField; - } -} - -public class SpineAttachment : SpineAttributeBase { - public bool returnAttachmentPath = false; - public bool currentSkinOnly = false; - public bool placeholdersOnly = false; - public string slotField = ""; - - /// - /// Smart popup menu for Spine Attachments - /// - /// Filters popup results to only include the current Skin. Only valid when a SkeletonRenderer is the data source. - /// Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system. - /// Filters popup results to exclude attachments that are not children of Skin Placeholders - /// If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot - /// 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 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; +namespace Spine.Unity { + public abstract class SpineAttributeBase : PropertyAttribute { + public string dataField = ""; + public string startsWith = ""; } - public static SpineAttachment.Hierarchy GetHierarchy (string fullPath) { - return new SpineAttachment.Hierarchy(fullPath); + public class SpineSlot : SpineAttributeBase { + public bool containsBoundingBoxes = false; + + /// + /// Smart popup menu for Spine Slots + /// + /// 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. + /// + /// Disables popup results that don't contain bounding box attachments when true. + public SpineSlot(string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) { + this.startsWith = startsWith; + this.dataField = dataField; + this.containsBoundingBoxes = containsBoundingBoxes; + } } - public static Spine.Attachment GetAttachment (string attachmentPath, Spine.SkeletonData skeletonData) { - var hierarchy = SpineAttachment.GetHierarchy(attachmentPath); - if (hierarchy.name == "") - return null; - - return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name); + public class SpineEvent : 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 SpineEvent(string startsWith = "", string dataField = "") { + this.startsWith = startsWith; + this.dataField = dataField; + } } - public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) { - return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true)); + public class SpineSkin : SpineAttributeBase { + /// + /// Smart popup menu for Spine Skins + /// + /// 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 SpineSkin(string startsWith = "", string dataField = "") { + this.startsWith = startsWith; + this.dataField = dataField; + } + } + public class SpineAnimation : SpineAttributeBase { + /// + /// Smart popup menu for Spine Animations + /// + /// 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 SpineAnimation(string startsWith = "", string dataField = "") { + this.startsWith = startsWith; + this.dataField = dataField; + } } - /// - /// A struct that represents 3 strings that help identify and locate an attachment in a skeleton. - public struct Hierarchy { - public string skin; - public string slot; - public string name; + public class SpineAttachment : SpineAttributeBase { + public bool returnAttachmentPath = false; + public bool currentSkinOnly = false; + public bool placeholdersOnly = false; + public string slotField = ""; - public Hierarchy (string fullPath) { - string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries); - if (chunks.Length == 0) { - skin = ""; - slot = ""; + /// + /// Smart popup menu for Spine Attachments + /// + /// Filters popup results to only include the current Skin. Only valid when a SkeletonRenderer is the data source. + /// Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system. + /// Filters popup results to exclude attachments that are not children of Skin Placeholders + /// If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot + /// 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 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; + } + + public static SpineAttachment.Hierarchy GetHierarchy (string fullPath) { + return new SpineAttachment.Hierarchy(fullPath); + } + + public static Spine.Attachment GetAttachment (string attachmentPath, Spine.SkeletonData skeletonData) { + var hierarchy = SpineAttachment.GetHierarchy(attachmentPath); + if (hierarchy.name == "") + return null; + + return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name); + } + + public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) { + return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true)); + } + + /// + /// A struct that represents 3 strings that help identify and locate an attachment in a skeleton. + public struct Hierarchy { + public string skin; + public string slot; + public string name; + + 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) { + throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]"); + } + skin = chunks[0]; + slot = chunks[1]; name = ""; - return; - } - else if (chunks.Length < 2) { - throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]"); - } - skin = chunks[0]; - slot = chunks[1]; - name = ""; - for (int i = 2; i < chunks.Length; i++) { - name += chunks[i]; + for (int i = 2; i < chunks.Length; i++) { + name += chunks[i]; + } } } } -} -public class SpineBone : SpineAttributeBase { - /// - /// Smart popup menu for Spine Bones - /// - /// 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 SpineBone(string startsWith = "", string dataField = "") { - this.startsWith = startsWith; - this.dataField = dataField; + public class SpineBone : SpineAttributeBase { + /// + /// Smart popup menu for Spine Bones + /// + /// 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 SpineBone(string startsWith = "", string dataField = "") { + this.startsWith = startsWith; + this.dataField = dataField; + } + + public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) { + return renderer.skeleton == null ? null : renderer.skeleton.FindBone(boneName); + } + + public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) { + var data = skeletonDataAsset.GetSkeletonData(true); + return data.FindBone(boneName); + } } - public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) { - return renderer.skeleton == null ? null : renderer.skeleton.FindBone(boneName); + public class SpineAtlasRegion : PropertyAttribute { + //TODO: Standardize with Skeleton attributes + //NOTE: For now, relies on locally scoped field named "atlasAsset" for source. } - public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) { - var data = skeletonDataAsset.GetSkeletonData(true); - return data.FindBone(boneName); - } -} - -public class SpineAtlasRegion : PropertyAttribute { - //TODO: Standardize with Skeleton attributes - //NOTE: For now, relies on locally scoped field named "atlasAsset" for source. }