Эх сурвалжийг харах

Merge branch 'unity-skeletonrenderer'

pharan 9 жил өмнө
parent
commit
42981266d1
96 өөрчлөгдсөн 9245 нэмэгдсэн , 8174 устгасан
  1. BIN
      spine-unity/Assets/Examples/Getting Started/6 SkeletonGraphic.unity
  2. 1 0
      spine-unity/Assets/Examples/Getting Started/Scripts/BasicPlatformerController.cs
  3. 1 0
      spine-unity/Assets/Examples/Getting Started/Scripts/Raptor.cs
  4. 1 0
      spine-unity/Assets/Examples/Getting Started/Scripts/SpineBeginnerTwo.cs
  5. 1 0
      spine-unity/Assets/Examples/Getting Started/Scripts/SpineBlinkPlayer.cs
  6. 1 0
      spine-unity/Assets/Examples/Getting Started/Scripts/SpineboyBeginnerView.cs
  7. BIN
      spine-unity/Assets/Examples/Other Examples/SkeletonRenderSeparator.unity
  8. 8 0
      spine-unity/Assets/Examples/Other Examples/SkeletonRenderSeparator.unity.meta
  9. 1 1
      spine-unity/Assets/Examples/Scripts/Chimera.cs
  10. 1 0
      spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs
  11. 1 0
      spine-unity/Assets/Examples/Scripts/Goblins.cs
  12. 3 2
      spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs
  13. 1 0
      spine-unity/Assets/Examples/Scripts/SpineGauge.cs
  14. 1 0
      spine-unity/Assets/Examples/Scripts/Spineboy.cs
  15. 52 0
      spine-unity/Assets/Examples/Scripts/SpineboyPole.cs
  16. 2 2
      spine-unity/Assets/Examples/Scripts/SpineboyPole.cs.meta
  17. 0 0
      spine-unity/Assets/Examples/Spine/Spineboy/spineboy.json
  18. BIN
      spine-unity/Assets/Examples/Spine/Spineboy/spineboy_Atlas.asset
  19. BIN
      spine-unity/Assets/Examples/Spine/Spineboy/spineboy_SkeletonData.asset
  20. BIN
      spine-unity/Assets/Examples/Spine/square32.png
  21. 57 0
      spine-unity/Assets/Examples/Spine/square32.png.meta
  22. 121 119
      spine-unity/Assets/spine-unity/Asset Types/AtlasAsset.cs
  23. 150 107
      spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs
  24. 616 632
      spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs
  25. 114 112
      spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs
  26. 86 87
      spine-unity/Assets/spine-unity/BoneFollower.cs
  27. 1 1
      spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs
  28. 50 45
      spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs
  29. 42 42
      spine-unity/Assets/spine-unity/Editor/Menus.cs
  30. 61 59
      spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs
  31. 25 26
      spine-unity/Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs
  32. 507 506
      spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs
  33. 113 111
      spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs
  34. 279 279
      spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs
  35. 797 783
      spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs
  36. 101 0
      spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs
  37. 12 0
      spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs.meta
  38. 2 2
      spine-unity/Assets/spine-unity/Mesh Generation/Arrays.meta
  39. 351 0
      spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysMeshGenerator.cs
  40. 12 0
      spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysMeshGenerator.cs.meta
  41. 135 0
      spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs
  42. 0 0
      spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs.meta
  43. 194 0
      spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshSetMeshGenerator.cs
  44. 12 0
      spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshSetMeshGenerator.cs.meta
  45. 304 0
      spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshedMeshGenerator.cs
  46. 0 0
      spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshedMeshGenerator.cs.meta
  47. 15 0
      spine-unity/Assets/spine-unity/Mesh Generation/DoubleBuffered.cs
  48. 12 0
      spine-unity/Assets/spine-unity/Mesh Generation/DoubleBuffered.cs.meta
  49. 1 1
      spine-unity/Assets/spine-unity/Mesh Generation/ISimpleMeshGenerator.cs
  50. 0 0
      spine-unity/Assets/spine-unity/Mesh Generation/ISimpleMeshGenerator.cs.meta
  51. 84 0
      spine-unity/Assets/spine-unity/Mesh Generation/ISubmeshedMeshGenerator.cs
  52. 2 2
      spine-unity/Assets/spine-unity/Mesh Generation/ISubmeshedMeshGenerator.cs.meta
  53. 0 326
      spine-unity/Assets/spine-unity/Mesh Generation/Simple/ArraysSimpleMeshGenerator.cs
  54. 0 500
      spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs
  55. 0 69
      spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs
  56. 31 31
      spine-unity/Assets/spine-unity/Modules/AtlasRegionAttacher.cs
  57. 109 117
      spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs
  58. 43 39
      spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs
  59. 37 35
      spine-unity/Assets/spine-unity/Modules/CustomSkin.cs
  60. 120 117
      spine-unity/Assets/spine-unity/Modules/Ghost/SkeletonGhost.cs
  61. 0 240
      spine-unity/Assets/spine-unity/Modules/Mesh Generation Samples/VertexHelperSpineMeshGenerator.cs
  62. 42 41
      spine-unity/Assets/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs
  63. 40 37
      spine-unity/Assets/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs
  64. 327 325
      spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs
  65. 336 341
      spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs
  66. 188 157
      spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs
  67. 147 147
      spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs
  68. 2 2
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator.meta
  69. 2 2
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor.meta
  70. 49 0
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs
  71. 12 0
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs.meta
  72. 269 0
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs
  73. 12 0
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs.meta
  74. 96 0
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs
  75. 12 0
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs.meta
  76. 161 0
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs
  77. 12 0
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs.meta
  78. 41 40
      spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs
  79. 104 101
      spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs
  80. 58 57
      spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs
  81. 159 131
      spine-unity/Assets/spine-unity/Modules/SpriteAttacher.cs
  82. 119 118
      spine-unity/Assets/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs
  83. 1 1
      spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs
  84. 1 1
      spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs
  85. 129 126
      spine-unity/Assets/spine-unity/SkeletonAnimation.cs
  86. 149 149
      spine-unity/Assets/spine-unity/SkeletonAnimator.cs
  87. 121 149
      spine-unity/Assets/spine-unity/SkeletonExtensions.cs
  88. 826 663
      spine-unity/Assets/spine-unity/SkeletonRenderer.cs
  89. 272 269
      spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs
  90. 196 193
      spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs
  91. 11 41
      spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilitySubmeshRendererInspector.cs
  92. 264 281
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs
  93. 226 224
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs
  94. 14 13
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs
  95. 32 31
      spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilitySubmeshRenderer.cs
  96. 144 141
      spine-unity/Assets/spine-unity/SpineAttributes.cs

BIN
spine-unity/Assets/Examples/Getting Started/6 SkeletonGraphic.unity


+ 1 - 0
spine-unity/Assets/Examples/Getting Started/Scripts/BasicPlatformerController.cs

@@ -7,6 +7,7 @@
 
 using UnityEngine;
 using System.Collections;
+using Spine.Unity;
 
 [RequireComponent(typeof(CharacterController))]
 public class BasicPlatformerController : MonoBehaviour {

+ 1 - 0
spine-unity/Assets/Examples/Getting Started/Scripts/Raptor.cs

@@ -31,6 +31,7 @@
 
 using UnityEngine;
 using System.Collections;
+using Spine.Unity;
 
 public class Raptor : MonoBehaviour {
 

+ 1 - 0
spine-unity/Assets/Examples/Getting Started/Scripts/SpineBeginnerTwo.cs

@@ -1,5 +1,6 @@
 using UnityEngine;
 using System.Collections;
+using Spine.Unity;
 
 public class SpineBeginnerTwo : MonoBehaviour {
 

+ 1 - 0
spine-unity/Assets/Examples/Getting Started/Scripts/SpineBlinkPlayer.cs

@@ -1,5 +1,6 @@
 using UnityEngine;
 using System.Collections;
+using Spine.Unity;
 
 public class SpineBlinkPlayer : MonoBehaviour {
 	const int BlinkTrack = 1;

+ 1 - 0
spine-unity/Assets/Examples/Getting Started/Scripts/SpineboyBeginnerView.cs

@@ -1,5 +1,6 @@
 using UnityEngine;
 using System.Collections;
+using Spine.Unity;
 
 public class SpineboyBeginnerView : MonoBehaviour {
 	

BIN
spine-unity/Assets/Examples/Other Examples/SkeletonRenderSeparator.unity


+ 8 - 0
spine-unity/Assets/Examples/Other Examples/SkeletonRenderSeparator.unity.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 65ea82f2322d63247a1ee886ef3ba820
+timeCreated: 1458684353
+licenseType: Free
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1 - 1
spine-unity/Assets/Examples/Scripts/Chimera.cs

@@ -5,7 +5,7 @@
  * Full irrevocable rights and permissions granted to Esoteric Software
 *****************************************************************************/
 using UnityEngine;
-using System.Collections;
+using Spine.Unity;
 
 public class Chimera : MonoBehaviour {
 

+ 1 - 0
spine-unity/Assets/Examples/Scripts/FootSoldierExample.cs

@@ -6,6 +6,7 @@
 *****************************************************************************/
 using UnityEngine;
 using System.Collections;
+using Spine.Unity;
 
 public class FootSoldierExample : MonoBehaviour {
 	[SpineAnimation("Idle")]

+ 1 - 0
spine-unity/Assets/Examples/Scripts/Goblins.cs

@@ -32,6 +32,7 @@
 using UnityEngine;
 using System.Collections;
 using Spine;
+using Spine.Unity;
 
 public class Goblins : MonoBehaviour {
 	private bool girlSkin;

+ 3 - 2
spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs

@@ -1,5 +1,6 @@
 using UnityEngine;
 using System.Collections;
+using Spine.Unity;
 
 public class RaggedySpineboy : MonoBehaviour {
 
@@ -7,12 +8,12 @@ public class RaggedySpineboy : MonoBehaviour {
 	public float restoreDuration = 0.5f;
 	public Vector2 launchVelocity = new Vector2(50,100);
 
-	SkeletonRagdoll2D ragdoll;
+	Spine.Unity.Modules.SkeletonRagdoll2D ragdoll;
 	Collider2D naturalCollider;
 
 	void Start () {
 		
-		ragdoll = GetComponent<SkeletonRagdoll2D>();
+		ragdoll = GetComponent<Spine.Unity.Modules.SkeletonRagdoll2D>();
 		naturalCollider = GetComponent<Collider2D>();
 	}
 

+ 1 - 0
spine-unity/Assets/Examples/Scripts/SpineGauge.cs

@@ -1,5 +1,6 @@
 using UnityEngine;
 using System.Collections;
+using Spine.Unity;
 
 [ExecuteInEditMode]
 [RequireComponent(typeof(SkeletonRenderer))]

+ 1 - 0
spine-unity/Assets/Examples/Scripts/Spineboy.cs

@@ -33,6 +33,7 @@ using UnityEngine;
 using System.Collections;
 using Spine;
 using System;
+using Spine.Unity;
 
 public class Spineboy : MonoBehaviour {
 	SkeletonAnimation skeletonAnimation;

+ 52 - 0
spine-unity/Assets/Examples/Scripts/SpineboyPole.cs

@@ -0,0 +1,52 @@
+using UnityEngine;
+using System.Collections;
+using Spine.Unity;
+
+using Spine.Unity.Modules;
+
+public class SpineboyPole : MonoBehaviour {
+	public SkeletonAnimation skeletonAnimation;
+	public SkeletonRenderSeparator separator;
+
+	[Space]
+	[SpineAnimation]
+	public string run;
+	[SpineAnimation]
+	public string pole;
+	public float startX;
+	public float endX;
+
+	const float Speed = 18f;
+	const float RunTimeScale = 1.5f;
+
+	IEnumerator Start () {
+		var state = skeletonAnimation.state;
+
+		while (true) {
+			// Run phase
+			SetXPosition(startX);
+			separator.enabled = false; // Disable Separator during run.
+			state.SetAnimation(0, run, true);
+			state.TimeScale = RunTimeScale;
+
+			while (transform.localPosition.x < endX) {
+				transform.Translate(Vector3.right * Speed * Time.deltaTime);
+				yield return null;
+			}
+
+			// Hit phase
+			SetXPosition(endX);
+			separator.enabled = true; // Enable Separator when hit
+			var poleTrack = state.SetAnimation(0, pole, false);
+			yield return new WaitForSpineAnimationComplete(poleTrack);
+			yield return new WaitForSeconds(1f);
+		}
+	}
+
+	void SetXPosition (float x) {
+		var tp = transform.localPosition;
+		tp.x = x;
+		transform.localPosition = tp;
+	}
+}
+

+ 2 - 2
spine-unity/Assets/spine-unity/Modules/Mesh Generation Samples/VertexHelperSpineMeshGenerator.cs.meta → spine-unity/Assets/Examples/Scripts/SpineboyPole.cs.meta

@@ -1,6 +1,6 @@
 fileFormatVersion: 2
-guid: 90314614996d9074ab6de408d0baa004
-timeCreated: 1455147712
+guid: 66b573446c3300f45b950b243338b97c
+timeCreated: 1458684804
 licenseType: Free
 MonoImporter:
   serializedVersion: 2

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
spine-unity/Assets/Examples/Spine/Spineboy/spineboy.json


BIN
spine-unity/Assets/Examples/Spine/Spineboy/spineboy_Atlas.asset


BIN
spine-unity/Assets/Examples/Spine/Spineboy/spineboy_SkeletonData.asset


BIN
spine-unity/Assets/Examples/Spine/square32.png


+ 57 - 0
spine-unity/Assets/Examples/Spine/square32.png.meta

@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 718074e4e56a5404e824bf8e6571ea7d
+timeCreated: 1458684538
+licenseType: Free
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 2
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    linearTexture: 0
+    correctGamma: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 0
+  cubemapConvolution: 0
+  cubemapConvolutionSteps: 7
+  cubemapConvolutionExponent: 1.5
+  seamlessCubemap: 0
+  textureFormat: -1
+  maxTextureSize: 2048
+  textureSettings:
+    filterMode: -1
+    aniso: -1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  rGBM: 0
+  compressionQuality: 50
+  allowsAlphaSplitting: 0
+  spriteMode: 1
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaIsTransparency: 1
+  textureType: 8
+  buildTargetSettings: []
+  spriteSheet:
+    sprites: []
+    outline: []
+  spritePackingTag: 
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 121 - 119
spine-unity/Assets/spine-unity/Asset Types/AtlasAsset.cs

@@ -34,148 +34,150 @@ using System.IO;
 using UnityEngine;
 using Spine;
 
-/// <summary>Loads and stores a Spine atlas and list of materials.</summary>
-public class AtlasAsset : ScriptableObject {
-	public TextAsset atlasFile;
-	public Material[] materials;
-	private Atlas atlas;
-
-	public void Reset () {
-		atlas = null;
-	}
-
-	/// <returns>The atlas or null if it could not be loaded.</returns>
-	public Atlas GetAtlas () {
-		if (atlasFile == null) {
-			Debug.LogError("Atlas file not set for atlas asset: " + name, this);
-			Reset();
-			return null;
-		}
-
-		if (materials == null || materials.Length == 0) {
-			Debug.LogError("Materials not set for atlas asset: " + name, this);
-			Reset();
-			return null;
+namespace Spine.Unity {
+	/// <summary>Loads and stores a Spine atlas and list of materials.</summary>
+	public class AtlasAsset : ScriptableObject {
+		public TextAsset atlasFile;
+		public Material[] materials;
+		private Atlas atlas;
+
+		public void Reset () {
+			atlas = 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;
-		}
-	}
+		/// <returns>The atlas or null if it could not be loaded.</returns>
+		public Atlas GetAtlas () {
+			if (atlasFile == null) {
+				Debug.LogError("Atlas file not set for atlas asset: " + name, this);
+				Reset();
+				return null;
+			}
 
-	public Sprite GenerateSprite (string name, out Material material) {
-		AtlasRegion region = atlas.FindRegion(name);
+			if (materials == null || materials.Length == 0) {
+				Debug.LogError("Materials not set for atlas asset: " + name, this);
+				Reset();
+				return null;
+			}
 
-		Sprite sprite = null;
-		material = null;
+			if (atlas != null)
+				return atlas;
 
-		if (region != null) {
-			//sprite.rect
+			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;
+			}
 		}
 
-		return sprite;
-	}
+		public Sprite GenerateSprite (string name, out Material material) {
+			AtlasRegion region = atlas.FindRegion(name);
+
+			Sprite sprite = null;
+			material = null;
 
-	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;
+			if (region != null) {
+				//sprite.rect
 			}
 
-			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);
+			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();
-
-			material = (Material)region.page.rendererObject;
-		} else {
-			mesh = null;
+			return mesh;
 		}
-
-		return mesh;
 	}
-}
 
-public class MaterialsTextureLoader : TextureLoader {
-	AtlasAsset atlasAsset;
+	public class MaterialsTextureLoader : TextureLoader {
+		AtlasAsset atlasAsset;
 
-	public MaterialsTextureLoader (AtlasAsset atlasAsset) {
-		this.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) {
-	}
 }

+ 150 - 107
spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs

@@ -28,126 +28,125 @@
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
+//#define BAKE_ALL_BUTTON
 
 using System;
-using System.Collections;
 using System.Collections.Generic;
-using System.Linq;
 using System.Reflection;
 using System.IO;
 using UnityEditor;
 using UnityEngine;
 using Spine;
 
+namespace Spine.Unity.Editor {
+	
+	[CustomEditor(typeof(AtlasAsset))]
+	public class AtlasAssetInspector : UnityEditor.Editor {
+		private SerializedProperty atlasFile, materials;
+		private AtlasAsset atlasAsset;
+		private List<bool> baked;
+		private List<GameObject> bakedObjects;
+
+		void OnEnable () {
+			SpineEditorUtilities.ConfirmInitialization();
+			atlasFile = serializedObject.FindProperty("atlasFile");
+			materials = serializedObject.FindProperty("materials");
+			materials.isExpanded = true;
+			atlasAsset = (AtlasAsset)target;
+			UpdateBakedList();
+		}
 
-[CustomEditor(typeof(AtlasAsset))]
-public class AtlasAssetInspector : Editor {
-	private SerializedProperty atlasFile, materials;
-	private AtlasAsset atlasAsset;
-	private List<bool> baked;
-	private List<GameObject> bakedObjects;
-
-	void OnEnable () {
-		SpineEditorUtilities.ConfirmInitialization();
-		atlasFile = serializedObject.FindProperty("atlasFile");
-		materials = serializedObject.FindProperty("materials");
-		atlasAsset = (AtlasAsset)target;
-		UpdateBakedList();
-	}
-
-	void UpdateBakedList () {
-		AtlasAsset asset = (AtlasAsset)target;
-		baked = new List<bool>();
-		bakedObjects = new List<GameObject>();
-		if (atlasFile.objectReferenceValue != null) {
-			Atlas atlas = asset.GetAtlas();
-			FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
-			List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
-			string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
-			string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath);
-			string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name);
-
-
-			for (int i = 0; i < regions.Count; i++) {
-				AtlasRegion region = regions[i];
-				string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/");
-				GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject));
-				baked.Add(prefab != null);
-				bakedObjects.Add(prefab);
+		void UpdateBakedList () {
+			AtlasAsset asset = (AtlasAsset)target;
+			baked = new List<bool>();
+			bakedObjects = new List<GameObject>();
+			if (atlasFile.objectReferenceValue != null) {
+				Atlas atlas = asset.GetAtlas();
+				FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
+				List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
+				string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
+				string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath);
+				string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name);
+
+
+				for (int i = 0; i < regions.Count; i++) {
+					AtlasRegion region = regions[i];
+					string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/");
+					GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject));
+					baked.Add(prefab != null);
+					bakedObjects.Add(prefab);
+				}
 			}
 		}
-	}
-
-
 
-	override public void OnInspectorGUI () {
-		serializedObject.Update();
-		AtlasAsset asset = (AtlasAsset)target;
+		override public void OnInspectorGUI () {
+			serializedObject.Update();
+			AtlasAsset asset = (AtlasAsset)target;
 
-		EditorGUI.BeginChangeCheck();
-		EditorGUILayout.PropertyField(atlasFile);
-		EditorGUILayout.PropertyField(materials, true);
-		if (EditorGUI.EndChangeCheck())
-			serializedObject.ApplyModifiedProperties();
-
-		if (materials.arraySize == 0) {
-			EditorGUILayout.LabelField(new GUIContent("Error:  Missing materials", SpineEditorUtilities.Icons.warning));
-			return;
-		}
+			EditorGUI.BeginChangeCheck();
+			EditorGUILayout.PropertyField(atlasFile);
+			EditorGUILayout.PropertyField(materials, true);
+			if (EditorGUI.EndChangeCheck())
+				serializedObject.ApplyModifiedProperties();
 
-		for (int i = 0; i < materials.arraySize; i++) {
-			SerializedProperty prop = materials.GetArrayElementAtIndex(i);
-			Material mat = (Material)prop.objectReferenceValue;
-			if (mat == null) {
-				EditorGUILayout.LabelField(new GUIContent("Error:  Materials cannot be null", SpineEditorUtilities.Icons.warning));
+			if (materials.arraySize == 0) {
+				EditorGUILayout.LabelField(new GUIContent("Error:  Missing materials", SpineEditorUtilities.Icons.warning));
 				return;
 			}
-		}
 
-		if (atlasFile.objectReferenceValue != null) {
-			Atlas atlas = asset.GetAtlas();
-			FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
-			List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
-			EditorGUILayout.LabelField(new GUIContent("Region Baking", SpineEditorUtilities.Icons.unityIcon));
-			EditorGUI.indentLevel++;
-			AtlasPage lastPage = null;
-			for (int i = 0; i < regions.Count; i++) {
-				if (lastPage != regions[i].page) {
-					if (lastPage != null) {
-						EditorGUILayout.Separator();
-						EditorGUILayout.Separator();
-					}
-					lastPage = regions[i].page;
-					Material mat = ((Material)lastPage.rendererObject);
-					if (mat != null) {
-						GUILayout.BeginHorizontal();
-						{
-							EditorGUI.BeginDisabledGroup(true);
-							EditorGUILayout.ObjectField(mat, typeof(Material), false, GUILayout.Width(250));
-							EditorGUI.EndDisabledGroup();
-						}
-						GUILayout.EndHorizontal();
-
-					} else {
-						EditorGUILayout.LabelField(new GUIContent("Page missing material!", SpineEditorUtilities.Icons.warning));
-					}
+			for (int i = 0; i < materials.arraySize; i++) {
+				SerializedProperty prop = materials.GetArrayElementAtIndex(i);
+				Material mat = (Material)prop.objectReferenceValue;
+				if (mat == null) {
+					EditorGUILayout.LabelField(new GUIContent("Error:  Materials cannot be null", SpineEditorUtilities.Icons.warning));
+					return;
 				}
-				GUILayout.BeginHorizontal();
-				{
-					 //EditorGUILayout.ToggleLeft(baked[i] ? "" : regions[i].name, baked[i]);
-					bool result = baked[i] ? EditorGUILayout.ToggleLeft("", baked[i], GUILayout.Width(24)) : EditorGUILayout.ToggleLeft("    " + regions[i].name, baked[i]);
-					if(baked[i]){
-						EditorGUILayout.ObjectField(bakedObjects[i], typeof(GameObject), false, GUILayout.Width(250));
+			}
+
+			if (atlasFile.objectReferenceValue != null) {
+				Atlas atlas = asset.GetAtlas();
+				FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
+				List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
+				EditorGUILayout.LabelField(new GUIContent("Region Baking", SpineEditorUtilities.Icons.unityIcon));
+				EditorGUI.indentLevel++;
+				AtlasPage lastPage = null;
+				for (int i = 0; i < regions.Count; i++) {
+					if (lastPage != regions[i].page) {
+						if (lastPage != null) {
+							EditorGUILayout.Separator();
+							EditorGUILayout.Separator();
+						}
+						lastPage = regions[i].page;
+						Material mat = ((Material)lastPage.rendererObject);
+						if (mat != null) {
+							GUILayout.BeginHorizontal();
+							{
+								EditorGUI.BeginDisabledGroup(true);
+								EditorGUILayout.ObjectField(mat, typeof(Material), false, GUILayout.Width(250));
+								EditorGUI.EndDisabledGroup();
+							}
+							GUILayout.EndHorizontal();
+
+						} else {
+							EditorGUILayout.LabelField(new GUIContent("Page missing material!", SpineEditorUtilities.Icons.warning));
+						}
 					}
-					if (result && !baked[i]) {
-						//bake
-						baked[i] = true;
-						bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]);
-						EditorGUIUtility.PingObject(bakedObjects[i]);
-					} else if (!result && baked[i]) {
-						//unbake
-						bool unbakeResult = EditorUtility.DisplayDialog("Delete Baked Region", "Do you want to delete the prefab for " + regions[i].name, "Yes", "Cancel");
-						switch (unbakeResult) {
+					GUILayout.BeginHorizontal();
+					{
+						//EditorGUILayout.ToggleLeft(baked[i] ? "" : regions[i].name, baked[i]);
+						bool result = baked[i] ? EditorGUILayout.ToggleLeft("", baked[i], GUILayout.Width(24)) : EditorGUILayout.ToggleLeft("    " + regions[i].name, baked[i]);
+						if(baked[i]){
+							EditorGUILayout.ObjectField(bakedObjects[i], typeof(GameObject), false, GUILayout.Width(250));
+						}
+						if (result && !baked[i]) {
+							//bake
+							baked[i] = true;
+							bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]);
+							EditorGUIUtility.PingObject(bakedObjects[i]);
+						} else if (!result && baked[i]) {
+							//unbake
+							bool unbakeResult = EditorUtility.DisplayDialog("Delete Baked Region", "Do you want to delete the prefab for " + regions[i].name, "Yes", "Cancel");
+							switch (unbakeResult) {
 							case true:
 								//delete
 								string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
@@ -160,18 +159,62 @@ public class AtlasAssetInspector : Editor {
 							case false:
 								//do nothing
 								break;
+							}
 						}
 					}
+					GUILayout.EndHorizontal();
 				}
-				GUILayout.EndHorizontal();
+				EditorGUI.indentLevel--;
+
+				#if BAKE_ALL_BUTTON
+				// Check state
+				bool allBaked = true;
+				bool allUnbaked = true;
+				for (int i = 0; i < regions.Count; i++) {
+					allBaked &= baked[i];
+					allUnbaked &= !baked[i];
+				}
+
+				if (!allBaked && GUILayout.Button("Bake All")) {
+					for (int i = 0; i < regions.Count; i++) {
+						if (!baked[i]) {
+							baked[i] = true;
+							bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]);
+						}
+					}
+
+				} else if (!allUnbaked && GUILayout.Button("Unbake All")) {
+					bool unbakeResult = EditorUtility.DisplayDialog("Delete All Baked Regions", "Are you sure you want to unbake all region prefabs? This cannot be undone.", "Yes", "Cancel");
+					switch (unbakeResult) {
+					case true:
+						//delete
+						for (int i = 0; i < regions.Count; i++) {
+							if (baked[i]) {
+								string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
+								string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath);
+								string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name);
+								string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(regions[i]) + ".prefab").Replace("\\", "/");
+								AssetDatabase.DeleteAsset(bakedPrefabPath);
+								baked[i] = false;
+							}
+						}
+						break;
+					case false:
+						//do nothing
+						break;
+					}
+
+				}
+				#endif
+
 			}
-			EditorGUI.indentLevel--;
-		}
 
-		if (serializedObject.ApplyModifiedProperties() ||
-			(UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed")
-		) {
-			asset.Reset();
+			if (serializedObject.ApplyModifiedProperties() ||
+				(UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed")
+			) {
+				asset.Reset();
+			}
 		}
 	}
+
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 616 - 632
spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs


+ 114 - 112
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;
-
-	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;
+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];
 		}
 
-		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;
+			}
+			#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;
 			}
-			//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();
+			stateData = new AnimationStateData(skeletonData);
+			FillStateData();
 
-		return skeletonData;
-	}
+			return skeletonData;
+		}
 
-	public void FillStateData () {
-		if (stateData == null)
-			return;
+		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;
+		}
 	}
+
 }

+ 86 - 87
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;
-
-/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
-[ExecuteInEditMode]
-[AddComponentMenu("Spine/BoneFollower")]
-public class BoneFollower : MonoBehaviour {
-
-	#region Inspector
-	public SkeletonRenderer skeletonRenderer;
-	public SkeletonRenderer SkeletonRenderer {
-		get { return skeletonRenderer; }
-		set {
-			skeletonRenderer = value;
-			Reset();
-		}
-	}
-	/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
-	[SpineBone(dataField: "skeletonRenderer")]
-	public String boneName;
 
-	public bool followZPosition = true;
-	public bool followBoneRotation = true;
-	public bool resetOnAwake = true;
-	#endregion
-
-	[NonSerialized]
-	public bool valid;
+namespace Spine.Unity {
+	/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
+	[ExecuteInEditMode]
+	[AddComponentMenu("Spine/BoneFollower")]
+	public class BoneFollower : MonoBehaviour {
+
+		#region Inspector
+		public SkeletonRenderer skeletonRenderer;
+		public SkeletonRenderer SkeletonRenderer {
+			get { return skeletonRenderer; }
+			set {
+				skeletonRenderer = value;
+				Reset();
+			}
+		}
+		/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
+		[SpineBone(dataField: "skeletonRenderer")]
+		public String boneName;
 
-	[NonSerialized]
-	public Bone bone;
-	Transform skeletonTransform;
+		public bool followZPosition = true;
+		public bool followBoneRotation = true;
+		public bool resetOnAwake = true;
+		#endregion
 
-	public void HandleResetRenderer (SkeletonRenderer skeletonRenderer) {
-		Reset();
-	}
+		[NonSerialized]
+		public bool valid;
 
-	public void Reset () {
-		bone = null;
-		valid = skeletonRenderer != null && skeletonRenderer.valid;
+		[NonSerialized]
+		public Bone bone;
+		Transform skeletonTransform;
 
-		if (!valid) return;
+		public void HandleResetRenderer (SkeletonRenderer skeletonRenderer) {
+			Reset();
+		}
 
-		skeletonTransform = skeletonRenderer.transform;
-		skeletonRenderer.OnRebuild -= HandleResetRenderer;
-		skeletonRenderer.OnRebuild += HandleResetRenderer;
+		public void Reset () {
+			bone = null;
+			valid = skeletonRenderer != null && skeletonRenderer.valid;
 
-		#if UNITY_EDITOR
-		if (Application.isEditor)
-			DoUpdate();
-		#endif
-	}
+			if (!valid) return;
 
-	void OnDestroy () {
-		if (skeletonRenderer != null)
+			skeletonTransform = skeletonRenderer.transform;
 			skeletonRenderer.OnRebuild -= HandleResetRenderer;
-	}
+			skeletonRenderer.OnRebuild += HandleResetRenderer;
 
-	public void Awake () {
-		if (resetOnAwake)
-			Reset();
-	}
+			#if UNITY_EDITOR
+			if (Application.isEditor)
+				DoUpdate();
+			#endif
+		}
 
-	void LateUpdate () {
-		DoUpdate();
-	}
+		void OnDestroy () {
+			if (skeletonRenderer != null)
+				skeletonRenderer.OnRebuild -= HandleResetRenderer;
+		}
 
-	public void DoUpdate () {
-		if (!valid) {
-			Reset();
-			return;
+		public void Awake () {
+			if (resetOnAwake)
+				Reset();
 		}
 
-		if (bone == null) {
-			if (boneName == null || boneName.Length == 0)
+		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;
+			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);
+			// 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);
-			}
+				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;
+				// 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;
+				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));
+				if (followBoneRotation) {
+					Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
+					thisTransform.rotation = Quaternion.Euler(worldRotation.x, worldRotation.y, skeletonTransform.rotation.eulerAngles.z + (bone.WorldRotationX * flipRotation));
+				}
 			}
-		}
 
+		}
 	}
+
 }

+ 1 - 1
spine-unity/Assets/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs

@@ -1,6 +1,6 @@
 using UnityEngine;
 
-namespace Spine {
+namespace Spine.Unity.Editor {
 	public static class AssetDatabaseAvailabilityDetector {
 		const string MARKER_RESOURCE_NAME = "SpineAssetDatabaseMarker";
 		private static bool _isMarkerLoaded;

+ 50 - 45
spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs

@@ -33,60 +33,65 @@ using System;
 using UnityEditor;
 using UnityEngine;
 
-[CustomEditor(typeof(BoneFollower))]
-public class BoneFollowerInspector : Editor {
-	SerializedProperty boneName, skeletonRenderer, followZPosition, followBoneRotation;
-	BoneFollower component;
-	bool needsReset;
 
-	void OnEnable () {
-		skeletonRenderer = serializedObject.FindProperty("skeletonRenderer");
-		boneName = serializedObject.FindProperty("boneName");
-		followBoneRotation = serializedObject.FindProperty("followBoneRotation");
-		followZPosition = serializedObject.FindProperty("followZPosition");
-		component = (BoneFollower)target;
-		component.skeletonRenderer.Initialize(false);
-	}
-		
-	override public void OnInspectorGUI () {
-		if (needsReset) {
-			component.Reset();
-			component.DoUpdate();
-			needsReset = false;
-			SceneView.RepaintAll();
+namespace Spine.Unity.Editor {
+	
+	[CustomEditor(typeof(BoneFollower))]
+	public class BoneFollowerInspector : UnityEditor.Editor {
+		SerializedProperty boneName, skeletonRenderer, followZPosition, followBoneRotation;
+		BoneFollower component;
+		bool needsReset;
+
+		void OnEnable () {
+			skeletonRenderer = serializedObject.FindProperty("skeletonRenderer");
+			boneName = serializedObject.FindProperty("boneName");
+			followBoneRotation = serializedObject.FindProperty("followBoneRotation");
+			followZPosition = serializedObject.FindProperty("followZPosition");
+			component = (BoneFollower)target;
+			component.skeletonRenderer.Initialize(false);
 		}
-		serializedObject.Update();
 
-		// FindRenderer()
-		if (skeletonRenderer.objectReferenceValue == null) {
-			SkeletonRenderer parentRenderer = SkeletonUtility.GetInParent<SkeletonRenderer>(component.transform);
+		override public void OnInspectorGUI () {
+			if (needsReset) {
+				component.Reset();
+				component.DoUpdate();
+				needsReset = false;
+				SceneView.RepaintAll();
+			}
+			serializedObject.Update();
 
-			if (parentRenderer != null) {
-				skeletonRenderer.objectReferenceValue = (UnityEngine.Object)parentRenderer;
+			// FindRenderer()
+			if (skeletonRenderer.objectReferenceValue == null) {
+				SkeletonRenderer parentRenderer = SkeletonUtility.GetInParent<SkeletonRenderer>(component.transform);
+
+				if (parentRenderer != null) {
+					skeletonRenderer.objectReferenceValue = (UnityEngine.Object)parentRenderer;
+				}
 			}
-		}
 
-		EditorGUILayout.PropertyField(skeletonRenderer);
+			EditorGUILayout.PropertyField(skeletonRenderer);
 
-		if (component.valid) {
-			EditorGUI.BeginChangeCheck();
-			EditorGUILayout.PropertyField(boneName);
-			if (EditorGUI.EndChangeCheck()) {
-				serializedObject.ApplyModifiedProperties();
-				needsReset = true;
-				serializedObject.Update();
+			if (component.valid) {
+				EditorGUI.BeginChangeCheck();
+				EditorGUILayout.PropertyField(boneName);
+				if (EditorGUI.EndChangeCheck()) {
+					serializedObject.ApplyModifiedProperties();
+					needsReset = true;
+					serializedObject.Update();
+				}
+
+				EditorGUILayout.PropertyField(followBoneRotation);
+				EditorGUILayout.PropertyField(followZPosition);
+			} else {
+				GUILayout.Label("INVALID");
 			}
-				
-			EditorGUILayout.PropertyField(followBoneRotation);
-			EditorGUILayout.PropertyField(followZPosition);
-		} else {
-			GUILayout.Label("INVALID");
-		}
 
-		if (serializedObject.ApplyModifiedProperties() ||
-			(Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed")
-	    ) {
-			component.Reset();
+			if (serializedObject.ApplyModifiedProperties() ||
+				(UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed")
+			) {
+				component.Reset();
+			}
 		}
 	}
+
 }

+ 42 - 42
spine-unity/Assets/spine-unity/Editor/Menus.cs

@@ -28,57 +28,57 @@
  * 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 UnityEditor;
 using UnityEngine;
-using Spine;
 
-public class Menus {
-	[MenuItem("Assets/Create/Spine Atlas")]
-	static public void CreateAtlas () {
-		CreateAsset<AtlasAsset>("New Atlas");
-	}
-	
-	[MenuItem("Assets/Create/Spine SkeletonData")]
-	static public void CreateSkeletonData () {
-		CreateAsset<SkeletonDataAsset>("New SkeletonData");
-	}
-	
-	static private void CreateAsset <T> (String name) where T : ScriptableObject {
-		var dir = "Assets/";
-		var selected = Selection.activeObject;
-		if (selected != null) {
-			var assetDir = AssetDatabase.GetAssetPath(selected.GetInstanceID());
-			if (assetDir.Length > 0 && Directory.Exists(assetDir))
-				dir = assetDir + "/";
+namespace Spine.Unity.Editor {
+	public static class Menus {
+		[MenuItem("Assets/Create/Spine Atlas")]
+		static public void CreateAtlas () {
+			CreateAsset<AtlasAsset>("New Atlas");
 		}
-		ScriptableObject asset = ScriptableObject.CreateInstance<T>();
-		AssetDatabase.CreateAsset(asset, dir + name + ".asset");
-		AssetDatabase.SaveAssets();
-		EditorUtility.FocusProjectWindow();
-		Selection.activeObject = asset;
-	}
 
-	[MenuItem("GameObject/Spine/SkeletonRenderer", false, 10)]
-	static public void CreateSkeletonRendererGameObject () {
-		CreateSpineGameObject<SkeletonRenderer>("New SkeletonRenderer");
-	}
+		[MenuItem("Assets/Create/Spine SkeletonData")]
+		static public void CreateSkeletonData () {
+			CreateAsset<SkeletonDataAsset>("New SkeletonData");
+		}
 
-	[MenuItem("GameObject/Spine/SkeletonAnimation", false, 10)]
-	static public void CreateSkeletonAnimationGameObject () {
-		CreateSpineGameObject<SkeletonAnimation>("New SkeletonAnimation");
-	}
+		static private void CreateAsset <T> (String name) where T : ScriptableObject {
+			var dir = "Assets/";
+			var selected = Selection.activeObject;
+			if (selected != null) {
+				var assetDir = AssetDatabase.GetAssetPath(selected.GetInstanceID());
+				if (assetDir.Length > 0 && Directory.Exists(assetDir))
+					dir = assetDir + "/";
+			}
+			ScriptableObject asset = ScriptableObject.CreateInstance<T>();
+			AssetDatabase.CreateAsset(asset, dir + name + ".asset");
+			AssetDatabase.SaveAssets();
+			EditorUtility.FocusProjectWindow();
+			Selection.activeObject = asset;
+		}
+
+		[MenuItem("GameObject/Spine/SkeletonRenderer", false, 10)]
+		static public void CreateSkeletonRendererGameObject () {
+			CreateSpineGameObject<SkeletonRenderer>("New SkeletonRenderer");
+		}
 
-	static public void CreateSpineGameObject<T> (string name) where T : MonoBehaviour {
-		var parentGameObject = Selection.activeObject as GameObject;
-		var parentTransform = parentGameObject == null ? null : parentGameObject.transform;
+		[MenuItem("GameObject/Spine/SkeletonAnimation", false, 10)]
+		static public void CreateSkeletonAnimationGameObject () {
+			CreateSpineGameObject<SkeletonAnimation>("New SkeletonAnimation");
+		}
+
+		static public void CreateSpineGameObject<T> (string name) where T : MonoBehaviour {
+			var parentGameObject = Selection.activeObject as GameObject;
+			var parentTransform = parentGameObject == null ? null : parentGameObject.transform;
 
-		var gameObject = new GameObject("New SkeletonRenderer", typeof(T));
-		gameObject.transform.SetParent(parentTransform, false);
-		EditorUtility.FocusProjectWindow();
-		Selection.activeObject = gameObject;
-		EditorGUIUtility.PingObject(Selection.activeObject);
+			var gameObject = new GameObject("New SkeletonRenderer", typeof(T));
+			gameObject.transform.SetParent(parentTransform, false);
+			EditorUtility.FocusProjectWindow();
+			Selection.activeObject = gameObject;
+			EditorGUIUtility.PingObject(Selection.activeObject);
+		}
 	}
 }

+ 61 - 59
spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs

@@ -28,83 +28,85 @@
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
-
 using System;
 using UnityEditor;
 using UnityEngine;
 using Spine;
 
-[CustomEditor(typeof(SkeletonAnimation))]
-public class SkeletonAnimationInspector : SkeletonRendererInspector {
-	protected SerializedProperty animationName, loop, timeScale, autoReset;
-	protected bool m_isPrefab;
-	protected bool wasAnimationNameChanged;
+namespace Spine.Unity.Editor {
+	
+	[CustomEditor(typeof(SkeletonAnimation))]
+	public class SkeletonAnimationInspector : SkeletonRendererInspector {
+		protected SerializedProperty animationName, loop, timeScale, autoReset;
+		protected bool m_isPrefab;
+		protected bool wasAnimationNameChanged;
 
-	protected override void OnEnable () {
-		base.OnEnable();
-		animationName = serializedObject.FindProperty("_animationName");
-		loop = serializedObject.FindProperty("loop");
-		timeScale = serializedObject.FindProperty("timeScale");
+		protected override void OnEnable () {
+			base.OnEnable();
+			animationName = serializedObject.FindProperty("_animationName");
+			loop = serializedObject.FindProperty("loop");
+			timeScale = serializedObject.FindProperty("timeScale");
 
-		if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
-			m_isPrefab = true;
-	}
+			if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
+				m_isPrefab = true;
+		}
 
-	protected override void DrawInspectorGUI () {
-		base.DrawInspectorGUI();
+		protected override void DrawInspectorGUI () {
+			base.DrawInspectorGUI();
 
-		SkeletonAnimation component = (SkeletonAnimation)target;
-		if (!component.valid)
-			return;
-		
-		if (wasAnimationNameChanged) {
-			if (!Application.isPlaying) {
-				if (component.state != null) component.state.ClearTrack(0);
-				component.skeleton.SetToSetupPose();
-			}
+			SkeletonAnimation component = (SkeletonAnimation)target;
+			if (!component.valid)
+				return;
 
-			Spine.Animation animationToUse = component.skeleton.Data.FindAnimation(animationName.stringValue);
+			if (wasAnimationNameChanged) {
+				if (!Application.isPlaying) {
+					if (component.state != null) component.state.ClearTrack(0);
+					component.skeleton.SetToSetupPose();
+				}
 
-			if (!Application.isPlaying) {
-				if (animationToUse != null) animationToUse.Apply(component.skeleton, 0f, 0f, false, null);
-				component.Update();
-				component.LateUpdate();
-				SceneView.RepaintAll();
-			} else {
-				if (animationToUse != null)
-					component.state.SetAnimation(0, animationToUse, loop.boolValue);
-				else
-					component.state.ClearTrack(0);
-			}
+				Spine.Animation animationToUse = component.skeleton.Data.FindAnimation(animationName.stringValue);
 
-			wasAnimationNameChanged = false;
-		}
+				if (!Application.isPlaying) {
+					if (animationToUse != null) animationToUse.Apply(component.skeleton, 0f, 0f, false, null);
+					component.Update();
+					component.LateUpdate();
+					SceneView.RepaintAll();
+				} else {
+					if (animationToUse != null)
+						component.state.SetAnimation(0, animationToUse, loop.boolValue);
+					else
+						component.state.ClearTrack(0);
+				}
+
+				wasAnimationNameChanged = false;
+			}
 
-		// Reflect animationName serialized property in the inspector even if SetAnimation API was used.
-		if (Application.isPlaying) {
-			TrackEntry current = component.state.GetCurrent(0);
-			if (current != null) {
-				if (component.AnimationName != animationName.stringValue) {
-					animationName.stringValue = current.Animation.Name;
+			// Reflect animationName serialized property in the inspector even if SetAnimation API was used.
+			if (Application.isPlaying) {
+				TrackEntry current = component.state.GetCurrent(0);
+				if (current != null) {
+					if (component.AnimationName != animationName.stringValue) {
+						animationName.stringValue = current.Animation.Name;
+					}
 				}
 			}
-		}
 
-		EditorGUILayout.Space();
-		EditorGUI.BeginChangeCheck();
-		EditorGUILayout.PropertyField(animationName);
-		wasAnimationNameChanged |= EditorGUI.EndChangeCheck();
-			
-		EditorGUILayout.PropertyField(loop);
-		EditorGUILayout.PropertyField(timeScale);
-		component.timeScale = Math.Max(component.timeScale, 0);
+			EditorGUILayout.Space();
+			EditorGUI.BeginChangeCheck();
+			EditorGUILayout.PropertyField(animationName);
+			wasAnimationNameChanged |= EditorGUI.EndChangeCheck();
+
+			EditorGUILayout.PropertyField(loop);
+			EditorGUILayout.PropertyField(timeScale);
+			component.timeScale = Math.Max(component.timeScale, 0);
 
-		EditorGUILayout.Space();
+			EditorGUILayout.Space();
 
-		if (!m_isPrefab) {
-			if (component.GetComponent<SkeletonUtility>() == null) {
-				if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) {
-					component.gameObject.AddComponent<SkeletonUtility>();
+			if (!m_isPrefab) {
+				if (component.GetComponent<SkeletonUtility>() == null) {
+					if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) {
+						component.gameObject.AddComponent<SkeletonUtility>();
+					}
 				}
 			}
 		}

+ 25 - 26
spine-unity/Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs

@@ -1,5 +1,3 @@
-
-
 /*****************************************************************************
  * SkeletonAnimatorInspector created by Mitch Thompson
  * Full irrevocable rights and permissions granted to Esoteric Software
@@ -7,37 +5,38 @@
 using System;
 using UnityEditor;
 using UnityEngine;
-using Spine;
-
-[CustomEditor(typeof(SkeletonAnimator))]
-public class SkeletonAnimatorInspector : SkeletonRendererInspector {
-	protected SerializedProperty layerMixModes;
-	protected bool isPrefab;
-	protected override void OnEnable () {
-		base.OnEnable();
-		layerMixModes = serializedObject.FindProperty("layerMixModes");
 
-		if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
-			isPrefab = true;
-	}
+namespace Spine.Unity.Editor {
+	[CustomEditor(typeof(SkeletonAnimator))]
+	public class SkeletonAnimatorInspector : SkeletonRendererInspector {
+		protected SerializedProperty layerMixModes;
+		protected bool isPrefab;
+		protected override void OnEnable () {
+			base.OnEnable();
+			layerMixModes = serializedObject.FindProperty("layerMixModes");
+
+			if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
+				isPrefab = true;
+		}
 
-	protected override void DrawInspectorGUI () {
-		base.DrawInspectorGUI();
+		protected override void DrawInspectorGUI () {
+			base.DrawInspectorGUI();
 
-		EditorGUILayout.PropertyField(layerMixModes, true);
+			EditorGUILayout.PropertyField(layerMixModes, true);
 
-		SkeletonAnimator component = (SkeletonAnimator)target;
-		if (!component.valid)
-			return;
+			SkeletonAnimator component = (SkeletonAnimator)target;
+			if (!component.valid)
+				return;
 
-		EditorGUILayout.Space();
+			EditorGUILayout.Space();
 
-		if (!isPrefab) {
-			if (component.GetComponent<SkeletonUtility>() == null) {
-				if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) {
-					component.gameObject.AddComponent<SkeletonUtility>();
+			if (!isPrefab) {
+				if (component.GetComponent<SkeletonUtility>() == null) {
+					if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) {
+						component.gameObject.AddComponent<SkeletonUtility>();
+					}
 				}
 			}
 		}
 	}
-}
+}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 507 - 506
spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs


+ 113 - 111
spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs

@@ -30,137 +30,139 @@
  *****************************************************************************/
 
 using System;
-using System.Reflection;
 using UnityEditor;
 using UnityEngine;
 
-[CustomEditor(typeof(SkeletonRenderer))]
-public class SkeletonRendererInspector : Editor {
-	protected static bool advancedFoldout;
-
-	protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles, submeshSeparators, front, zSpacing;
-
-	private static MethodInfo EditorGUILayoutSortingLayerField;
-	protected SerializedObject rendererSerializedObject;
-	protected SerializedProperty sortingLayerIDProperty;
-
-	protected virtual void OnEnable () {
-		SpineEditorUtilities.ConfirmInitialization();
-		skeletonDataAsset = serializedObject.FindProperty("skeletonDataAsset");
-		initialSkinName = serializedObject.FindProperty("initialSkinName");
-		normals = serializedObject.FindProperty("calculateNormals");
-		tangents = serializedObject.FindProperty("calculateTangents");
-		meshes = serializedObject.FindProperty("renderMeshes");
-		immutableTriangles = serializedObject.FindProperty("immutableTriangles");
-		submeshSeparators = serializedObject.FindProperty("submeshSeparators");
-		front = serializedObject.FindProperty("frontFacing");
-		zSpacing = serializedObject.FindProperty("zSpacing");
-
-		if(EditorGUILayoutSortingLayerField == null)
-			EditorGUILayoutSortingLayerField = typeof(EditorGUILayout).GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(GUIContent), typeof(SerializedProperty), typeof(GUIStyle) }, null);
-
-		rendererSerializedObject = new SerializedObject(((SkeletonRenderer)target).GetComponent<Renderer>());
-		sortingLayerIDProperty = rendererSerializedObject.FindProperty("m_SortingLayerID");
-	}
+namespace Spine.Unity.Editor {
+	
+	[CustomEditor(typeof(SkeletonRenderer))]
+	public class SkeletonRendererInspector : UnityEditor.Editor {
+		protected static bool advancedFoldout;
 
-	protected virtual void DrawInspectorGUI () {
-		SkeletonRenderer component = (SkeletonRenderer)target;
-		EditorGUILayout.BeginHorizontal();
-		EditorGUILayout.PropertyField(skeletonDataAsset);
-		float reloadWidth = GUI.skin.label.CalcSize(new GUIContent("Reload")).x + 20;
-		if (GUILayout.Button("Reload", GUILayout.Width(reloadWidth))) {
-			if (component.skeletonDataAsset != null) {
-				foreach (AtlasAsset aa in component.skeletonDataAsset.atlasAssets) {
-					if (aa != null)
-						aa.Reset();
-				}
-				
-				component.skeletonDataAsset.Reset();
-			}
-			component.Initialize(true);
-		}
-		EditorGUILayout.EndHorizontal();
+		protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles, separatorSlotNames, front, zSpacing;
 
-		if (!component.valid) {
-			component.Initialize(true);
-			component.LateUpdate();
-			if (!component.valid)
-				return;
-		}
+		protected SpineInspectorUtility.SerializedSortingProperties sortingProperties;
 
-		// Initial skin name.
-		{
-			String[] skins = new String[component.skeleton.Data.Skins.Count];
-			int skinIndex = 0;
-			for (int i = 0; i < skins.Length; i++) {
-				String name = component.skeleton.Data.Skins.Items[i].Name;
-				skins[i] = name;
-				if (name == initialSkinName.stringValue)
-					skinIndex = i;
-			}
+		protected virtual void OnEnable () {
+			SpineEditorUtilities.ConfirmInitialization();
+			skeletonDataAsset = serializedObject.FindProperty("skeletonDataAsset");
+			initialSkinName = serializedObject.FindProperty("initialSkinName");
+			normals = serializedObject.FindProperty("calculateNormals");
+			tangents = serializedObject.FindProperty("calculateTangents");
+			meshes = serializedObject.FindProperty("renderMeshes");
+			immutableTriangles = serializedObject.FindProperty("immutableTriangles");
+			separatorSlotNames = serializedObject.FindProperty("separatorSlotNames");
+			separatorSlotNames.isExpanded = true;
 
-			skinIndex = EditorGUILayout.Popup("Initial Skin", skinIndex, skins);			
-			initialSkinName.stringValue = skins[skinIndex];
+			front = serializedObject.FindProperty("frontFacing");
+			zSpacing = serializedObject.FindProperty("zSpacing");
+
+			var renderer = ((SkeletonRenderer)target).GetComponent<Renderer>();
+			sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(renderer);
 		}
 
-		EditorGUILayout.Space();
+		protected virtual void DrawInspectorGUI () {
+			SkeletonRenderer component = (SkeletonRenderer)target;
+			EditorGUILayout.BeginHorizontal();
+			EditorGUILayout.PropertyField(skeletonDataAsset);
+			float reloadWidth = GUI.skin.label.CalcSize(new GUIContent("Reload")).x + 20;
+			if (GUILayout.Button("Reload", GUILayout.Width(reloadWidth))) {
+				if (component.skeletonDataAsset != null) {
+					foreach (AtlasAsset aa in component.skeletonDataAsset.atlasAssets) {
+						if (aa != null)
+							aa.Reset();
+					}
+
+					component.skeletonDataAsset.Reset();
+				}
+				component.Initialize(true);
+			}
+			EditorGUILayout.EndHorizontal();
 
-		// Sorting Layers
-		{
-			var renderer = component.GetComponent<Renderer>();
-			if(renderer != null) {
-				EditorGUI.BeginChangeCheck();
+			if (!component.valid) {
+				component.Initialize(true);
+				component.LateUpdate();
+				if (!component.valid)
+					return;
+			}
 
-				if(EditorGUILayoutSortingLayerField != null && sortingLayerIDProperty != null) {
-					EditorGUILayoutSortingLayerField.Invoke(null, new object[] { new GUIContent("Sorting Layer"), sortingLayerIDProperty, EditorStyles.popup } );
-				} else {
-					renderer.sortingLayerID = EditorGUILayout.IntField("Sorting Layer ID", renderer.sortingLayerID);
+			// Initial skin name.
+			{
+				String[] skins = new String[component.skeleton.Data.Skins.Count];
+				int skinIndex = 0;
+				for (int i = 0; i < skins.Length; i++) {
+					String skinNameString = component.skeleton.Data.Skins.Items[i].Name;
+					skins[i] = skinNameString;
+					if (skinNameString == initialSkinName.stringValue)
+						skinIndex = i;
 				}
 
-				renderer.sortingOrder = EditorGUILayout.IntField("Order in Layer", renderer.sortingOrder);
+				skinIndex = EditorGUILayout.Popup("Initial Skin", skinIndex, skins);			
+				initialSkinName.stringValue = skins[skinIndex];
+			}
+
+			EditorGUILayout.Space();
+
+			// Sorting Layers
+			{
+				SpineInspectorUtility.SortingPropertyFields(sortingProperties, applyModifiedProperties: true);
+			}
+
+			// More Render Options...
+			{
+				using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
+					EditorGUI.indentLevel++;
+					advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced");
+					if(advancedFoldout) {
+						EditorGUI.indentLevel++;
+						SeparatorsField(separatorSlotNames);
+						EditorGUILayout.PropertyField(meshes,
+							new GUIContent("Render Mesh Attachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments"));
+						EditorGUILayout.PropertyField(immutableTriangles,
+							new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"));
+						EditorGUILayout.Space();
+
+						const float MinZSpacing = -0.1f;
+						const float MaxZSpacing = 0f;
+						EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing);
+
+						if (normals != null) {
+							EditorGUILayout.PropertyField(normals);
+							EditorGUILayout.PropertyField(tangents);
+						}
+
+						if (front != null) {
+							EditorGUILayout.PropertyField(front);
+						}
+						EditorGUI.indentLevel--;
+					}
+					EditorGUI.indentLevel--;
 
-				if(EditorGUI.EndChangeCheck()) {
-					rendererSerializedObject.ApplyModifiedProperties();
-					EditorUtility.SetDirty(renderer);
 				}
 			}
 		}
 
-		// More Render Options...
-		{
-			advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced");
-			if(advancedFoldout) {
-				EditorGUI.indentLevel++;
-				EditorGUILayout.PropertyField(submeshSeparators, true);
-				EditorGUILayout.Space();
-				EditorGUILayout.PropertyField(meshes,
-					new GUIContent("Render Mesh Attachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments"));
-				EditorGUILayout.PropertyField(immutableTriangles,
-					new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"));
-				EditorGUILayout.Space();
-
-				const float MinZSpacing = -0.1f;
-				const float MaxZSpacing = 0f;
-				EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing);
-
-				EditorGUILayout.PropertyField(normals);
-				EditorGUILayout.PropertyField(tangents);
-				EditorGUILayout.PropertyField(front);
-				EditorGUILayout.Separator();
-				EditorGUI.indentLevel--;
+		public static void SeparatorsField (SerializedProperty separatorSlotNames) {
+			using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
+				if (separatorSlotNames.isExpanded) {
+					EditorGUILayout.PropertyField(separatorSlotNames, includeChildren: true);
+				} else {
+					EditorGUILayout.PropertyField(separatorSlotNames, new GUIContent(separatorSlotNames.displayName + string.Format(" [{0}]", separatorSlotNames.arraySize)), includeChildren: true);
+				}
+
 			}
 		}
-	}
 
-	override public void OnInspectorGUI () {
-		serializedObject.Update();
-		DrawInspectorGUI();
-		if (serializedObject.ApplyModifiedProperties() ||
-			(Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed")
-		) {
-			if (!Application.isPlaying)
-				((SkeletonRenderer)target).Initialize(true);
+		override public void OnInspectorGUI () {
+			serializedObject.Update();
+			DrawInspectorGUI();
+			if (serializedObject.ApplyModifiedProperties() ||
+				(UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed")
+			) {
+				if (!Application.isPlaying)
+					((SkeletonRenderer)target).Initialize(true);
+			}
 		}
-	}
 
-}
+	}
+}

+ 279 - 279
spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs

@@ -6,351 +6,351 @@
 using UnityEngine;
 using UnityEditor;
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using System.Reflection;
 using Spine;
 
+namespace Spine.Unity.Editor {
+	public struct SpineDrawerValuePair {
+		public string str;
+		public SerializedProperty property;
 
+		public SpineDrawerValuePair (string val, SerializedProperty property) {
+			this.str = val;
+			this.property = property;
+		}
+	}
 
-public struct SpineDrawerValuePair {
-	public string str;
-	public SerializedProperty property;
+	public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineAttributeBase {
+		protected SkeletonDataAsset skeletonDataAsset;
+		internal const string NoneLabel = "<None>";
 
-	public SpineDrawerValuePair (string val, SerializedProperty property) {
-		this.str = val;
-		this.property = property;
-	}
-}
+		protected T TargetAttribute { get { return (T)attribute; } }
 
-public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineAttributeBase {
-	protected SkeletonDataAsset skeletonDataAsset;
-	internal const string NoneLabel = "<None>";
+		public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
+			if (property.propertyType != SerializedPropertyType.String) {
+				EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
+				return;
+			}
 
-	protected T TargetAttribute { get { return (T)attribute; } }
+			var dataProperty = property.serializedObject.FindProperty(TargetAttribute.dataField);
 
-	public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
-		if (property.propertyType != SerializedPropertyType.String) {
-			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
-			return;
-		}
-		
-		var dataProperty = property.serializedObject.FindProperty(TargetAttribute.dataField);
-		
-		if (dataProperty != null) {
-			if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
-				skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
-			} else if (dataProperty.objectReferenceValue is SkeletonRenderer) {
-				var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue;
-				if (renderer != null)
-					skeletonDataAsset = renderer.skeletonDataAsset;
-			} else {
-				EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
+			if (dataProperty != null) {
+				if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
+					skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
+				} else if (dataProperty.objectReferenceValue is SkeletonRenderer) {
+					var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue;
+					if (renderer != null)
+						skeletonDataAsset = renderer.skeletonDataAsset;
+				} else {
+					EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
+					return;
+				}
+
+			} else if (property.serializedObject.targetObject is Component) {
+				var component = (Component)property.serializedObject.targetObject;
+				if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
+					var skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
+					skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
+				}
+			}
+
+			if (skeletonDataAsset == null) {
+				EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset");
 				return;
 			}
-			
-		} else if (property.serializedObject.targetObject is Component) {
-			var component = (Component)property.serializedObject.targetObject;
-			if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
-				var skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
-				skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
+
+			position = EditorGUI.PrefixLabel(position, label);
+
+			var propertyStringValue = property.stringValue;
+			if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel : propertyStringValue, EditorStyles.popup)) {
+				Selector(property);
 			}
+
+		}
+
+		protected virtual void Selector (SerializedProperty property) {
+			SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
+			if (data == null)
+				return;
+
+			GenericMenu menu = new GenericMenu();
+			PopulateMenu(menu, property, this.TargetAttribute, data);
+			menu.ShowAsContext();
 		}
-		
-		if (skeletonDataAsset == null) {
-			EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset");
-			return;
+
+		protected abstract void PopulateMenu (GenericMenu menu, SerializedProperty property, T targetAttribute, SkeletonData data);
+
+		protected virtual void HandleSelect (object val) {
+			var pair = (SpineDrawerValuePair)val;
+			pair.property.stringValue = pair.str;
+			pair.property.serializedObject.ApplyModifiedProperties();
 		}
-		
-		position = EditorGUI.PrefixLabel(position, label);
 
-		var propertyStringValue = property.stringValue;
-		if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel : propertyStringValue, EditorStyles.popup)) {
-			Selector(property);
+		public override float GetPropertyHeight (SerializedProperty property, GUIContent label) {
+			return 18;
 		}
-		
-	}
 
-	protected virtual void Selector (SerializedProperty property) {
-		SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
-		if (data == null)
-			return;
-		
-		GenericMenu menu = new GenericMenu();
-		PopulateMenu(menu, property, this.TargetAttribute, data);
-		menu.ShowAsContext();
 	}
 
-	protected abstract void PopulateMenu (GenericMenu menu, SerializedProperty property, T targetAttribute, SkeletonData data);
+	[CustomPropertyDrawer(typeof(SpineSlot))]
+	public class SpineSlotDrawer : SpineTreeItemDrawerBase<SpineSlot> {
 
-	protected virtual void HandleSelect (object val) {
-		var pair = (SpineDrawerValuePair)val;
-		pair.property.stringValue = pair.str;
-		pair.property.serializedObject.ApplyModifiedProperties();
-	}
+		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) {
+			for (int i = 0; i < data.Slots.Count; i++) {
+				string name = data.Slots.Items[i].Name;
+				if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) {
+					if (targetAttribute.containsBoundingBoxes) {
 
-	public override float GetPropertyHeight (SerializedProperty property, GUIContent label) {
-		return 18;
-	}
+						int slotIndex = i;
 
-}
+						List<Attachment> attachments = new List<Attachment>();
+						foreach (var skin in data.Skins) {
+							skin.FindAttachmentsForSlot(slotIndex, attachments);
+						}
 
-[CustomPropertyDrawer(typeof(SpineSlot))]
-public class SpineSlotDrawer : SpineTreeItemDrawerBase<SpineSlot> {
-
-	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) {
-		for (int i = 0; i < data.Slots.Count; i++) {
-			string name = data.Slots.Items[i].Name;
-			if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) {
-				if (targetAttribute.containsBoundingBoxes) {
-					
-					int slotIndex = i;
-					
-					List<Attachment> attachments = new List<Attachment>();
-					foreach (var skin in data.Skins) {
-						skin.FindAttachmentsForSlot(slotIndex, attachments);
-					}
-					
-					bool hasBoundingBox = false;
-					foreach (var attachment in attachments) {
-						if (attachment is BoundingBoxAttachment) {
-							menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
-							hasBoundingBox = true;
-							break;
+						bool hasBoundingBox = false;
+						foreach (var attachment in attachments) {
+							if (attachment is BoundingBoxAttachment) {
+								menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+								hasBoundingBox = true;
+								break;
+							}
 						}
+
+						if (!hasBoundingBox)
+							menu.AddDisabledItem(new GUIContent(name));
+
+
+					} else {
+						menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 					}
-					
-					if (!hasBoundingBox)
-						menu.AddDisabledItem(new GUIContent(name));
-					
-					
-				} else {
-					menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+
 				}
-				
+
 			}
-			
 		}
+
 	}
 
-}
+	[CustomPropertyDrawer(typeof(SpineSkin))]
+	public class SpineSkinDrawer : SpineTreeItemDrawerBase<SpineSkin> {
 
-[CustomPropertyDrawer(typeof(SpineSkin))]
-public class SpineSkinDrawer : SpineTreeItemDrawerBase<SpineSkin> {
+		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSkin targetAttribute, SkeletonData data) {
+			menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
+			menu.AddSeparator("");
 
-	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSkin targetAttribute, SkeletonData data) {
-		menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
-		menu.AddSeparator("");
-		
-		for (int i = 0; i < data.Skins.Count; i++) {
-			string name = data.Skins.Items[i].Name;
-			if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
-				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+			for (int i = 0; i < data.Skins.Count; i++) {
+				string name = data.Skins.Items[i].Name;
+				if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
+					menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+			}
 		}
-	}
 
-}
+	}
 
-[CustomPropertyDrawer(typeof(SpineAnimation))]
-public class SpineAnimationDrawer : SpineTreeItemDrawerBase<SpineAnimation> {
-	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) {
-		var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
+	[CustomPropertyDrawer(typeof(SpineAnimation))]
+	public class SpineAnimationDrawer : SpineTreeItemDrawerBase<SpineAnimation> {
+		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) {
+			var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
 
-		// <None> item
-		menu.AddItem(new GUIContent(NoneLabel), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair("", property));
+			// <None> item
+			menu.AddItem(new GUIContent(NoneLabel), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair("", property));
 
-		for (int i = 0; i < animations.Count; i++) {
-			string name = animations.Items[i].Name;
-			if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
-				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+			for (int i = 0; i < animations.Count; i++) {
+				string name = animations.Items[i].Name;
+				if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
+					menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+			}
 		}
-	}
 
-}
+	}
 
-[CustomPropertyDrawer(typeof(SpineEvent))]
-public class SpineEventNameDrawer : SpineTreeItemDrawerBase<SpineEvent> {
-	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEvent targetAttribute, SkeletonData data) {
-		var events = skeletonDataAsset.GetSkeletonData(false).Events;
-		for (int i = 0; i < events.Count; i++) {
-			string name = events.Items[i].Name;
-			if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
-				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+	[CustomPropertyDrawer(typeof(SpineEvent))]
+	public class SpineEventNameDrawer : SpineTreeItemDrawerBase<SpineEvent> {
+		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEvent targetAttribute, SkeletonData data) {
+			var events = skeletonDataAsset.GetSkeletonData(false).Events;
+			for (int i = 0; i < events.Count; i++) {
+				string name = events.Items[i].Name;
+				if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))
+					menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+			}
 		}
-	}
 
-}
+	}
 
-[CustomPropertyDrawer(typeof(SpineAttachment))]
-public class SpineAttachmentDrawer : SpineTreeItemDrawerBase<SpineAttachment> {
-	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAttachment targetAttribute, SkeletonData data) {
-		List<Skin> validSkins = new List<Skin>();
-		SkeletonRenderer skeletonRenderer = null;
-
-		var component = property.serializedObject.targetObject as Component;
-		if (component != null) {
-			if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
-				skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
-				//if (skeletonDataAsset != skeletonRenderer.skeletonDataAsset) Debug.LogWarning("DataField SkeletonDataAsset and SkeletonRenderer/SkeletonAnimation's SkeletonDataAsset do not match. Remove the explicit dataField parameter of your [SpineAttachment] field.");
-				skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
+	[CustomPropertyDrawer(typeof(SpineAttachment))]
+	public class SpineAttachmentDrawer : SpineTreeItemDrawerBase<SpineAttachment> {
+		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAttachment targetAttribute, SkeletonData data) {
+			List<Skin> validSkins = new List<Skin>();
+			SkeletonRenderer skeletonRenderer = null;
+
+			var component = property.serializedObject.targetObject as Component;
+			if (component != null) {
+				if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
+					skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
+					//if (skeletonDataAsset != skeletonRenderer.skeletonDataAsset) Debug.LogWarning("DataField SkeletonDataAsset and SkeletonRenderer/SkeletonAnimation's SkeletonDataAsset do not match. Remove the explicit dataField parameter of your [SpineAttachment] field.");
+					skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
+				}
 			}
-		}
 
-		if (skeletonRenderer != null && targetAttribute.currentSkinOnly) {
-			if (skeletonRenderer.skeleton.Skin != null) {
-				validSkins.Add(skeletonRenderer.skeleton.Skin);
+			if (skeletonRenderer != null && targetAttribute.currentSkinOnly) {
+				if (skeletonRenderer.skeleton.Skin != null) {
+					validSkins.Add(skeletonRenderer.skeleton.Skin);
+				} else {
+					validSkins.Add(data.Skins.Items[0]);
+				}
 			} else {
-				validSkins.Add(data.Skins.Items[0]);
-			}
-		} else {
-			foreach (Skin skin in data.Skins) {
-				if (skin != null)
-					validSkins.Add(skin);
-			}
-		}
-		
-		List<string> attachmentNames = new List<string>();
-		List<string> placeholderNames = new List<string>();
-		
-		string prefix = "";
-		
-		if (skeletonRenderer != null && targetAttribute.currentSkinOnly)
-			menu.AddDisabledItem(new GUIContent(skeletonRenderer.gameObject.name + " (SkeletonRenderer)"));
-		else
-			menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
-		
-		menu.AddSeparator("");
-		
-		menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property));
-
-		menu.AddSeparator("");
-		
-		Skin defaultSkin = data.Skins.Items[0];
-		
-		SerializedProperty slotProperty = property.serializedObject.FindProperty(targetAttribute.slotField);
-		string slotMatch = "";
-		if (slotProperty != null) {
-			if (slotProperty.propertyType == SerializedPropertyType.String) {
-				slotMatch = slotProperty.stringValue.ToLower();
+				foreach (Skin skin in data.Skins) {
+					if (skin != null)
+						validSkins.Add(skin);
+				}
 			}
-		}
-		
-		foreach (Skin skin in validSkins) {
-			string skinPrefix = skin.Name + "/";
-			
-			if (validSkins.Count > 1)
-				prefix = skinPrefix;
-			
-			for (int i = 0; i < data.Slots.Count; i++) {
-				if (slotMatch.Length > 0 && data.Slots.Items[i].Name.ToLower().Contains(slotMatch) == false)
-					continue;
-				
-				attachmentNames.Clear();
-				placeholderNames.Clear();
-				
-				skin.FindNamesForSlot(i, attachmentNames);
-				if (skin != defaultSkin) {
-					defaultSkin.FindNamesForSlot(i, attachmentNames);
-					skin.FindNamesForSlot(i, placeholderNames);
+
+			List<string> attachmentNames = new List<string>();
+			List<string> placeholderNames = new List<string>();
+
+			string prefix = "";
+
+			if (skeletonRenderer != null && targetAttribute.currentSkinOnly)
+				menu.AddDisabledItem(new GUIContent(skeletonRenderer.gameObject.name + " (SkeletonRenderer)"));
+			else
+				menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
+
+			menu.AddSeparator("");
+
+			menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property));
+
+			menu.AddSeparator("");
+
+			Skin defaultSkin = data.Skins.Items[0];
+
+			SerializedProperty slotProperty = property.serializedObject.FindProperty(targetAttribute.slotField);
+			string slotMatch = "";
+			if (slotProperty != null) {
+				if (slotProperty.propertyType == SerializedPropertyType.String) {
+					slotMatch = slotProperty.stringValue.ToLower();
 				}
-				
-				
-				for (int a = 0; a < attachmentNames.Count; a++) {
-					
-					string attachmentPath = attachmentNames[a];
-					string menuPath = prefix + data.Slots.Items[i].Name + "/" + attachmentPath;
-					string name = attachmentNames[a];
-					
-					if (targetAttribute.returnAttachmentPath)
-						name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath;
-					
-					if (targetAttribute.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) {
-						menu.AddDisabledItem(new GUIContent(menuPath));
-					} else {
-						menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+			}
+
+			foreach (Skin skin in validSkins) {
+				string skinPrefix = skin.Name + "/";
+
+				if (validSkins.Count > 1)
+					prefix = skinPrefix;
+
+				for (int i = 0; i < data.Slots.Count; i++) {
+					if (slotMatch.Length > 0 && data.Slots.Items[i].Name.ToLower().Contains(slotMatch) == false)
+						continue;
+
+					attachmentNames.Clear();
+					placeholderNames.Clear();
+
+					skin.FindNamesForSlot(i, attachmentNames);
+					if (skin != defaultSkin) {
+						defaultSkin.FindNamesForSlot(i, attachmentNames);
+						skin.FindNamesForSlot(i, placeholderNames);
+					}
+
+
+					for (int a = 0; a < attachmentNames.Count; a++) {
+
+						string attachmentPath = attachmentNames[a];
+						string menuPath = prefix + data.Slots.Items[i].Name + "/" + attachmentPath;
+						string name = attachmentNames[a];
+
+						if (targetAttribute.returnAttachmentPath)
+							name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath;
+
+						if (targetAttribute.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) {
+							menu.AddDisabledItem(new GUIContent(menuPath));
+						} else {
+							menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+						}
+
+
 					}
-					
-					
 				}
 			}
 		}
+
 	}
 
-}
+	[CustomPropertyDrawer(typeof(SpineBone))]
+	public class SpineBoneDrawer : SpineTreeItemDrawerBase<SpineBone> {
 
-[CustomPropertyDrawer(typeof(SpineBone))]
-public class SpineBoneDrawer : SpineTreeItemDrawerBase<SpineBone> {
+		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineBone targetAttribute, SkeletonData data) {
+			menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
+			menu.AddSeparator("");
 
-	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineBone targetAttribute, SkeletonData data) {
-		menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
-		menu.AddSeparator("");
-		
-		for (int i = 0; i < data.Bones.Count; i++) {
-			string name = data.Bones.Items[i].Name;
-			if (name.StartsWith(targetAttribute.startsWith))
-				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+			for (int i = 0; i < data.Bones.Count; i++) {
+				string name = data.Bones.Items[i].Name;
+				if (name.StartsWith(targetAttribute.startsWith))
+					menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+			}
 		}
+
 	}
 
-}
+	[CustomPropertyDrawer(typeof(SpineAtlasRegion))]
+	public class SpineAtlasRegionDrawer : PropertyDrawer {
+		Component component;
+		SerializedProperty atlasProp;
 
-[CustomPropertyDrawer(typeof(SpineAtlasRegion))]
-public class SpineAtlasRegionDrawer : PropertyDrawer {
-	Component component;
-	SerializedProperty atlasProp;
+		public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
+			if (property.propertyType != SerializedPropertyType.String) {
+				EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
+				return;
+			}
+
+			component = (Component)property.serializedObject.targetObject;
+
+			if (component != null)
+				atlasProp = property.serializedObject.FindProperty("atlasAsset");
+			else
+				atlasProp = null;
+
+
+			if (atlasProp == null) {
+				EditorGUI.LabelField(position, "ERROR:", "Must have AtlasAsset variable!");
+				return;
+			} else if (atlasProp.objectReferenceValue == null) {
+				EditorGUI.LabelField(position, "ERROR:", "Atlas variable must not be null!");
+				return;
+			} else if (atlasProp.objectReferenceValue.GetType() != typeof(AtlasAsset)) {
+				EditorGUI.LabelField(position, "ERROR:", "Atlas variable must be of type AtlasAsset!");
+			}
+
+			position = EditorGUI.PrefixLabel(position, label);
+
+			if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
+				Selector(property);
+			}
 
-	public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
-		if (property.propertyType != SerializedPropertyType.String) {
-			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
-			return;
-		}
-		
-		component = (Component)property.serializedObject.targetObject;
-		
-		if (component != null)
-			atlasProp = property.serializedObject.FindProperty("atlasAsset");
-		else
-			atlasProp = null;
-		
-		
-		if (atlasProp == null) {
-			EditorGUI.LabelField(position, "ERROR:", "Must have AtlasAsset variable!");
-			return;
-		} else if (atlasProp.objectReferenceValue == null) {
-			EditorGUI.LabelField(position, "ERROR:", "Atlas variable must not be null!");
-			return;
-		} else if (atlasProp.objectReferenceValue.GetType() != typeof(AtlasAsset)) {
-			EditorGUI.LabelField(position, "ERROR:", "Atlas variable must be of type AtlasAsset!");
 		}
-		
-		position = EditorGUI.PrefixLabel(position, label);
-		
-		if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
-			Selector(property);
+
+		void Selector (SerializedProperty property) {
+			GenericMenu menu = new GenericMenu();
+			AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue;
+			Atlas atlas = atlasAsset.GetAtlas();
+			FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
+			List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
+
+			for (int i = 0; i < regions.Count; i++) {
+				string name = regions[i].name;
+				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+			}
+
+
+			menu.ShowAsContext();
 		}
-		
-	}
 
-	void Selector (SerializedProperty property) {
-		GenericMenu menu = new GenericMenu();
-		AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue;
-		Atlas atlas = atlasAsset.GetAtlas();
-		FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
-		List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
-		
-		for (int i = 0; i < regions.Count; i++) {
-			string name = regions[i].name;
-			menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+		static void HandleSelect (object val) {
+			var pair = (SpineDrawerValuePair)val;
+			pair.property.stringValue = pair.str;
+			pair.property.serializedObject.ApplyModifiedProperties();
 		}
-		
-		
-		menu.ShowAsContext();
-	}
 
-	static void HandleSelect (object val) {
-		var pair = (SpineDrawerValuePair)val;
-		pair.property.stringValue = pair.str;
-		pair.property.serializedObject.ApplyModifiedProperties();
 	}
-	
+
 }

+ 797 - 783
spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs

@@ -35,6 +35,7 @@
  * Spine Editor Utilities created by Mitch Thompson
  * Full irrevocable rights and permissions granted to Esoteric Software
 *****************************************************************************/
+#define SPINE_SKELETONANIMATOR
 using UnityEngine;
 using UnityEditor;
 using System.Collections;
@@ -45,253 +46,257 @@ using System.Linq;
 using System.Reflection;
 using Spine;
 
-[InitializeOnLoad]
-public class SpineEditorUtilities : AssetPostprocessor {
-
-	public static class Icons {
-		public static Texture2D skeleton;
-		public static Texture2D nullBone;
-		public static Texture2D bone;
-		public static Texture2D poseBones;
-		public static Texture2D boneNib;
-		public static Texture2D slot;
-		public static Texture2D slotRoot;
-		public static Texture2D skinPlaceholder;
-		public static Texture2D image;
-		public static Texture2D boundingBox;
-		public static Texture2D mesh;
-		public static Texture2D weights;
-		public static Texture2D skin;
-		public static Texture2D skinsRoot;
-		public static Texture2D animation;
-		public static Texture2D animationRoot;
-		public static Texture2D spine;
-		public static Texture2D _event;
-		public static Texture2D constraintNib;
-		public static Texture2D warning;
-		public static Texture2D skeletonUtility;
-		public static Texture2D hingeChain;
-		public static Texture2D subMeshRenderer;
-		public static Texture2D unityIcon;
-		public static Texture2D controllerIcon;
-
-		public static Mesh boneMesh {
-			get {
-				if (_boneMesh == null) {
-					_boneMesh = new Mesh();
-					_boneMesh.vertices = new Vector3[4] {
-						Vector3.zero,
-						new Vector3(-0.1f, 0.1f, 0),
-						Vector3.up,
-						new Vector3(0.1f, 0.1f, 0)
-					};
-					_boneMesh.uv = new Vector2[4];
-					_boneMesh.triangles = new int[6] { 0, 1, 2, 2, 3, 0 };
-					_boneMesh.RecalculateBounds();
-					_boneMesh.RecalculateNormals();
-				}
+namespace Spine.Unity.Editor {
+	
+	[InitializeOnLoad]
+	public class SpineEditorUtilities : AssetPostprocessor {
+
+		public static class Icons {
+			public static Texture2D skeleton;
+			public static Texture2D nullBone;
+			public static Texture2D bone;
+			public static Texture2D poseBones;
+			public static Texture2D boneNib;
+			public static Texture2D slot;
+			public static Texture2D slotRoot;
+			public static Texture2D skinPlaceholder;
+			public static Texture2D image;
+			public static Texture2D boundingBox;
+			public static Texture2D mesh;
+			public static Texture2D weights;
+			public static Texture2D skin;
+			public static Texture2D skinsRoot;
+			public static Texture2D animation;
+			public static Texture2D animationRoot;
+			public static Texture2D spine;
+			public static Texture2D _event;
+			public static Texture2D constraintNib;
+			public static Texture2D warning;
+			public static Texture2D skeletonUtility;
+			public static Texture2D hingeChain;
+			public static Texture2D subMeshRenderer;
+			public static Texture2D unityIcon;
+			public static Texture2D controllerIcon;
+
+			public static Mesh boneMesh {
+				get {
+					if (_boneMesh == null) {
+						_boneMesh = new Mesh();
+						_boneMesh.vertices = new Vector3[4] {
+							Vector3.zero,
+							new Vector3(-0.1f, 0.1f, 0),
+							Vector3.up,
+							new Vector3(0.1f, 0.1f, 0)
+						};
+						_boneMesh.uv = new Vector2[4];
+						_boneMesh.triangles = new int[6] { 0, 1, 2, 2, 3, 0 };
+						_boneMesh.RecalculateBounds();
+						_boneMesh.RecalculateNormals();
+					}
 
-				return _boneMesh;
+					return _boneMesh;
+				}
 			}
-		}
 
-		internal static Mesh _boneMesh;
+			internal static Mesh _boneMesh;
+
+			public static Material boneMaterial {
+				get {
+					if (_boneMaterial == null) {
+						#if UNITY_4_3
+						_boneMaterial = new Material(Shader.Find("Particles/Alpha Blended"));
+						_boneMaterial.SetColor("_TintColor", new Color(0.4f, 0.4f, 0.4f, 0.25f));
+						#else
+						_boneMaterial = new Material(Shader.Find("Spine/Bones"));
+						_boneMaterial.SetColor("_Color", new Color(0.4f, 0.4f, 0.4f, 0.25f));
+						#endif
 
-		public static Material boneMaterial {
-			get {
-				if (_boneMaterial == null) {
-#if UNITY_4_3
-					_boneMaterial = new Material(Shader.Find("Particles/Alpha Blended"));
-					_boneMaterial.SetColor("_TintColor", new Color(0.4f, 0.4f, 0.4f, 0.25f));
-#else
-					_boneMaterial = new Material(Shader.Find("Spine/Bones"));
-					_boneMaterial.SetColor("_Color", new Color(0.4f, 0.4f, 0.4f, 0.25f));
-#endif
+					}
 
+					return _boneMaterial;
 				}
+			}
 
-				return _boneMaterial;
+			internal static Material _boneMaterial;
+
+			public static void Initialize () {
+				skeleton = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skeleton.png");
+				nullBone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-null.png");
+				bone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-bone.png");
+				poseBones = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-poseBones.png");
+				boneNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boneNib.png");
+				slot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slot.png");
+				slotRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slotRoot.png");
+				skinPlaceholder = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png");
+				image = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-image.png");
+				boundingBox = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boundingBox.png");
+				mesh = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-mesh.png");
+				weights = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-weights.png");
+				skin = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png");
+				skinsRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinsRoot.png");
+				animation = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animation.png");
+				animationRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animationRoot.png");
+				spine = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-spine.png");
+				_event = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-event.png");
+				constraintNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-constraintNib.png");
+				warning = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-warning.png");
+				skeletonUtility = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skeletonUtility.png");
+				hingeChain = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-hingeChain.png");
+				subMeshRenderer = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-subMeshRenderer.png");
+
+				unityIcon = EditorGUIUtility.FindTexture("SceneAsset Icon");
+
+				controllerIcon = EditorGUIUtility.FindTexture("AnimatorController Icon");
 			}
 		}
 
-		internal static Material _boneMaterial;
-
-		public static void Initialize () {
-			skeleton = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skeleton.png");
-			nullBone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-null.png");
-			bone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-bone.png");
-			poseBones = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-poseBones.png");
-			boneNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boneNib.png");
-			slot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slot.png");
-			slotRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slotRoot.png");
-			skinPlaceholder = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png");
-			image = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-image.png");
-			boundingBox = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boundingBox.png");
-			mesh = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-mesh.png");
-			weights = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-weights.png");
-			skin = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png");
-			skinsRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinsRoot.png");
-			animation = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animation.png");
-			animationRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animationRoot.png");
-			spine = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-spine.png");
-			_event = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-event.png");
-			constraintNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-constraintNib.png");
-			warning = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-warning.png");
-			skeletonUtility = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skeletonUtility.png");
-			hingeChain = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-hingeChain.png");
-			subMeshRenderer = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-subMeshRenderer.png");
-
-			unityIcon = EditorGUIUtility.FindTexture("SceneAsset Icon");
-
-			controllerIcon = EditorGUIUtility.FindTexture("AnimatorController Icon");
-		}
-	}
+		public static string editorPath = "";
+		public static string editorGUIPath = "";
+		static HashSet<string> assetsImportedInWrongState;
+		static Dictionary<int, GameObject> skeletonRendererTable;
+		static Dictionary<int, SkeletonUtilityBone> skeletonUtilityBoneTable;
+		static Dictionary<int, BoundingBoxFollower> boundingBoxFollowerTable;
+		public static float defaultScale = 0.01f;
+		public static float defaultMix = 0.2f;
+		public static string defaultShader = "Spine/Skeleton";
+		public static bool initialized;
 
-	public static string editorPath = "";
-	public static string editorGUIPath = "";
-	static HashSet<string> assetsImportedInWrongState;
-	static Dictionary<int, GameObject> skeletonRendererTable;
-	static Dictionary<int, SkeletonUtilityBone> skeletonUtilityBoneTable;
-	static Dictionary<int, BoundingBoxFollower> boundingBoxFollowerTable;
-	public static float defaultScale = 0.01f;
-	public static float defaultMix = 0.2f;
-	public static string defaultShader = "Spine/Skeleton";
-	public static bool initialized;
-
-	const string DEFAULT_MIX_KEY = "SPINE_DEFAULT_MIX";
-
-	static SpineEditorUtilities () {
-		Initialize();
-	}
+		const string DEFAULT_MIX_KEY = "SPINE_DEFAULT_MIX";
 
-	static void Initialize () {
-		defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f);
+		static SpineEditorUtilities () {
+			Initialize();
+		}
 
-		DirectoryInfo rootDir = new DirectoryInfo(Application.dataPath);
-		FileInfo[] files = rootDir.GetFiles("SpineEditorUtilities.cs", SearchOption.AllDirectories);
-		editorPath = Path.GetDirectoryName(files[0].FullName.Replace("\\", "/").Replace(Application.dataPath, "Assets"));
-		editorGUIPath = editorPath + "/GUI";
+		static void Initialize () {
+			defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f);
 
-		Icons.Initialize();
+			DirectoryInfo rootDir = new DirectoryInfo(Application.dataPath);
+			FileInfo[] files = rootDir.GetFiles("SpineEditorUtilities.cs", SearchOption.AllDirectories);
+			editorPath = Path.GetDirectoryName(files[0].FullName.Replace("\\", "/").Replace(Application.dataPath, "Assets"));
+			editorGUIPath = editorPath + "/GUI";
 
-		assetsImportedInWrongState = new HashSet<string>();
-		skeletonRendererTable = new Dictionary<int, GameObject>();
-		skeletonUtilityBoneTable = new Dictionary<int, SkeletonUtilityBone>();
-		boundingBoxFollowerTable = new Dictionary<int, BoundingBoxFollower>();
+			Icons.Initialize();
 
-		EditorApplication.hierarchyWindowChanged += HierarchyWindowChanged;
-		EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI;
+			assetsImportedInWrongState = new HashSet<string>();
+			skeletonRendererTable = new Dictionary<int, GameObject>();
+			skeletonUtilityBoneTable = new Dictionary<int, SkeletonUtilityBone>();
+			boundingBoxFollowerTable = new Dictionary<int, BoundingBoxFollower>();
 
-		HierarchyWindowChanged();
-		initialized = true;
-	}
+			EditorApplication.hierarchyWindowChanged += HierarchyWindowChanged;
+			EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI;
 
-	public static void ConfirmInitialization () {
-		if (!initialized || Icons.skeleton == null)
-			Initialize();
-	}
+			HierarchyWindowChanged();
+			initialized = true;
+		}
+
+		public static void ConfirmInitialization () {
+			if (!initialized || Icons.skeleton == null)
+				Initialize();
+		}
 
-	static void HierarchyWindowChanged () {
-		skeletonRendererTable.Clear();
-		skeletonUtilityBoneTable.Clear();
-		boundingBoxFollowerTable.Clear();
+		#region Hierarchy Icon
+		static void HierarchyWindowChanged () {
+			skeletonRendererTable.Clear();
+			skeletonUtilityBoneTable.Clear();
+			boundingBoxFollowerTable.Clear();
 
-		SkeletonRenderer[] arr = Object.FindObjectsOfType<SkeletonRenderer>();
-		foreach (SkeletonRenderer r in arr)
-			skeletonRendererTable.Add(r.gameObject.GetInstanceID(), r.gameObject);
+			SkeletonRenderer[] arr = Object.FindObjectsOfType<SkeletonRenderer>();
+			foreach (SkeletonRenderer r in arr)
+				skeletonRendererTable.Add(r.gameObject.GetInstanceID(), r.gameObject);
 
-		SkeletonUtilityBone[] boneArr = Object.FindObjectsOfType<SkeletonUtilityBone>();
-		foreach (SkeletonUtilityBone b in boneArr)
-			skeletonUtilityBoneTable.Add(b.gameObject.GetInstanceID(), b);
+			SkeletonUtilityBone[] boneArr = Object.FindObjectsOfType<SkeletonUtilityBone>();
+			foreach (SkeletonUtilityBone b in boneArr)
+				skeletonUtilityBoneTable.Add(b.gameObject.GetInstanceID(), b);
 
-		BoundingBoxFollower[] bbfArr = Object.FindObjectsOfType<BoundingBoxFollower>();
-		foreach (BoundingBoxFollower bbf in bbfArr)
-			boundingBoxFollowerTable.Add(bbf.gameObject.GetInstanceID(), bbf);
-	}
+			BoundingBoxFollower[] bbfArr = Object.FindObjectsOfType<BoundingBoxFollower>();
+			foreach (BoundingBoxFollower bbf in bbfArr)
+				boundingBoxFollowerTable.Add(bbf.gameObject.GetInstanceID(), bbf);
+		}
 
-	static void HierarchyWindowItemOnGUI (int instanceId, Rect selectionRect) {
-		if (skeletonRendererTable.ContainsKey(instanceId)) {
-			Rect r = new Rect(selectionRect);
-			r.x = r.width - 15;
-			r.width = 15;
+		static void HierarchyWindowItemOnGUI (int instanceId, Rect selectionRect) {
+			if (skeletonRendererTable.ContainsKey(instanceId)) {
+				Rect r = new Rect(selectionRect);
+				r.x = r.width - 15;
+				r.width = 15;
 
-			GUI.Label(r, Icons.spine);
-		} else if (skeletonUtilityBoneTable.ContainsKey(instanceId)) {
-			Rect r = new Rect(selectionRect);
-			r.x -= 26;
+				GUI.Label(r, Icons.spine);
+			} else if (skeletonUtilityBoneTable.ContainsKey(instanceId)) {
+				Rect r = new Rect(selectionRect);
+				r.x -= 26;
 
-			if (skeletonUtilityBoneTable[instanceId] != null) {
-				if (skeletonUtilityBoneTable[instanceId].transform.childCount == 0)
-					r.x += 13;
+				if (skeletonUtilityBoneTable[instanceId] != null) {
+					if (skeletonUtilityBoneTable[instanceId].transform.childCount == 0)
+						r.x += 13;
 
-				r.y += 2;
+					r.y += 2;
 
-				r.width = 13;
-				r.height = 13;
+					r.width = 13;
+					r.height = 13;
 
-				if (skeletonUtilityBoneTable[instanceId].mode == SkeletonUtilityBone.Mode.Follow) {
-					GUI.DrawTexture(r, Icons.bone);
-				} else {
-					GUI.DrawTexture(r, Icons.poseBones);
+					if (skeletonUtilityBoneTable[instanceId].mode == SkeletonUtilityBone.Mode.Follow) {
+						GUI.DrawTexture(r, Icons.bone);
+					} else {
+						GUI.DrawTexture(r, Icons.poseBones);
+					}
 				}
-			}
 
-		} else if (boundingBoxFollowerTable.ContainsKey(instanceId)) {
-			Rect r = new Rect(selectionRect);
-			r.x -= 26;
+			} else if (boundingBoxFollowerTable.ContainsKey(instanceId)) {
+				Rect r = new Rect(selectionRect);
+				r.x -= 26;
 
-			if (boundingBoxFollowerTable[instanceId] != null) {
-				if (boundingBoxFollowerTable[instanceId].transform.childCount == 0)
-					r.x += 13;
+				if (boundingBoxFollowerTable[instanceId] != null) {
+					if (boundingBoxFollowerTable[instanceId].transform.childCount == 0)
+						r.x += 13;
 
-				r.y += 2;
+					r.y += 2;
 
-				r.width = 13;
-				r.height = 13;
+					r.width = 13;
+					r.height = 13;
 
-				GUI.DrawTexture(r, Icons.boundingBox);
+					GUI.DrawTexture(r, Icons.boundingBox);
+				}
 			}
-		}
 
-	}
-
-	static void OnPostprocessAllAssets (string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) {
-		if (imported.Length == 0)
-			return;
-
-		// In case user used "Assets -> Reimport All", during the import process,
-		// asset database is not initialized until some point. During that period,
-		// all attempts to load any assets using API (i.e. AssetDatabase.LoadAssetAtPath)
-		// will return null, and as result, assets won't be loaded even if they actually exists,
-		// which may lead to numerous importing errors.
-		// This situation also happens if Library folder is deleted from the project, which is a pretty
-		// common case, since when using version control systems, the Library folder must be excluded.
-		// 
-		// So to avoid this, in case asset database is not available, we delay loading the assets
-		// until next time.
-		//
-		// Unity *always* reimports some internal assets after the process is done, so this method
-		// is always called once again in a state when asset database is available.
-		//
-		// Checking whether AssetDatabase is initialized is done by attempting to load
-		// a known "marker" asset that should always be available. Failing to load this asset
-		// means that AssetDatabase is not initialized.
-		assetsImportedInWrongState.UnionWith(imported);
-		if (AssetDatabaseAvailabilityDetector.IsAssetDatabaseAvailable()) {
-			string[] combinedAssets = assetsImportedInWrongState.ToArray();
-			assetsImportedInWrongState.Clear();
-			ImportSpineContent(combinedAssets);
 		}
-	}
+		#endregion
+
+		static void OnPostprocessAllAssets (string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) {
+			if (imported.Length == 0)
+				return;
+
+			// In case user used "Assets -> Reimport All", during the import process,
+			// asset database is not initialized until some point. During that period,
+			// all attempts to load any assets using API (i.e. AssetDatabase.LoadAssetAtPath)
+			// will return null, and as result, assets won't be loaded even if they actually exists,
+			// which may lead to numerous importing errors.
+			// This situation also happens if Library folder is deleted from the project, which is a pretty
+			// common case, since when using version control systems, the Library folder must be excluded.
+			// 
+			// So to avoid this, in case asset database is not available, we delay loading the assets
+			// until next time.
+			//
+			// Unity *always* reimports some internal assets after the process is done, so this method
+			// is always called once again in a state when asset database is available.
+			//
+			// Checking whether AssetDatabase is initialized is done by attempting to load
+			// a known "marker" asset that should always be available. Failing to load this asset
+			// means that AssetDatabase is not initialized.
+			assetsImportedInWrongState.UnionWith(imported);
+			if (AssetDatabaseAvailabilityDetector.IsAssetDatabaseAvailable()) {
+				string[] combinedAssets = assetsImportedInWrongState.ToArray();
+				assetsImportedInWrongState.Clear();
+				ImportSpineContent(combinedAssets);
+			}
+		}
 
-	public static void ImportSpineContent (string[] imported, bool reimport = false) {
-		List<string> atlasPaths = new List<string>();
-		List<string> imagePaths = new List<string>();
-		List<string> skeletonPaths = new List<string>();
+		public static void ImportSpineContent (string[] imported, bool reimport = false) {
+			List<string> atlasPaths = new List<string>();
+			List<string> imagePaths = new List<string>();
+			List<string> skeletonPaths = new List<string>();
 
-		foreach (string str in imported) {
-			string extension = Path.GetExtension(str).ToLower();
-			switch (extension) {
+			foreach (string str in imported) {
+				string extension = Path.GetExtension(str).ToLower();
+				switch (extension) {
 				case ".txt":
 					if (str.EndsWith(".atlas.txt")) {
 						atlasPaths.Add(str);
@@ -311,44 +316,44 @@ public class SpineEditorUtilities : AssetPostprocessor {
 							skeletonPaths.Add(str);
 					}
 					break;
+				}
 			}
-		}
 
 
-		List<AtlasAsset> atlases = new List<AtlasAsset>();
+			List<AtlasAsset> atlases = new List<AtlasAsset>();
 
-		//import atlases first
-		foreach (string ap in atlasPaths) {
-			if (!reimport && CheckForValidAtlas(ap))
-				continue;
+			//import atlases first
+			foreach (string ap in atlasPaths) {
+				if (!reimport && CheckForValidAtlas(ap))
+					continue;
 
-			TextAsset atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(ap, typeof(TextAsset));
-			AtlasAsset atlas = IngestSpineAtlas(atlasText);
-			atlases.Add(atlas);
-		}
-
-		//import skeletons and match them with atlases
-		bool abortSkeletonImport = false;
-		foreach (string sp in skeletonPaths) {
-			if (!reimport && CheckForValidSkeletonData(sp)) {
-				ResetExistingSkeletonData(sp);
-				continue;
+				TextAsset atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(ap, typeof(TextAsset));
+				AtlasAsset atlas = IngestSpineAtlas(atlasText);
+				atlases.Add(atlas);
 			}
 
+			//import skeletons and match them with atlases
+			bool abortSkeletonImport = false;
+			foreach (string sp in skeletonPaths) {
+				if (!reimport && CheckForValidSkeletonData(sp)) {
+					ResetExistingSkeletonData(sp);
+					continue;
+				}
 
-			string dir = Path.GetDirectoryName(sp);
 
-			var localAtlases = FindAtlasesAtPath(dir);
-			var requiredPaths = GetRequiredAtlasRegions(sp);
-			var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases);
+				string dir = Path.GetDirectoryName(sp);
 
-			if (atlasMatch != null) {
-				IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch);
-			} else {
-				bool resolved = false;
-				while (!resolved) {
-					int result = EditorUtility.DisplayDialogComplex("Skeleton JSON Import Error!", "Could not find matching AtlasAsset for " + Path.GetFileNameWithoutExtension(sp), "Select", "Skip", "Abort");
-					switch (result) {
+				var localAtlases = FindAtlasesAtPath(dir);
+				var requiredPaths = GetRequiredAtlasRegions(sp);
+				var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases);
+
+				if (atlasMatch != null) {
+					IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch);
+				} else {
+					bool resolved = false;
+					while (!resolved) {
+						int result = EditorUtility.DisplayDialogComplex("Skeleton JSON Import Error!", "Could not find matching AtlasAsset for " + Path.GetFileNameWithoutExtension(sp), "Select", "Skip", "Abort");
+						switch (result) {
 						case -1:
 							Debug.Log("Select Atlas");
 							AtlasAsset selectedAtlas = GetAtlasDialog(Path.GetDirectoryName(sp));
@@ -383,104 +388,96 @@ public class SpineEditorUtilities : AssetPostprocessor {
 							abortSkeletonImport = true;
 							resolved = true;
 							break;
+						}
 					}
 				}
+
+				if (abortSkeletonImport)
+					break;
 			}
 
-			if (abortSkeletonImport)
-				break;
+			//TODO:  any post processing of images
 		}
 
-		//TODO:  any post processing of images
-	}
-
-	static bool CheckForValidSkeletonData (string skeletonJSONPath) {
+		static bool CheckForValidSkeletonData (string skeletonJSONPath) {
 
-		string dir = Path.GetDirectoryName(skeletonJSONPath);
-		TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset));
-		DirectoryInfo dirInfo = new DirectoryInfo(dir);
+			string dir = Path.GetDirectoryName(skeletonJSONPath);
+			TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset));
+			DirectoryInfo dirInfo = new DirectoryInfo(dir);
 
-		FileInfo[] files = dirInfo.GetFiles("*.asset");
+			FileInfo[] files = dirInfo.GetFiles("*.asset");
 
-		foreach (var f in files) {
-			string localPath = dir + "/" + f.Name;
-			var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object));
-			if (obj is SkeletonDataAsset) {
-				var skeletonDataAsset = (SkeletonDataAsset)obj;
-				if (skeletonDataAsset.skeletonJSON == textAsset)
-					return true;
+			foreach (var f in files) {
+				string localPath = dir + "/" + f.Name;
+				var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object));
+				if (obj is SkeletonDataAsset) {
+					var skeletonDataAsset = (SkeletonDataAsset)obj;
+					if (skeletonDataAsset.skeletonJSON == textAsset)
+						return true;
+				}
 			}
+
+			return false;
 		}
 
-		return false;
-	}
+		static void ResetExistingSkeletonData (string skeletonJSONPath) {
 
-	static void ResetExistingSkeletonData (string skeletonJSONPath) {
+			string dir = Path.GetDirectoryName(skeletonJSONPath);
+			TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset));
+			DirectoryInfo dirInfo = new DirectoryInfo(dir);
 
-		string dir = Path.GetDirectoryName(skeletonJSONPath);
-		TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset));
-		DirectoryInfo dirInfo = new DirectoryInfo(dir);
+			FileInfo[] files = dirInfo.GetFiles("*.asset");
 
-		FileInfo[] files = dirInfo.GetFiles("*.asset");
+			foreach (var f in files) {
+				string localPath = dir + "/" + f.Name;
+				var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object));
+				if (obj is SkeletonDataAsset) {
+					var skeletonDataAsset = (SkeletonDataAsset)obj;
 
-		foreach (var f in files) {
-			string localPath = dir + "/" + f.Name;
-			var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object));
-			if (obj is SkeletonDataAsset) {
-				var skeletonDataAsset = (SkeletonDataAsset)obj;
+					if (skeletonDataAsset.skeletonJSON == textAsset) {
+						if (Selection.activeObject == skeletonDataAsset)
+							Selection.activeObject = null;
 
-				if (skeletonDataAsset.skeletonJSON == textAsset) {
-					if (Selection.activeObject == skeletonDataAsset)
-						Selection.activeObject = null;
+						skeletonDataAsset.Reset();
 
-					skeletonDataAsset.Reset();
+						string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(skeletonDataAsset));
+						string lastHash = EditorPrefs.GetString(guid + "_hash");
 
-					string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(skeletonDataAsset));
-					string lastHash = EditorPrefs.GetString(guid + "_hash");
-					
-					// For some weird reason sometimes Unity loses the internal Object pointer,
-					// and as a result, all comparisons with null returns true.
-					// But the C# wrapper is still alive, so we can "restore" the object
-					// by reloading it from its Instance ID.
-					AtlasAsset[] skeletonDataAtlasAssets = skeletonDataAsset.atlasAssets;
-					if (skeletonDataAtlasAssets != null) {
-						for (int i = 0; i < skeletonDataAtlasAssets.Length; i++) {
-							if (!ReferenceEquals(null, skeletonDataAtlasAssets[i]) &&
-								skeletonDataAtlasAssets[i].Equals(null) &&
-								skeletonDataAtlasAssets[i].GetInstanceID() != 0
+						// For some weird reason sometimes Unity loses the internal Object pointer,
+						// and as a result, all comparisons with null returns true.
+						// But the C# wrapper is still alive, so we can "restore" the object
+						// by reloading it from its Instance ID.
+						AtlasAsset[] skeletonDataAtlasAssets = skeletonDataAsset.atlasAssets;
+						if (skeletonDataAtlasAssets != null) {
+							for (int i = 0; i < skeletonDataAtlasAssets.Length; i++) {
+								if (!ReferenceEquals(null, skeletonDataAtlasAssets[i]) &&
+									skeletonDataAtlasAssets[i].Equals(null) &&
+									skeletonDataAtlasAssets[i].GetInstanceID() != 0
 								) {
-								skeletonDataAtlasAssets[i] = EditorUtility.InstanceIDToObject(skeletonDataAtlasAssets[i].GetInstanceID()) as AtlasAsset;
+									skeletonDataAtlasAssets[i] = EditorUtility.InstanceIDToObject(skeletonDataAtlasAssets[i].GetInstanceID()) as AtlasAsset;
+								}
 							}
 						}
-					}
 
-					SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true);
-					string currentHash = skeletonData != null ? skeletonData.Hash : null;
-					if (currentHash == null || lastHash != currentHash) {
-						//do any upkeep on synchronized assets
-						UpdateMecanimClips(skeletonDataAsset);
-					}
+						SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true);
+						string currentHash = skeletonData != null ? skeletonData.Hash : null;
+						if (currentHash == null || lastHash != currentHash) {
+							//do any upkeep on synchronized assets
+							UpdateMecanimClips(skeletonDataAsset);
+						}
 
-					if (currentHash != null) {
-						EditorPrefs.SetString(guid + "_hash", currentHash);
+						if (currentHash != null) {
+							EditorPrefs.SetString(guid + "_hash", currentHash);
+						}
 					}
 				}
 			}
 		}
-	}
 
-	static void UpdateMecanimClips (SkeletonDataAsset skeletonDataAsset) {
-		if (skeletonDataAsset.controller == null)
-			return;
-
-		SkeletonBaker.GenerateMecanimAnimationClips(skeletonDataAsset);
-	}
-
-
-	static bool CheckForValidAtlas (string atlasPath) {
-		return false;
-		//////////////DEPRECATED - always check for new atlas data now
-		/*
+		static bool CheckForValidAtlas (string atlasPath) {
+			return false;
+			//////////////DEPRECATED - always check for new atlas data now
+			/*
 		string dir = Path.GetDirectoryName(atlasPath);
 		TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(TextAsset));
 		DirectoryInfo dirInfo = new DirectoryInfo(dir);
@@ -523,51 +520,51 @@ public class SpineEditorUtilities : AssetPostprocessor {
 		return false;
 	
 		*/
-	}
+		}
 
-	static List<AtlasAsset> MultiAtlasDialog (List<string> requiredPaths, string initialDirectory, string header = "") {
+		static List<AtlasAsset> MultiAtlasDialog (List<string> requiredPaths, string initialDirectory, string header = "") {
 
-		List<AtlasAsset> atlasAssets = new List<AtlasAsset>();
+			List<AtlasAsset> atlasAssets = new List<AtlasAsset>();
 
-		bool resolved = false;
-		string lastAtlasPath = initialDirectory;
-		while (!resolved) {
-			StringBuilder sb = new StringBuilder();
-			sb.AppendLine(header);
-			sb.AppendLine("Atlases:");
-			if (atlasAssets.Count == 0) {
-				sb.AppendLine("\t--none--");
-			}
-			for (int i = 0; i < atlasAssets.Count; i++) {
-				sb.AppendLine("\t" + atlasAssets[i].name);
-			}
+			bool resolved = false;
+			string lastAtlasPath = initialDirectory;
+			while (!resolved) {
+				StringBuilder sb = new StringBuilder();
+				sb.AppendLine(header);
+				sb.AppendLine("Atlases:");
+				if (atlasAssets.Count == 0) {
+					sb.AppendLine("\t--none--");
+				}
+				for (int i = 0; i < atlasAssets.Count; i++) {
+					sb.AppendLine("\t" + atlasAssets[i].name);
+				}
 
-			sb.AppendLine();
-			sb.AppendLine("Missing Regions:");
+				sb.AppendLine();
+				sb.AppendLine("Missing Regions:");
 
-			List<string> missingRegions = new List<string>(requiredPaths);
+				List<string> missingRegions = new List<string>(requiredPaths);
 
-			foreach (var atlasAsset in atlasAssets) {
-				var atlas = atlasAsset.GetAtlas();
-				for (int i = 0; i < missingRegions.Count; i++) {
-					if (atlas.FindRegion(missingRegions[i]) != null) {
-						missingRegions.RemoveAt(i);
-						i--;
+				foreach (var atlasAsset in atlasAssets) {
+					var atlas = atlasAsset.GetAtlas();
+					for (int i = 0; i < missingRegions.Count; i++) {
+						if (atlas.FindRegion(missingRegions[i]) != null) {
+							missingRegions.RemoveAt(i);
+							i--;
+						}
 					}
 				}
-			}
 
-			if (missingRegions.Count == 0) {
-				break;
-			}
+				if (missingRegions.Count == 0) {
+					break;
+				}
 
-			for (int i = 0; i < missingRegions.Count; i++) {
-				sb.AppendLine("\t" + missingRegions[i]);
-			}
+				for (int i = 0; i < missingRegions.Count; i++) {
+					sb.AppendLine("\t" + missingRegions[i]);
+				}
 
-			int result = EditorUtility.DisplayDialogComplex("Atlas Selection", sb.ToString(), "Select", "Finish", "Abort");
+				int result = EditorUtility.DisplayDialogComplex("Atlas Selection", sb.ToString(), "Select", "Finish", "Abort");
 
-			switch (result) {
+				switch (result) {
 				case 0:
 					AtlasAsset selectedAtlasAsset = GetAtlasDialog(lastAtlasPath);
 					if (selectedAtlasAsset != null) {
@@ -592,618 +589,635 @@ public class SpineEditorUtilities : AssetPostprocessor {
 					atlasAssets = null;
 					resolved = true;
 					break;
+				}
+
+
 			}
 
 
+			return atlasAssets;
 		}
 
+		static AtlasAsset GetAtlasDialog (string dirPath) {
+			string path = EditorUtility.OpenFilePanel("Select AtlasAsset...", dirPath, "asset");
+			if (path == "")
+				return null;
 
-		return atlasAssets;
-	}
+			int subLen = Application.dataPath.Length - 6;
+			string assetRelativePath = path.Substring(subLen, path.Length - subLen).Replace("\\", "/");
 
-	static AtlasAsset GetAtlasDialog (string dirPath) {
-		string path = EditorUtility.OpenFilePanel("Select AtlasAsset...", dirPath, "asset");
-		if (path == "")
-			return null;
+			Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset));
 
-		int subLen = Application.dataPath.Length - 6;
-		string assetRelativePath = path.Substring(subLen, path.Length - subLen).Replace("\\", "/");
+			if (obj == null || obj.GetType() != typeof(AtlasAsset))
+				return null;
 
-		Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset));
+			return (AtlasAsset)obj;
+		}
 
-		if (obj == null || obj.GetType() != typeof(AtlasAsset))
-			return null;
+		static void AddRequiredAtlasRegionsFromBinary (string skeletonDataPath, List<string> requiredPaths) {
+			SkeletonBinary binary = new SkeletonBinary(new AtlasRequirementLoader(requiredPaths));
+			TextAsset data = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonDataPath, typeof(TextAsset));
+			MemoryStream input = new MemoryStream(data.bytes);
+			binary.ReadSkeletonData(input);
+			binary = null;
+		}
 
-		return (AtlasAsset)obj;
-	}
+		public static List<string> GetRequiredAtlasRegions (string skeletonDataPath) {
+			List<string> requiredPaths = new List<string>();
 
-	static void AddRequiredAtlasRegionsFromBinary (string skeletonDataPath, List<string> requiredPaths) {
-		SkeletonBinary binary = new SkeletonBinary(new AtlasRequirementLoader(requiredPaths));
-		TextAsset data = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonDataPath, typeof(TextAsset));
-		MemoryStream input = new MemoryStream(data.bytes);
-		binary.ReadSkeletonData(input);
-		binary = null;
-	}
-
-	public static List<string> GetRequiredAtlasRegions (string skeletonDataPath) {
-		List<string> requiredPaths = new List<string>();
+			if (skeletonDataPath.Contains(".skel")) {
+				AddRequiredAtlasRegionsFromBinary(skeletonDataPath, requiredPaths);
+				return requiredPaths;
+			}
 
-		if (skeletonDataPath.Contains(".skel")) {
-			AddRequiredAtlasRegionsFromBinary(skeletonDataPath, requiredPaths);
-			return requiredPaths;
-		}
+			TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonDataPath, typeof(TextAsset));
 
-		TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonDataPath, typeof(TextAsset));
+			StringReader reader = new StringReader(spineJson.text);
+			var root = Json.Deserialize(reader) as Dictionary<string, object>;
 
-		StringReader reader = new StringReader(spineJson.text);
-		var root = Json.Deserialize(reader) as Dictionary<string, object>;
+			foreach (KeyValuePair<string, object> entry in (Dictionary<string, object>)root["skins"]) {
+				foreach (KeyValuePair<string, object> slotEntry in (Dictionary<string, object>)entry.Value) {
 
-		foreach (KeyValuePair<string, object> entry in (Dictionary<string, object>)root["skins"]) {
-			foreach (KeyValuePair<string, object> slotEntry in (Dictionary<string, object>)entry.Value) {
+					foreach (KeyValuePair<string, object> attachmentEntry in ((Dictionary<string, object>)slotEntry.Value)) {
+						var data = ((Dictionary<string, object>)attachmentEntry.Value);
+						if (data.ContainsKey("type")) {
+							if ((string)data["type"] == "boundingbox") {
+								continue;
+							}
 
-				foreach (KeyValuePair<string, object> attachmentEntry in ((Dictionary<string, object>)slotEntry.Value)) {
-					var data = ((Dictionary<string, object>)attachmentEntry.Value);
-					if (data.ContainsKey("type")) {
-						if ((string)data["type"] == "boundingbox") {
-							continue;
 						}
-
+						if (data.ContainsKey("path"))
+							requiredPaths.Add((string)data["path"]);
+						else if (data.ContainsKey("name"))
+							requiredPaths.Add((string)data["name"]);
+						else
+							requiredPaths.Add(attachmentEntry.Key);
+						//requiredPaths.Add((string)sdf["path"]);
 					}
-					if (data.ContainsKey("path"))
-						requiredPaths.Add((string)data["path"]);
-					else if (data.ContainsKey("name"))
-						requiredPaths.Add((string)data["name"]);
-					else
-						requiredPaths.Add(attachmentEntry.Key);
-					//requiredPaths.Add((string)sdf["path"]);
 				}
 			}
+
+			return requiredPaths;
 		}
+		static AtlasAsset GetMatchingAtlas (List<string> requiredPaths, List<AtlasAsset> atlasAssets) {
+			AtlasAsset atlasAssetMatch = null;
+
+			foreach (AtlasAsset a in atlasAssets) {
+				Atlas atlas = a.GetAtlas();
+				bool failed = false;
+				foreach (string regionPath in requiredPaths) {
+					if (atlas.FindRegion(regionPath) == null) {
+						failed = true;
+						break;
+					}
+				}
 
-		return requiredPaths;
-	}
-	static AtlasAsset GetMatchingAtlas (List<string> requiredPaths, List<AtlasAsset> atlasAssets) {
-		AtlasAsset atlasAssetMatch = null;
-
-		foreach (AtlasAsset a in atlasAssets) {
-			Atlas atlas = a.GetAtlas();
-			bool failed = false;
-			foreach (string regionPath in requiredPaths) {
-				if (atlas.FindRegion(regionPath) == null) {
-					failed = true;
+				if (!failed) {
+					atlasAssetMatch = a;
 					break;
 				}
-			}
 
-			if (!failed) {
-				atlasAssetMatch = a;
-				break;
 			}
 
+			return atlasAssetMatch;
 		}
 
-		return atlasAssetMatch;
-	}
+		static List<AtlasAsset> FindAtlasesAtPath (string path) {
+			List<AtlasAsset> arr = new List<AtlasAsset>();
 
-	static List<AtlasAsset> FindAtlasesAtPath (string path) {
-		List<AtlasAsset> arr = new List<AtlasAsset>();
+			DirectoryInfo dir = new DirectoryInfo(path);
+			FileInfo[] assetInfoArr = dir.GetFiles("*.asset");
 
-		DirectoryInfo dir = new DirectoryInfo(path);
-		FileInfo[] assetInfoArr = dir.GetFiles("*.asset");
+			int subLen = Application.dataPath.Length - 6;
 
-		int subLen = Application.dataPath.Length - 6;
+			foreach (var f in assetInfoArr) {
+				string assetRelativePath = f.FullName.Substring(subLen, f.FullName.Length - subLen).Replace("\\", "/");
 
-		foreach (var f in assetInfoArr) {
-			string assetRelativePath = f.FullName.Substring(subLen, f.FullName.Length - subLen).Replace("\\", "/");
+				Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset));
+				if (obj != null) {
+					arr.Add(obj as AtlasAsset);
+				}
 
-			Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset));
-			if (obj != null) {
-				arr.Add(obj as AtlasAsset);
 			}
 
+
+			return arr;
 		}
 
+		public static bool IsValidSpineData (TextAsset asset) {
+			if (asset.name.Contains(".skel")) return true;
 
-		return arr;
-	}
+			object obj = null;
+			try {
+				obj = Json.Deserialize(new StringReader(asset.text));
+			} catch (System.Exception) {
+			}
+			if (obj == null) {
+				Debug.LogError("Is not valid JSON");
+				return false;
+			}
 
-	public static bool IsValidSpineData (TextAsset asset) {
-		if (asset.name.Contains(".skel")) return true;
+			Dictionary<string, object> root = (Dictionary<string, object>)obj;
 
-		object obj = null;
-		try {
-			obj = Json.Deserialize(new StringReader(asset.text));
-		} catch (System.Exception) {
-		}
-		if (obj == null) {
-			Debug.LogError("Is not valid JSON");
-			return false;
-		}
+			if (!root.ContainsKey("skeleton"))
+				return false;
 
-		Dictionary<string, object> root = (Dictionary<string, object>)obj;
+			Dictionary<string, object> skeletonInfo = (Dictionary<string, object>)root["skeleton"];
 
-		if (!root.ContainsKey("skeleton"))
-			return false;
+			string spineVersion = (string)skeletonInfo["spine"];
+			//TODO:  reject old versions
 
-		Dictionary<string, object> skeletonInfo = (Dictionary<string, object>)root["skeleton"];
+			return true;
+		}
 
-		string spineVersion = (string)skeletonInfo["spine"];
-		//TODO:  reject old versions
+		static AtlasAsset IngestSpineAtlas (TextAsset atlasText) {
+			if (atlasText == null) {
+				Debug.LogWarning("Atlas source cannot be null!");
+				return null;
+			}
 
-		return true;
-	}
+			string primaryName = Path.GetFileNameWithoutExtension(atlasText.name).Replace(".atlas", "");
+			string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(atlasText));
 
-	static AtlasAsset IngestSpineAtlas (TextAsset atlasText) {
-		if (atlasText == null) {
-			Debug.LogWarning("Atlas source cannot be null!");
-			return null;
-		}
+			string atlasPath = assetPath + "/" + primaryName + "_Atlas.asset";
 
-		string primaryName = Path.GetFileNameWithoutExtension(atlasText.name).Replace(".atlas", "");
-		string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(atlasText));
+			AtlasAsset atlasAsset = (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset));
 
-		string atlasPath = assetPath + "/" + primaryName + "_Atlas.asset";
+			List<Material> vestigialMaterials = new List<Material>();
 
-		AtlasAsset atlasAsset = (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset));
+			if (atlasAsset == null)
+				atlasAsset = AtlasAsset.CreateInstance<AtlasAsset>();
+			else {
+				foreach (Material m in atlasAsset.materials)
+					vestigialMaterials.Add(m);
+			}
 
-		List<Material> vestigialMaterials = new List<Material>();
+			atlasAsset.atlasFile = atlasText;
 
-		if (atlasAsset == null)
-			atlasAsset = AtlasAsset.CreateInstance<AtlasAsset>();
-		else {
-			foreach (Material m in atlasAsset.materials)
-				vestigialMaterials.Add(m);
-		}
+			//strip CR
+			string atlasStr = atlasText.text;
+			atlasStr = atlasStr.Replace("\r", "");
 
-		atlasAsset.atlasFile = atlasText;
+			string[] atlasLines = atlasStr.Split('\n');
+			List<string> pageFiles = new List<string>();
+			for (int i = 0; i < atlasLines.Length - 1; i++) {
+				if (atlasLines[i].Trim().Length == 0)
+					pageFiles.Add(atlasLines[i + 1].Trim());
+			}
 
-		//strip CR
-		string atlasStr = atlasText.text;
-		atlasStr = atlasStr.Replace("\r", "");
+			atlasAsset.materials = new Material[pageFiles.Count];
 
-		string[] atlasLines = atlasStr.Split('\n');
-		List<string> pageFiles = new List<string>();
-		for (int i = 0; i < atlasLines.Length - 1; i++) {
-			if (atlasLines[i].Trim().Length == 0)
-				pageFiles.Add(atlasLines[i + 1].Trim());
-		}
+			for (int i = 0; i < pageFiles.Count; i++) {
+				string texturePath = assetPath + "/" + pageFiles[i];
+				Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D));
 
-		atlasAsset.materials = new Material[pageFiles.Count];
+				TextureImporter texImporter = (TextureImporter)TextureImporter.GetAtPath(texturePath);
+				texImporter.textureType = TextureImporterType.Advanced;
+				texImporter.textureFormat = TextureImporterFormat.AutomaticTruecolor;
+				texImporter.mipmapEnabled = false;
+				texImporter.alphaIsTransparency = false;
+				texImporter.maxTextureSize = 2048;
 
-		for (int i = 0; i < pageFiles.Count; i++) {
-			string texturePath = assetPath + "/" + pageFiles[i];
-			Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D));
+				EditorUtility.SetDirty(texImporter);
+				AssetDatabase.ImportAsset(texturePath);
+				AssetDatabase.SaveAssets();
 
-			TextureImporter texImporter = (TextureImporter)TextureImporter.GetAtPath(texturePath);
-			texImporter.textureType = TextureImporterType.Advanced;
-			texImporter.textureFormat = TextureImporterFormat.AutomaticTruecolor;
-			texImporter.mipmapEnabled = false;
-			texImporter.alphaIsTransparency = false;
-			texImporter.maxTextureSize = 2048;
+				string pageName = Path.GetFileNameWithoutExtension(pageFiles[i]);
 
-			EditorUtility.SetDirty(texImporter);
-			AssetDatabase.ImportAsset(texturePath);
-			AssetDatabase.SaveAssets();
+				//because this looks silly
+				if (pageName == primaryName && pageFiles.Count == 1)
+					pageName = "Material";
+
+				string materialPath = assetPath + "/" + primaryName + "_" + pageName + ".mat";
+				Material mat = (Material)AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material));
 
-			string pageName = Path.GetFileNameWithoutExtension(pageFiles[i]);
+				if (mat == null) {
+					mat = new Material(Shader.Find(defaultShader));
+					AssetDatabase.CreateAsset(mat, materialPath);
+				} else {
+					vestigialMaterials.Remove(mat);
+				}
 
-			//because this looks silly
-			if (pageName == primaryName && pageFiles.Count == 1)
-				pageName = "Material";
+				mat.mainTexture = texture;
+				EditorUtility.SetDirty(mat);
 
-			string materialPath = assetPath + "/" + primaryName + "_" + pageName + ".mat";
-			Material mat = (Material)AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material));
+				AssetDatabase.SaveAssets();
 
-			if (mat == null) {
-				mat = new Material(Shader.Find(defaultShader));
-				AssetDatabase.CreateAsset(mat, materialPath);
-			} else {
-				vestigialMaterials.Remove(mat);
+				atlasAsset.materials[i] = mat;
 			}
 
-			mat.mainTexture = texture;
-			EditorUtility.SetDirty(mat);
+			for (int i = 0; i < vestigialMaterials.Count; i++)
+				AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(vestigialMaterials[i]));
+
+			if (AssetDatabase.GetAssetPath(atlasAsset) == "")
+				AssetDatabase.CreateAsset(atlasAsset, atlasPath);
+			else
+				atlasAsset.Reset();
+
+			EditorUtility.SetDirty(atlasAsset);
 
 			AssetDatabase.SaveAssets();
 
-			atlasAsset.materials[i] = mat;
-		}
 
-		for (int i = 0; i < vestigialMaterials.Count; i++)
-			AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(vestigialMaterials[i]));
+			//iterate regions and bake marked
+			Atlas atlas = atlasAsset.GetAtlas();
+			FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
+			List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
+			string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
+			string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath);
+			string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name);
 
-		if (AssetDatabase.GetAssetPath(atlasAsset) == "")
-			AssetDatabase.CreateAsset(atlasAsset, atlasPath);
-		else
-			atlasAsset.Reset();
+			bool hasBakedRegions = false;
+			for (int i = 0; i < regions.Count; i++) {
+				AtlasRegion region = regions[i];
+				string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/");
+				GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject));
 
-		EditorUtility.SetDirty(atlasAsset);
+				if (prefab != null) {
+					BakeRegion(atlasAsset, region, false);
+					hasBakedRegions = true;
+				}
+			}
 
-		AssetDatabase.SaveAssets();
+			if (hasBakedRegions) {
+				AssetDatabase.SaveAssets();
+				AssetDatabase.Refresh();
+			}
 
+			return (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset));
+		}
 
-		//iterate regions and bake marked
-		Atlas atlas = atlasAsset.GetAtlas();
-		FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
-		List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
-		string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
-		string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath);
-		string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name);
+		public static GameObject BakeRegion (AtlasAsset atlasAsset, AtlasRegion region, bool autoSave = true) {
+			Atlas atlas = atlasAsset.GetAtlas();
+			string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
+			string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath);
+			string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name);
+			string bakedPrefabPath = Path.Combine(bakedDirPath, GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/");
 
-		bool hasBakedRegions = false;
-		for (int i = 0; i < regions.Count; i++) {
-			AtlasRegion region = regions[i];
-			string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/");
 			GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject));
-
-			if (prefab != null) {
-				BakeRegion(atlasAsset, region, false);
-				hasBakedRegions = true;
+			GameObject root;
+			Mesh mesh;
+			bool isNewPrefab = false;
+
+			if (!Directory.Exists(bakedDirPath))
+				Directory.CreateDirectory(bakedDirPath);
+
+			if (prefab == null) {
+				root = new GameObject("temp", typeof(MeshFilter), typeof(MeshRenderer));
+				prefab = (GameObject)PrefabUtility.CreatePrefab(bakedPrefabPath, root);
+				isNewPrefab = true;
+				Object.DestroyImmediate(root);
 			}
-		}
 
-		if (hasBakedRegions) {
-			AssetDatabase.SaveAssets();
-			AssetDatabase.Refresh();
-		}
+			mesh = (Mesh)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(Mesh));
 
-		return (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset));
-	}
+			Material mat = null;
+			mesh = atlasAsset.GenerateMesh(region.name, mesh, out mat);
+			if (isNewPrefab) {
+				AssetDatabase.AddObjectToAsset(mesh, prefab);
+				prefab.GetComponent<MeshFilter>().sharedMesh = mesh;
+			}
 
-	public static GameObject BakeRegion (AtlasAsset atlasAsset, AtlasRegion region, bool autoSave = true) {
-		Atlas atlas = atlasAsset.GetAtlas();
-		string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset);
-		string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath);
-		string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name);
-		string bakedPrefabPath = Path.Combine(bakedDirPath, GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/");
-
-		GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject));
-		GameObject root;
-		Mesh mesh;
-		bool isNewPrefab = false;
-
-		if (!Directory.Exists(bakedDirPath))
-			Directory.CreateDirectory(bakedDirPath);
-
-		if (prefab == null) {
-			root = new GameObject("temp", typeof(MeshFilter), typeof(MeshRenderer));
-			prefab = (GameObject)PrefabUtility.CreatePrefab(bakedPrefabPath, root);
-			isNewPrefab = true;
-			Object.DestroyImmediate(root);
-		}
+			EditorUtility.SetDirty(mesh);
+			EditorUtility.SetDirty(prefab);
 
-		mesh = (Mesh)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(Mesh));
+			if (autoSave) {
+				AssetDatabase.SaveAssets();
+				AssetDatabase.Refresh();
+			}
 
-		Material mat = null;
-		mesh = atlasAsset.GenerateMesh(region.name, mesh, out mat);
-		if (isNewPrefab) {
-			AssetDatabase.AddObjectToAsset(mesh, prefab);
-			prefab.GetComponent<MeshFilter>().sharedMesh = mesh;
-		}
 
-		EditorUtility.SetDirty(mesh);
-		EditorUtility.SetDirty(prefab);
+			prefab.GetComponent<MeshRenderer>().sharedMaterial = mat;
 
-		if (autoSave) {
-			AssetDatabase.SaveAssets();
-			AssetDatabase.Refresh();
+			return prefab;
 		}
 
+		public static string GetPathSafeRegionName (AtlasRegion region) {
+			return region.name.Replace("/", "_");
+		}
 
-		prefab.GetComponent<MeshRenderer>().sharedMaterial = mat;
-
-		return prefab;
-	}
-
-	public static string GetPathSafeRegionName (AtlasRegion region) {
-		return region.name.Replace("/", "_");
-	}
+		static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, params AtlasAsset[] atlasAssets) {
+			string primaryName = Path.GetFileNameWithoutExtension(spineJson.name);
+			string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson));
+			string filePath = assetPath + "/" + primaryName + "_SkeletonData.asset";
+
+			if (spineJson != null && atlasAssets != null) {
+
+				SkeletonDataAsset skelDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset));
+				if (skelDataAsset == null) {
+					skelDataAsset = SkeletonDataAsset.CreateInstance<SkeletonDataAsset>();
+					skelDataAsset.atlasAssets = atlasAssets;
+					skelDataAsset.skeletonJSON = spineJson;
+					skelDataAsset.fromAnimation = new string[0];
+					skelDataAsset.toAnimation = new string[0];
+					skelDataAsset.duration = new float[0];
+					skelDataAsset.defaultMix = defaultMix;
+					skelDataAsset.scale = defaultScale;
+
+					AssetDatabase.CreateAsset(skelDataAsset, filePath);
+					AssetDatabase.SaveAssets();
+				} else {
+					skelDataAsset.atlasAssets = atlasAssets;
+					skelDataAsset.Reset();
+					skelDataAsset.GetSkeletonData(true);
+				}
 
-	static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, params AtlasAsset[] atlasAssets) {
-		string primaryName = Path.GetFileNameWithoutExtension(spineJson.name);
-		string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson));
-		string filePath = assetPath + "/" + primaryName + "_SkeletonData.asset";
-
-		if (spineJson != null && atlasAssets != null) {
-
-			SkeletonDataAsset skelDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset));
-			if (skelDataAsset == null) {
-				skelDataAsset = SkeletonDataAsset.CreateInstance<SkeletonDataAsset>();
-				skelDataAsset.atlasAssets = atlasAssets;
-				skelDataAsset.skeletonJSON = spineJson;
-				skelDataAsset.fromAnimation = new string[0];
-				skelDataAsset.toAnimation = new string[0];
-				skelDataAsset.duration = new float[0];
-				skelDataAsset.defaultMix = defaultMix;
-				skelDataAsset.scale = defaultScale;
-
-				AssetDatabase.CreateAsset(skelDataAsset, filePath);
-				AssetDatabase.SaveAssets();
+				return skelDataAsset;
 			} else {
-				skelDataAsset.atlasAssets = atlasAssets;
-				skelDataAsset.Reset();
-				skelDataAsset.GetSkeletonData(true);
+				EditorUtility.DisplayDialog("Error!", "Must specify both Spine JSON and AtlasAsset array", "OK");
+				return null;
 			}
-
-			return skelDataAsset;
-		} else {
-			EditorUtility.DisplayDialog("Error!", "Must specify both Spine JSON and AtlasAsset array", "OK");
-			return null;
 		}
-	}
 
-	[MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)")]
-	static void InstantiateSkeletonAnimation () {
-		Object[] arr = Selection.objects;
-		foreach (Object o in arr) {
-			string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o));
-			string skinName = EditorPrefs.GetString(guid + "_lastSkin", "");
+		#region SkeletonAnimation Menu
+		[MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)", false, 10)]
+		static void InstantiateSkeletonAnimation () {
+			Object[] arr = Selection.objects;
+			foreach (Object o in arr) {
+				string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o));
+				string skinName = EditorPrefs.GetString(guid + "_lastSkin", "");
 
-			InstantiateSkeletonAnimation((SkeletonDataAsset)o, skinName);
-			SceneView.RepaintAll();
+				InstantiateSkeletonAnimation((SkeletonDataAsset)o, skinName);
+				SceneView.RepaintAll();
+			}
 		}
-	}
-
-	[MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)", true)]
-	static bool ValidateInstantiateSkeletonAnimation () {
-		Object[] arr = Selection.objects;
 
-		if (arr.Length == 0)
-			return false;
+		[MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)", true, 10)]
+		static bool ValidateInstantiateSkeletonAnimation () {
+			Object[] arr = Selection.objects;
 
-		foreach (Object o in arr) {
-			if (o.GetType() != typeof(SkeletonDataAsset))
+			if (arr.Length == 0)
 				return false;
-		}
 
-		return true;
-	}
+			foreach (Object o in arr) {
+				if (o.GetType() != typeof(SkeletonDataAsset))
+					return false;
+			}
 
-	public static SkeletonAnimation InstantiateSkeletonAnimation (SkeletonDataAsset skeletonDataAsset, string skinName) {
-		return InstantiateSkeletonAnimation(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName));
-	}
+			return true;
+		}
 
-	public static SkeletonAnimation InstantiateSkeletonAnimation (SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
-		string spineGameObjectName = string.Format("Spine GameObject ({0})", skeletonDataAsset.name.Replace("_SkeletonData", ""));
-		GameObject go = new GameObject(spineGameObjectName, typeof(MeshFilter), typeof(MeshRenderer), typeof(SkeletonAnimation));
-		SkeletonAnimation anim = go.GetComponent<SkeletonAnimation>();
-		anim.skeletonDataAsset = skeletonDataAsset;
+		public static SkeletonAnimation InstantiateSkeletonAnimation (SkeletonDataAsset skeletonDataAsset, string skinName) {
+			return InstantiateSkeletonAnimation(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName));
+		}
 
-		bool requiresNormals = false;
+		public static SkeletonAnimation InstantiateSkeletonAnimation (SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
+			string spineGameObjectName = string.Format("Spine GameObject ({0})", skeletonDataAsset.name.Replace("_SkeletonData", ""));
+			GameObject go = new GameObject(spineGameObjectName, typeof(MeshFilter), typeof(MeshRenderer), typeof(SkeletonAnimation));
+			SkeletonAnimation anim = go.GetComponent<SkeletonAnimation>();
+			anim.skeletonDataAsset = skeletonDataAsset;
 
-		foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) {
-			foreach (Material m in atlasAsset.materials) {
-				if (m.shader.name.Contains("Lit")) {
-					requiresNormals = true;
-					break;
+			bool requiresNormals = false;
+
+			foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) {
+				foreach (Material m in atlasAsset.materials) {
+					if (m.shader.name.Contains("Lit")) {
+						requiresNormals = true;
+						break;
+					}
 				}
 			}
-		}
 
 
 
-		anim.calculateNormals = requiresNormals;
+			anim.calculateNormals = requiresNormals;
 
-		SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
+			SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
 
-		if (data == null) {
-			for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) {
-				string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]);
-				skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset));
-			}
+			if (data == null) {
+				for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) {
+					string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]);
+					skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset));
+				}
 
-			data = skeletonDataAsset.GetSkeletonData(true);
-		}
+				data = skeletonDataAsset.GetSkeletonData(true);
+			}
 
-		if (skin == null)
-			skin = data.DefaultSkin;
+			if (skin == null)
+				skin = data.DefaultSkin;
 
-		if (skin == null)
-			skin = data.Skins.Items[0];
+			if (skin == null)
+				skin = data.Skins.Items[0];
 
-		anim.Initialize(false);
+			anim.Initialize(false);
 
-		anim.skeleton.SetSkin(skin);
-		anim.initialSkinName = skin.Name;
+			anim.skeleton.SetSkin(skin);
+			anim.initialSkinName = skin.Name;
 
-		anim.skeleton.Update(1);
-		anim.state.Update(1);
-		anim.state.Apply(anim.skeleton);
-		anim.skeleton.UpdateWorldTransform();
+			anim.skeleton.Update(1);
+			anim.state.Update(1);
+			anim.state.Apply(anim.skeleton);
+			anim.skeleton.UpdateWorldTransform();
 
-		return anim;
-	}
+			return anim;
+		}
+		#endregion
 
-	[MenuItem("Assets/Spine/Instantiate (Mecanim)")]
-	static void InstantiateSkeletonAnimator () {
-		Object[] arr = Selection.objects;
-		foreach (Object o in arr) {
-			string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o));
-			string skinName = EditorPrefs.GetString(guid + "_lastSkin", "");
+		#region SkeletonAnimator
+		#if SPINE_SKELETONANIMATOR
+		static void UpdateMecanimClips (SkeletonDataAsset skeletonDataAsset) {
+			if (skeletonDataAsset.controller == null)
+				return;
 
-			InstantiateSkeletonAnimator((SkeletonDataAsset)o, skinName);
-			SceneView.RepaintAll();
+			SkeletonBaker.GenerateMecanimAnimationClips(skeletonDataAsset);
 		}
-	}
 
-	[MenuItem("Assets/Spine/Instantiate (SkeletonAnimation)", true)]
-	static bool ValidateInstantiateSkeletonAnimator () {
-		Object[] arr = Selection.objects;
-
-		if (arr.Length == 0)
-			return false;
+		[MenuItem("Assets/Spine/Instantiate (Mecanim)", false, 100)]
+		static void InstantiateSkeletonAnimator () {
+			Object[] arr = Selection.objects;
+			foreach (Object o in arr) {
+				string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o));
+				string skinName = EditorPrefs.GetString(guid + "_lastSkin", "");
 
-		foreach (Object o in arr) {
-			if (o.GetType() != typeof(SkeletonDataAsset))
-				return false;
+				InstantiateSkeletonAnimator((SkeletonDataAsset)o, skinName);
+				SceneView.RepaintAll();
+			}
 		}
 
-		return true;
-	}
+		[MenuItem("Assets/Spine/Instantiate (Mecanim)", true, 100)]
+		static bool ValidateInstantiateSkeletonAnimator () {
+			Object[] arr = Selection.objects;
 
-	public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, string skinName) {
-		return InstantiateSkeletonAnimator(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName));
-	}
+			if (arr.Length == 0)
+				return false;
 
-	public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
-		string spineGameObjectName = string.Format("Spine Mecanim GameObject ({0})", skeletonDataAsset.name.Replace("_SkeletonData", ""));
-		GameObject go = new GameObject(spineGameObjectName, typeof(MeshFilter), typeof(MeshRenderer), typeof(Animator), typeof(SkeletonAnimator));
+			foreach (Object o in arr) {
+				if (o.GetType() != typeof(SkeletonDataAsset))
+					return false;
+			}
 
-		if (skeletonDataAsset.controller == null) {
-			SkeletonBaker.GenerateMecanimAnimationClips(skeletonDataAsset);
+			return true;
 		}
 
-		go.GetComponent<Animator>().runtimeAnimatorController = skeletonDataAsset.controller;
-
-		SkeletonAnimator anim = go.GetComponent<SkeletonAnimator>();
-		anim.skeletonDataAsset = skeletonDataAsset;
+		public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, string skinName) {
+			return InstantiateSkeletonAnimator(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName));
+		}
 
-		bool requiresNormals = false;
+		public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
+			string spineGameObjectName = string.Format("Spine Mecanim GameObject ({0})", skeletonDataAsset.name.Replace("_SkeletonData", ""));
+			GameObject go = new GameObject(spineGameObjectName, typeof(MeshFilter), typeof(MeshRenderer), typeof(Animator), typeof(SkeletonAnimator));
 
-		foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) {
-			foreach (Material m in atlasAsset.materials) {
-				if (m.shader.name.Contains("Lit")) {
-					requiresNormals = true;
-					break;
-				}
+			if (skeletonDataAsset.controller == null) {
+				SkeletonBaker.GenerateMecanimAnimationClips(skeletonDataAsset);
 			}
-		}
 
-		anim.calculateNormals = requiresNormals;
+			go.GetComponent<Animator>().runtimeAnimatorController = skeletonDataAsset.controller;
+
+			SkeletonAnimator anim = go.GetComponent<SkeletonAnimator>();
+			anim.skeletonDataAsset = skeletonDataAsset;
 
-		SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
+			bool requiresNormals = false;
 
-		if (data == null) {
-			for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) {
-				string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]);
-				skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset));
+			foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) {
+				foreach (Material m in atlasAsset.materials) {
+					if (m.shader.name.Contains("Lit")) {
+						requiresNormals = true;
+						break;
+					}
+				}
 			}
 
-			data = skeletonDataAsset.GetSkeletonData(true);
-		}
+			anim.calculateNormals = requiresNormals;
+
+			SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
 
-		if (skin == null)
-			skin = data.DefaultSkin;
+			if (data == null) {
+				for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) {
+					string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]);
+					skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset));
+				}
 
-		if (skin == null)
-			skin = data.Skins.Items[0];
+				data = skeletonDataAsset.GetSkeletonData(true);
+			}
 
-		anim.Initialize(false);
+			if (skin == null)
+				skin = data.DefaultSkin;
 
-		anim.skeleton.SetSkin(skin);
-		anim.initialSkinName = skin.Name;
+			if (skin == null)
+				skin = data.Skins.Items[0];
 
-		anim.skeleton.Update(1);
-		anim.skeleton.UpdateWorldTransform();
-		anim.LateUpdate();
+			anim.Initialize(false);
 
-		return anim;
-	}
+			anim.skeleton.SetSkin(skin);
+			anim.initialSkinName = skin.Name;
 
-	static bool preferencesLoaded = false;
+			anim.skeleton.Update(1);
+			anim.skeleton.UpdateWorldTransform();
+			anim.LateUpdate();
 
-	[PreferenceItem("Spine")]
-	static void PreferencesGUI () {
-		if (!preferencesLoaded) {
-			preferencesLoaded = true;
-			defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f);
+			return anim;
 		}
+		#endif
+		#endregion
 
+		#region Spine Preferences
+		static bool preferencesLoaded = false;
 
-		EditorGUILayout.LabelField("Auto-Import Settings", EditorStyles.boldLabel);
-		EditorGUI.BeginChangeCheck();
-		defaultMix = EditorGUILayout.FloatField("Default Mix", defaultMix);
-		if (EditorGUI.EndChangeCheck())
-			EditorPrefs.SetFloat(DEFAULT_MIX_KEY, defaultMix);
+		[PreferenceItem("Spine")]
+		static void PreferencesGUI () {
+			if (!preferencesLoaded) {
+				preferencesLoaded = true;
+				defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, 0.2f);
+			}
 
-		GUILayout.Space(20);
-		EditorGUILayout.LabelField("3rd Party Settings", EditorStyles.boldLabel);
-		GUILayout.BeginHorizontal();
-		EditorGUILayout.PrefixLabel("TK2D");
 
-		if (GUILayout.Button("Enable", GUILayout.Width(64)))
-			EnableTK2D();
-		if (GUILayout.Button("Disable", GUILayout.Width(64)))
-			DisableTK2D();
-		GUILayout.EndHorizontal();
-	}
+			EditorGUILayout.LabelField("Auto-Import Settings", EditorStyles.boldLabel);
+			EditorGUI.BeginChangeCheck();
+			defaultMix = EditorGUILayout.FloatField("Default Mix", defaultMix);
+			if (EditorGUI.EndChangeCheck())
+				EditorPrefs.SetFloat(DEFAULT_MIX_KEY, defaultMix);
 
+			GUILayout.Space(20);
+			EditorGUILayout.LabelField("3rd Party Settings", EditorStyles.boldLabel);
+			GUILayout.BeginHorizontal();
+			EditorGUILayout.PrefixLabel("TK2D");
 
-	//TK2D Support
-	const string SPINE_TK2D_DEFINE = "SPINE_TK2D";
-
-	static void EnableTK2D () {
-		bool added = false;
-		foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) {
-			if (group == BuildTargetGroup.Unknown)
-				continue;
-			
-			string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
-			if (!defines.Contains(SPINE_TK2D_DEFINE)) {
-				added = true;
-				if (defines.EndsWith(";", System.StringComparison.Ordinal))
-					defines = defines + SPINE_TK2D_DEFINE;
-				else
-					defines = defines + ";" + SPINE_TK2D_DEFINE;
-
-				PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
-			}
+			if (GUILayout.Button("Enable", GUILayout.Width(64)))
+				EnableTK2D();
+			if (GUILayout.Button("Disable", GUILayout.Width(64)))
+				DisableTK2D();
+			GUILayout.EndHorizontal();
 		}
+		#endregion
+
+		//TK2D Support
+		const string SPINE_TK2D_DEFINE = "SPINE_TK2D";
+
+		static void EnableTK2D () {
+			bool added = false;
+			foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) {
+				if (group == BuildTargetGroup.Unknown)
+					continue;
+
+				string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
+				if (!defines.Contains(SPINE_TK2D_DEFINE)) {
+					added = true;
+					if (defines.EndsWith(";", System.StringComparison.Ordinal))
+						defines = defines + SPINE_TK2D_DEFINE;
+					else
+						defines = defines + ";" + SPINE_TK2D_DEFINE;
 
-		if (added) {
-			Debug.LogWarning("Setting Scripting Define Symbol " + SPINE_TK2D_DEFINE);
-		} else {
-			Debug.LogWarning("Already Set Scripting Define Symbol " + SPINE_TK2D_DEFINE);
+					PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
+				}
+			}
+
+			if (added) {
+				Debug.LogWarning("Setting Scripting Define Symbol " + SPINE_TK2D_DEFINE);
+			} else {
+				Debug.LogWarning("Already Set Scripting Define Symbol " + SPINE_TK2D_DEFINE);
+			}
 		}
-	}
 
 
-	static void DisableTK2D () {
-		bool removed = false;
-		foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) {
-			string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
-			if (defines.Contains(SPINE_TK2D_DEFINE)) {
-				removed = true;
-				if (defines.Contains(SPINE_TK2D_DEFINE + ";"))
-					defines = defines.Replace(SPINE_TK2D_DEFINE + ";", "");
-				else
-					defines = defines.Replace(SPINE_TK2D_DEFINE, "");
+		static void DisableTK2D () {
+			bool removed = false;
+			foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) {
+				string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
+				if (defines.Contains(SPINE_TK2D_DEFINE)) {
+					removed = true;
+					if (defines.Contains(SPINE_TK2D_DEFINE + ";"))
+						defines = defines.Replace(SPINE_TK2D_DEFINE + ";", "");
+					else
+						defines = defines.Replace(SPINE_TK2D_DEFINE, "");
 
-				PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
+					PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
+				}
 			}
-		}
 
-		if (removed) {
-			Debug.LogWarning("Removing Scripting Define Symbol " + SPINE_TK2D_DEFINE);
-		} else {
-			Debug.LogWarning("Already Removed Scripting Define Symbol " + SPINE_TK2D_DEFINE);
+			if (removed) {
+				Debug.LogWarning("Removing Scripting Define Symbol " + SPINE_TK2D_DEFINE);
+			} else {
+				Debug.LogWarning("Already Removed Scripting Define Symbol " + SPINE_TK2D_DEFINE);
+			}
 		}
-	}
 
-	public class AtlasRequirementLoader : AttachmentLoader {
+		public class AtlasRequirementLoader : AttachmentLoader {
 
-		List<string> requirementList;
-		public AtlasRequirementLoader (List<string> requirementList) {
-			this.requirementList = requirementList;
-		}
+			List<string> requirementList;
+			public AtlasRequirementLoader (List<string> requirementList) {
+				this.requirementList = requirementList;
+			}
 
-		public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) {
-			requirementList.Add(path);
-			return new RegionAttachment(name);
-		}
+			public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) {
+				requirementList.Add(path);
+				return new RegionAttachment(name);
+			}
 
-		public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
-			requirementList.Add(path);
-			return new MeshAttachment(name);
-		}
+			public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
+				requirementList.Add(path);
+				return new MeshAttachment(name);
+			}
 
-		public WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, string name, string path) {
-			requirementList.Add(path);
-			return new WeightedMeshAttachment(name);
-		}
+			public WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, string name, string path) {
+				requirementList.Add(path);
+				return new WeightedMeshAttachment(name);
+			}
 
-		public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
-			return new BoundingBoxAttachment(name);
+			public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
+				return new BoundingBoxAttachment(name);
+			}
 		}
 	}
+
 }
+

+ 101 - 0
spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs

@@ -0,0 +1,101 @@
+/******************************************************************************
+ * 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 System.Reflection;
+
+namespace Spine.Unity.Editor {
+	public static class SpineInspectorUtility {
+
+		public static string Pluralize (int n, string singular, string plural) {
+			return n + " " + (n == 1 ? singular : plural);
+		}
+
+		public static string PluralThenS (int n) {
+			return n == 1 ? "" : "s";
+		}
+
+		#region Sorting Layer Field Helpers
+		static readonly GUIContent SortingLayerLabel = new GUIContent("Sorting Layer");
+		static readonly GUIContent OrderInLayerLabel = new GUIContent("Order in Layer");
+
+		static MethodInfo m_SortingLayerFieldMethod;
+		static MethodInfo SortingLayerFieldMethod {
+			get {
+				if (m_SortingLayerFieldMethod == null)
+					m_SortingLayerFieldMethod = typeof(EditorGUILayout).GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic, null, new [] { typeof(GUIContent), typeof(SerializedProperty), typeof(GUIStyle) }, null);
+
+				return m_SortingLayerFieldMethod;
+			}
+		}
+
+		public struct SerializedSortingProperties {
+			public SerializedObject renderer;
+			public SerializedProperty sortingLayerID;
+			public SerializedProperty sortingOrder;
+
+			public SerializedSortingProperties (Renderer r) {
+				renderer = new SerializedObject(r);
+				sortingLayerID = renderer.FindProperty("m_SortingLayerID");
+				sortingOrder = renderer.FindProperty("m_SortingOrder");
+			}
+
+			public void ApplyModifiedProperties () {
+				renderer.ApplyModifiedProperties();
+			}
+		}
+
+		public static void SortingPropertyFields (SerializedSortingProperties prop, bool applyModifiedProperties) {
+			if (applyModifiedProperties) {
+				EditorGUI.BeginChangeCheck();
+				SortingPropertyFields(prop.sortingLayerID, prop.sortingOrder);
+				if(EditorGUI.EndChangeCheck()) {
+					prop.ApplyModifiedProperties();
+					EditorUtility.SetDirty(prop.renderer.targetObject);
+				}
+			} else {
+				SortingPropertyFields(prop.sortingLayerID, prop.sortingOrder);
+			}
+		}
+
+		public static void SortingPropertyFields (SerializedProperty m_SortingLayerID, SerializedProperty m_SortingOrder) {
+			if (SpineInspectorUtility.SortingLayerFieldMethod != null && m_SortingLayerID != null) {
+				SpineInspectorUtility.SortingLayerFieldMethod.Invoke(null, new object[] { SortingLayerLabel, m_SortingLayerID, EditorStyles.popup } );
+			} else {
+				EditorGUILayout.PropertyField(m_SortingLayerID);
+			}
+
+			EditorGUILayout.PropertyField(m_SortingOrder, OrderInLayerLabel);
+		}
+		#endregion
+	}
+}

+ 12 - 0
spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 663715b5714e2db499192c8d91ef1f86
+timeCreated: 1457404957
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 2 - 2
spine-unity/Assets/spine-unity/Mesh Generation/Simple.meta → spine-unity/Assets/spine-unity/Mesh Generation/Arrays.meta

@@ -1,7 +1,7 @@
 fileFormatVersion: 2
-guid: 8e1503fd3294b7042a676e58798e4136
+guid: ce7d323baddaa244c8f9f45b38b68193
 folderAsset: yes
-timeCreated: 1455486312
+timeCreated: 1457398522
 licenseType: Free
 DefaultImporter:
   userData: 

+ 351 - 0
spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysMeshGenerator.cs

@@ -0,0 +1,351 @@
+/******************************************************************************
+ * 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.
+ *****************************************************************************/
+#define SPINE_OPTIONAL_NORMALS
+using UnityEngine;
+
+namespace Spine.Unity.MeshGeneration {
+	public class ArraysMeshGenerator {
+		#region Settings
+		protected bool premultiplyVertexColors = true;
+		public bool PremultiplyVertexColors { get { return this.premultiplyVertexColors; } set { this.premultiplyVertexColors = value; } }
+		#endregion
+
+		protected float[] attachmentVertexBuffer = new float[8];
+		protected Vector3[] meshVertices;
+		protected Color32[] meshColors32;
+		protected Vector2[] meshUVs;
+
+
+		protected bool generateNormals = false;
+		public bool GenerateNormals {
+			get { return generateNormals; }
+			set { generateNormals = value; }
+		}
+
+		Vector3[] meshNormals;
+
+		public void TryAddNormalsTo (Mesh mesh, int targetVertexCount) {
+			#if SPINE_OPTIONAL_NORMALS
+			if (generateNormals) {
+				bool verticesWasResized = this.meshNormals == null || targetVertexCount > meshNormals.Length;
+				if (verticesWasResized) {
+					this.meshNormals = new Vector3[targetVertexCount];
+					Vector3 normal = new Vector3(0, 0, -1);
+					Vector3[] normals = this.meshNormals;
+					for (int i = 0; i < targetVertexCount; i++)
+						normals[i] = normal;
+				}
+
+				mesh.normals = this.meshNormals;
+			}
+			#endif
+		}
+
+
+		public static bool EnsureSize (int targetVertexCount, ref Vector3[] vertices, ref Vector2[] uvs, ref Color32[] colors) {
+			Vector3[] verts = vertices;
+			bool verticesWasResized = verts == null || targetVertexCount > verts.Length;
+			if (verticesWasResized) {
+				// Not enough space, increase size.
+				vertices = new Vector3[targetVertexCount];
+				colors = new Color32[targetVertexCount];
+				uvs = new Vector2[targetVertexCount];
+			} else {
+				// Too many vertices, zero the extra.
+				Vector3 zero = Vector3.zero;
+				for (int i = targetVertexCount, n = verts.Length; i < n; i++)
+					verts[i] = zero;
+			}
+			return verticesWasResized;
+		}
+
+		public static bool EnsureTriangleBuffersSize (ExposedList<SubmeshTriangleBuffer> submeshBuffers, int targetSubmeshCount, SubmeshInstruction[] instructionItems) {
+			bool submeshBuffersWasResized = submeshBuffers.Count < targetSubmeshCount;
+			if (submeshBuffersWasResized) {
+				submeshBuffers.GrowIfNeeded(targetSubmeshCount - submeshBuffers.Count);
+				for (int i = submeshBuffers.Count; submeshBuffers.Count < targetSubmeshCount; i++)
+					submeshBuffers.Add(new SubmeshTriangleBuffer(instructionItems[i].triangleCount));
+			}
+			return submeshBuffersWasResized;
+		}
+
+		/// <summary>
+		/// Fills vertex arrays.
+		/// </summary>
+		/// <param name="skeleton">Spine.Skeleton source of the drawOrder array</param>
+		/// <param name="startSlot">Slot index of the first slot.</param>
+		/// <param name="endSlot">The index bounding the slot list. endSlot - 1 is the last slot to be added.</param>
+		/// <param name="zSpacing">Spacing along the z-axis between attachments.</param>
+		/// <param name="pmaColors">If set to <c>true</c>, vertex colors will be premultiplied. This will also enable additive.</param>
+		/// <param name="verts">Vertex positions array. </param>
+		/// <param name="uvs">Vertex UV array.</param>
+		/// <param name="colors">Vertex color array (Color32).</param>
+		/// <param name="vertexIndex">A reference to the running vertex index. This is used when more than one submesh is to be added.</param>
+		/// <param name="tempVertBuffer">A temporary vertex position buffer for attachment position values.</param>
+		/// <param name="boundsMin">Reference to the running calculated minimum bounds.</param>
+		/// <param name="boundsMax">Reference to the running calculated maximum bounds.</param>
+		public static void FillVerts (Skeleton skeleton, int startSlot, int endSlot, float zSpacing, bool pmaColors, Vector3[] verts, Vector2[] uvs, Color32[] colors, ref int vertexIndex, ref float[] tempVertBuffer, ref Vector3 boundsMin, ref Vector3 boundsMax) {
+			Color32 color;
+			var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
+			float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
+
+			int vi = vertexIndex;
+			var tempVerts = tempVertBuffer;
+			Vector3 bmin = boundsMin;
+			Vector3 bmax = boundsMax;
+
+			// drawOrder[endSlot] is excluded
+			for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
+				var slot = skeletonDrawOrderItems[slotIndex];
+				var attachment = slot.attachment;
+				float z = slotIndex * zSpacing;
+
+				var regionAttachment = attachment as RegionAttachment;
+				if (regionAttachment != null) {
+					regionAttachment.ComputeWorldVertices(slot.bone, tempVerts);
+
+					float x1 = tempVerts[RegionAttachment.X1], y1 = tempVerts[RegionAttachment.Y1];
+					float x2 = tempVerts[RegionAttachment.X2], y2 = tempVerts[RegionAttachment.Y2];
+					float x3 = tempVerts[RegionAttachment.X3], y3 = tempVerts[RegionAttachment.Y3];
+					float x4 = tempVerts[RegionAttachment.X4], y4 = tempVerts[RegionAttachment.Y4];
+					verts[vi].x = x1; verts[vi].y = y1; verts[vi].z = z;
+					verts[vi + 1].x = x4; verts[vi + 1].y = y4; verts[vi + 1].z = z;
+					verts[vi + 2].x = x2; verts[vi + 2].y = y2; verts[vi + 2].z = z;
+					verts[vi + 3].x = x3; verts[vi + 3].y = y3;	verts[vi + 3].z = z;
+
+					if (pmaColors) {
+						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[vi] = color; colors[vi + 1] = color; colors[vi + 2] = color; colors[vi + 3] = color;
+
+					float[] regionUVs = regionAttachment.uvs;
+					uvs[vi].x = regionUVs[RegionAttachment.X1]; uvs[vi].y = regionUVs[RegionAttachment.Y1];
+					uvs[vi + 1].x = regionUVs[RegionAttachment.X4]; uvs[vi + 1].y = regionUVs[RegionAttachment.Y4];
+					uvs[vi + 2].x = regionUVs[RegionAttachment.X2]; uvs[vi + 2].y = regionUVs[RegionAttachment.Y2];
+					uvs[vi + 3].x = regionUVs[RegionAttachment.X3]; uvs[vi + 3].y = regionUVs[RegionAttachment.Y3];
+
+					// Calculate min/max X
+					if (x1 < bmin.x) bmin.x = x1;
+					else if (x1 > bmax.x) bmax.x = x1;
+					if (x2 < bmin.x) bmin.x = x2;
+					else if (x2 > bmax.x) bmax.x = x2;
+					if (x3 < bmin.x) bmin.x = x3;
+					else if (x3 > bmax.x) bmax.x = x3;
+					if (x4 < bmin.x) bmin.x = x4;
+					else if (x4 > bmax.x) bmax.x = x4;
+
+					// Calculate min/max Y
+					if (y1 < bmin.y) bmin.y = y1;
+					else if (y1 > bmax.y) bmax.y = y1;
+					if (y2 < bmin.y) bmin.y = y2;
+					else if (y2 > bmax.y) bmax.y = y2;
+					if (y3 < bmin.y) bmin.y = y3;
+					else if (y3 > bmax.y) bmax.y = y3;
+					if (y4 < bmin.y) bmin.y = y4;
+					else if (y4 > bmax.y) bmax.y = y4;
+
+					vi += 4;
+				} else {
+					var meshAttachment = attachment as MeshAttachment;
+					if (meshAttachment != null) {
+						int meshVertexCount = meshAttachment.vertices.Length;
+						if (tempVerts.Length < meshVertexCount) tempVerts = new float[meshVertexCount];
+						meshAttachment.ComputeWorldVertices(slot, tempVerts);
+
+						if (pmaColors) {
+							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 * 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[] attachmentUVs = meshAttachment.uvs;
+						for (int iii = 0; iii < meshVertexCount; iii += 2) {
+							float x = tempVerts[iii], y = tempVerts[iii + 1];
+							verts[vi].x = x; verts[vi].y = y; verts[vi].z = z;
+							colors[vi] = color; uvs[vi].x = attachmentUVs[iii]; uvs[vi].y = attachmentUVs[iii + 1];
+
+							if (x < bmin.x) bmin.x = x;
+							else if (x > bmax.x) bmax.x = x;
+
+							if (y < bmin.y) bmin.y = y;
+							else if (y > bmax.y) bmax.y = y;
+
+							vi++;
+						}
+					} else {
+						var weightedMeshAttachment = attachment as WeightedMeshAttachment;
+						if (weightedMeshAttachment != null) {
+							int meshVertexCount = weightedMeshAttachment.uvs.Length;
+							if (tempVerts.Length < meshVertexCount) tempVerts = new float[meshVertexCount];
+							weightedMeshAttachment.ComputeWorldVertices(slot, tempVerts);
+
+							if (pmaColors) {
+								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[] attachmentUVs = weightedMeshAttachment.uvs;
+							for (int iii = 0; iii < meshVertexCount; iii += 2) {
+								float x = tempVerts[iii], y = tempVerts[iii + 1];
+								verts[vi].x = x; verts[vi].y = y; verts[vi].z = z;
+								colors[vi] = color;
+								uvs[vi].x = attachmentUVs[iii]; uvs[vi].y = attachmentUVs[iii + 1];
+
+								if (x < bmin.x) bmin.x = x;
+								else if (x > bmax.x) bmax.x = x;
+								if (y < bmin.y) bmin.y = y;
+								else if (y > bmax.y) bmax.y = y;
+
+								vi++;
+							}
+						}
+					}
+				}
+			}
+
+			// ref return values
+			vertexIndex = vi;
+			tempVertBuffer = tempVerts;
+			boundsMin = bmin;
+			boundsMax = bmax;
+		}
+
+
+		/// <summary>
+		/// Fills a submesh triangle buffer array.
+		/// </summary>
+		/// <param name="skeleton">Spine.Skeleton source of draw order slots.</param>
+		/// <param name="triangleCount">The target triangle count.</param>
+		/// <param name="firstVertex">First vertex of this submesh.</param>
+		/// <param name="startSlot">Start slot.</param>
+		/// <param name="endSlot">End slot.</param>
+		/// <param name="triangleBuffer">The triangle buffer array to be filled. This reference will be replaced in case the triangle values don't fit.</param>
+		/// <param name="bufferTriangleCount">The current triangle count of the submesh buffer. This is not always equal to triangleBuffer.Length because for last submeshes, length may be larger than needed.</param>
+		/// <param name="isLastSubmesh">If set to <c>true</c>, the triangle buffer is allowed to be larger than needed.</param>
+		public static void FillTriangles (Skeleton skeleton, int triangleCount, int firstVertex, int startSlot, int endSlot, ref int[] triangleBuffer, bool isLastSubmesh) {
+			int trianglesCapacity = triangleBuffer.Length;
+			var tris = triangleBuffer;
+
+			// Ensure triangleBuffer size.
+			if (isLastSubmesh) {
+				if (trianglesCapacity > triangleCount) {
+					for (int i = triangleCount; i < trianglesCapacity; i++)
+						tris[i] = 0;
+				} else if (trianglesCapacity < triangleCount) {
+					triangleBuffer = tris = new int[triangleCount];
+				}
+			} else if (trianglesCapacity != triangleCount) {
+				triangleBuffer = tris = new int[triangleCount];
+			}
+
+			// Iterate through submesh slots and store the triangles. 
+			int triangleIndex = 0;
+			int afv = firstVertex; // attachment first vertex
+			var skeletonDrawOrderItems = skeleton.drawOrder.Items;
+			for (int i = startSlot, n = endSlot; i < n; i++) {			
+				var attachment = skeletonDrawOrderItems[i].attachment;
+
+				if (attachment is RegionAttachment) {
+					tris[triangleIndex] = afv; tris[triangleIndex + 1] = afv + 2; tris[triangleIndex + 2] = afv + 1;
+					tris[triangleIndex + 3] = afv + 2; tris[triangleIndex + 4] = afv + 3; tris[triangleIndex + 5] = afv + 1;
+
+					triangleIndex += 6;
+					afv += 4;
+				} else {
+					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;
+					}
+
+					for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
+						tris[triangleIndex] = afv + attachmentTriangles[ii];
+
+					afv += attachmentVertexCount;
+				}
+			} // Done adding current submesh triangles
+		}
+
+
+		public static Bounds ToBounds (Vector3 boundsMin, Vector3 boundsMax) {
+			Vector3 size = (boundsMax - boundsMin);
+			Vector3 center = boundsMin + size * 0.5f;
+			return new Bounds(center, size);
+		}
+
+		#region SubmeshTriangleBuffer
+		public class SubmeshTriangleBuffer {
+			public int[] triangles;
+			//public int triangleCount;
+
+			public SubmeshTriangleBuffer (int triangleCount) {
+				triangles = new int[triangleCount];
+			}
+		}
+		#endregion
+
+	}
+}
+

+ 12 - 0
spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysMeshGenerator.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ed047fa8737dedb4081579983c193db5
+timeCreated: 1458124926
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 135 - 0
spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs

@@ -0,0 +1,135 @@
+/******************************************************************************
+ * 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.MeshGeneration {
+	public class ArraysSimpleMeshGenerator : ArraysMeshGenerator, ISimpleMeshGenerator {
+		#region Settings
+		protected float scale = 1f;
+		public float Scale {
+			get { return scale; }
+			set { scale = value; }
+		}
+		#endregion
+
+		private Mesh lastGeneratedMesh;
+		public Mesh LastGeneratedMesh {	get { return lastGeneratedMesh; } }
+
+		readonly DoubleBufferedMesh doubleBufferedMesh = new DoubleBufferedMesh();
+		int[] triangles;
+		int triangleBufferCount;
+
+		public Mesh GenerateMesh (Skeleton skeleton) {
+			int totalVertexCount = 0; // size of vertex arrays
+			int totalTriangleCount = 0; // size of index array
+
+			// STEP 1 : GenerateInstruction(). Count verts and tris to determine array sizes.
+			var drawOrderItems = skeleton.drawOrder.Items;
+			int drawOrderCount = skeleton.drawOrder.Count;
+			for (int i = 0; i < drawOrderCount; i++) {
+				Slot slot = drawOrderItems[i];
+				Attachment attachment = slot.attachment;
+				int attachmentVertexCount, attachmentTriangleCount;
+				var regionAttachment = attachment as RegionAttachment;
+				if (regionAttachment != null) {					
+					attachmentVertexCount = 4;
+					attachmentTriangleCount = 6;
+				} else {
+					var meshAttachment = attachment as MeshAttachment;
+					if (meshAttachment != null) {
+						attachmentVertexCount = meshAttachment.vertices.Length >> 1;
+						attachmentTriangleCount = meshAttachment.triangles.Length;
+					} else {
+						var skinnedMeshAttachment = attachment as WeightedMeshAttachment;
+						if (skinnedMeshAttachment != null) {
+							attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
+							attachmentTriangleCount = skinnedMeshAttachment.triangles.Length;
+						} else
+							continue;
+					}
+				}
+				totalTriangleCount += attachmentTriangleCount;
+				totalVertexCount += attachmentVertexCount;
+			}
+
+			// STEP 2 : Ensure buffers are the correct size
+			ArraysMeshGenerator.EnsureSize(totalVertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
+			this.triangles = this.triangles ?? new int[totalTriangleCount];
+				
+			// STEP 3 : Update vertex buffer
+			const float zSpacing = 0;
+			const float zFauxHalfThickness = 0.01f;	// Somehow needs this thickness for bounds to work properly in some cases (eg, Unity UI clipping)
+			Vector3 meshBoundsMin;
+			Vector3 meshBoundsMax;
+			if (totalVertexCount == 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;
+				meshBoundsMin.z = -zFauxHalfThickness * scale;
+				meshBoundsMax.z = zFauxHalfThickness * scale;
+
+				int vertexIndex = 0;
+				ArraysMeshGenerator.FillVerts(skeleton, 0, drawOrderCount, zSpacing, this.premultiplyVertexColors, this.meshVertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
+
+				// Apply scale to vertices
+				meshBoundsMax.x *= scale; meshBoundsMax.y *= scale;
+				meshBoundsMin.x *= scale; meshBoundsMax.y *= scale;
+				var vertices = this.meshVertices;
+				for (int i = 0; i < totalVertexCount; i++) {
+					Vector3 p = vertices[i];
+					p.x *= scale;
+					p.y *= scale;
+					vertices[i] = p;
+				}
+			}
+				
+			// Step 4 : Update Triangles buffer
+			ArraysMeshGenerator.FillTriangles(skeleton, totalTriangleCount, 0, 0, drawOrderCount, ref this.triangles, true);
+
+			// Step 5 : Update Mesh with buffers
+			var mesh = doubleBufferedMesh.GetNextMesh();
+			mesh.vertices = this.meshVertices;
+			mesh.colors32 = meshColors32;
+			mesh.uv = meshUVs;
+			mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
+			mesh.triangles = triangles;
+
+			lastGeneratedMesh = mesh;
+			return mesh;
+		}
+
+	}
+
+}

+ 0 - 0
spine-unity/Assets/spine-unity/Mesh Generation/Simple/ArraysSimpleMeshGenerator.cs.meta → spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs.meta


+ 194 - 0
spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshSetMeshGenerator.cs

@@ -0,0 +1,194 @@
+/******************************************************************************
+ * 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.
+ *****************************************************************************/
+#define SPINE_OPTIONAL_NORMALS
+using UnityEngine;
+
+namespace Spine.Unity.MeshGeneration {
+	public class ArraysSubmeshSetMeshGenerator : ArraysMeshGenerator, ISubmeshSetMeshGenerator {
+		#region Settings
+		public float zSpacing = 0f;
+		#endregion
+
+		readonly DoubleBuffered<SmartMesh> doubleBufferedSmartMesh = new DoubleBuffered<SmartMesh>();
+		readonly ExposedList<SubmeshInstruction> currentInstructions = new ExposedList<SubmeshInstruction>();
+		readonly ExposedList<Attachment> currentAttachments = new ExposedList<Attachment>();
+		readonly ExposedList<SubmeshTriangleBuffer> submeshBuffers = new ExposedList<SubmeshTriangleBuffer>();
+		Material[] sharedMaterials = new Material[0];
+
+		public MeshAndMaterials GenerateMesh (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
+			// STEP 0: Prepare instructions.
+			var paramItems = instructions.Items;
+			currentInstructions.Clear(false);
+			for (int i = startSubmesh, n = endSubmesh; i < n; i++) {
+				this.currentInstructions.Add(paramItems[i]);
+			}
+			var smartMesh = doubleBufferedSmartMesh.GetNext();
+			var mesh = smartMesh.mesh;
+			int submeshCount = currentInstructions.Count;
+			var currentInstructionsItems = currentInstructions.Items;
+			int vertexCount = 0;
+			for (int i = 0; i < submeshCount; i++) {
+				currentInstructionsItems[i].firstVertexIndex = vertexCount;// Ensure current instructions have correct cached values.
+				vertexCount += currentInstructionsItems[i].vertexCount; // vertexCount will also be used for the rest of this method.
+			}
+
+			// STEP 1: Ensure correct buffer sizes.
+			bool vertBufferResized = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32); 
+			bool submeshBuffersResized = ArraysMeshGenerator.EnsureTriangleBuffersSize(submeshBuffers, submeshCount, currentInstructionsItems);
+
+			// STEP 2: Update buffers based on Skeleton.
+
+			// Initial values for manual Mesh Bounds calculation
+			Vector3 meshBoundsMin;
+			Vector3 meshBoundsMax;
+			float zSpacing = this.zSpacing;
+			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;
+
+				int endSlot = currentInstructionsItems[submeshCount - 1].endSlot;
+				if (zSpacing > 0f) {
+					meshBoundsMin.z = 0f;
+					meshBoundsMax.z = zSpacing * endSlot;
+				} else {
+					meshBoundsMin.z = zSpacing * endSlot;
+					meshBoundsMax.z = 0f;
+				}
+			}
+				
+			// For each submesh, add vertex data from attachments.
+			var currentAttachments_ = this.currentAttachments;
+			bool structureDoesntMatch = vertBufferResized || submeshBuffersResized || smartMesh.StructureDoesntMatch(currentAttachments_, currentInstructions);
+			currentAttachments_.Clear(false);
+			int vertexIndex = 0; // modified by FillVerts
+			for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
+				var currentInstruction = currentInstructionsItems[submeshIndex];
+				int startSlot = currentInstruction.startSlot;
+				int endSlot = currentInstruction.endSlot;
+				var skeleton = currentInstruction.skeleton;
+				var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
+				for (int i = startSlot; i < endSlot; i++) {
+					var ca = skeletonDrawOrderItems[i].attachment;
+					if (ca != null) currentAttachments_.Add(ca); // Includes BoundingBoxes. This is ok.
+				}
+				ArraysMeshGenerator.FillVerts(skeleton, startSlot, endSlot, zSpacing, this.premultiplyVertexColors, this.meshVertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
+				if (structureDoesntMatch) {
+					var currentBuffer = submeshBuffers.Items[submeshIndex];
+					bool isLastSubmesh = (submeshIndex == submeshCount - 1);
+					ArraysMeshGenerator.FillTriangles(skeleton, currentInstruction.triangleCount, currentInstruction.firstVertexIndex, startSlot, endSlot, ref currentBuffer.triangles, isLastSubmesh);
+				}
+			}
+
+			if (structureDoesntMatch) {
+				mesh.Clear();
+				this.sharedMaterials = currentInstructions.GetUpdatedMaterialArray(this.sharedMaterials);
+			}
+
+			// STEP 3: Assign the buffers into the Mesh.
+			smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, currentAttachments, currentInstructions);
+			mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
+			#if SPINE_OPTIONAL_NORMALS
+			this.TryAddNormalsTo(mesh, vertexCount);
+			#endif
+
+			if (structureDoesntMatch) {
+				// Push new triangles if doesn't match.
+				mesh.subMeshCount = submeshCount;
+				for (int i = 0; i < submeshCount; i++)
+					mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);			
+			}
+				
+			return new MeshAndMaterials(smartMesh.mesh, sharedMaterials);
+		}
+
+		#region Types
+		// A SmartMesh is a Mesh (with submeshes) that knows what attachments and instructions were used to generate it.
+		class SmartMesh {
+			public readonly Mesh mesh = SpineMesh.NewMesh();
+			readonly ExposedList<Attachment> attachmentsUsed = new ExposedList<Attachment>();
+			readonly ExposedList<SubmeshInstruction> instructionsUsed = new ExposedList<SubmeshInstruction>();
+
+			public void Set (Vector3[] verts, Vector2[] uvs, Color32[] colors, ExposedList<Attachment> attachments, ExposedList<SubmeshInstruction> instructions) {
+				mesh.vertices = verts;
+				mesh.uv = uvs;
+				mesh.colors32 = colors;
+
+				attachmentsUsed.Clear(false);
+				attachmentsUsed.GrowIfNeeded(attachments.Capacity);
+				attachmentsUsed.Count = attachments.Count;
+				attachments.CopyTo(attachmentsUsed.Items);
+
+				instructionsUsed.Clear(false);
+				instructionsUsed.GrowIfNeeded(instructions.Capacity);
+				instructionsUsed.Count = instructions.Count;
+				instructions.CopyTo(instructionsUsed.Items);
+			}
+
+			public bool StructureDoesntMatch (ExposedList<Attachment> attachments, ExposedList<SubmeshInstruction> instructions) {
+				// Check count inequality.
+				if (attachments.Count != this.attachmentsUsed.Count) return true;
+				if (instructions.Count != this.instructionsUsed.Count) return true;
+
+				// Check each attachment.
+				var attachmentsPassed = attachments.Items;
+				var myAttachments = this.attachmentsUsed.Items;
+				for (int i = 0, n = attachmentsUsed.Count; i < n; i++)
+					if (attachmentsPassed[i] != myAttachments[i]) return true;
+
+				// Check each submesh for equal arrangement.
+				var instructionListItems = instructions.Items;
+				var myInstructions = this.instructionsUsed.Items;
+				for (int i = 0, n = this.instructionsUsed.Count; i < n; i++) {
+					var lhs = instructionListItems[i];
+					var rhs = myInstructions[i];
+					if (
+						lhs.material.GetInstanceID() != rhs.material.GetInstanceID() ||
+						lhs.startSlot != rhs.startSlot ||
+						lhs.endSlot != rhs.endSlot ||
+						lhs.triangleCount != rhs.triangleCount ||
+						lhs.vertexCount != rhs.vertexCount ||
+						lhs.firstVertexIndex != rhs.firstVertexIndex
+					) return true;
+				}
+
+				//Debug.Log("structure matched");
+				return false;
+			}
+		}
+		#endregion
+	}
+
+}

+ 12 - 0
spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshSetMeshGenerator.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 11ba077d7d984814db31d054192be532
+timeCreated: 1458047178
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 304 - 0
spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshedMeshGenerator.cs

@@ -0,0 +1,304 @@
+/******************************************************************************
+ * 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.
+ *****************************************************************************/
+//#define SPINE_OPTIONAL_NORMALS
+
+using UnityEngine;
+using System.Collections.Generic;
+
+namespace Spine.Unity.MeshGeneration {
+	/// <summary>
+	/// Arrays submeshed mesh generator.
+	/// </summary>
+	public class ArraysSubmeshedMeshGenerator : ArraysMeshGenerator, ISubmeshedMeshGenerator {
+
+		readonly List<Slot> separators = new List<Slot>();
+		public List<Slot> Separators { get { return this.separators; } }
+
+		public float zSpacing = 0f;
+		#if SPINE_OPTIONAL_NORMALS
+		public bool generateNormals;
+		public bool generateTangents;
+		#endif
+
+		readonly DoubleBuffered<SmartMesh> doubleBufferedSmartMesh = new DoubleBuffered<SmartMesh>();
+		readonly SubmeshedMeshInstruction currentInstructions = new SubmeshedMeshInstruction();
+		readonly ExposedList<SubmeshTriangleBuffer> submeshBuffers = new ExposedList<SubmeshTriangleBuffer>();
+		Material[] sharedMaterials = new Material[0];
+
+		public SubmeshedMeshInstruction GenerateInstruction (Skeleton skeleton) {
+			if (skeleton == null) throw new System.ArgumentNullException("skeleton");
+
+			// Count vertices and submesh triangles.
+			int runningVertexCount = 0;
+
+			int submeshTriangleCount = 0;
+			int submeshFirstVertex = 0;
+			int submeshVertexCount = 0;
+			int submeshStartSlotIndex = 0;
+			Material lastMaterial = null;
+
+			var drawOrder = skeleton.drawOrder;
+			var drawOrderItems = drawOrder.Items;
+			int drawOrderCount = drawOrder.Count;
+			int separatorCount = separators.Count;
+
+			var instructionList = this.currentInstructions.submeshInstructions;
+			instructionList.Clear(false);
+
+			currentInstructions.attachmentList.Clear(false);
+
+			for (int i = 0; i < drawOrderCount; i++) {
+				var slot = drawOrderItems[i];
+				var attachment = slot.attachment;
+
+				object rendererObject; // An AtlasRegion in plain Spine-Unity. eventual source of Material object.
+				int attachmentVertexCount, attachmentTriangleCount;
+
+				var regionAttachment = attachment as RegionAttachment;
+				if (regionAttachment != null) {
+					rendererObject = regionAttachment.RendererObject;
+					attachmentVertexCount = 4;
+					attachmentTriangleCount = 6;
+				} else {
+					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;
+					}
+				}
+
+				var attachmentMaterial = (Material)((AtlasRegion)rendererObject).page.rendererObject;
+
+				// Populate submesh when material changes. (or when forced to separate by a submeshSeparator)
+				bool separatedBySlot  = ( separatorCount > 0 && separators.Contains(slot) );
+				if (( runningVertexCount > 0 && lastMaterial.GetInstanceID() != attachmentMaterial.GetInstanceID() ) ||	separatedBySlot) {
+
+					instructionList.Add(
+						new SubmeshInstruction {
+							skeleton = skeleton,
+							material = lastMaterial,
+							triangleCount = submeshTriangleCount,
+							vertexCount = submeshVertexCount,
+							startSlot = submeshStartSlotIndex,
+							endSlot = i,
+							firstVertexIndex = submeshFirstVertex,
+							forceSeparate = separatedBySlot
+						}
+					);
+
+					// Prepare for next submesh
+					submeshTriangleCount = 0;
+					submeshVertexCount = 0;
+					submeshFirstVertex = runningVertexCount;
+					submeshStartSlotIndex = i;
+				}
+				lastMaterial = attachmentMaterial;
+
+				submeshTriangleCount += attachmentTriangleCount;
+				submeshVertexCount += attachmentVertexCount;
+				runningVertexCount += attachmentVertexCount;
+
+				currentInstructions.attachmentList.Add(attachment);
+			}
+
+			instructionList.Add(
+				new SubmeshInstruction {
+					skeleton = skeleton,
+					material = lastMaterial,
+					triangleCount = submeshTriangleCount,
+					vertexCount = submeshVertexCount,
+					startSlot = submeshStartSlotIndex,
+					endSlot = drawOrderCount,
+					firstVertexIndex = submeshFirstVertex,
+					forceSeparate = false
+				}
+			);
+
+			currentInstructions.vertexCount = runningVertexCount;
+			return currentInstructions;
+		}
+
+		// ISubmeshedMeshGenerator.GenerateMesh
+		/// <summary>Generates a mesh based on SubmeshedMeshInstructions</summary>
+		public MeshAndMaterials GenerateMesh (SubmeshedMeshInstruction meshInstructions) {
+			var smartMesh = doubleBufferedSmartMesh.GetNext();
+			var mesh = smartMesh.mesh;
+			int submeshCount = meshInstructions.submeshInstructions.Count;
+			var instructionList = meshInstructions.submeshInstructions;
+
+			// STEP 1: Ensure correct buffer sizes.
+			bool submeshBuffersResized = ArraysMeshGenerator.EnsureTriangleBuffersSize(submeshBuffers, submeshCount, instructionList.Items);
+			bool vertBufferResized = ArraysMeshGenerator.EnsureSize(meshInstructions.vertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
+			Vector3[] vertices = this.meshVertices;
+
+			// STEP 2: Update buffers based on Skeleton.
+			float zSpacing = this.zSpacing;
+			Vector3 meshBoundsMin;
+			Vector3 meshBoundsMax;
+			int attachmentCount = meshInstructions.attachmentList.Count;
+			if (attachmentCount <= 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 * (attachmentCount - 1);
+				} else {
+					meshBoundsMin.z = zSpacing * (attachmentCount - 1);
+					meshBoundsMax.z = 0f;
+				}
+			}
+			bool structureDoesntMatch = vertBufferResized || submeshBuffersResized || smartMesh.StructureDoesntMatch(meshInstructions);
+			// For each submesh, add vertex data from attachments. Also triangles, but only if needed.
+			int vertexIndex = 0; // modified by FillVerts
+			for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
+				var submeshInstruction = instructionList.Items[submeshIndex];
+				int start = submeshInstruction.startSlot;
+				int end = submeshInstruction.endSlot;
+				var skeleton = submeshInstruction.skeleton;
+				ArraysMeshGenerator.FillVerts(skeleton, start, end, zSpacing, this.premultiplyVertexColors, vertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
+				if (structureDoesntMatch) {
+					var currentBuffer = submeshBuffers.Items[submeshIndex];
+					bool isLastSubmesh = (submeshIndex == submeshCount - 1);
+					ArraysMeshGenerator.FillTriangles(skeleton, submeshInstruction.triangleCount, submeshInstruction.firstVertexIndex, start, end, ref currentBuffer.triangles, isLastSubmesh);
+				}
+			}
+
+			if (structureDoesntMatch) {
+				mesh.Clear();
+				this.sharedMaterials = meshInstructions.GetUpdatedMaterialArray(this.sharedMaterials);
+			}
+
+			// STEP 3: Assign the buffers into the Mesh.
+			smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, meshInstructions);
+			mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
+
+			if (structureDoesntMatch) {
+				// Push new triangles if doesn't match.
+				mesh.subMeshCount = submeshCount;
+				for (int i = 0; i < submeshCount; i++)
+					mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);			
+
+				#if SPINE_OPTIONAL_NORMALS
+				if (generateNormals) {
+					int vertexCount = meshInstructions.vertexCount;
+					Vector3[] normals = new Vector3[vertexCount];
+					Vector3 normal = new Vector3(0, 0, -1);
+					for (int i = 0; i < vertexCount; i++)
+						normals[i] = normal;			
+					mesh.normals = normals;
+
+					if (generateTangents) {
+						Vector4[] tangents = new Vector4[vertexCount];
+						Vector4 tangent = new Vector4(1, 0, 0, -1);
+						for (int i = 0; i < vertexCount; i++)
+							tangents[i] = tangent;
+						mesh.tangents = tangents;
+					}	
+				}
+				#endif
+			}
+				
+			return new MeshAndMaterials(smartMesh.mesh, sharedMaterials);
+		}
+
+		#region Types
+		// A SmartMesh is a Mesh (with submeshes) that knows what attachments and instructions were used to generate it.
+		class SmartMesh {
+			public readonly Mesh mesh = SpineMesh.NewMesh();
+			readonly ExposedList<Attachment> attachmentsUsed = new ExposedList<Attachment>();
+			readonly ExposedList<SubmeshInstruction> instructionsUsed = new ExposedList<SubmeshInstruction>();
+
+			public void Set (Vector3[] verts, Vector2[] uvs, Color32[] colors, SubmeshedMeshInstruction instruction) {
+				mesh.vertices = verts;
+				mesh.uv = uvs;
+				mesh.colors32 = colors;
+
+				attachmentsUsed.Clear(false);
+				attachmentsUsed.GrowIfNeeded(instruction.attachmentList.Capacity);
+				attachmentsUsed.Count = instruction.attachmentList.Count;
+				instruction.attachmentList.CopyTo(attachmentsUsed.Items);
+
+				instructionsUsed.Clear(false);
+				instructionsUsed.GrowIfNeeded(instruction.submeshInstructions.Capacity);
+				instructionsUsed.Count = instruction.submeshInstructions.Count;
+				instruction.submeshInstructions.CopyTo(instructionsUsed.Items);
+			}
+
+			public bool StructureDoesntMatch (SubmeshedMeshInstruction instructions) {
+				// Check count inequality.
+				if (instructions.attachmentList.Count != this.attachmentsUsed.Count) return true;
+				if (instructions.submeshInstructions.Count != this.instructionsUsed.Count) return true;
+
+				// Check each attachment.
+				var attachmentsPassed = instructions.attachmentList.Items;
+				var myAttachments = this.attachmentsUsed.Items;
+				for (int i = 0, n = attachmentsUsed.Count; i < n; i++)
+					if (attachmentsPassed[i] != myAttachments[i]) return true;
+
+				// Check each submesh for equal arrangement.
+				var instructionListItems = instructions.submeshInstructions.Items;
+				var myInstructions = this.instructionsUsed.Items;
+				for (int i = 0, n = this.instructionsUsed.Count; i < n; i++) {
+					var lhs = instructionListItems[i];
+					var rhs = myInstructions[i];
+					if (
+						lhs.material.GetInstanceID() != rhs.material.GetInstanceID() ||
+						lhs.startSlot != rhs.startSlot ||
+						lhs.endSlot != rhs.endSlot ||
+						lhs.triangleCount != rhs.triangleCount ||
+						lhs.vertexCount != rhs.vertexCount ||
+						lhs.firstVertexIndex != rhs.firstVertexIndex
+					) return true;
+				}
+
+				//Debug.Log("structure matched");
+				return false;
+			}
+		}
+		#endregion
+	}
+
+}

+ 0 - 0
spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs.meta → spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshedMeshGenerator.cs.meta


+ 15 - 0
spine-unity/Assets/spine-unity/Mesh Generation/DoubleBuffered.cs

@@ -0,0 +1,15 @@
+using UnityEngine;
+using System.Collections;
+
+namespace Spine.Unity {
+	public class DoubleBuffered<T> where T : new() {
+		readonly T a = new T();
+		readonly T b = new T();
+		bool usingA;
+
+		public T GetNext () {
+			usingA = !usingA;
+			return usingA ? a : b;
+		}
+	}
+}

+ 12 - 0
spine-unity/Assets/spine-unity/Mesh Generation/DoubleBuffered.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 08b76da7751523448a87e528c48a5399
+timeCreated: 1457396939
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1 - 1
spine-unity/Assets/spine-unity/Mesh Generation/Simple/ISimpleMeshGenerator.cs → spine-unity/Assets/spine-unity/Mesh Generation/ISimpleMeshGenerator.cs

@@ -1,4 +1,4 @@
-namespace Spine.Unity { 
+namespace Spine.Unity.MeshGeneration { 
 	// Typically, each ISpineMeshGenerator implementation will handle double-buffering meshes, handling any other optimization behavior
 	// and operating on assumptions (eg, only handling one skeleton, not updating triangles all the time).
 	// The Scale property allows generated mesh to match external systems like Canvas referencePixelsPerUnit

+ 0 - 0
spine-unity/Assets/spine-unity/Mesh Generation/Simple/ISimpleMeshGenerator.cs.meta → spine-unity/Assets/spine-unity/Mesh Generation/ISimpleMeshGenerator.cs.meta


+ 84 - 0
spine-unity/Assets/spine-unity/Mesh Generation/ISubmeshedMeshGenerator.cs

@@ -0,0 +1,84 @@
+using UnityEngine;
+using System.Collections.Generic;
+
+namespace Spine.Unity.MeshGeneration {
+	// ISubmeshedMeshGenerator:
+	// How to use:
+	// Step 1: Have a SubmeshedMeshGenerator instance, and a Spine.Skeleton
+	// Step 2: Call GenerateInstruction. Pass it your Skeleton. Keep the return value (a SubmeshedMeshInstruction, you can use it in other classes too).
+	// Step 3: Pass the SubmeshedMeshInstruction into GenerateMesh. You'll get a Mesh and Materials.
+	// Step 4: Put the Mesh in MeshFilter. Put the Materials in MeshRenderer.sharedMaterials.
+	public interface ISubmeshedMeshGenerator {
+		SubmeshedMeshInstruction GenerateInstruction (Skeleton skeleton);
+		MeshAndMaterials GenerateMesh (SubmeshedMeshInstruction wholeMeshInstruction);
+		List<Slot> Separators { get; }
+	}
+
+	// ISubmeshSetMeshGenerator
+	// How to use:
+	// Step 1: Get a list of SubmeshInstruction. You can get this from SkeletonRenderer or an ISubmeshedMeshGenerator's returned SubmeshedMeshInstruction.
+	// Step 2: Call AddInstruction one by one, or AddInstructions once.
+	// Step 3: Call GenerateMesh. You'll get a Mesh and Materials.
+	// Step 4: Put the Mesh in MeshFilter. Put the Materials in MeshRenderer.sharedMaterials.
+	public interface ISubmeshSetMeshGenerator {
+		MeshAndMaterials GenerateMesh (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh);
+		bool GenerateNormals { get; set; }
+	}
+
+	/// <summary>Primarily a collection of Submesh Instructions. This constitutes instructions for how to construct a mesh containing submeshes.</summary>
+	public class SubmeshedMeshInstruction {
+		public readonly ExposedList<SubmeshInstruction> submeshInstructions = new ExposedList<SubmeshInstruction>();
+		public readonly ExposedList<Attachment> attachmentList = new ExposedList<Attachment>();
+		public int vertexCount = -1;
+
+		/// <summary>Returns a material array of the SubmeshedMeshInstruction. Fills the passed array if it's the correct size. Creates a new array if it's a different size.</summary>
+		public Material[] GetUpdatedMaterialArray (Material[] materials) {
+			return submeshInstructions.GetUpdatedMaterialArray(materials);
+		}
+	}
+
+	/// <summary>Instructions for how to generate a mesh or submesh out of a range of slots in a given skeleton.</summary>
+	public struct SubmeshInstruction {
+		public Skeleton skeleton;
+		public int startSlot;
+		public int endSlot;
+
+		// Cached values because they are determined in the process of generating instructions,
+		// but could otherwise be pulled from accessing attachments, checking materials and counting tris and verts.
+		public Material material;
+		public int triangleCount;
+		public int vertexCount;
+
+		// Vertex index offset. Used by submesh generation if part of a bigger mesh.
+		public int firstVertexIndex;
+		public bool forceSeparate;
+
+		/// <summary>The number of slots in this SubmeshInstruction's range. Not necessarily the number of attachments.</summary>
+		public int SlotCount { get { return endSlot - startSlot; } }
+	}
+
+	public static class SubmeshInstructionExtensions {
+		/// <summary>Returns a material array of the instructions. Fills the passed array if it's the correct size. Creates a new array if it's a different size.</summary>
+		public static Material[] GetUpdatedMaterialArray (this ExposedList<SubmeshInstruction> instructions, Material[] materials) {
+			int submeshCount = instructions.Count;
+
+			if (submeshCount != materials.Length)
+				materials = new Material[submeshCount];
+
+			for (int i = 0, n = materials.Length; i < n; i++)
+				materials[i] = instructions.Items[i].material;
+
+			return materials;
+		}
+	}
+
+	public struct MeshAndMaterials {
+		public readonly Mesh mesh;
+		public readonly Material[] materials;
+
+		public MeshAndMaterials (Mesh mesh, Material[] materials) {
+			this.mesh = mesh;
+			this.materials = materials;
+		}
+	}
+}

+ 2 - 2
spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs.meta → spine-unity/Assets/spine-unity/Mesh Generation/ISubmeshedMeshGenerator.cs.meta

@@ -1,6 +1,6 @@
 fileFormatVersion: 2
-guid: c518552ea622e98418ce673c5febc468
-timeCreated: 1455406760
+guid: 7492a8ff24ea1884f92307ab1121e451
+timeCreated: 1458046464
 licenseType: Free
 MonoImporter:
   serializedVersion: 2

+ 0 - 326
spine-unity/Assets/spine-unity/Mesh Generation/Simple/ArraysSimpleMeshGenerator.cs

@@ -1,326 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License
- * Version 2.3
- * 
- * Copyright (c) 2013-2015, Esoteric Software
- * All rights reserved.
- * 
- * You are granted a perpetual, non-exclusive, non-sublicensable and
- * non-transferable license to use, install, execute and perform the Spine
- * Runtimes Software (the "Software") and derivative works solely for personal
- * or internal use. Without the written permission of Esoteric Software (see
- * Section 2 of the Spine Software License Agreement), you may not (a) modify,
- * translate, adapt or otherwise create derivative works, improvements of the
- * Software or develop new applications using the Software or (b) remove,
- * delete, alter or obscure any trademarks or any copyright, trademark, patent
- * or other intellectual property or proprietary rights notices on or in the
- * Software, including any copy thereof. Redistributions in binary or source
- * form must include this license and terms.
- * 
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-using UnityEngine;
-using System.Collections;
-
-namespace Spine.Unity {
-	public class ArraysSimpleMeshGenerator : ISimpleMeshGenerator {
-		#region Settings
-		protected float scale = 1f;
-		public float Scale {
-			get { return scale; }
-			set { scale = value; }
-		}
-
-		public bool renderMeshes = true;
-		#endregion
-
-		#region Buffers
-		readonly DoubleBufferedMesh doubleBufferedMesh = new DoubleBufferedMesh();
-		private float[] tempVertices = new float[8];
-		private Vector3[] vertices;
-		private Color32[] colors;
-		private Vector2[] uvs;
-		private int[] triangles;
-		#endregion
-
-		private Mesh lastGeneratedMesh;
-		public Mesh LastGeneratedMesh {	get { return lastGeneratedMesh; } }
-
-		public Mesh GenerateMesh (Skeleton skeleton) {
-			int totalVertexCount = 0; // size of vertex arrays
-			int totalTriangleCount = 0; // size of index array
-
-			// Step 1 : Count verts and tris to determine array sizes.
-			//
-			var drawOrderItems = skeleton.drawOrder.Items;
-			int drawOrderCount = skeleton.drawOrder.Count;
-			for (int i = 0; i < drawOrderCount; i++) {
-				Slot slot = drawOrderItems[i];
-				Attachment attachment = slot.attachment;
-				int attachmentVertexCount, attachmentTriangleCount;
-				var regionAttachment = attachment as RegionAttachment;
-				if (regionAttachment != null) {					
-					attachmentVertexCount = 4;
-					attachmentTriangleCount = 6;
-				} else {
-					if (!renderMeshes) continue;
-					var meshAttachment = attachment as MeshAttachment;
-					if (meshAttachment != null) {
-						attachmentVertexCount = meshAttachment.vertices.Length >> 1;
-						attachmentTriangleCount = meshAttachment.triangles.Length;
-					} else {
-						var skinnedMeshAttachment = attachment as WeightedMeshAttachment;
-						if (skinnedMeshAttachment != null) {
-							attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
-							attachmentTriangleCount = skinnedMeshAttachment.triangles.Length;
-						} else
-							continue;
-					}
-				}
-				totalTriangleCount += attachmentTriangleCount;
-				totalVertexCount += attachmentVertexCount;
-			}
-
-
-			// Step 2 : Prepare vertex arrays.
-			//
-			Vector3[] vertices = this.vertices;
-			bool verticesDontFit = vertices == null || totalVertexCount > vertices.Length;
-			if (verticesDontFit) {
-				// Not enough space, increase size.
-				this.vertices = vertices = new Vector3[totalVertexCount];
-				this.colors = new Color32[totalVertexCount];
-				this.uvs = new Vector2[totalVertexCount];
-
-			} else {
-				// Too many vertices, zero the extra.
-				Vector3 zero = Vector3.zero;
-				for (int i = totalVertexCount, n = vertices.Length; i < n; i++)
-					vertices[i] = zero;
-			}
-				
-
-			// Step 3 : Push vertices to arrays
-			//
-			const float z = 0;
-			const float zFauxHalfThickness = 0.01f;	// Somehow needs this thickness for bounds to work properly in some cases (eg, Unity UI clipping)
-			float[] tempVertices = this.tempVertices;
-			Vector2[] uvs = this.uvs;
-			Color32[] colors = this.colors;
-			int vertexIndex = 0;
-			Color32 color;
-			float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
-
-			Vector3 meshBoundsMin;
-			Vector3 meshBoundsMax;
-			if (totalVertexCount == 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;
-				meshBoundsMin.z = -zFauxHalfThickness;
-				meshBoundsMax.z = zFauxHalfThickness;
-
-				int i = 0;
-				do {
-					Slot slot = drawOrderItems[i];
-					Attachment attachment = slot.attachment;
-					var regionAttachment = attachment as RegionAttachment;
-					if (regionAttachment != null) {
-						regionAttachment.ComputeWorldVertices(slot.bone, tempVertices);
-
-						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 * scale; vertices[vertexIndex].y = y1 * scale;	vertices[vertexIndex].z = z;
-						vertices[vertexIndex + 1].x = x4 * scale; vertices[vertexIndex + 1].y = y4 * scale; vertices[vertexIndex + 1].z = z;
-						vertices[vertexIndex + 2].x = x2 * scale; vertices[vertexIndex + 2].y = y2 * scale; vertices[vertexIndex + 2].z = z;
-						vertices[vertexIndex + 3].x = x3 * scale; vertices[vertexIndex + 3].y = y3 * scale; vertices[vertexIndex + 3].z = z;
-
-						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;
-						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;
-					} else {
-						if (!renderMeshes) continue;
-						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);
-
-							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;
-
-							float[] meshUVs = meshAttachment.uvs;
-							for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) {
-								float x = tempVertices[ii], y = tempVertices[ii + 1];
-								vertices[vertexIndex].x = x * scale; vertices[vertexIndex].y = y * scale; 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;
-							}
-						} else {
-							var skinnedMeshAttachment = attachment as WeightedMeshAttachment;
-							if (skinnedMeshAttachment != null) {
-								int meshVertexCount = skinnedMeshAttachment.uvs.Length;
-								if (tempVertices.Length < meshVertexCount)
-									this.tempVertices = tempVertices = new float[meshVertexCount];
-								skinnedMeshAttachment.ComputeWorldVertices(slot, tempVertices);
-
-								color.a = (byte)(a * slot.a * skinnedMeshAttachment.a);
-								color.r = (byte)(r * slot.r * skinnedMeshAttachment.r * color.a);
-								color.g = (byte)(g * slot.g * skinnedMeshAttachment.g * color.a);
-								color.b = (byte)(b * slot.b * skinnedMeshAttachment.b * color.a);
-								if (slot.data.blendMode == BlendMode.additive) color.a = 0;
-
-								float[] meshUVs = skinnedMeshAttachment.uvs;
-								for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) {
-									float x = tempVertices[ii], y = tempVertices[ii + 1];
-									vertices[vertexIndex].x = x * scale; vertices[vertexIndex].y = y * scale; 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 : Ensure correct triangle array size
-			// 
-			var triangles = this.triangles;
-			bool trianglesDontFit = triangles == null || totalTriangleCount > triangles.Length;
-			if (trianglesDontFit) {
-				// Not enough space, increase size
-				this.triangles = triangles = new int[totalTriangleCount];
-			} else {				
-				// Too many indices, zero the extra.
-				for (int i = totalTriangleCount, n = triangles.Length; i < n; i++)
-					triangles[i] = 0;
-			}
-
-
-			// Step 4 : Push triangles to triangle array.
-			//
-			int triangleArrayIndex = 0; // next triangle index. modified by loop
-			int firstAttachmentVertex = 0;
-			for (int i = 0, n = drawOrderCount; i < n; i++) {			
-				Attachment attachment = drawOrderItems[i].attachment;
-
-				if (attachment is RegionAttachment) {
-					triangles[triangleArrayIndex] = firstAttachmentVertex;
-					triangles[triangleArrayIndex + 1] = firstAttachmentVertex + 2;
-					triangles[triangleArrayIndex + 2] = firstAttachmentVertex + 1;
-					triangles[triangleArrayIndex + 3] = firstAttachmentVertex + 2;
-					triangles[triangleArrayIndex + 4] = firstAttachmentVertex + 3;
-					triangles[triangleArrayIndex + 5] = firstAttachmentVertex + 1;
-
-					triangleArrayIndex += 6;
-					firstAttachmentVertex += 4;
-					continue;
-				} else {
-					if (!renderMeshes) continue;
-					int[] attachmentTriangles;
-					int attachmentVertexCount;
-					var meshAttachment = attachment as MeshAttachment;
-					if (meshAttachment != null) {
-						attachmentVertexCount = meshAttachment.vertices.Length >> 1; //  length/2
-						attachmentTriangles = meshAttachment.triangles;
-					} else {
-						var skinnedMeshAttachment = attachment as WeightedMeshAttachment;
-						if (skinnedMeshAttachment != null) {
-							attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; // length/2
-							attachmentTriangles = skinnedMeshAttachment.triangles;
-						} else
-							continue;
-					}
-
-					for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleArrayIndex++)
-						triangles[triangleArrayIndex] = firstAttachmentVertex + attachmentTriangles[ii];
-
-					firstAttachmentVertex += attachmentVertexCount;
-				}
-			}
-
-
-			// Step 5 : Push Data To Mesh
-			//
-			var mesh = doubleBufferedMesh.GetNextMesh();
-			mesh.vertices = vertices;
-			mesh.colors32 = colors;
-			mesh.uv = uvs;
-
-			Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin) * scale;
-			Vector3 meshCenter = (meshBoundsMin * scale) + meshBoundsExtents * 0.5f;
-			mesh.bounds = new Bounds(meshCenter, meshBoundsExtents);
-
-			mesh.SetTriangles(triangles, 0);
-
-			lastGeneratedMesh = mesh;
-			return mesh;
-		}
-
-	}
-
-}

+ 0 - 500
spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs

@@ -1,500 +0,0 @@
-using UnityEngine;
-using System.Collections;
-using System.Collections.Generic;
-
-using Spine;
-using Spine.Unity;
-
-public class ArraysSubmeshedMeshGenerator : Spine.Unity.ISubmeshedMeshGenerator {
-
-	readonly List<Slot> separators = new List<Slot>();
-	public List<Slot> Separators { get { return this.separators; } }
-
-	public bool generateNormals;
-	public bool generateTangents;
-
-	public float zSpacing = 0f;
-
-	public SubmeshedMeshInstructions GenerateInstructions (Skeleton skeleton) {
-		if (skeleton == null) throw new System.ArgumentNullException("skeleton");
-
-		// Count vertices and submesh triangles.
-		int runningVertexCount = 0;
-
-		int submeshTriangleCount = 0;
-		int submeshFirstVertex = 0;
-		int submeshVertexCount = 0;
-		int submeshStartSlotIndex = 0;
-		Material lastMaterial = null;
-
-		var drawOrder = skeleton.drawOrder;
-		var drawOrderItems = drawOrder.Items;
-		int drawOrderCount = drawOrder.Count;
-		int separatorCount = separators.Count;
-
-		var instructionList = this.currentInstructions.submeshInstructions;
-		instructionList.Clear();
-
-		currentInstructions.attachmentList.Clear();
-
-		for (int i = 0; i < drawOrderCount; i++) {
-			var slot = drawOrderItems[i];
-			var attachment = slot.attachment;
-
-			object rendererObject; // An AtlasRegion in plain Spine-Unity. eventual source of Material object.
-			int attachmentVertexCount, attachmentTriangleCount;
-
-			var regionAttachment = attachment as RegionAttachment;
-			if (regionAttachment != null) {
-				rendererObject = regionAttachment.RendererObject;
-				attachmentVertexCount = 4;
-				attachmentTriangleCount = 6;
-			} else {
-				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;
-				}
-			}
-				
-			var attachmentMaterial = (Material)((AtlasRegion)rendererObject).page.rendererObject;
-
-			// Populate submesh when material changes. (or when forced to separate by a submeshSeparator)
-			if (( runningVertexCount > 0 && lastMaterial.GetInstanceID() != attachmentMaterial.GetInstanceID() ) ||
-			//if (( lastMaterial != null && lastMaterial.GetInstanceID() != attachmentMaterial.GetInstanceID() ) ||
-				( separatorCount > 0 && separators.Contains(slot) )) {
-
-				instructionList.Add(
-					new SubmeshInstructions {
-						skeleton = skeleton,
-						material = lastMaterial,
-						triangleCount = submeshTriangleCount,
-						vertexCount = submeshVertexCount,
-						startSlot = submeshStartSlotIndex,
-						endSlot = i,
-						firstVertexIndex = submeshFirstVertex
-					}
-				);
-
-				// Prepare for next submesh
-				submeshTriangleCount = 0;
-				submeshVertexCount = 0;
-				submeshFirstVertex = runningVertexCount;
-				submeshStartSlotIndex = i;
-			}
-			lastMaterial = attachmentMaterial;
-
-			submeshTriangleCount += attachmentTriangleCount;
-			submeshVertexCount += attachmentVertexCount;
-			runningVertexCount += attachmentVertexCount;
-
-			currentInstructions.attachmentList.Add(attachment);
-		}
-
-		instructionList.Add(
-			new SubmeshInstructions {
-				skeleton = skeleton,
-				material = lastMaterial,
-				triangleCount = submeshTriangleCount,
-				vertexCount = submeshVertexCount,
-				startSlot = submeshStartSlotIndex,
-				endSlot = drawOrderCount,
-				firstVertexIndex = submeshFirstVertex
-			}
-		);
-
-		currentInstructions.vertexCount = runningVertexCount;
-		return currentInstructions;
-	}
-
-	public SubmeshedMesh GenerateMesh (SubmeshedMeshInstructions meshInstructions) {
-		var smartMesh = doubleBufferedSmartMesh.GetNextMesh();
-		var mesh = smartMesh.mesh;
-
-		int submeshCount = meshInstructions.submeshInstructions.Count;
-
-		var instructionList = meshInstructions.submeshInstructions;
-		float zSpacing = this.zSpacing;
-		float[] attVertBuffer = this.attachmentVertexBuffer;
-		Vector2[] uvs = this.meshUVs;
-		Color32[] colors32 = this.meshColors32;
-		Color32 color;
-
-		// Ensure correct buffer sizes.
-		Vector3[] vertices = this.meshVertices;
-
-		bool newVertices = vertices == null || meshInstructions.vertexCount > vertices.Length;
-		int instructionVertexCount = meshInstructions.vertexCount;
-		if (newVertices) {
-			this.meshVertices = vertices = new Vector3[instructionVertexCount];
-			this.meshColors32 = colors32 = new Color32[instructionVertexCount];
-			this.meshUVs = uvs = new Vector2[instructionVertexCount];
-		} else {
-			var zero = Vector3.zero;
-			for (int i = instructionVertexCount, n = this.meshVertices.Length; i < n; i++)
-				vertices[i] = zero;
-		}
-
-		bool newSubmeshBuffers = submeshBuffers.Count < submeshCount;
-		if (newSubmeshBuffers) {
-			submeshBuffers.GrowIfNeeded(submeshCount);
-			for (int i = submeshBuffers.Count; submeshBuffers.Count < submeshCount; i++) {
-				submeshBuffers.Add(new SubmeshTriangleBuffer(instructionList.Items[i].triangleCount));
-				//submeshBuffers.Items[i] = new SubmeshTriangleBuffer(tc);
-				//submeshBuffers.Count = i;
-			}
-		}
-			
-		Vector3 meshBoundsMin;
-		Vector3 meshBoundsMax;
-
-		int attachmentCount = meshInstructions.attachmentList.Count;
-
-		// Initial values for manual Mesh Bounds calculation
-		if (meshInstructions.attachmentList.Count <= 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 * (attachmentCount - 1);
-			} else {
-				meshBoundsMin.z = zSpacing * (attachmentCount - 1);
-				meshBoundsMax.z = 0f;
-			}
-		}
-
-		bool structureDoesntMatch = newVertices || newSubmeshBuffers || smartMesh.StructureDoesntMatch(meshInstructions);
-
-		if (structureDoesntMatch) {
-			mesh.Clear();
-
-			if (submeshCount == sharedMaterials.Length)
-				meshInstructions.FillMaterialArray(this.sharedMaterials);
-			else
-				this.sharedMaterials = meshInstructions.GetNewMaterialArray();
-		}
-
-		int vertexIndex = 0;
-
-		// For each submesh, add vertex data from attachments.
-		for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
-			var currentSubmeshInstruction = instructionList.Items[submeshIndex];
-			var skeleton = currentSubmeshInstruction.skeleton;
-			var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
-			float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
-
-			for (int slotIndex = currentSubmeshInstruction.startSlot, endSlot = currentSubmeshInstruction.endSlot; slotIndex < endSlot; slotIndex++) {
-				var slot = skeletonDrawOrderItems[slotIndex];
-				var attachment = slot.attachment;
-				float z = slotIndex * zSpacing;
-
-				var regionAttachment = attachment as RegionAttachment;
-				if (regionAttachment != null) {
-					regionAttachment.ComputeWorldVertices(slot.bone, attVertBuffer);
-
-					float x1 = attVertBuffer[RegionAttachment.X1], y1 = attVertBuffer[RegionAttachment.Y1];
-					float x2 = attVertBuffer[RegionAttachment.X2], y2 = attVertBuffer[RegionAttachment.Y2];
-					float x3 = attVertBuffer[RegionAttachment.X3], y3 = attVertBuffer[RegionAttachment.Y3];
-					float x4 = attVertBuffer[RegionAttachment.X4], y4 = attVertBuffer[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;
-
-					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;
-					colors32[vertexIndex] = color; colors32[vertexIndex + 1] = color; colors32[vertexIndex + 2] = color; colors32[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;
-				} else {
-					var meshAttachment = attachment as MeshAttachment;
-					if (meshAttachment != null) {
-						int meshVertexCount = meshAttachment.vertices.Length;
-						if (attVertBuffer.Length < meshVertexCount) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount];
-						meshAttachment.ComputeWorldVertices(slot, attVertBuffer);
-
-						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;
-
-						float[] attachmentUVs = meshAttachment.uvs;
-						for (int iii = 0; iii < meshVertexCount; iii += 2) {
-							float x = attVertBuffer[iii], y = attVertBuffer[iii + 1];
-							vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z;
-							colors32[vertexIndex] = color; uvs[vertexIndex].x = attachmentUVs[iii]; uvs[vertexIndex].y = attachmentUVs[iii + 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;
-
-							vertexIndex++;
-						}
-					} else {
-						var weightedMeshAttachment = attachment as WeightedMeshAttachment;
-						if (weightedMeshAttachment != null) {
-							int meshVertexCount = weightedMeshAttachment.uvs.Length;
-							if (attVertBuffer.Length < meshVertexCount) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount];
-							weightedMeshAttachment.ComputeWorldVertices(slot, attVertBuffer);
-
-							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;
-
-							float[] attachmentUVs = weightedMeshAttachment.uvs;
-							for (int iii = 0; iii < meshVertexCount; iii += 2) {
-								float x = attVertBuffer[iii], y = attVertBuffer[iii + 1];
-								vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z;
-								colors32[vertexIndex] = color;
-								uvs[vertexIndex].x = attachmentUVs[iii]; uvs[vertexIndex].y = attachmentUVs[iii + 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;
-
-								vertexIndex++;
-							}
-						}
-					}
-				}
-			}
-
-			// Push triangles in this submesh
-			if (structureDoesntMatch) {
-				smartMesh.mesh.Clear(); // rebuild triangle array.
-
-				var currentSubmesh = submeshBuffers.Items[submeshIndex];
-				bool isLastSubmesh = (submeshIndex == submeshCount - 1);
-
-				int triangleCount = currentSubmesh.triangleCount = currentSubmeshInstruction.triangleCount;
-				int trianglesCapacity = currentSubmesh.triangles.Length;
-
-				int[] triangles = currentSubmesh.triangles;
-				if (isLastSubmesh) {
-					if (trianglesCapacity > triangleCount) {
-						for (int i = triangleCount; i < trianglesCapacity; i++)
-							triangles[i] = 0;
-					}
-				} else if (trianglesCapacity != triangleCount) {
-					triangles = currentSubmesh.triangles = new int[triangleCount];
-					currentSubmesh.triangleCount = 0;					 
-				}
-
-				// Iterate through submesh slots and store the triangles. 
-				int triangleIndex = 0;
-				int afv = currentSubmeshInstruction.firstVertexIndex; // attachment first vertex
-
-				for (int i = currentSubmeshInstruction.startSlot, n = currentSubmeshInstruction.endSlot; i < n; i++) {			
-					var attachment = skeletonDrawOrderItems[i].attachment;
-
-					if (attachment is RegionAttachment) {
-						triangles[triangleIndex] = afv; triangles[triangleIndex + 1] = afv + 2; triangles[triangleIndex + 2] = afv + 1;
-						triangles[triangleIndex + 3] = afv + 2; triangles[triangleIndex + 4] = afv + 3; triangles[triangleIndex + 5] = afv + 1;
-
-						triangleIndex += 6;
-						afv += 4;
-					} else {
-						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;
-						}
-
-						for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
-							triangles[triangleIndex] = afv + attachmentTriangles[ii];
-
-						afv += attachmentVertexCount;
-					}
-				} // Done adding current submesh triangles
-			}
-		}
-
-
-		Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin);
-		Vector3 meshCenter = meshBoundsMin + meshBoundsExtents * 0.5f;
-
-		smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, meshInstructions);
-		mesh.bounds = new Bounds(meshCenter, meshBoundsExtents);
-
-		if (structureDoesntMatch) {
-		
-			if (generateNormals) {
-				int vertexCount = meshInstructions.vertexCount;
-				Vector3[] normals = new Vector3[vertexCount];
-				Vector3 normal = new Vector3(0, 0, -1);
-				for (int i = 0; i < vertexCount; i++)
-					normals[i] = normal;			
-				mesh.normals = normals;
-
-				if (generateTangents) {
-					Vector4[] tangents = new Vector4[vertexCount];
-					Vector4 tangent = new Vector4(1, 0, 0, -1);
-					for (int i = 0; i < vertexCount; i++)
-						tangents[i] = tangent;
-					mesh.tangents = tangents;
-				}	
-			}
-				
-			// push new triangles if doesn't match.
-			mesh.subMeshCount = submeshCount;
-			for (int i = 0; i < submeshCount; i++)
-				mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);			
-		}
-
-
-		return new SubmeshedMesh(smartMesh.mesh, sharedMaterials);
-	}
-
-	#region Internals
-	readonly DoubleBufferedSmartMesh doubleBufferedSmartMesh = new DoubleBufferedSmartMesh();
-	readonly SubmeshedMeshInstructions currentInstructions = new SubmeshedMeshInstructions();
-
-	float[] attachmentVertexBuffer = new float[8];
-	Vector3[] meshVertices;
-	Color32[] meshColors32;
-	Vector2[] meshUVs;
-	Material[] sharedMaterials = new Material[0];
-	readonly ExposedList<SubmeshTriangleBuffer> submeshBuffers = new ExposedList<SubmeshTriangleBuffer>();
-	#endregion
-
-	#region Types
-	class SubmeshTriangleBuffer {
-		public int[] triangles;
-		public int triangleCount;
-
-		public SubmeshTriangleBuffer (int triangleCount) {
-			triangles = new int[triangleCount];
-			this.triangleCount = triangleCount;
-		}
-	}
-
-	class DoubleBufferedSmartMesh {
-		readonly SmartMesh mesh1 = new SmartMesh();
-		readonly SmartMesh mesh2 = new SmartMesh();
-		bool usingMesh1;
-
-		public SmartMesh GetNextMesh () {
-			usingMesh1 = !usingMesh1;
-			return usingMesh1 ? mesh1 : mesh2;
-		}
-	}
-
-	// A SmartMesh is a Mesh (with submeshes) that knows what attachments and instructions were used to generate it.
-	class SmartMesh {
-		public readonly Mesh mesh = SpineMesh.NewMesh();
-		readonly ExposedList<Attachment> attachmentsUsed = new ExposedList<Attachment>();
-		readonly ExposedList<SubmeshInstructions> instructionsUsed = new ExposedList<SubmeshInstructions>();
-
-		public void Set (Vector3[] verts, Vector2[] uvs, Color32[] colors, SubmeshedMeshInstructions instructions) {
-			mesh.vertices = verts;
-			mesh.uv = uvs;
-			mesh.colors32 = colors;
-
-			attachmentsUsed.Clear();
-			attachmentsUsed.GrowIfNeeded(instructions.attachmentList.Capacity);
-			attachmentsUsed.Count = instructions.attachmentList.Count;
-			instructions.attachmentList.CopyTo(attachmentsUsed.Items);
-
-			instructionsUsed.Clear();
-			instructionsUsed.GrowIfNeeded(instructions.submeshInstructions.Capacity);
-			instructionsUsed.Count = instructions.submeshInstructions.Count;
-			instructions.submeshInstructions.CopyTo(instructionsUsed.Items);
-		}
-
-		public bool StructureDoesntMatch (SubmeshedMeshInstructions instructions) {
-			// Check count inequality.
-			if (instructions.attachmentList.Count != this.attachmentsUsed.Count) return true;
-			if (instructions.submeshInstructions.Count != this.instructionsUsed.Count) return true;
-
-			//Debug.Log("broadphase matched");
-
-			// Check each attachment.
-			var attachmentsPassed = instructions.attachmentList.Items;
-			var myAttachments = this.attachmentsUsed.Items;
-			for (int i = 0, n = attachmentsUsed.Count; i < n; i++)
-				if (attachmentsPassed[i] != myAttachments[i]) return true;
-
-			//Debug.Log("attachments matched");
-
-			// Check each submesh for equal arrangement.
-			var instructionListItems = instructions.submeshInstructions.Items;
-			var myInstructions = this.instructionsUsed.Items;
-			for (int i = 0, n = this.instructionsUsed.Count; i < n; i++) {
-				var lhs = instructionListItems[i];
-				var rhs = myInstructions[i];
-				if (
-					lhs.skeleton != rhs.skeleton ||
-					lhs.material.GetInstanceID() != rhs.material.GetInstanceID() ||
-					lhs.startSlot != rhs.startSlot ||
-					lhs.endSlot != rhs.endSlot ||
-					lhs.triangleCount != rhs.triangleCount ||
-					lhs.vertexCount != rhs.vertexCount ||
-					lhs.firstVertexIndex != rhs.firstVertexIndex
-				) return true;
-			}
-
-			//Debug.Log("structure matched");
-			return false;
-		}
-	}
-	#endregion
-}

+ 0 - 69
spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs

@@ -1,69 +0,0 @@
-using UnityEngine;
-using System.Collections.Generic;
-
-namespace Spine.Unity {
-	public interface ISubmeshedMeshGenerator {
-		/// <summary>Generates instructions for how to generate the submeshed mesh based on the given state of the
-		/// skeleton. The returned instructions can be used to generate a whole submeshed mesh or individual submeshes.</summary>
-		SubmeshedMeshInstructions GenerateInstructions (Skeleton skeleton);
-
-		/// <summary>Returns a SubmeshedMesh (a mesh and a material array coupled in a struct). 
-		/// Call GenerateInstructions to get the SubmeshedMeshInstructions to pass into this.</summary>
-		SubmeshedMesh GenerateMesh (SubmeshedMeshInstructions wholeMeshInstruction);
-
-		/// <summary>A list of slots that mark the end of a submesh. The slot after it will be the start of a new submesh.</summary>
-		List<Slot> Separators { get; }
-	}
-
-	public interface ISingleSubmeshGenerator {
-		void FillMesh (SubmeshInstructions instructions, Mesh meshToFill);
-	}
-
-	/// <summary>A Submeshed mesh is a return type so the mesh with
-	/// multiple submeshes can be coupled with a material array to render its submeshes.</summary>
-	public struct SubmeshedMesh {
-		public readonly Mesh mesh;
-		public readonly Material[] materials;
-		public SubmeshedMesh (Mesh mesh, Material[] materials) {
-			this.mesh = mesh;
-			this.materials = materials;
-		}
-	}
-
-	/// <summary>Primarily a collection of Submesh Instructions. This constitutes instructions for how to construct a mesh containing submeshes.</summary>
-	public class SubmeshedMeshInstructions {
-		public readonly ExposedList<SubmeshInstructions> submeshInstructions = new ExposedList<SubmeshInstructions>();
-		public readonly ExposedList<Attachment> attachmentList = new ExposedList<Attachment>();
-		public int vertexCount = -1;
-
-		/// <summary>Allocates a new material array to render this mesh and its constituent submeshes.</summary>
-		public Material[] GetNewMaterialArray () {
-			var materials = new Material[submeshInstructions.Count];
-			FillMaterialArray(materials);
-			return materials;
-		}
-
-		/// <summary>Fills a given array with the materials needed to render this submeshed mesh.</summary>
-		public void FillMaterialArray (Material[] materialArray) {
-			var instructionsItems = submeshInstructions.Items;
-			for (int i = 0, n = materialArray.Length; i < n; i++)
-				materialArray[i] = instructionsItems[i].material;
-		}
-	}
-
-	/// <summary>Instructions for how to generate a mesh or submesh out of a range of slots in a given skeleton.</summary>
-	public struct SubmeshInstructions {
-		public Skeleton skeleton;
-		public int startSlot;
-		public int endSlot;
-
-		// Cached values because they are determined in the process of generating instructions,
-		// but could otherwise be pulled from accessing attachments, checking materials and counting tris and verts.
-		public Material material;
-		public int triangleCount;
-		public int vertexCount;
-
-		// Vertex index offset. Used by submesh generation if part of a bigger mesh.
-		public int firstVertexIndex;
-	}
-}

+ 31 - 31
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;
-	}
+			[SpineAtlasRegion]
+			public string region;
+		}
 
-	public AtlasAsset atlasAsset;
-	public SlotRegionPair[] attachments;
+		public AtlasAsset atlasAsset;
+		public SlotRegionPair[] attachments;
 
-	Atlas atlas;
+		Atlas atlas;
 
-	void Awake () {
-		GetComponent<SkeletonRenderer>().OnRebuild += Apply;
-	}
+		void Awake () {
+			GetComponent<SkeletonRenderer>().OnRebuild += Apply;
+		}
 
 
-	void Apply (SkeletonRenderer skeletonRenderer) {
-		atlas = atlasAsset.GetAtlas();
+		void Apply (SkeletonRenderer skeletonRenderer) {
+			atlas = atlasAsset.GetAtlas();
 
-		AtlasAttachmentLoader loader = new AtlasAttachmentLoader(atlas);
+			AtlasAttachmentLoader loader = new AtlasAttachmentLoader(atlas);
 
-		float scaleMultiplier = skeletonRenderer.skeletonDataAsset.scale;
+			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;
+			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();
+				regionAttachment.SetColor(new Color(1, 1, 1, 1));
+				regionAttachment.UpdateOffset();
 
-			var slot = skeletonRenderer.skeleton.FindSlot(entry.slot);
-			slot.Attachment = regionAttachment;
+				var slot = skeletonRenderer.skeleton.FindSlot(entry.slot);
+				slot.Attachment = regionAttachment;
+			}
 		}
-	}
 
+	}
 }

+ 109 - 117
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 {
 
-	public SkeletonRenderer skeletonRenderer;
+namespace Spine.Unity {
+	[ExecuteInEditMode]
+	public class BoundingBoxFollower : MonoBehaviour {
+		
+		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<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
-	public Dictionary<BoundingBoxAttachment, string> attachmentNameTable = new Dictionary<BoundingBoxAttachment, string>();
+		public Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
+		public Dictionary<BoundingBoxAttachment, string> attachmentNameTable = new Dictionary<BoundingBoxAttachment, string>();
 
-	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<SkeletonRenderer>();
+			if (skeletonRenderer == null)
+				skeletonRenderer = GetComponentInParent<SkeletonRenderer>();
 
-		if (skeletonRenderer != null) {
-			skeletonRenderer.OnRebuild -= HandleReset;
-			skeletonRenderer.OnRebuild += HandleReset;
+			if (skeletonRenderer != null) {
+				skeletonRenderer.OnRebuild -= HandleReset;
+				skeletonRenderer.OnRebuild += HandleReset;
 
-			if (hasReset)
-				HandleReset(skeletonRenderer);
+				if (hasReset)
+					HandleReset(skeletonRenderer);
+			}
 		}
-	}
 
-	void OnDisable () {
-		skeletonRenderer.OnRebuild -= HandleReset;
-	}
+		void OnDisable () {
+			skeletonRenderer.OnRebuild -= HandleReset;
+		}
 
-	void Start () {
-		if (!hasReset && skeletonRenderer != null)
-			HandleReset(skeletonRenderer);
-	}
+		void Start () {
+			if (!hasReset && skeletonRenderer != null)
+				HandleReset(skeletonRenderer);
+		}
 
-	public void HandleReset (SkeletonRenderer renderer) {
-		if (slotName == null || slotName == "")
-			return;
+		public void HandleReset (SkeletonRenderer renderer) {
+			if (slotName == null || slotName == "")
+				return;
 
-		hasReset = true;
+			hasReset = true;
 
-		ClearColliders();
-		colliderTable.Clear();
+			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<string> attachmentNames = new List<string>();
-			skin.FindNamesForSlot(slotIndex, attachmentNames);
+			foreach (var skin in skeleton.Data.Skins) {
+				List<string> attachmentNames = new List<string>();
+				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 (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 (!valid)
+				Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
+		}
 
-	void ClearColliders () {
-		var colliders = GetComponents<PolygonCollider2D>();
-		if (Application.isPlaying) {
-			foreach (var c in colliders) {
-				Destroy(c);
-			}
-		} else {
-			foreach (var c in colliders) {
-				DestroyImmediate(c);
+		void ClearColliders () {
+			var colliders = GetComponents<PolygonCollider2D>();
+			if (Application.isPlaying) {
+				foreach (var c in colliders) {
+					Destroy(c);
+				}
+			} else {
+				foreach (var c in colliders) {
+					DestroyImmediate(c);
+				}
 			}
-		}
 
-		colliderTable.Clear();
-		attachmentNameTable.Clear();
-	}
+			colliderTable.Clear();
+			attachmentNameTable.Clear();
+		}
 
-	void LateUpdate () {
-		if (!skeletonRenderer.valid)
-			return;
+		void LateUpdate () {
+			if (!skeletonRenderer.valid)
+				return;
 
-		if (slot != null) {
-			if (slot.Attachment != currentAttachment)
-				SetCurrent((BoundingBoxAttachment)slot.Attachment);
+			if (slot != null) {
+				if (slot.Attachment != currentAttachment)
+					SetCurrent((BoundingBoxAttachment)slot.Attachment);
+			}
 		}
-	}
 
-	void SetCurrent (BoundingBoxAttachment attachment) {
-		if (currentCollider)
-			currentCollider.enabled = false;
+		void SetCurrent (BoundingBoxAttachment attachment) {
+			if (currentCollider)
+				currentCollider.enabled = false;
 
-		if (attachment != null) {
-			currentCollider = colliderTable[attachment];
-			currentCollider.enabled = true;
-		} else {
-			currentCollider = null;
-		}
+			if (attachment != null) {
+				currentCollider = colliderTable[attachment];
+				currentCollider.enabled = true;
+			} else {
+				currentCollider = null;
+			}
 
-		currentAttachment = attachment;
+			currentAttachment = attachment;
 
-		currentAttachmentName = currentAttachment == null ? null : attachmentNameTable[attachment];
+			currentAttachmentName = currentAttachment == null ? null : attachmentNameTable[attachment];
+		}
 	}
+
 }

+ 43 - 39
spine-unity/Assets/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs

@@ -33,52 +33,56 @@ using UnityEngine;
 using UnityEditor;
 using System.Collections;
 
-[CustomEditor(typeof(BoundingBoxFollower))]
-public class BoundingBoxFollowerInspector : Editor {
-	SerializedProperty skeletonRenderer, slotName;
-	BoundingBoxFollower follower;
-	bool needToReset = false;
+namespace Spine.Unity.Editor {
+	
+	[CustomEditor(typeof(BoundingBoxFollower))]
+	public class BoundingBoxFollowerInspector : UnityEditor.Editor {
+		SerializedProperty skeletonRenderer, slotName;
+		BoundingBoxFollower follower;
+		bool needToReset = false;
 
-	void OnEnable () {
-		skeletonRenderer = serializedObject.FindProperty("skeletonRenderer");
-		slotName = serializedObject.FindProperty("slotName");
-		follower = (BoundingBoxFollower)target;
-	}
-
-	public override void OnInspectorGUI () {
-		if (needToReset) {
-			follower.HandleReset(null);
-			needToReset = false;
+		void OnEnable () {
+			skeletonRenderer = serializedObject.FindProperty("skeletonRenderer");
+			slotName = serializedObject.FindProperty("slotName");
+			follower = (BoundingBoxFollower)target;
 		}
-		EditorGUI.BeginChangeCheck();
-		EditorGUILayout.PropertyField(skeletonRenderer);
-		EditorGUILayout.PropertyField(slotName, new GUIContent("Slot"));
 
-		if (EditorGUI.EndChangeCheck()){
-			serializedObject.ApplyModifiedProperties();
-			needToReset = true;
-		}
+		public override void OnInspectorGUI () {
+			if (needToReset) {
+				follower.HandleReset(null);
+				needToReset = false;
+			}
+			EditorGUI.BeginChangeCheck();
+			EditorGUILayout.PropertyField(skeletonRenderer);
+			EditorGUILayout.PropertyField(slotName, new GUIContent("Slot"));
+
+			if (EditorGUI.EndChangeCheck()){
+				serializedObject.ApplyModifiedProperties();
+				needToReset = true;
+			}
 
-		bool hasBone = follower.GetComponent<BoneFollower>() != null;
+			bool hasBone = follower.GetComponent<BoneFollower>() != null;
 
-		EditorGUI.BeginDisabledGroup(hasBone || follower.Slot == null);
-		{
-			if (GUILayout.Button(new GUIContent("Add Bone Follower", SpineEditorUtilities.Icons.bone))) {
-				var boneFollower = follower.gameObject.AddComponent<BoneFollower>();
-				boneFollower.boneName = follower.Slot.Data.BoneData.Name;
+			EditorGUI.BeginDisabledGroup(hasBone || follower.Slot == null);
+			{
+				if (GUILayout.Button(new GUIContent("Add Bone Follower", SpineEditorUtilities.Icons.bone))) {
+					var boneFollower = follower.gameObject.AddComponent<BoneFollower>();
+					boneFollower.boneName = follower.Slot.Data.BoneData.Name;
+				}
 			}
-		}
-		EditorGUI.EndDisabledGroup();
-		
-		
+			EditorGUI.EndDisabledGroup();
+
+
 
-		//GUILayout.Space(20);
-		GUILayout.Label("Attachment Names", EditorStyles.boldLabel);
-		foreach (var kp in follower.attachmentNameTable) {
-			string name = kp.Value;
-			var collider = follower.colliderTable[kp.Key];
-			bool isPlaceholder = name != kp.Key.Name;
-			collider.enabled = EditorGUILayout.ToggleLeft(new GUIContent(!isPlaceholder ? name : name + " [" + kp.Key.Name + "]", isPlaceholder ? SpineEditorUtilities.Icons.skinPlaceholder : SpineEditorUtilities.Icons.boundingBox), collider.enabled);
+			//GUILayout.Space(20);
+			GUILayout.Label("Attachment Names", EditorStyles.boldLabel);
+			foreach (var kp in follower.attachmentNameTable) {
+				string name = kp.Value;
+				var collider = follower.colliderTable[kp.Key];
+				bool isPlaceholder = name != kp.Key.Name;
+				collider.enabled = EditorGUILayout.ToggleLeft(new GUIContent(!isPlaceholder ? name : name + " [" + kp.Key.Name + "]", isPlaceholder ? SpineEditorUtilities.Icons.skinPlaceholder : SpineEditorUtilities.Icons.boundingBox), collider.enabled);
+			}
 		}
 	}
+
 }

+ 37 - 35
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 {
-		/// <summary>SpineAttachment attachment path to help find the attachment.</summary>
-		/// <remarks>This use of SpineAttachment generates an attachment path string that can only be used by SpineAttachment.GetAttachment.</remarks>
-		[SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")]
-		[UnityEngine.Serialization.FormerlySerializedAs("sourceAttachment")]
-		public string sourceAttachmentPath;
+		[System.Serializable]
+		public class SkinPair {
+			/// <summary>SpineAttachment attachment path to help find the attachment.</summary>
+			/// <remarks>This use of SpineAttachment generates an attachment path string that can only be used by SpineAttachment.GetAttachment.</remarks>
+			[SpineAttachment(currentSkinOnly: false, returnAttachmentPath: true, dataField: "skinSource")]
+			[UnityEngine.Serialization.FormerlySerializedAs("sourceAttachment")]
+			public string sourceAttachmentPath;
 
-		[SpineSlot]
-		public string targetSlot;
+			[SpineSlot]
+			public string targetSlot;
 
-		/// <summary>The name of the skin placeholder/skin dictionary entry this attachment should be associated with.</summary>
-		/// <remarks>This name is used by the skin dictionary, used in the method Skin.AddAttachment as well as setting a slot attachment</remarks>
-		[SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)]
-		public string targetAttachment;
-	}
+			/// <summary>The name of the skin placeholder/skin dictionary entry this attachment should be associated with.</summary>
+			/// <remarks>This name is used by the skin dictionary, used in the method Skin.AddAttachment as well as setting a slot attachment</remarks>
+			[SpineAttachment(currentSkinOnly: true, placeholdersOnly: true)]
+			public string targetAttachment;
+		}
 
-	#region Inspector
-	public SkeletonDataAsset skinSource;
+		#region Inspector
+		public SkeletonDataAsset skinSource;
 
-	[UnityEngine.Serialization.FormerlySerializedAs("skinning")]
-	public SkinPair[] skinItems;
+		[UnityEngine.Serialization.FormerlySerializedAs("skinning")]
+		public SkinPair[] skinItems;
 
-	public Skin customSkin;
-	#endregion
+		public Skin customSkin;
+		#endregion
 
-	SkeletonRenderer skeletonRenderer;
+		SkeletonRenderer skeletonRenderer;
 
-	void Start () {
-		skeletonRenderer = GetComponent<SkeletonRenderer>();
-		Skeleton skeleton = skeletonRenderer.skeleton;
+		void Start () {
+			skeletonRenderer = GetComponent<SkeletonRenderer>();
+			Skeleton skeleton = skeletonRenderer.skeleton;
 
-		customSkin = new Skin("CustomSkin");
+			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);
-		}
+			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);
+			// 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);
+		}
 	}
+
 }

+ 120 - 117
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;
-
-	[Header("Sorting")]
-	public bool sortWithDistanceOnly;
-	public float zOffset = 0f;
-
-	float nextSpawnTime;
-	SkeletonGhostRenderer[] pool;
-	int poolIndex = 0;
-	SkeletonRenderer skeletonRenderer;
-	MeshRenderer meshRenderer;
-	MeshFilter meshFilter;
-
-
-	Dictionary<Material, Material> materialTable = new Dictionary<Material, Material>();
-
-	void Start () {
-		if (ghostShader == null)
-			ghostShader = Shader.Find("Spine/SkeletonGhost");
-
-		skeletonRenderer = GetComponent<SkeletonRenderer>();
-		meshFilter = GetComponent<MeshFilter>();
-		meshRenderer = GetComponent<MeshRenderer>();
-		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<SkeletonGhostRenderer>();
-			go.SetActive(false);
-			go.hideFlags = HideFlags.HideInHierarchy;
-		}
+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;
+
+		float nextSpawnTime;
+		SkeletonGhostRenderer[] pool;
+		int poolIndex = 0;
+		SkeletonRenderer skeletonRenderer;
+		MeshRenderer meshRenderer;
+		MeshFilter meshFilter;
+
+
+		Dictionary<Material, Material> materialTable = new Dictionary<Material, Material>();
+
+		void Start () {
+			if (ghostShader == null)
+				ghostShader = Shader.Find("Spine/SkeletonGhost");
+
+			skeletonRenderer = GetComponent<SkeletonRenderer>();
+			meshFilter = GetComponent<MeshFilter>();
+			meshRenderer = GetComponent<MeshRenderer>();
+			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<SkeletonGhostRenderer>();
+				go.SetActive(false);
+				go.hideFlags = HideFlags.HideInHierarchy;
+			}
 
-		if (skeletonRenderer is SkeletonAnimation)
-			((SkeletonAnimation)skeletonRenderer).state.Event += OnEvent;
+			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;
-
-		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];
+		void Update () {
+			if (!ghostingEnabled)
+				return;
+
+			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];
+					}
+
+					materials[i] = ghostMat;
 				}
 
-				materials[i] = ghostMat;
-			}
-
-			var goTransform = go.transform;
-			goTransform.parent = transform;
+				var goTransform = go.transform;
+				goTransform.parent = transform;
 
-			pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingLayerID, (sortWithDistanceOnly) ? meshRenderer.sortingOrder : meshRenderer.sortingOrder - 1);
+				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.localPosition = new Vector3(0f, 0f, zOffset);
+				goTransform.localRotation = Quaternion.identity;
+				goTransform.localScale = Vector3.one;
 
-			goTransform.parent = null;
+				goTransform.parent = null;
 
-			poolIndex++;
+				poolIndex++;
 
-			if (poolIndex == pool.Length)
-				poolIndex = 0;
+				if (poolIndex == pool.Length)
+					poolIndex = 0;
 
-			nextSpawnTime = Time.time + spawnRate;
+				nextSpawnTime = Time.time + spawnRate;
+			}
 		}
-	}
 
-	void OnDestroy () {
-		for (int i = 0; i < maximumGhosts; i++) {
-			if (pool[i] != null)
-				pool[i].Cleanup();
-		}
+		void OnDestroy () {
+			for (int i = 0; i < maximumGhosts; i++) {
+				if (pool[i] != null)
+					pool[i].Cleanup();
+			}
 
-		foreach (var mat in materialTable.Values)
-			Destroy(mat);
-	}
+			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;
+		//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);
+			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);
+			return new Color32(r, g, b, a);
+		}
 	}
+
 }

+ 0 - 240
spine-unity/Assets/spine-unity/Modules/Mesh Generation Samples/VertexHelperSpineMeshGenerator.cs

@@ -1,240 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License
- * Version 2.3
- * 
- * Copyright (c) 2013-2015, Esoteric Software
- * All rights reserved.
- * 
- * You are granted a perpetual, non-exclusive, non-sublicensable and
- * non-transferable license to use, install, execute and perform the Spine
- * Runtimes Software (the "Software") and derivative works solely for personal
- * or internal use. Without the written permission of Esoteric Software (see
- * Section 2 of the Spine Software License Agreement), you may not (a) modify,
- * translate, adapt or otherwise create derivative works, improvements of the
- * Software or develop new applications using the Software or (b) remove,
- * delete, alter or obscure any trademarks or any copyright, trademark, patent
- * or other intellectual property or proprietary rights notices on or in the
- * Software, including any copy thereof. Redistributions in binary or source
- * form must include this license and terms.
- * 
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#if (UNITY_5_0 || UNITY_5_1 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7)
-#define PREUNITY_5_2
-#endif
-
-using UnityEngine;
-using System.Collections.Generic;
-using System.Collections;
-
-#if !(PREUNITY_5_2)
-namespace Spine.Unity {
-	/// <summary>This is for testing and educational purposes only. This takes about 10 times longer to render than ArraySpineMeshGenerator.</summary>
-	public class VertexHelperSpineMeshGenerator : ISimpleMeshGenerator {
-
-		public float scale = 1f;
-		public float Scale { get { return scale; } set { scale = value; } }
-
-		public bool premultiplyAlpha = true;
-		public bool PremultiplyAlpha { get { return premultiplyAlpha; } set { premultiplyAlpha = value; } }
-
-		public int CurrentVertexCount { get { return this.positions.Count; } }
-
-		private Mesh lastGeneratedMesh;
-		public Mesh LastGeneratedMesh {	get { return lastGeneratedMesh; } }
-
-		public Mesh GenerateMesh (Skeleton skeleton) {
-			skeletonColor.r = skeleton.r;
-			skeletonColor.g = skeleton.g;
-			skeletonColor.b = skeleton.b;
-			skeletonColor.a = skeleton.a;
-
-			ClearBuffers();
-			var drawOrderItems = skeleton.drawOrder.Items;
-			for (int i = 0, n = skeleton.drawOrder.Count; i < n; i++) {
-				AddSlot(drawOrderItems[i]);
-			}
-
-			Mesh currentMesh = doubleBufferedMesh.GetNextMesh();
-			FillMesh(currentMesh);
-			lastGeneratedMesh = currentMesh;
-			return currentMesh;
-		}
-
-		#region Internals
-		private DoubleBufferedMesh doubleBufferedMesh = new DoubleBufferedMesh();
-		protected Color skeletonColor = Color.white;
-		protected List<Vector3> positions = new List<Vector3>();
-		//protected List<Color> colors = new List<Color>(); // 5.3 mesh.SetColors(Color) is broken in UI.
-		protected List<Color32> colors32 = new List<Color32>();
-		protected List<Vector2> uvs = new List<Vector2>();
-		protected List<int> indices = new List<int>();
-		protected List<Vector3> normals = new List<Vector3>();
-
-		// Buffers
-		protected float[] tempVertices = new float[8];
-
-		static readonly Vector3 DefaultNormal = Vector3.back;
-		const float Z = 0f;
-
-		protected void FillMesh (Mesh mesh) {
-			mesh.Clear();
-
-			if (positions.Count > 65000)
-				throw new System.ArgumentException("Mesh cannot have more than 65000 vertices.");	// Limitation based on UnityEngine.UI.VertexHelper
-
-			mesh.SetVertices(positions);
-			//mesh.SetColors(colors); // 5.3 mesh.SetColors(Color) is broken in UI.
-			mesh.SetColors(colors32);
-			mesh.SetUVs(0, uvs);
-			mesh.SetNormals(normals);
-			mesh.SetTriangles(indices, 0);
-			mesh.RecalculateBounds();
-		}
-
-		protected void ClearBuffers () {
-			positions.Clear();
-			//colors.Clear(); // 5.3 mesh.SetColors(Color) is broken.
-			colors32.Clear();
-			uvs.Clear();
-			indices.Clear();
-			normals.Clear();
-		}
-
-		protected void AddVert (Vector3 position, Color color, Vector2 uv) {
-			positions.Add(position);
-			//colors.Add(color); // 5.3 mesh.SetColors(Color) is broken in UI.
-			Color32 c; c.r = (byte)(color.r * 255); c.g = (byte)(color.g * 255); c.b = (byte)(color.b * 255); c.a = (byte)(color.a * 255);
-			colors32.Add(c);
-			uvs.Add(uv);
-			normals.Add(DefaultNormal);
-		}
-
-		protected void AddTriangle (int id0, int id1, int id2) {
-			indices.Add(id0);
-			indices.Add(id1);
-			indices.Add(id2);
-		}
-
-		protected void AddSlot (Slot slot) {
-			var a = slot.attachment;
-
-			var regionAttachment = a as RegionAttachment;
-			if (regionAttachment != null) {
-				AddAttachment(slot, regionAttachment);
-				return;
-			}
-
-			var meshAttachment = a as MeshAttachment;
-			if (meshAttachment != null) {
-				AddAttachment(slot, meshAttachment);
-				return;
-			}
-
-			var skinnedMeshAttachment = a as WeightedMeshAttachment;
-			if (skinnedMeshAttachment != null) {
-				AddAttachment(slot, skinnedMeshAttachment);
-				return;
-			}
-		}
-
-		// RegionAttachment
-		protected void AddAttachment (Slot slot, RegionAttachment attachment) {
-			var tempVertices = this.tempVertices;
-			attachment.ComputeWorldVertices(slot.bone, tempVertices);
-
-			float[] regionUVs = attachment.uvs;
-
-			Color color = skeletonColor;
-			color.r = color.r * attachment.r * slot.r;
-			color.g = color.g * attachment.g * slot.g;
-			color.b = color.b * attachment.b * slot.b;
-			color.a = color.a * attachment.a * slot.a;
-			if (premultiplyAlpha) {
-				color.r *= color.a; color.g *= color.a; color.b *= color.a;
-				if (slot.data.blendMode == BlendMode.additive) color.a = 0;
-			}
-
-			int fv = positions.Count; // first vertex index
-			AddVert(new Vector3(tempVertices[RegionAttachment.X1] * scale, tempVertices[RegionAttachment.Y1] * scale), color, new Vector2(regionUVs[RegionAttachment.X1], regionUVs[RegionAttachment.Y1]));
-			AddVert(new Vector3(tempVertices[RegionAttachment.X4] * scale, tempVertices[RegionAttachment.Y4] * scale), color, new Vector2(regionUVs[RegionAttachment.X4], regionUVs[RegionAttachment.Y4]));
-			AddVert(new Vector3(tempVertices[RegionAttachment.X2] * scale, tempVertices[RegionAttachment.Y2] * scale), color, new Vector2(regionUVs[RegionAttachment.X2], regionUVs[RegionAttachment.Y2]));
-			AddVert(new Vector3(tempVertices[RegionAttachment.X3] * scale, tempVertices[RegionAttachment.Y3] * scale), color, new Vector2(regionUVs[RegionAttachment.X3], regionUVs[RegionAttachment.Y3]));
-
-			AddTriangle(fv, fv+2, fv+1);
-			AddTriangle(fv+2, fv+3, fv+1);
-		}
-
-		// MeshAttachment
-		protected void AddAttachment (Slot slot, MeshAttachment attachment) {
-			var tempVertices = this.tempVertices;
-			var meshUVs = attachment.uvs;
-			int meshVertexCount = attachment.vertices.Length;
-
-			if (tempVertices.Length < meshVertexCount)
-				this.tempVertices = tempVertices = new float[meshVertexCount];
-			attachment.ComputeWorldVertices(slot, tempVertices);
-
-			Color color = skeletonColor;
-			color.r = color.r * attachment.r * slot.r;
-			color.g = color.g * attachment.g * slot.g;
-			color.b = color.b * attachment.b * slot.b;
-			color.a = color.a * attachment.a * slot.a;
-			if (premultiplyAlpha) {
-				color.r *= color.a; color.g *= color.a; color.b *= color.a;
-				if (slot.data.blendMode == BlendMode.additive) color.a = 0;
-			}
-
-			int fv = positions.Count; // first vertex index
-			for (int ii = 0; ii < meshVertexCount; ii += 2)
-				AddVert(new Vector3(tempVertices[ii], tempVertices[ii + 1]) * scale, color, new Vector2(meshUVs[ii], meshUVs[ii + 1]));
-
-			var attachmentTriangles = attachment.triangles;
-			for (int ii = 0, n = attachmentTriangles.Length; ii < n; ii++)
-				indices.Add(attachmentTriangles[ii] + fv);
-		}
-
-		// SkinnedMeshAttachment
-		protected void AddAttachment (Slot slot, WeightedMeshAttachment attachment) {
-			var tempVertices = this.tempVertices;
-			float[] meshUVs = attachment.uvs;
-
-			int meshVertexCount = attachment.uvs.Length;
-			if (tempVertices.Length < meshVertexCount)
-				this.tempVertices = tempVertices = new float[meshVertexCount];
-			attachment.ComputeWorldVertices(slot, tempVertices);
-
-			Color color = skeletonColor;
-			color.r = color.r * attachment.r * slot.r;
-			color.g = color.g * attachment.g * slot.g;
-			color.b = color.b * attachment.b * slot.b;
-			color.a = color.a * attachment.a * slot.a;
-			if (premultiplyAlpha) {
-				color.r *= color.a; color.g *= color.a; color.b *= color.a;
-				if (slot.data.blendMode == BlendMode.additive) color.a = 0;
-			}
-
-			int fv = positions.Count; // first vertex index
-			for (int ii = 0; ii < meshVertexCount; ii += 2)
-				AddVert(new Vector3(tempVertices[ii], tempVertices[ii + 1]) * scale, color, new Vector2(meshUVs[ii], meshUVs[ii + 1]));
-
-			var attachmentTriangles = attachment.triangles;
-			for (int ii = 0, n = attachmentTriangles.Length; ii < n; ii++)
-				indices.Add(attachmentTriangles[ii] + fv);
-		}
-		#endregion
-
-	}
-
-}
-#endif

+ 42 - 41
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;
-
-	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();
-	}
+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");
+		}
+
+		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);
 
-	void Header (string name) {
-		GUILayout.Space(20);
-		EditorGUILayout.LabelField(name, EditorStyles.boldLabel);
+
+			serializedObject.ApplyModifiedProperties();
+		}
+
+		void Header (string name) {
+			GUILayout.Space(20);
+			EditorGUILayout.LabelField(name, EditorStyles.boldLabel);
+		}
 	}
+
 }

+ 40 - 37
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);
+		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();
-	}
+			serializedObject.ApplyModifiedProperties();
+		}
 
-	void Header (string name) {
-		GUILayout.Space(20);
-		EditorGUILayout.LabelField(name, EditorStyles.boldLabel);
+		void Header (string name) {
+			GUILayout.Space(20);
+			EditorGUILayout.LabelField(name, EditorStyles.boldLabel);
+		}
 	}
+
 }

+ 327 - 325
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;
-
-	[Header("Hierarchy")]
-	[SpineBone]
-	public string startingBoneName = "";
-	[SpineBone]
-	public List<string> stopBoneNames = new List<string>();
-
-	[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;
+namespace Spine.Unity.Modules {
+	[RequireComponent(typeof(SkeletonRenderer))]
+	public class SkeletonRagdoll : MonoBehaviour {
+		private static Transform helper;
+
+		[Header("Hierarchy")]
+		[SpineBone]
+		public string startingBoneName = "";
+		[SpineBone]
+		public List<string> stopBoneNames = new List<string>();
+
+		[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 bool IsActive {
-		get {
-			return this.isActive;
+		public Vector3 RootOffset {
+			get {
+				return this.rootOffset;
+			}
 		}
-	}
 
-	private Rigidbody rootRigidbody;
-	private ISkeletonAnimation skeletonAnim;
-	private Skeleton skeleton;
-	private Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
-	private Bone startingBone;
-	private Transform ragdollRoot;
-	private Vector3 rootOffset;
-	private bool isActive;
-
-	IEnumerator Start () {
-		skeletonAnim = (ISkeletonAnimation)GetComponent<SkeletonRenderer>();
-		if (helper == null) {
-			helper = (Transform)(new GameObject("Helper")).transform;
-			helper.hideFlags = HideFlags.HideInHierarchy;
+		public Vector3 EstimatedSkeletonPosition {
+			get {
+				return rootRigidbody.position - rootOffset;
+			}
 		}
 
-		if (applyOnStart) {
-			yield return null;
-			Apply();
+		public bool IsActive {
+			get {
+				return this.isActive;
+			}
 		}
-	}
 
-	public Coroutine SmoothMix (float target, float duration) {
-		return StartCoroutine(SmoothMixCoroutine(target, duration));
-	}
+		private Rigidbody rootRigidbody;
+		private ISkeletonAnimation skeletonAnim;
+		private Skeleton skeleton;
+		private Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
+		private Bone startingBone;
+		private Transform ragdollRoot;
+		private Vector3 rootOffset;
+		private bool isActive;
+
+		IEnumerator Start () {
+			skeletonAnim = (ISkeletonAnimation)GetComponent<SkeletonRenderer>();
+			if (helper == null) {
+				helper = (Transform)(new GameObject("Helper")).transform;
+				helper.hideFlags = HideFlags.HideInHierarchy;
+			}
 
-	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;
+			if (applyOnStart) {
+				yield return null;
+				Apply();
+			}
 		}
-	}
 
-	public void SetSkeletonPosition (Vector3 worldPosition) {
-		if (!isActive) {
-			Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
-			return;
+		public Coroutine SmoothMix (float target, float duration) {
+			return StartCoroutine(SmoothMixCoroutine(target, duration));
 		}
 
-		Vector3 offset = worldPosition - transform.position;
-		transform.position = worldPosition;
-		foreach (Transform t in boneTable.Values) {
-			t.position -= offset;
+		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;
+			}
 		}
 
-		UpdateWorld(null);
-		skeleton.UpdateWorldTransform();
-	}
+		public void SetSkeletonPosition (Vector3 worldPosition) {
+			if (!isActive) {
+				Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
+				return;
+			}
 
-	public Rigidbody[] GetRigidbodyArray () {
-		if (!isActive)
-			return new Rigidbody[0];
+			Vector3 offset = worldPosition - transform.position;
+			transform.position = worldPosition;
+			foreach (Transform t in boneTable.Values) {
+				t.position -= offset;
+			}
 
-		Rigidbody[] arr = new Rigidbody[boneTable.Count];
-		int i = 0;
-		foreach (Transform t in boneTable.Values) {
-			arr[i] = t.GetComponent<Rigidbody>();
-			i++;
+			UpdateWorld(null);
+			skeleton.UpdateWorldTransform();
 		}
 
-		return arr;
-	}
+		public Rigidbody[] GetRigidbodyArray () {
+			if (!isActive)
+				return new Rigidbody[0];
 
-	public Rigidbody GetRigidbody (string boneName) {
-		var bone = skeleton.FindBone(boneName);
-		if (bone == null)
-			return null;
+			Rigidbody[] arr = new Rigidbody[boneTable.Count];
+			int i = 0;
+			foreach (Transform t in boneTable.Values) {
+				arr[i] = t.GetComponent<Rigidbody>();
+				i++;
+			}
 
-		if (boneTable.ContainsKey(bone))
-			return boneTable[bone].GetComponent<Rigidbody>();
+			return arr;
+		}
 
-		return null;
-	}
+		public Rigidbody GetRigidbody (string boneName) {
+			var bone = skeleton.FindBone(boneName);
+			if (bone == null)
+				return null;
 
-	public void Remove () {
-		isActive = false;
-		foreach (var t in boneTable.Values) {
-			Destroy(t.gameObject);
+			if (boneTable.ContainsKey(bone))
+				return boneTable[bone].GetComponent<Rigidbody>();
+
+			return null;
 		}
-		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<Rigidbody>();
-		rootRigidbody.isKinematic = pinStartBone;
+			var ragdollRootBone = skeleton.FindBone(startingBoneName);
+			startingBone = ragdollRootBone;
+			RecursivelyCreateBoneProxies(ragdollRootBone);
 
-		rootRigidbody.mass = rootMass;
+			rootRigidbody = boneTable[ragdollRootBone].GetComponent<Rigidbody>();
+			rootRigidbody.isKinematic = pinStartBone;
 
-		List<Collider> boneColliders = new List<Collider>();
+			rootRigidbody.mass = rootMass;
 
-		foreach (var pair in boneTable) {
-			var b = pair.Key;
-			var t = pair.Value;
-			Bone parentBone = null;
-			Transform parentTransform = transform;
+			List<Collider> boneColliders = new List<Collider>();
 
-			boneColliders.Add(t.GetComponent<Collider>());
+			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<Collider>());
 
-				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<Rigidbody>();
-
-			if (rbParent != null) {
-				var joint = t.gameObject.AddComponent<HingeJoint>();
-				joint.connectedBody = rbParent;
-				Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
-				localPos.x *= 1;
-				joint.connectedAnchor = localPos;
-				joint.axis = Vector3.forward;
-				joint.GetComponent<Rigidbody>().mass = joint.connectedBody.mass * massFalloffFactor;
-				JointLimits limits = new JointLimits();
-				limits.min = -rotationLimit;
-				limits.max = rotationLimit;
-				joint.limits = limits;
-				joint.useLimits = true;
-				joint.enableCollision = enableJointCollision;
+				var rbParent = parentTransform.GetComponent<Rigidbody>();
+
+				if (rbParent != null) {
+					var joint = t.gameObject.AddComponent<HingeJoint>();
+					joint.connectedBody = rbParent;
+					Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
+					localPos.x *= 1;
+					joint.connectedAnchor = localPos;
+					joint.axis = Vector3.forward;
+					joint.GetComponent<Rigidbody>().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]);
+			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<SkeletonUtilityBone>();
-		if (utilityBones.Length > 0) {
-			List<string> destroyedUtilityBoneNames = new List<string>();
-			foreach (var ub in utilityBones) {
-				if (ub.mode == SkeletonUtilityBone.Mode.Override) {
-					destroyedUtilityBoneNames.Add(ub.gameObject.name);
-					Destroy(ub.gameObject);
+			var utilityBones = GetComponentsInChildren<SkeletonUtilityBone>();
+			if (utilityBones.Length > 0) {
+				List<string> destroyedUtilityBoneNames = new List<string>();
+				foreach (var ub in utilityBones) {
+					if (ub.mode == SkeletonUtilityBone.Mode.Override) {
+						destroyedUtilityBoneNames.Add(ub.gameObject.name);
+						Destroy(ub.gameObject);
+					}
 				}
-			}
 
-			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 += ",";
+				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);
 				}
-				Debug.LogWarning(msg);
 			}
-		}
 
-		if (disableIK) {
-			foreach (IkConstraint ik in skeleton.IkConstraints) {
-				ik.Mix = 0;
+			if (disableIK) {
+				foreach (IkConstraint ik in skeleton.IkConstraints) {
+					ik.Mix = 0;
+				}
 			}
+
+			skeletonAnim.UpdateWorld += UpdateWorld;
 		}
 
-		skeletonAnim.UpdateWorld += UpdateWorld;
-	}
+		void RecursivelyCreateBoneProxies (Bone b) {
+			if (stopBoneNames.Contains(b.Data.Name))
+				return;
 
-	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);
 
-		GameObject go = new GameObject(b.Data.Name);
-		go.layer = colliderLayer;
-		Transform t = go.transform;
-		boneTable.Add(b, t);
+			t.parent = transform;
 
-		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);
 
-		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;
 
-		float length = b.Data.Length;
+			var colliders = AttachBoundingBoxRagdollColliders(b);
 
-		var colliders = AttachBoundingBoxRagdollColliders(b);
+			if (length == 0) {
+				//physics
+				if (colliders.Count == 0) {
+					var ball = go.AddComponent<SphereCollider>();
+					ball.radius = thickness / 2f;
+				}
+			} else {
+				//physics
+				if (colliders.Count == 0) {
+					var box = go.AddComponent<BoxCollider>();
+					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);
+				}
+			}
 
-		if (length == 0) {
-			//physics
-			if (colliders.Count == 0) {
-				var ball = go.AddComponent<SphereCollider>();
-				ball.radius = thickness / 2f;
+			var rb = go.AddComponent<Rigidbody>();
+			rb.constraints = RigidbodyConstraints.FreezePositionZ;
+			foreach (Bone child in b.Children) {
+				RecursivelyCreateBoneProxies(child);
 			}
-		} else {
-			//physics
-			if (colliders.Count == 0) {
-				var box = go.AddComponent<BoxCollider>();
-				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<Rigidbody>();
-		rb.constraints = RigidbodyConstraints.FreezePositionZ;
-		foreach (Bone child in b.Children) {
-			RecursivelyCreateBoneProxies(child);
-		}
-	}
+		List<Collider> AttachBoundingBoxRagdollColliders (Bone b) {
+			List<Collider> colliders = new List<Collider>();
 
-	List<Collider> AttachBoundingBoxRagdollColliders (Bone b) {
-		List<Collider> colliders = new List<Collider>();
+			Transform t = boneTable[b];
+			GameObject go = t.gameObject;
+			var skin = skeleton.Skin;
+			if (skin == null)
+				skin = skeleton.Data.DefaultSkin;
 
-		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;
 
-        // MITCH
-        // bool flipX = b.WorldFlipX;
-        // bool flipY = b.WorldFlipY;
-        bool flipX = false;
-        bool flipY = false;
+			List<Attachment> attachments = new List<Attachment>();
+			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;
 
-        List<Attachment> attachments = new List<Attachment>();
-		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<BoxCollider>();
+							var bounds = SkeletonUtility.GetBoundingBoxBounds((BoundingBoxAttachment)a, thickness);
 
-						var collider = go.AddComponent<BoxCollider>();
-						var bounds = SkeletonUtility.GetBoundingBoxBounds((BoundingBoxAttachment)a, thickness);
+							collider.center = bounds.center;
+							collider.size = bounds.size;
 
-						collider.center = bounds.center;
-						collider.size = bounds.size;
+							if (flipX || flipY) {
+								Vector3 center = collider.center;
 
-						if (flipX || flipY) {
-							Vector3 center = collider.center;
+								if (flipX)
+									center.x *= -1;
 
-							if (flipX)
-								center.x *= -1;
+								if (flipY)
+									center.y *= -1;
 
-							if (flipY)
-								center.y *= -1;
+								collider.center = center;
+							}
 
-							collider.center = center;
+							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 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;
 
-	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;
-
-            } 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;
 				//}
-			}
 
-			b.Rotation = Mathf.Lerp(b.Rotation, a, mix);
-            // MITCH
-			// b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix);
-		}
-	}
+				if (parentBone != null) {
+					// MITCH
+					//if ((b.WorldFlipX ^ b.WorldFlipY) != flip) {
+					//	a -= GetCompensatedRotationIK(parentBone) * 2;
+					//}
+				}
 
-	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;
+		}
 	}
+
 }

+ 336 - 341
spine-unity/Assets/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs

@@ -6,432 +6,427 @@
 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
-using Spine;
-
-[RequireComponent(typeof(SkeletonRenderer))]
-public class SkeletonRagdoll2D : MonoBehaviour {
-	private static Transform helper;
-
-	[Header("Hierarchy")]
-	[SpineBone]
-	public string startingBoneName = "";
-	[SpineBone]
-	public List<string> stopBoneNames = new List<string>();
-
-	[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;
+using Spine.Unity;
+
+namespace Spine.Unity.Modules {
+	[RequireComponent(typeof(SkeletonRenderer))]
+	public class SkeletonRagdoll2D : MonoBehaviour {
+		private static Transform helper;
+
+		[Header("Hierarchy")]
+		[SpineBone]
+		public string startingBoneName = "";
+		[SpineBone]
+		public List<string> stopBoneNames = new List<string>();
+
+		[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;
+		public Vector3 RootOffset {
+			get { return this.rootOffset; }
 		}
-	}
 
-	private Rigidbody2D rootRigidbody;
-	private ISkeletonAnimation skeletonAnim;
-	private Skeleton skeleton;
-	private Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
-	private Bone startingBone;
-	private Transform ragdollRoot;
-	private Vector2 rootOffset;
-	private bool isActive;
-
-
-	IEnumerator Start () {
-		skeletonAnim = (ISkeletonAnimation)GetComponent<SkeletonRenderer>();
-		if (helper == null) {
-			helper = (Transform)(new GameObject("Helper")).transform;
-			helper.hideFlags = HideFlags.HideInHierarchy;
+		public Vector3 EstimatedSkeletonPosition {
+			get { return this.rootRigidbody.position - rootOffset; }
 		}
 
-		if (applyOnStart) {
-			yield return null;
-			Apply();
+		public bool IsActive {
+			get { return this.isActive; }
 		}
-	}
 
-	public Coroutine SmoothMix (float target, float duration) {
-		return StartCoroutine(SmoothMixCoroutine(target, duration));
-	}
+		private Rigidbody2D rootRigidbody;
+		private ISkeletonAnimation skeletonAnim;
+		private Skeleton skeleton;
+		private Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
+		private Bone startingBone;
+		private Transform ragdollRoot;
+		private Vector2 rootOffset;
+		private bool isActive;
+
+
+		IEnumerator Start () {
+			skeletonAnim = (ISkeletonAnimation)GetComponent<SkeletonRenderer>();
+			if (helper == null) {
+				helper = (Transform)(new GameObject("Helper")).transform;
+				helper.hideFlags = HideFlags.HideInHierarchy;
+			}
 
-	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;
+			if (applyOnStart) {
+				yield return null;
+				Apply();
+			}
 		}
-	}
 
-	public void SetSkeletonPosition (Vector3 worldPosition) {
-		if (!isActive) {
-			Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
-			return;
+		public Coroutine SmoothMix (float target, float duration) {
+			return StartCoroutine(SmoothMixCoroutine(target, duration));
 		}
 
-		Vector3 offset = worldPosition - transform.position;
-		transform.position = worldPosition;
-		foreach (Transform t in boneTable.Values) {
-			t.position -= offset;
+		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;
+			}
 		}
 
-		UpdateWorld(null);
-		skeleton.UpdateWorldTransform();
-	}
+		public void SetSkeletonPosition (Vector3 worldPosition) {
+			if (!isActive) {
+				Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
+				return;
+			}
 
-	public Rigidbody2D[] GetRigidbodyArray () {
-		if (!isActive)
-			return new Rigidbody2D[0];
+			Vector3 offset = worldPosition - transform.position;
+			transform.position = worldPosition;
+			foreach (Transform t in boneTable.Values) {
+				t.position -= offset;
+			}
 
-		Rigidbody2D[] arr = new Rigidbody2D[boneTable.Count];
-		int i = 0;
-		foreach (Transform t in boneTable.Values) {
-			arr[i] = t.GetComponent<Rigidbody2D>();
-			i++;
+			UpdateWorld(null);
+			skeleton.UpdateWorldTransform();
 		}
 
-		return arr;
-	}
+		public Rigidbody2D[] GetRigidbodyArray () {
+			if (!isActive)
+				return new Rigidbody2D[0];
 
-	public Rigidbody2D GetRigidbody (string boneName) {
-		var bone = skeleton.FindBone(boneName);
-		if (bone == null)
-			return null;
+			Rigidbody2D[] arr = new Rigidbody2D[boneTable.Count];
+			int i = 0;
+			foreach (Transform t in boneTable.Values) {
+				arr[i] = t.GetComponent<Rigidbody2D>();
+				i++;
+			}
 
-		if (boneTable.ContainsKey(bone))
-			return boneTable[bone].GetComponent<Rigidbody2D>();
+			return arr;
+		}
 
-		return null;
-	}
+		public Rigidbody2D GetRigidbody (string boneName) {
+			var bone = skeleton.FindBone(boneName);
+			if (bone == null)
+				return null;
 
-	public void Remove () {
-		isActive = false;
-		foreach (var t in boneTable.Values) {
-			Destroy(t.gameObject);
+			if (boneTable.ContainsKey(bone))
+				return boneTable[bone].GetComponent<Rigidbody2D>();
+
+			return null;
 		}
-		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<Rigidbody2D>();
-		rootRigidbody.isKinematic = pinStartBone;
-		rootRigidbody.mass = rootMass;
+			var ragdollRootBone = skeleton.FindBone(startingBoneName);
+			startingBone = ragdollRootBone;
+			RecursivelyCreateBoneProxies(ragdollRootBone);
 
-		List<Collider2D> boneColliders = new List<Collider2D>();
+			rootRigidbody = boneTable[ragdollRootBone].GetComponent<Rigidbody2D>();
+			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<Collider2D> boneColliders = new List<Collider2D>();
 
-			boneColliders.Add(t.GetComponent<Collider2D>());
+			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<Collider2D>());
 
-				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<Rigidbody2D>();
+
+				if (rbParent != null) {
+					var joint = t.gameObject.AddComponent<HingeJoint2D>();
+					joint.connectedBody = rbParent;
+					Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
+					localPos.x *= 1;
+					joint.connectedAnchor = localPos;
+					joint.GetComponent<Rigidbody2D>().mass = joint.connectedBody.mass * massFalloffFactor;
+					JointAngleLimits2D limits = new JointAngleLimits2D();
+					limits.min = -rotationLimit;
+					limits.max = rotationLimit;
+					joint.limits = limits;
+					joint.useLimits = true;
+				}
 			}
 
-			var rbParent = parentTransform.GetComponent<Rigidbody2D>();
-
-			if (rbParent != null) {
-				var joint = t.gameObject.AddComponent<HingeJoint2D>();
-				joint.connectedBody = rbParent;
-				Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
-				localPos.x *= 1;
-				joint.connectedAnchor = localPos;
-				joint.GetComponent<Rigidbody2D>().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]);
+				}
 			}
-		}
 
-		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<SkeletonUtilityBone>();
-		if (utilityBones.Length > 0) {
-			List<string> destroyedUtilityBoneNames = new List<string>();
-			foreach (var ub in utilityBones) {
-				if (ub.mode == SkeletonUtilityBone.Mode.Override) {
-					destroyedUtilityBoneNames.Add(ub.gameObject.name);
-					Destroy(ub.gameObject);
+			var utilityBones = GetComponentsInChildren<SkeletonUtilityBone>();
+			if (utilityBones.Length > 0) {
+				List<string> destroyedUtilityBoneNames = new List<string>();
+				foreach (var ub in utilityBones) {
+					if (ub.mode == SkeletonUtilityBone.Mode.Override) {
+						destroyedUtilityBoneNames.Add(ub.gameObject.name);
+						Destroy(ub.gameObject);
+					}
 				}
-			}
 
-			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 += ",";
+				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);
 				}
-				Debug.LogWarning(msg);
 			}
-		}
 
-		if (disableIK) {
-			foreach (IkConstraint ik in skeleton.IkConstraints) {
-				ik.Mix = 0;
+			if (disableIK) {
+				foreach (IkConstraint ik in skeleton.IkConstraints) {
+					ik.Mix = 0;
+				}
 			}
-		}
 
-		skeletonAnim.UpdateWorld += UpdateWorld;
-	}
+			skeletonAnim.UpdateWorld += UpdateWorld;
+		}
 
-	void RecursivelyCreateBoneProxies (Bone b) {
-		if (stopBoneNames.Contains(b.Data.Name))
-			return;
+		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);
+			GameObject go = new GameObject(b.Data.Name);
+			go.layer = colliderLayer;
+			Transform t = go.transform;
+			boneTable.Add(b, t);
 
-		t.parent = transform;
+			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);
+			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;
+			float length = b.Data.Length;
 
-		//TODO proper ragdoll branching
-		var colliders = AttachBoundingBoxRagdollColliders(b);
+			//TODO proper ragdoll branching
+			var colliders = AttachBoundingBoxRagdollColliders(b);
 
-		if (length == 0) {
-			//physics
-			if (colliders.Count == 0) {
-				var circle = go.AddComponent<CircleCollider2D>();
-				circle.radius = thickness / 2f;
-			}
-		} else {
-			//physics
-			if (colliders.Count == 0) {
-				var box = go.AddComponent<BoxCollider2D>();
-				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
+			if (length == 0) {
+				//physics
+				if (colliders.Count == 0) {
+					var circle = go.AddComponent<CircleCollider2D>();
+					circle.radius = thickness / 2f;
+				}
+			} else {
+				//physics
+				if (colliders.Count == 0) {
+					var box = go.AddComponent<BoxCollider2D>();
+					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<Rigidbody2D>();
-		rb.gravityScale = gravityScale;
+			var rb = go.AddComponent<Rigidbody2D>();
+			rb.gravityScale = gravityScale;
 
-		foreach (Bone child in b.Children) {
-			RecursivelyCreateBoneProxies(child);
+			foreach (Bone child in b.Children) {
+				RecursivelyCreateBoneProxies(child);
+			}
 		}
-	}
 
-	List<Collider2D> AttachBoundingBoxRagdollColliders (Bone b) {
-		List<Collider2D> colliders = new List<Collider2D>();
-		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<Attachment> attachments = new List<Attachment>();
-		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;
+		List<Collider2D> AttachBoundingBoxRagdollColliders (Bone b) {
+			List<Collider2D> colliders = new List<Collider2D>();
+			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<Attachment> attachments = new List<Attachment>();
+			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;
-
-			if (b != startingBone) {
-				parentBone = b.Parent;
-				parentTransform = boneTable[parentBone];
-                // MITCH
-				// flipX = parentBone.WorldFlipX;
-				// flipY = parentBone.WorldFlipY;
+		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;
 
-			} 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;
 
-			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);
+				//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);
 
-			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 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);
 
-			float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
+				Vector3 right = helper.InverseTransformDirection(t.right);
 
-            // MITCH
-			//if (b.WorldFlipX ^ b.WorldFlipY) {
-			//	a *= -1;
-			//}
+				float a = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
 
-			if (parentBone != null) {
-                // MITCH
-				//if ((b.WorldFlipX ^ b.WorldFlipY) != flip) {
-				//	a -= GetCompensatedRotationIK(parentBone) * 2;
+				// MITCH
+				//if (b.WorldFlipX ^ b.WorldFlipY) {
+				//	a *= -1;
 				//}
-			}
 
-			b.Rotation = Mathf.Lerp(b.Rotation, a, mix);
-            // MITCH
-			// b.RotationIK = Mathf.Lerp(b.rotationIK, a, mix);
-		}
-	}
+				if (parentBone != null) {
+					// MITCH
+					//if ((b.WorldFlipX ^ b.WorldFlipY) != flip) {
+					//	a -= GetCompensatedRotationIK(parentBone) * 2;
+					//}
+				}
 
-	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;
+			}
 
-	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);
+			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);
+			}
+		}
+
 	}
 
 }

+ 188 - 157
spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs

@@ -1,195 +1,226 @@
-#if (UNITY_5_0 || UNITY_5_1 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7)
+/******************************************************************************
+ * 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.
+ *****************************************************************************/
+#if (UNITY_5_0 || UNITY_5_1 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7)
 #define PREUNITY_5_2
 #endif
 
 using UnityEngine;
-using System.Collections;
-
 using UnityEditor;
 using Spine;
 
-[CustomEditor(typeof(SkeletonGraphic))]
-public class SkeletonGraphicInspector : Editor {
-	SerializedProperty material_, color_;
-	SerializedProperty skeletonDataAsset_, initialSkinName_;
-	SerializedProperty startingAnimation_, startingLoop_, timeScale_, freeze_;
-#if !PREUNITY_5_2
-	SerializedProperty raycastTarget_;
-
-	SkeletonGraphic thisSkeletonGraphic;
-
-	void OnEnable () {
-		var so = this.serializedObject;
-		thisSkeletonGraphic = target as SkeletonGraphic;
-
-		// MaskableGraphic
-		material_ = so.FindProperty("m_Material");
-		color_ = so.FindProperty("m_Color");
-		raycastTarget_ = so.FindProperty("m_RaycastTarget");
-
-		// SkeletonRenderer
-		skeletonDataAsset_ = so.FindProperty("skeletonDataAsset");
-		initialSkinName_ = so.FindProperty("initialSkinName");
-
-		// SkeletonAnimation
-		startingAnimation_ = so.FindProperty("startingAnimation");
-		startingLoop_ = so.FindProperty("startingLoop");
-		timeScale_ = so.FindProperty("timeScale");
-		freeze_ = so.FindProperty("freeze");
-	}
+namespace Spine.Unity.Editor {
+	
+	[CustomEditor(typeof(SkeletonGraphic))]
+	public class SkeletonGraphicInspector : UnityEditor.Editor {
+		SerializedProperty material_, color_;
+		SerializedProperty skeletonDataAsset_, initialSkinName_;
+		SerializedProperty startingAnimation_, startingLoop_, timeScale_, freeze_;
+	#if !PREUNITY_5_2
+		SerializedProperty raycastTarget_;
+
+		SkeletonGraphic thisSkeletonGraphic;
+
+		void OnEnable () {
+			var so = this.serializedObject;
+			thisSkeletonGraphic = target as SkeletonGraphic;
+
+			// MaskableGraphic
+			material_ = so.FindProperty("m_Material");
+			color_ = so.FindProperty("m_Color");
+			raycastTarget_ = so.FindProperty("m_RaycastTarget");
+
+			// SkeletonRenderer
+			skeletonDataAsset_ = so.FindProperty("skeletonDataAsset");
+			initialSkinName_ = so.FindProperty("initialSkinName");
+
+			// SkeletonAnimation
+			startingAnimation_ = so.FindProperty("startingAnimation");
+			startingLoop_ = so.FindProperty("startingLoop");
+			timeScale_ = so.FindProperty("timeScale");
+			freeze_ = so.FindProperty("freeze");
+		}
 
-	public override void OnInspectorGUI () {
-		EditorGUI.BeginChangeCheck();
+		public override void OnInspectorGUI () {
+			EditorGUI.BeginChangeCheck();
 
-		EditorGUILayout.PropertyField(skeletonDataAsset_);
-		EditorGUILayout.PropertyField(material_);
-		EditorGUILayout.PropertyField(color_);
+			EditorGUILayout.PropertyField(skeletonDataAsset_);
+			EditorGUILayout.PropertyField(material_);
+			EditorGUILayout.PropertyField(color_);
 
-		if (thisSkeletonGraphic.skeletonDataAsset == null) {
-			EditorGUILayout.HelpBox("You need to assign a SkeletonDataAsset first.", MessageType.Info);
-			serializedObject.ApplyModifiedProperties();
-			serializedObject.Update();
-			return;
-		}
+			if (thisSkeletonGraphic.skeletonDataAsset == null) {
+				EditorGUILayout.HelpBox("You need to assign a SkeletonDataAsset first.", MessageType.Info);
+				serializedObject.ApplyModifiedProperties();
+				serializedObject.Update();
+				return;
+			}
 
-		EditorGUILayout.Space();
-		EditorGUILayout.PropertyField(initialSkinName_);
-		EditorGUILayout.Space();
-		EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel);
-		EditorGUILayout.PropertyField(startingAnimation_);
-		EditorGUILayout.PropertyField(startingLoop_);
-		EditorGUILayout.PropertyField(timeScale_);
-		EditorGUILayout.Space();
-		EditorGUILayout.PropertyField(freeze_);
-		EditorGUILayout.Space();
-		EditorGUILayout.LabelField("UI", EditorStyles.boldLabel);
-		EditorGUILayout.PropertyField(raycastTarget_);
-
-		bool wasChanged = EditorGUI.EndChangeCheck();
-
-		if (wasChanged) {
-			serializedObject.ApplyModifiedProperties();
+			EditorGUILayout.Space();
+			EditorGUILayout.PropertyField(initialSkinName_);
+			EditorGUILayout.Space();
+			EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel);
+			EditorGUILayout.PropertyField(startingAnimation_);
+			EditorGUILayout.PropertyField(startingLoop_);
+			EditorGUILayout.PropertyField(timeScale_);
+			EditorGUILayout.Space();
+			EditorGUILayout.PropertyField(freeze_);
+			EditorGUILayout.Space();
+			EditorGUILayout.LabelField("UI", EditorStyles.boldLabel);
+			EditorGUILayout.PropertyField(raycastTarget_);
+
+			bool wasChanged = EditorGUI.EndChangeCheck();
+
+			if (wasChanged) {
+				serializedObject.ApplyModifiedProperties();
+			}
 		}
-	}
-
-	#region Menus
-	[MenuItem("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")]
-	static void MatchRectTransformWithBounds (MenuCommand command) {
-		var skeletonGraphic = (SkeletonGraphic)command.context;
-		var mesh = skeletonGraphic.SpineMeshGenerator.LastGeneratedMesh;
-
-		var bounds = mesh.bounds;
-		var size = bounds.size;
-		var center = bounds.center;
-		var p = new Vector2(
-			        0.5f - (center.x / size.x),
-			        0.5f - (center.y / size.y)
-		        );
-
-		skeletonGraphic.rectTransform.sizeDelta = size;
-		skeletonGraphic.rectTransform.pivot = p;
-	}
 
-	public static Material DefaultSkeletonGraphicMaterial {
-		get {
-			var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material");
-			if (guids.Length <= 0)
-				return null;
-			var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
-			if (string.IsNullOrEmpty(firstAssetPath))
-				return null;
-			var firstMaterial = AssetDatabase.LoadAssetAtPath<Material>(firstAssetPath);
-			return firstMaterial;
+		#region Menus
+		[MenuItem("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")]
+		static void MatchRectTransformWithBounds (MenuCommand command) {
+			var skeletonGraphic = (SkeletonGraphic)command.context;
+			var mesh = skeletonGraphic.SpineMeshGenerator.LastGeneratedMesh;
+
+			var bounds = mesh.bounds;
+			var size = bounds.size;
+			var center = bounds.center;
+			var p = new Vector2(
+				        0.5f - (center.x / size.x),
+				        0.5f - (center.y / size.y)
+			        );
+
+			skeletonGraphic.rectTransform.sizeDelta = size;
+			skeletonGraphic.rectTransform.pivot = p;
 		}
-	}
 
-	[MenuItem("GameObject/Spine/SkeletonGraphic (UnityUI)", false, 10)]
-	static public void SkeletonGraphicCreateMenuItem () {
-		var parentGameObject = Selection.activeObject as GameObject;
-		var parentTransform = parentGameObject == null ? null : parentGameObject.GetComponent<RectTransform>();
-
-		if (parentTransform == null) {
-			Debug.LogWarning("Your new SkeletonGraphic will not be visible until it is placed under a Canvas");
+		public static Material DefaultSkeletonGraphicMaterial {
+			get {
+				var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material");
+				if (guids.Length <= 0)
+					return null;
+				var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
+				if (string.IsNullOrEmpty(firstAssetPath))
+					return null;
+				var firstMaterial = AssetDatabase.LoadAssetAtPath<Material>(firstAssetPath);
+				return firstMaterial;
+			}
 		}
 
-		var gameObject = NewSkeletonGraphicGameObject("New SkeletonGraphic");
-		gameObject.transform.SetParent(parentTransform, false);
-		EditorUtility.FocusProjectWindow();
-		Selection.activeObject = gameObject;
-		EditorGUIUtility.PingObject(Selection.activeObject);
-	}
+		[MenuItem("GameObject/Spine/SkeletonGraphic (UnityUI)", false, 15)]
+		static public void SkeletonGraphicCreateMenuItem () {
+			var parentGameObject = Selection.activeObject as GameObject;
+			var parentTransform = parentGameObject == null ? null : parentGameObject.GetComponent<RectTransform>();
 
-	[MenuItem("Assets/Spine/Instantiate (UnityUI)", false, 0)]
-	static void InstantiateSkeletonGraphic () {
-		Object[] arr = Selection.objects;
-		foreach (Object o in arr) {
-			string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o));
-			string skinName = EditorPrefs.GetString(guid + "_lastSkin", "");
+			if (parentTransform == null) {
+				Debug.LogWarning("Your new SkeletonGraphic will not be visible until it is placed under a Canvas");
+			}
 
-			InstantiateSkeletonGraphic((SkeletonDataAsset)o, skinName);
-			SceneView.RepaintAll();
+			var gameObject = NewSkeletonGraphicGameObject("New SkeletonGraphic");
+			gameObject.transform.SetParent(parentTransform, false);
+			EditorUtility.FocusProjectWindow();
+			Selection.activeObject = gameObject;
+			EditorGUIUtility.PingObject(Selection.activeObject);
 		}
-	}
 
-	[MenuItem("Assets/Spine/Instantiate (UnityUI)", true, 0)]
-	static bool ValidateInstantiateSkeletonGraphic () {
-		Object[] arr = Selection.objects;
+		[MenuItem("Assets/Spine/Instantiate (UnityUI)", false, 20)]
+		static void InstantiateSkeletonGraphic () {
+			Object[] arr = Selection.objects;
+			foreach (Object o in arr) {
+				string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o));
+				string skinName = EditorPrefs.GetString(guid + "_lastSkin", "");
+
+				InstantiateSkeletonGraphic((SkeletonDataAsset)o, skinName);
+				SceneView.RepaintAll();
+			}
+		}
 
-		if (arr.Length == 0)
-			return false;
+		[MenuItem("Assets/Spine/Instantiate (UnityUI)", true, 20)]
+		static bool ValidateInstantiateSkeletonGraphic () {
+			Object[] arr = Selection.objects;
 
-		foreach (var selected in arr) {
-			if (selected.GetType() != typeof(SkeletonDataAsset))
+			if (arr.Length == 0)
 				return false;
+
+			foreach (var selected in arr) {
+				if (selected.GetType() != typeof(SkeletonDataAsset))
+					return false;
+			}
+
+			return true;
 		}
 
-		return true;
-	}
+		public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, string skinName) {
+			return InstantiateSkeletonGraphic(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName));
+		}
 
-	public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, string skinName) {
-		return InstantiateSkeletonGraphic(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName));
-	}
+		public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
+			string spineGameObjectName = string.Format("SkeletonGraphic ({0})", skeletonDataAsset.name.Replace("_SkeletonData", ""));
+			var go = NewSkeletonGraphicGameObject(spineGameObjectName);
+			var graphic = go.GetComponent<SkeletonGraphic>();
+			graphic.skeletonDataAsset = skeletonDataAsset;
 
-	public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
-		string spineGameObjectName = string.Format("SkeletonGraphic ({0})", skeletonDataAsset.name.Replace("_SkeletonData", ""));
-		var go = NewSkeletonGraphicGameObject(spineGameObjectName);
-		var graphic = go.GetComponent<SkeletonGraphic>();
-		graphic.skeletonDataAsset = skeletonDataAsset;
+			SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
 
-		SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
+			if (data == null) {
+				for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) {
+					string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]);
+					skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset));
+				}
 
-		if (data == null) {
-			for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) {
-				string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]);
-				skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset));
+				data = skeletonDataAsset.GetSkeletonData(true);
 			}
 
-			data = skeletonDataAsset.GetSkeletonData(true);
-		}
-
-		if (skin == null)
-			skin = data.DefaultSkin;
+			if (skin == null)
+				skin = data.DefaultSkin;
 
-		if (skin == null)
-			skin = data.Skins.Items[0];
+			if (skin == null)
+				skin = data.Skins.Items[0];
 
-		graphic.Initialize(false);
-		graphic.Skeleton.SetSkin(skin);
-		graphic.initialSkinName = skin.Name;
-		graphic.Skeleton.UpdateWorldTransform();
-		graphic.UpdateMesh();
+			graphic.Initialize(false);
+			graphic.Skeleton.SetSkin(skin);
+			graphic.initialSkinName = skin.Name;
+			graphic.Skeleton.UpdateWorldTransform();
+			graphic.UpdateMesh();
 
-		return graphic;
-	}
+			return graphic;
+		}
 
-	static GameObject NewSkeletonGraphicGameObject (string gameObjectName) {
-		var go = new GameObject(gameObjectName, typeof(RectTransform), typeof(CanvasRenderer), typeof(SkeletonGraphic));
-		var graphic = go.GetComponent<SkeletonGraphic>();
-		graphic.material = SkeletonGraphicInspector.DefaultSkeletonGraphicMaterial;
-		return go;
-	}
+		static GameObject NewSkeletonGraphicGameObject (string gameObjectName) {
+			var go = new GameObject(gameObjectName, typeof(RectTransform), typeof(CanvasRenderer), typeof(SkeletonGraphic));
+			var graphic = go.GetComponent<SkeletonGraphic>();
+			graphic.material = SkeletonGraphicInspector.DefaultSkeletonGraphicMaterial;
+			return go;
+		}
 
-	#endregion
+		#endregion
 
-#endif
-}
+	#endif
+	}
+}

+ 147 - 147
spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs

@@ -28,194 +28,194 @@
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
-
 #if (UNITY_5_0 || UNITY_5_1 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7)
 #define PREUNITY_5_2
 #endif
 
 using UnityEngine;
-using System.Collections;
 using UnityEngine.UI;
 using Spine;
 
-[ExecuteInEditMode, RequireComponent(typeof(CanvasRenderer), typeof(RectTransform)), DisallowMultipleComponent]
-[AddComponentMenu("Spine/SkeletonGraphic (Unity UI Canvas)")]
-public class SkeletonGraphic : MaskableGraphic {
-
-	#region Inspector
-	public SkeletonDataAsset skeletonDataAsset;
-
-	[SpineSkin(dataField:"skeletonDataAsset")]
-	public string initialSkinName = "default";
-
-	[SpineAnimation(dataField:"skeletonDataAsset")]
-	public string startingAnimation;
-	public bool startingLoop;
-	public float timeScale = 1f;
-	public bool freeze;
-
-	#if UNITY_EDITOR
-	protected override void OnValidate () {
-		// This handles Scene View preview.
-		base.OnValidate ();
-		#if !PREUNITY_5_2
-		if (this.IsValid) {
-			if (skeletonDataAsset == null) {
-				Clear();
-				startingAnimation = "";
-			} else if (skeletonDataAsset.GetSkeletonData(true) != skeleton.data) {
-				Clear();
-				Initialize(true);
-				startingAnimation = "";
-				if (skeletonDataAsset.atlasAssets.Length > 1 || skeletonDataAsset.atlasAssets[0].materials.Length > 1) {
-					Debug.LogError("Unity UI does not support multiple textures per Renderer. Your skeleton will not be rendered correctly. Recommend using SkeletonAnimation instead. This requires the use of a Screen space camera canvas.");
+namespace Spine.Unity {
+	[ExecuteInEditMode, RequireComponent(typeof(CanvasRenderer), typeof(RectTransform)), DisallowMultipleComponent]
+	[AddComponentMenu("Spine/SkeletonGraphic (Unity UI Canvas)")]
+	public class SkeletonGraphic : MaskableGraphic {
+
+		#region Inspector
+		public SkeletonDataAsset skeletonDataAsset;
+
+		[SpineSkin(dataField:"skeletonDataAsset")]
+		public string initialSkinName = "default";
+
+		[SpineAnimation(dataField:"skeletonDataAsset")]
+		public string startingAnimation;
+		public bool startingLoop;
+		public float timeScale = 1f;
+		public bool freeze;
+
+		#if UNITY_EDITOR
+		protected override void OnValidate () {
+			// This handles Scene View preview.
+			base.OnValidate ();
+			#if !PREUNITY_5_2
+			if (this.IsValid) {
+				if (skeletonDataAsset == null) {
+					Clear();
+					startingAnimation = "";
+				} else if (skeletonDataAsset.GetSkeletonData(true) != skeleton.data) {
+					Clear();
+					Initialize(true);
+					startingAnimation = "";
+					if (skeletonDataAsset.atlasAssets.Length > 1 || skeletonDataAsset.atlasAssets[0].materials.Length > 1) {
+						Debug.LogError("Unity UI does not support multiple textures per Renderer. Your skeleton will not be rendered correctly. Recommend using SkeletonAnimation instead. This requires the use of a Screen space camera canvas.");
+					}
+				} else {
+					if (freeze) return;
+					skeleton.SetToSetupPose();
+					if (!string.IsNullOrEmpty(startingAnimation)) {
+						skeleton.PoseWithAnimation(startingAnimation, 0f, false);
+					}
 				}
 			} else {
-				if (freeze) return;
-				skeleton.SetToSetupPose();
-				if (!string.IsNullOrEmpty(startingAnimation)) {
-					skeleton.PoseWithAnimation(startingAnimation, 0f, false);
-				}
+				if (skeletonDataAsset != null)
+					Initialize(true);
 			}
-		} else {
-			if (skeletonDataAsset != null)
-				Initialize(true);
+			#else
+			Debug.LogWarning("SkeletonGraphic requres Unity 5.2 or higher.\nUnityEngine.UI 5.1 and below does not accept meshes and can't be used to render Spine skeletons. You may delete the SkeletonGraphic folder under `Modules` if you want to exclude it from your project." );
+			#endif
+				
 		}
-		#else
-		Debug.LogWarning("SkeletonGraphic requres Unity 5.2 or higher.\nUnityEngine.UI 5.1 and below does not accept meshes and can't be used to render Spine skeletons. You may delete the SkeletonGraphic folder under `Modules` if you want to exclude it from your project." );
-		#endif
-			
-	}
 
-	protected override void Reset () {
-		base.Reset();
-		if (canvas == null) {
-			Debug.LogWarningFormat("SkeletonGraphic requires a Canvas to be visible. Move this GameObject ({0}) in the Hierarchy so it becomes a child of a Canvas.", gameObject.name);
+		protected override void Reset () {
+			base.Reset();
+			if (canvas == null) {
+				Debug.LogWarningFormat("SkeletonGraphic requires a Canvas to be visible. Move this GameObject ({0}) in the Hierarchy so it becomes a child of a Canvas.", gameObject.name);
+			}
+
+			if (material == null || material.shader != Shader.Find("Spine/SkeletonGraphic (Premultiply Alpha)")) {
+				Debug.LogWarning("SkeletonGraphic works best with the SkeletonGraphic material.");
+			}
 		}
+		#endif
+		#endregion
 
-		if (material == null || material.shader != Shader.Find("Spine/SkeletonGraphic (Premultiply Alpha)")) {
-			Debug.LogWarning("SkeletonGraphic works best with the SkeletonGraphic material.");
+		#if !PREUNITY_5_2
+		#region Internals
+		// This is used by the UI system to determine what to put in the MaterialPropertyBlock.
+		public override Texture mainTexture {
+			get { 
+				// Fail loudly when incorrectly set up.
+				return skeletonDataAsset == null ? null : skeletonDataAsset.atlasAssets[0].materials[0].mainTexture;
+			}
 		}
-	}
-	#endif
-	#endregion
-
-	#if !PREUNITY_5_2
-	#region Internals
-	// This is used by the UI system to determine what to put in the MaterialPropertyBlock.
-	public override Texture mainTexture {
-		get { 
-			// Fail loudly when incorrectly set up.
-			return skeletonDataAsset == null ? null : skeletonDataAsset.atlasAssets[0].materials[0].mainTexture;
+
+		protected override void Awake () {
+			base.Awake ();
+			if (!this.IsValid) {
+				Initialize(false);
+				Rebuild(CanvasUpdate.PreRender);
+			}
 		}
-	}
 
-	protected override void Awake () {
-		base.Awake ();
-		if (!this.IsValid) {
-			Initialize(false);
-			Rebuild(CanvasUpdate.PreRender);
+		public override void Rebuild (CanvasUpdate update) {
+			base.Rebuild(update);
+			if (canvasRenderer.cull) return;
+			if (update == CanvasUpdate.PreRender) UpdateMesh();
 		}
-	}
 
-	public override void Rebuild (CanvasUpdate update) {
-		base.Rebuild(update);
-		if (canvasRenderer.cull) return;
-		if (update == CanvasUpdate.PreRender) UpdateMesh();
-	}
+		public virtual void Update () {
+			if (freeze) return;
+			Update(Time.deltaTime);
+		}
 
-	public virtual void Update () {
-		if (freeze) return;
-		Update(Time.deltaTime);
-	}
+		public virtual void Update (float deltaTime) {
+			if (!this.IsValid) return;
 
-	public virtual void Update (float deltaTime) {
-		if (!this.IsValid) 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();
 
-		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);
-	}
-
-	void LateUpdate () {
-		if (freeze) return;
-		//this.SetVerticesDirty(); // Which is better?
-		UpdateMesh();
-	}
-	#endregion
+		void LateUpdate () {
+			if (freeze) return;
+			//this.SetVerticesDirty(); // Which is better?
+			UpdateMesh();
+		}
+		#endregion
 
-	#region API
-	protected Skeleton skeleton;
-	public Skeleton Skeleton { get { return skeleton; } }
-	public SkeletonData SkeletonData { get { return skeleton == null ? null : skeleton.data; } }
-	public bool IsValid { get { return skeleton != null; } }
+		#region API
+		protected Skeleton skeleton;
+		public Skeleton Skeleton { get { return skeleton; } }
+		public SkeletonData SkeletonData { get { return skeleton == null ? null : skeleton.data; } }
+		public bool IsValid { get { return skeleton != null; } }
 
-	protected Spine.AnimationState state;
-	public Spine.AnimationState AnimationState { get { return state; } }
+		protected Spine.AnimationState state;
+		public Spine.AnimationState AnimationState { get { return state; } }
 
-	// This is any object that can give you a mesh when you give it a skeleton to render.
-	protected Spine.Unity.ISimpleMeshGenerator spineMeshGenerator;
-	public Spine.Unity.ISimpleMeshGenerator SpineMeshGenerator { get { return this.spineMeshGenerator; } }
+		// This is any object that can give you a mesh when you give it a skeleton to render.
+		protected Spine.Unity.MeshGeneration.ISimpleMeshGenerator spineMeshGenerator;
+		public Spine.Unity.MeshGeneration.ISimpleMeshGenerator SpineMeshGenerator { get { return this.spineMeshGenerator; } }
 
-	public delegate void UpdateDelegate (SkeletonGraphic skeletonGraphic);
-	public event UpdateDelegate UpdateLocal;
-	public event UpdateDelegate UpdateWorld;
-	public event UpdateDelegate UpdateComplete;
+		public delegate void UpdateDelegate (SkeletonGraphic skeletonGraphic);
+		public event UpdateDelegate UpdateLocal;
+		public event UpdateDelegate UpdateWorld;
+		public event UpdateDelegate UpdateComplete;
 
-	public void Clear () {
-		skeleton = null;
-		canvasRenderer.Clear();
-	}
+		public void Clear () {
+			skeleton = null;
+			canvasRenderer.Clear();
+		}
 
-	public void Initialize (bool overwrite) {
-		if (this.IsValid && !overwrite) return;
+		public void Initialize (bool overwrite) {
+			if (this.IsValid && !overwrite) return;
 
-		// Make sure none of the stuff is null
-		if (this.skeletonDataAsset == null) return;
-		var skeletonData = this.skeletonDataAsset.GetSkeletonData(false);
-		if (skeletonData == null) return;
+			// Make sure none of the stuff is null
+			if (this.skeletonDataAsset == null) return;
+			var skeletonData = this.skeletonDataAsset.GetSkeletonData(false);
+			if (skeletonData == null) return;
 
-		if (skeletonDataAsset.atlasAssets.Length <= 0 || skeletonDataAsset.atlasAssets[0].materials.Length <= 0) return;
+			if (skeletonDataAsset.atlasAssets.Length <= 0 || skeletonDataAsset.atlasAssets[0].materials.Length <= 0) return;
 
-		this.state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
-		if (state == null) {
-			Clear();
-			return;
-		}
+			this.state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
+			if (state == null) {
+				Clear();
+				return;
+			}
 
-		this.skeleton = new Skeleton(skeletonData);
-		this.spineMeshGenerator = new Spine.Unity.ArraysSimpleMeshGenerator(); // You can switch this out with any other implementer of ISpineMeshGenerator
+			this.skeleton = new Skeleton(skeletonData);
+			this.spineMeshGenerator = new Spine.Unity.MeshGeneration.ArraysSimpleMeshGenerator(); // You can switch this out with any other implementer of Spine.Unity.MeshGeneration.ISimpleMeshGenerator
 
-		// Set the initial Skin and Animation
-		if (!string.IsNullOrEmpty(initialSkinName))
-			skeleton.SetSkin(initialSkinName);
+			// Set the initial Skin and Animation
+			if (!string.IsNullOrEmpty(initialSkinName))
+				skeleton.SetSkin(initialSkinName);
 
-		if (!string.IsNullOrEmpty(startingAnimation))
-			state.SetAnimation(0, startingAnimation, startingLoop);
-	}
+			if (!string.IsNullOrEmpty(startingAnimation))
+				state.SetAnimation(0, startingAnimation, startingLoop);
+		}
 
-	public void UpdateMesh () {
-		if (this.IsValid) {
-			skeleton.SetColor(this.color);
-			if (canvas != null)
-				spineMeshGenerator.Scale = canvas.referencePixelsPerUnit; // TODO: move this to a listener to of the canvas?
+		public void UpdateMesh () {
+			if (this.IsValid) {
+				skeleton.SetColor(this.color);
+				if (canvas != null)
+					spineMeshGenerator.Scale = canvas.referencePixelsPerUnit; // TODO: move this to a listener to of the canvas?
 
-			canvasRenderer.SetMesh(spineMeshGenerator.GenerateMesh(skeleton));
-			this.UpdateMaterial();
+				canvasRenderer.SetMesh(spineMeshGenerator.GenerateMesh(skeleton));
+				//this.UpdateMaterial(); // TODO: This allocates memory.
+			}
 		}
+		#endregion
+		#endif
 	}
-	#endregion
-	#endif
-}
+}

+ 2 - 2
spine-unity/Assets/spine-unity/Modules/Mesh Generation Samples.meta → spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator.meta

@@ -1,7 +1,7 @@
 fileFormatVersion: 2
-guid: 1a61c8f6e11823c488856e3c7ce9edc7
+guid: 3a361f5ac799a5149b340f9e20da27d1
 folderAsset: yes
-timeCreated: 1455160403
+timeCreated: 1457405502
 licenseType: Free
 DefaultImporter:
   userData: 

+ 2 - 2
spine-unity/Assets/spine-unity/Mesh Generation/Submeshed.meta → spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor.meta

@@ -1,7 +1,7 @@
 fileFormatVersion: 2
-guid: 93a799664eb27fd4183aed06719b306c
+guid: 211465c9f045fd142abe552a6ffdc799
 folderAsset: yes
-timeCreated: 1455486322
+timeCreated: 1457405813
 licenseType: Free
 DefaultImporter:
   userData: 

+ 49 - 0
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.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(SkeletonPartsRenderer))]
+	public class SkeletonRenderPartInspector : UnityEditor.Editor {
+		SpineInspectorUtility.SerializedSortingProperties sortingProperties;
+
+		void OnEnable () {			
+			sortingProperties = new SpineInspectorUtility.SerializedSortingProperties((target as Component).GetComponent<MeshRenderer>());
+		}
+
+		public override void OnInspectorGUI () {
+			SpineInspectorUtility.SortingPropertyFields(sortingProperties, true);
+		}
+	}
+
+}

+ 12 - 0
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 30e43037bf4433645ad70266f34c1c8b
+timeCreated: 1458051036
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 269 - 0
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs

@@ -0,0 +1,269 @@
+/******************************************************************************
+ * 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.Modules {
+	
+	[CustomEditor(typeof(SkeletonRenderSeparator))]
+	public class SkeletonRenderSeparatorInspector : UnityEditor.Editor {
+		SkeletonRenderSeparator component;
+
+		// Properties
+		SerializedProperty skeletonRenderer_, copyPropertyBlock_, copyMeshRendererFlags_, partsRenderers_;
+		static bool partsRenderersExpanded = false;
+
+		// For separator field.
+		SerializedObject skeletonRendererSerializedObject;
+		SerializedProperty separatorNamesProp;
+		static bool skeletonRendererExpanded = true;
+
+		void OnEnable () {
+			if (component == null)
+				component = target as SkeletonRenderSeparator;
+
+			skeletonRenderer_ = serializedObject.FindProperty("skeletonRenderer");
+			copyPropertyBlock_ = serializedObject.FindProperty("copyPropertyBlock");
+			copyMeshRendererFlags_ = serializedObject.FindProperty("copyMeshRendererFlags");
+
+			var partsRenderers = component.partsRenderers;
+			partsRenderers_ = serializedObject.FindProperty("partsRenderers");
+			partsRenderers_.isExpanded = partsRenderersExpanded ||	// last state
+				partsRenderers.Contains(null) ||	// null items found
+				partsRenderers.Count < 1 ||			// no parts renderers
+				(skeletonRenderer_.objectReferenceValue != null && SkeletonRendererSeparatorCount + 1 > partsRenderers.Count); // not enough parts renderers
+		}
+
+		int SkeletonRendererSeparatorCount {
+			get {
+				if (Application.isPlaying) {
+					return component.SkeletonRenderer.separatorSlots.Count;
+				} else {
+					return separatorNamesProp == null ? 0 : separatorNamesProp.arraySize;
+				}
+			}
+		}
+
+		public override void OnInspectorGUI () {
+			// TODO: Add Undo support
+			var componentRenderers = component.partsRenderers;
+			int totalParts;
+
+			bool componentEnabled = component.enabled;
+			bool checkBox = EditorGUILayout.Toggle("Enable Separator", componentEnabled);
+			if (checkBox != componentEnabled) {
+				component.enabled = checkBox;
+			}
+
+			EditorGUILayout.PropertyField(copyPropertyBlock_);
+			EditorGUILayout.PropertyField(copyMeshRendererFlags_);
+
+			// SkeletonRenderer Box
+			using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
+				// Fancy SkeletonRenderer foldout reference field
+				{
+					EditorGUI.indentLevel++;
+					EditorGUI.BeginChangeCheck();
+					var foldoutSkeletonRendererRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
+					EditorGUI.PropertyField(foldoutSkeletonRendererRect, skeletonRenderer_);
+					if (EditorGUI.EndChangeCheck())
+						serializedObject.ApplyModifiedProperties();
+					if (component.SkeletonRenderer != null) {
+						skeletonRendererExpanded = EditorGUI.Foldout(foldoutSkeletonRendererRect, skeletonRendererExpanded, "");
+					}
+					EditorGUI.indentLevel--;
+				}
+
+				int separatorCount = 0;
+				EditorGUI.BeginChangeCheck();
+				if (component.SkeletonRenderer != null) {
+					// Separators from SkeletonRenderer
+					{
+						bool skeletonRendererMismatch = skeletonRendererSerializedObject != null && skeletonRendererSerializedObject.targetObject != component.SkeletonRenderer;
+						if (separatorNamesProp == null || skeletonRendererMismatch) {
+							if (component.SkeletonRenderer != null) {
+								skeletonRendererSerializedObject = new SerializedObject(component.SkeletonRenderer);
+								separatorNamesProp = skeletonRendererSerializedObject.FindProperty("separatorSlotNames");
+								separatorNamesProp.isExpanded = true;
+							}
+						}
+							
+						if (separatorNamesProp != null) {
+							if (skeletonRendererExpanded) {
+								EditorGUI.indentLevel++;
+								SkeletonRendererInspector.SeparatorsField(separatorNamesProp);
+								EditorGUI.indentLevel--;
+							}
+							separatorCount = this.SkeletonRendererSeparatorCount;
+						}
+					}
+
+					if (SkeletonRendererSeparatorCount == 0) {
+						EditorGUILayout.HelpBox("Separators are empty. Change the size to 1 and choose a slot if you want the render to be separated.", MessageType.Info);
+					}
+				}
+				if (EditorGUI.EndChangeCheck())
+					skeletonRendererSerializedObject.ApplyModifiedProperties();
+
+				totalParts = separatorCount + 1;
+				var counterStyle = skeletonRendererExpanded ? EditorStyles.label : EditorStyles.miniLabel;
+				EditorGUILayout.LabelField(string.Format("{0}: separates into {1}.", SpineInspectorUtility.Pluralize(separatorCount, "separator", "separators"), SpineInspectorUtility.Pluralize(totalParts, "part", "parts") ), counterStyle);
+			}
+
+			// Parts renderers
+			using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
+				EditorGUI.indentLevel++;
+				EditorGUILayout.PropertyField(this.partsRenderers_, true);
+				EditorGUI.indentLevel--;
+
+				// Null items warning
+				bool nullItemsFound = componentRenderers.Contains(null);
+				if (nullItemsFound)
+					EditorGUILayout.HelpBox("Some items in the parts renderers list are null and may cause problems.\n\nYou can right-click on that element and choose 'Delete Array Element' to remove it.", MessageType.Warning);
+
+				// (Button) Match Separators count
+				if (separatorNamesProp != null) {
+					int currentRenderers = 0;
+					foreach (var r in componentRenderers) {
+						if (r != null)
+							currentRenderers++;
+					}
+					int extraRenderersNeeded = totalParts - currentRenderers;
+					if (component.enabled && component.SkeletonRenderer != null && extraRenderersNeeded > 0) {
+						EditorGUILayout.HelpBox(string.Format("Insufficient parts renderers. Some parts will not be rendered."), MessageType.Warning);
+						string addMissingLabel = string.Format("Add the missing renderer{1} ({0}) ", extraRenderersNeeded, SpineInspectorUtility.PluralThenS(extraRenderersNeeded));
+						if (GUILayout.Button(addMissingLabel, GUILayout.Height(40f))) {
+							AddPartsRenderer(extraRenderersNeeded);
+							DetectOrphanedPartsRenderers(component);
+						}
+					}
+				}
+					
+				if (partsRenderers_.isExpanded != partsRenderersExpanded) partsRenderersExpanded = partsRenderers_.isExpanded;
+				if (partsRenderers_.isExpanded) {
+					using (new EditorGUILayout.HorizontalScope()) {
+						// (Button) Destroy Renderers button
+						if (componentRenderers.Count > 0) {
+							if (GUILayout.Button("Clear Parts Renderers")) {
+								// Do you really want to destroy all?
+								if (EditorUtility.DisplayDialog("Destroy Renderers", "Do you really want to destroy all the Parts Renderer GameObjects in the list? (Undo will not work.)", "Destroy", "Cancel")) {						
+									foreach (var r in componentRenderers) {
+										if (r != null)
+											DestroyImmediate(r.gameObject, allowDestroyingAssets: false);
+									}
+									componentRenderers.Clear();
+									// Do you also want to destroy orphans? (You monster.)
+									DetectOrphanedPartsRenderers(component);
+								}
+							}
+						}
+
+						// (Button) Add Part Renderer button
+						if (GUILayout.Button("Add Parts Renderer"))
+							AddPartsRenderer(1);				
+					}
+				}
+			}
+
+			serializedObject.ApplyModifiedProperties();
+		}
+
+		public void AddPartsRenderer (int count) {
+			var componentRenderers = component.partsRenderers;
+			bool emptyFound = componentRenderers.Contains(null);
+			if (emptyFound) {
+				bool userClearEntries = EditorUtility.DisplayDialog("Empty entries found", "Null entries found. Do you want to remove null entries before adding the new renderer? ", "Clear Empty Entries", "Don't Clear");
+				if (userClearEntries) componentRenderers.RemoveAll(x => x == null);
+			}
+
+			for (int i = 0; i < count; i++) {
+				int index = componentRenderers.Count;
+				var smr = SkeletonPartsRenderer.NewPartsRendererGameObject(component.transform, index.ToString());
+				componentRenderers.Add(smr);
+				EditorGUIUtility.PingObject(smr);
+
+				// increment renderer sorting order.
+				if (index == 0) continue;
+				var prev = componentRenderers[index - 1]; if (prev == null) continue;
+
+				var prevMeshRenderer = prev.GetComponent<MeshRenderer>();
+				var currentMeshRenderer = smr.GetComponent<MeshRenderer>();
+				if (prevMeshRenderer == null || currentMeshRenderer == null) continue;
+
+				int prevSortingLayer = prevMeshRenderer.sortingLayerID;
+				int prevSortingOrder = prevMeshRenderer.sortingOrder;
+				currentMeshRenderer.sortingLayerID = prevSortingLayer;
+				currentMeshRenderer.sortingOrder = prevSortingOrder + SkeletonRenderSeparator.DefaultSortingOrderIncrement;
+			}
+
+		}
+
+		/// <summary>Detects orphaned parts renderers and offers to delete them.</summary>
+		public void DetectOrphanedPartsRenderers (SkeletonRenderSeparator component) {
+			var children = component.GetComponentsInChildren<SkeletonPartsRenderer>();
+
+			var orphans = new System.Collections.Generic.List<SkeletonPartsRenderer>();
+			foreach (var r in children) {
+				if (!component.partsRenderers.Contains(r))
+					orphans.Add(r);
+			}
+
+			if (orphans.Count > 0) {
+				if (EditorUtility.DisplayDialog("Destroy Submesh Renderers", "Unassigned renderers were found. Do you want to delete them? (These may belong to another Render Separator in the same hierarchy. If you don't have another Render Separator component in the children of this GameObject, it's likely safe to delete. Warning: This operation cannot be undone.)", "Delete", "Cancel")) {
+					foreach (var o in orphans) {
+						DestroyImmediate(o.gameObject, allowDestroyingAssets: false);
+					}
+				}
+			}
+		}
+
+		#region SkeletonRenderer Context Menu Item
+		[MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator")]
+		static void AddRenderSeparatorComponent (MenuCommand cmd) {
+			var skeletonRenderer = cmd.context as SkeletonRenderer;
+			skeletonRenderer.gameObject.AddComponent<SkeletonRenderSeparator>();
+		}
+
+		// Validate
+		[MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator", true)]
+		static bool ValidateAddRenderSeparatorComponent (MenuCommand cmd) {
+			var skeletonRenderer = cmd.context as SkeletonRenderer;
+			var separator = skeletonRenderer.GetComponent<SkeletonRenderSeparator>();
+			bool separatorNotOnObject = separator == null;
+			return separatorNotOnObject;
+		}
+		#endregion
+
+	}
+}

+ 12 - 0
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: d2a5062cfe5dd4344831cda4723128af
+timeCreated: 1458067064
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 96 - 0
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs

@@ -0,0 +1,96 @@
+/******************************************************************************
+ * 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.Modules {
+	[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
+	public class SkeletonPartsRenderer : MonoBehaviour {
+
+		#region Properties
+		ISubmeshSetMeshGenerator meshGenerator;
+		public ISubmeshSetMeshGenerator MeshGenerator {
+			get {
+				LazyIntialize();
+				return meshGenerator;
+			}
+		}
+
+		MeshRenderer meshRenderer;
+		public MeshRenderer MeshRenderer {
+			get {
+				LazyIntialize();
+				return meshRenderer;
+			}
+		}
+
+		MeshFilter meshFilter;
+		public MeshFilter MeshFilter {
+			get {
+				LazyIntialize();
+				return meshFilter;
+			}
+		}
+		#endregion
+
+		void LazyIntialize () {
+			if (meshGenerator != null) return;
+			meshGenerator = new ArraysSubmeshSetMeshGenerator();
+			meshFilter = GetComponent<MeshFilter>();
+			meshRenderer = GetComponent<MeshRenderer>();
+		}
+
+		public void ClearMesh () {
+			LazyIntialize();
+			meshFilter.sharedMesh = null;
+		}
+
+		public void RenderParts (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
+			LazyIntialize();
+			MeshAndMaterials m = meshGenerator.GenerateMesh(instructions, startSubmesh, endSubmesh);
+			meshFilter.sharedMesh = m.mesh;
+			meshRenderer.sharedMaterials = m.materials;
+		}
+
+		public void SetPropertyBlock (MaterialPropertyBlock block) {
+			LazyIntialize();
+			meshRenderer.SetPropertyBlock(block);
+		}
+
+		public static SkeletonPartsRenderer NewPartsRendererGameObject (Transform parent, string name) {
+			var go = new GameObject(name, typeof(MeshFilter), typeof(MeshRenderer));
+			go.transform.SetParent(parent, false);
+			var returnComponent = go.AddComponent<SkeletonPartsRenderer>();
+
+			return returnComponent;
+		}
+	}
+}

+ 12 - 0
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 1c0b968d1e7333b499e347acb644f1c1
+timeCreated: 1458045480
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 161 - 0
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs

@@ -0,0 +1,161 @@
+/******************************************************************************
+ * 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.Modules {
+	
+	[ExecuteInEditMode]
+	[HelpURL("https://github.com/pharan/spine-unity-docs/blob/master/SkeletonRenderSeparator.md")]
+	public class SkeletonRenderSeparator : MonoBehaviour {
+		public const int DefaultSortingOrderIncrement = 5;
+
+		#region Inspector
+		[SerializeField]
+		protected SkeletonRenderer skeletonRenderer;
+		public SkeletonRenderer SkeletonRenderer {
+			get { return skeletonRenderer; }
+			set {
+				if (skeletonRenderer != null)
+					skeletonRenderer.GenerateMeshOverride -= HandleRender;
+				
+				skeletonRenderer = value;
+				this.enabled = false; // Disable if nulled.
+			}
+		}
+
+		MeshRenderer mainMeshRenderer;
+		public bool copyPropertyBlock = false;
+		[Tooltip("Copies MeshRenderer flags into ")]
+		public bool copyMeshRendererFlags = false;
+		public List<Spine.Unity.Modules.SkeletonPartsRenderer> partsRenderers = new List<SkeletonPartsRenderer>();
+
+		#if UNITY_EDITOR
+		void Reset () {
+			if (skeletonRenderer == null)
+				skeletonRenderer = GetComponent<SkeletonRenderer>();
+		}
+		#endif
+		#endregion
+
+		void OnEnable () {
+			if (skeletonRenderer == null) return;
+			if (block == null) block = new MaterialPropertyBlock();	
+			mainMeshRenderer = skeletonRenderer.GetComponent<MeshRenderer>();
+
+			skeletonRenderer.GenerateMeshOverride -= HandleRender;
+			skeletonRenderer.GenerateMeshOverride += HandleRender;
+
+			if (copyMeshRendererFlags) {
+				bool useLightProbes = mainMeshRenderer.useLightProbes;
+				bool receiveShadows = mainMeshRenderer.receiveShadows;
+
+				for (int i = 0; i < partsRenderers.Count; i++) {
+					var currentRenderer = partsRenderers[i];
+					if (currentRenderer == null) continue; // skip null items.
+
+					var mr = currentRenderer.MeshRenderer;
+					mr.useLightProbes = useLightProbes;
+					mr.receiveShadows = receiveShadows;
+				}
+			}
+
+		}
+
+		void OnDisable () {
+			if (skeletonRenderer == null) return;
+			skeletonRenderer.GenerateMeshOverride -= HandleRender;
+
+			#if UNITY_EDITOR
+			skeletonRenderer.LateUpdate();
+			#endif
+
+			foreach (var s in partsRenderers)
+				s.ClearMesh();		
+		}
+
+		MaterialPropertyBlock block;
+
+		void HandleRender (SkeletonRenderer.SmartMesh.Instruction instruction) {
+			int rendererCount = partsRenderers.Count;
+			if (rendererCount <= 0) return;
+
+			int rendererIndex = 0;
+
+			if (copyPropertyBlock)
+				mainMeshRenderer.GetPropertyBlock(block);
+
+			var submeshInstructions = instruction.submeshInstructions;
+			var submeshInstructionsItems = submeshInstructions.Items;
+			int lastSubmeshInstruction = submeshInstructions.Count - 1;
+
+			var currentRenderer = partsRenderers[rendererIndex];
+			bool skeletonRendererCalculateNormals = skeletonRenderer.calculateNormals;
+				
+			for (int i = 0, start = 0; i <= lastSubmeshInstruction; i++) {
+				if (submeshInstructionsItems[i].forceSeparate) {
+					currentRenderer.RenderParts(instruction.submeshInstructions, start, i + 1);
+					currentRenderer.MeshGenerator.GenerateNormals = skeletonRendererCalculateNormals;
+					if (copyPropertyBlock)
+						currentRenderer.SetPropertyBlock(block);					
+
+					start = i + 1;
+					rendererIndex++;
+					if (rendererIndex < rendererCount) {
+						currentRenderer = partsRenderers[rendererIndex];
+					} else {
+						// Not enough renderers. Skip the rest of the instructions.
+						break;
+					}
+				} else if (i == lastSubmeshInstruction) {
+					currentRenderer.RenderParts(instruction.submeshInstructions, start, i + 1);
+					currentRenderer.MeshGenerator.GenerateNormals = skeletonRendererCalculateNormals;
+					if (copyPropertyBlock)
+						currentRenderer.SetPropertyBlock(block);
+				}
+			}
+
+			// If too many renderers. Clear the rest.
+			rendererIndex++;
+			if (rendererIndex < rendererCount - 1) {
+				for (int i = rendererIndex; i < rendererCount; i++) {
+					currentRenderer = partsRenderers[i];
+					currentRenderer.ClearMesh();
+				}
+			}
+
+		}
+
+
+
+	}
+}

+ 12 - 0
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5c70a5b35f6ff2541aed8e8346b7e4d5
+timeCreated: 1457405791
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 41 - 40
spine-unity/Assets/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs

@@ -32,56 +32,57 @@
 using UnityEngine;
 using System.Collections;
 
-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;
-
-	protected override void OnEnable () {
-		if (!Application.isPlaying)
-			return;
-
-		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]);
+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;
+
+		protected override void OnEnable () {
+			if (!Application.isPlaying)
+				return;
+
+			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]);
+			}
+
+			centerPoint = centerBounds.center;
 		}
 
-		centerPoint = centerBounds.center;
-	}
+		protected override void OnDisable () {
+			if (!Application.isPlaying)
+				return;
 
-	protected override void OnDisable () {
-		if (!Application.isPlaying)
-			return;
+			base.OnDisable();
+		}
 
-		base.OnDisable();
-	}
+		public override void DoUpdate () {
 
-	public override void DoUpdate () {
+			if (target != null)
+				targetPosition = target.position;
 
-		if (target != null)
-			targetPosition = target.position;
+			Vector3 goal = targetPosition;
 
-		Vector3 goal = targetPosition;
+			Vector3 center = transform.TransformPoint(centerPoint);
+			Vector3 dir = goal - center;
 
-		Vector3 center = transform.TransformPoint(centerPoint);
-		Vector3 dir = goal - center;
+			if (dir.magnitude > 1)
+				dir.Normalize();
 
-		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);
+			}
 
-		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);
 		}
-
 	}
 }

+ 104 - 101
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 {
-
-#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;
-
-	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);
+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
+
+
+		Vector3 rayOrigin;
+		Vector3 rayDir = new Vector3(0, -1, 0);
+		float hitY;
+		float lastHitY;
+
+		protected override void OnEnable () {
+			base.OnEnable();
+			lastHitY = transform.position.y;
+		}
 
-		hitY = float.MinValue;
-		if (use2D) {
-			RaycastHit2D hit;
+		protected override void OnDisable () {
+			base.OnDisable();
+		}
 
-			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);
-			}
+		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);
+				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 {
-				if (Application.isPlaying)
-					hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime);
-			}
-		} else {
-			RaycastHit hit;
-			bool validHit = false;
+				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 (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);
+				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);
 				}
-			} 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;
+			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;
+			utilBone.bone.X = transform.localPosition.x;
+			utilBone.bone.Y = transform.localPosition.y;
 
-		lastHitY = hitY;
-	}
+			lastHitY = hitY;
+		}
 
-	void OnDrawGizmos () {
-		Vector3 hitEnd = rayOrigin + (rayDir * Mathf.Min(castDistance, rayOrigin.y - hitY));
-		Vector3 clearEnd = rayOrigin + (rayDir * castDistance);
-		Gizmos.DrawLine(rayOrigin, hitEnd);
+		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));
-		}
+			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);
+			Gizmos.color = Color.red;
+			Gizmos.DrawLine(hitEnd, clearEnd);
+		}
 	}
+
 }

+ 58 - 57
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<Transform, Transform> shadowTable;
-	GameObject shadowRoot;
+namespace Spine.Unity.Modules {
+	public class SkeletonUtilityKinematicShadow : MonoBehaviour {
+		public bool hideShadow = true;
+		public Transform parent;
+		Dictionary<Transform, Transform> 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<Transform, Transform>();
+			shadowTable = new Dictionary<Transform, Transform>();
 
-		Destroy(shadowRoot.GetComponent<SkeletonUtilityKinematicShadow>());
+			Destroy(shadowRoot.GetComponent<SkeletonUtilityKinematicShadow>());
 
-		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<Joint>();
-		foreach (Joint j in shadowJoints) {
-			j.connectedAnchor *= scale;
-		}
-
-		var joints = GetComponentsInChildren<Joint>();
-		foreach (var j in joints)
-			Destroy(j);
+			var shadowJoints = shadowRoot.GetComponentsInChildren<Joint>();
+			foreach (Joint j in shadowJoints) {
+				j.connectedAnchor *= scale;
+			}
 
-		var rbs = GetComponentsInChildren<Rigidbody>();
-		foreach (var rb in rbs)
-			Destroy(rb);
+			var joints = GetComponentsInChildren<Joint>();
+			foreach (var j in joints)
+				Destroy(j);
 
-		var colliders = GetComponentsInChildren<Collider>();
-		foreach (var c in colliders)
-			Destroy(c);
+			var rbs = GetComponentsInChildren<Rigidbody>();
+			foreach (var rb in rbs)
+				Destroy(rb);
 
+			var colliders = GetComponentsInChildren<Collider>();
+			foreach (var c in colliders)
+				Destroy(c);
 
-		//match by bone name
-		var shadowBones = shadowRoot.GetComponentsInChildren<SkeletonUtilityBone>();
-		var bones = GetComponentsInChildren<SkeletonUtilityBone>();
 
-		//build bone lookup
-		foreach (var b in bones) {
-			if (b.gameObject == gameObject)
-				continue;
+			//match by bone name
+			var shadowBones = shadowRoot.GetComponentsInChildren<SkeletonUtilityBone>();
+			var bones = GetComponentsInChildren<SkeletonUtilityBone>();
 
-			foreach (var sb in shadowBones) {
-				if (sb.GetComponent<Rigidbody>() == 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<Rigidbody>() == 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<Rigidbody>().MovePosition(transform.position);
-		shadowRoot.GetComponent<Rigidbody>().MoveRotation(transform.rotation);
+		void FixedUpdate () {
+			shadowRoot.GetComponent<Rigidbody>().MovePosition(transform.position);
+			shadowRoot.GetComponent<Rigidbody>().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;
+			}
 		}
 	}
 }

+ 159 - 131
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();
-	}
+		void Start () {
+			if (attachOnStart)
+				Attach();
+		}
 
-	public void Attach () {
-		var skeletonRenderer = GetComponent<SkeletonRenderer>();
+		public void Attach () {
+			var skeletonRenderer = GetComponent<SkeletonRenderer>();
 
-		if (loader == null)
-			//create loader instance, tell it what sprite and shader to use
-			loader = new SpriteAttachmentLoader(sprite, Shader.Find("Spine/Skeleton"));
+			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, "");
+			if (attachment == null)
+				attachment = loader.NewRegionAttachment(null, sprite.name, "");
 
-		skeletonRenderer.skeleton.FindSlot(slot).Attachment = attachment;
+			skeletonRenderer.skeleton.FindSlot(slot).Attachment = attachment;
 
-		if (!keepLoaderInMemory)
-			loader = null;
+			if (!keepLoaderInMemory)
+				loader = null;
+		}
 	}
-}
 
-public class SpriteAttachmentLoader : AttachmentLoader {
+	public class SpriteAttachmentLoader : AttachmentLoader {
 
-	//TODO:  Memory cleanup functions
+		//TODO:  Memory cleanup functions
 
-	//IMPORTANT:  Make sure you clear this when you don't need it anymore. Goodluck.
-	public static Dictionary<int, AtlasRegion> atlasTable = new Dictionary<int, AtlasRegion>();
+		//IMPORTANT:  Make sure you clear this when you don't need it anymore. Goodluck.
+		public static Dictionary<int, AtlasRegion> atlasTable = new Dictionary<int, AtlasRegion>();
 
-	//Shouldn't need to clear this, should just prevent redoing premultiply alpha pass on packed atlases
-	public static List<int> premultipliedAtlasIds = new List<int>();
+		//Shouldn't need to clear this, should just prevent redoing premultiply alpha pass on packed atlases
+		public static List<int> premultipliedAtlasIds = new List<int>();
 
-	Sprite sprite;
-	Shader shader;
+		Sprite sprite;
+		Shader shader;
 
-	public SpriteAttachmentLoader (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;
-		}
+			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;
+			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;
+
+			//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);
 
-		Texture2D tex = sprite.texture;
-		int instanceId = tex.GetInstanceID();
-		AtlasRegion atlasRegion;
+			Bounds bounds = sprite.bounds;
+			Vector3 size = bounds.size;
 
-		//check cache first
-		if (atlasTable.ContainsKey(instanceId)) {
-			atlasRegion = atlasTable[instanceId];
-		} else {
-			//Setup new material
-			Material mat = new Material(shader);
+			//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;
-
-			//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;
+				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;
 		}
 
-		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)
-			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 MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
+			//TODO:  Unity 5 only
+			throw new System.NotImplementedException();
+		}
 
-	public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
-		//TODO:  Unity 5 only
-		throw new System.NotImplementedException();
-	}
+		public WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, string name, string path) {
+			throw new System.NotImplementedException();
+		}
 
-	public WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, string name, string path) {
-		throw new System.NotImplementedException();
+		public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
+			throw new System.NotImplementedException();
+		}
 	}
 
-	public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
-		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 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;
+		}
 	}
 }
+

+ 119 - 118
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;
-	}
+		public SpriteCollectionAttachmentLoader (tk2dSpriteCollectionData sprites) {
+			if (sprites == null)
+				throw new ArgumentNullException("sprites cannot be null.");
+			this.sprites = sprites;
+		}
+
+		private void ProcessSpriteDefinition (String name) {
+			// Strip folder names.
+			int index = name.LastIndexOfAny(new char[] {'/', '\\'});
+			if (index != -1)
+				name = name.Substring(index + 1);
+
+			tk2dSpriteDefinition def = sprites.inst.GetSpriteDefinition(name);
+
+			if (def == null) {
+				Debug.Log("Sprite not found in atlas: " + name, sprites);
+				throw new Exception("Sprite not found in atlas: " + name);
+			}
+			if (def.complexGeometry)
+				throw new NotImplementedException("Complex geometry is not supported: " + name);
+			if (def.flipped == tk2dSpriteDefinition.FlipMode.TPackerCW)
+				throw new NotImplementedException("Only 2D Toolkit atlases are supported: " + name);
+
+			Vector2 minTexCoords = Vector2.one, maxTexCoords = Vector2.zero;
+			for (int i = 0; i < def.uvs.Length; ++i) {
+				Vector2 uv = def.uvs[i];
+				minTexCoords = Vector2.Min(minTexCoords, uv);
+				maxTexCoords = Vector2.Max(maxTexCoords, uv);
+			}
+			regionRotated = def.flipped == tk2dSpriteDefinition.FlipMode.Tk2d;
+			if (regionRotated) {
+				float temp = minTexCoords.x;
+				minTexCoords.x = maxTexCoords.x;
+				maxTexCoords.x = temp;
+			}
+			u = minTexCoords.x;
+			v = maxTexCoords.y;
+			u2 = maxTexCoords.x;
+			v2 = minTexCoords.y;
 
-	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);
+			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;
 		}
-		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);
+
+		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;
 		}
-		regionRotated = def.flipped == tk2dSpriteDefinition.FlipMode.Tk2d;
-		if (regionRotated) {
-			float temp = minTexCoords.x;
-			minTexCoords.x = maxTexCoords.x;
-			maxTexCoords.x = temp;
+
+		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;
 		}
-		u = minTexCoords.x;
-		v = maxTexCoords.y;
-		u2 = maxTexCoords.x;
-		v2 = minTexCoords.y;
-
-		regionOriginalWidth = (int)(def.untrimmedBoundsData[1].x / def.texelSize.x);
-		regionOriginalHeight = (int)(def.untrimmedBoundsData[1].y / def.texelSize.y);
-		
-		regionWidth = (int)(def.boundsData[1].x / def.texelSize.x);
-		regionHeight = (int)(def.boundsData[1].y / def.texelSize.y);
-		
-		float x0 = def.untrimmedBoundsData[0].x - def.untrimmedBoundsData[1].x / 2;
-		float x1 = def.boundsData[0].x - def.boundsData[1].x / 2;
-		regionOffsetX = (int)((x1 - x0) / def.texelSize.x);
-		
-		float y0 = def.untrimmedBoundsData[0].y - def.untrimmedBoundsData[1].y / 2;
-		float y1 = def.boundsData[0].y - def.boundsData[1].y / 2;
-		regionOffsetY = (int)((y1 - y0) / def.texelSize.y);
-
-		material = def.materialInst;
-	}
 
-	public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) {
-		ProcessSpriteDefinition(path);
-		
-		RegionAttachment region = new RegionAttachment(name);
-		region.Path = path;
-		region.RendererObject = material;
-		region.SetUVs(u, v, u2, v2, regionRotated);
-		region.RegionOriginalWidth = regionOriginalWidth;
-		region.RegionOriginalHeight = regionOriginalHeight;
-		region.RegionWidth = regionWidth;
-		region.RegionHeight = regionHeight;
-		region.RegionOffsetX = regionOffsetX;
-		region.RegionOffsetY = regionOffsetY;
-		return region;
-	}
-	
-	public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) {
-		ProcessSpriteDefinition(path);
-		
-		MeshAttachment mesh = new MeshAttachment(name);
-		mesh.Path = path;
-		mesh.RendererObject = material;
-		mesh.RegionU = u;
-		mesh.RegionV = v;
-		mesh.RegionU2 = u2;
-		mesh.RegionV2 = v2;
-		mesh.RegionRotate = regionRotated;
-		mesh.RegionOriginalWidth = regionOriginalWidth;
-		mesh.RegionOriginalHeight = regionOriginalHeight;
-		mesh.RegionWidth = regionWidth;
-		mesh.RegionHeight = regionHeight;
-		mesh.RegionOffsetX = regionOffsetX;
-		mesh.RegionOffsetY = regionOffsetY;
-		return mesh;
-	}
-	
-	public 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

+ 1 - 1
spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs

@@ -37,7 +37,7 @@ using UnityEngine;
 using System.Collections;
 using Spine;
 
-namespace Spine {
+namespace Spine.Unity {
 	/// <summary>
 	/// Use this as a condition-blocking yield instruction for Unity Coroutines. 
 	/// The routine will pause until the AnimationState.TrackEntry fires its Complete event.</summary>

+ 1 - 1
spine-unity/Assets/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs

@@ -37,7 +37,7 @@ using UnityEngine;
 using System.Collections;
 using Spine;
 
-namespace Spine {
+namespace Spine.Unity {
 	/// <summary>
 	/// Use this as a condition-blocking yield instruction for Unity Coroutines. 
 	/// The routine will pause until the AnimationState fires an event matching the given event name or EventData reference.</summary>

+ 129 - 126
spine-unity/Assets/spine-unity/SkeletonAnimation.cs

@@ -30,162 +30,165 @@
  *****************************************************************************/
 
 using System;
-using System.Collections.Generic;
 using UnityEngine;
-using Spine;
 
-[ExecuteInEditMode]
-[AddComponentMenu("Spine/SkeletonAnimation")]
-public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
-
-	/// <summary>
-	/// 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</summary>
-	public Spine.AnimationState state;
-
-	public event UpdateBonesDelegate UpdateLocal {
-		add { _UpdateLocal += value; }
-		remove { _UpdateLocal -= value; }
-	}
+namespace Spine.Unity {
+	
+	[ExecuteInEditMode]
+	[AddComponentMenu("Spine/SkeletonAnimation")]
+	[HelpURL("http://esotericsoftware.com/spine-unity-documentation#Controlling-Animation")]
+	public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
+
+		/// <summary>
+		/// 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</summary>
+		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 UpdateWorld {
+			add { _UpdateWorld += value; }
+			remove { _UpdateWorld -= value; }
+		}
 
-	public event UpdateBonesDelegate UpdateComplete {
-		add { _UpdateComplete += value; }
-		remove { _UpdateComplete -= value; }
-	}
+		public event UpdateBonesDelegate UpdateComplete {
+			add { _UpdateComplete += value; }
+			remove { _UpdateComplete -= value; }
+		}
 
-	protected event UpdateBonesDelegate _UpdateLocal;
-	protected event UpdateBonesDelegate _UpdateWorld;
-	protected event UpdateBonesDelegate _UpdateComplete;
+		protected event UpdateBonesDelegate _UpdateLocal;
+		protected event UpdateBonesDelegate _UpdateWorld;
+		protected event UpdateBonesDelegate _UpdateComplete;
 
-	/// <summary>Gets the skeleton.</summary>
-	public Skeleton Skeleton {
-		get {
-			this.Initialize(false);
-			return this.skeleton;
+		/// <summary>Gets the skeleton.</summary>
+		public Skeleton Skeleton {
+			get {
+				this.Initialize(false);
+				return this.skeleton;
+			}
 		}
-	}
 
-	[SerializeField]
-	[SpineAnimation]
-	private String _animationName;
+		[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;
+		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;
 			}
-			
-			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;
+			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);
 			}
-			
-			if (value == null || value.Length == 0)
-				state.ClearTrack(0);
-			else
-				state.SetAnimation(0, value, loop);
 		}
-	}
 
-	/// <summary>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.</summary>
-	#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;
-
-	/// <summary>
-	/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.</summary>
-	/// <remarks>AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</remarks>
-	#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
-	/// <summary>Adds and prepares a SkeletonAnimation component to a GameObject at runtime.</summary>
-	/// <returns>The newly instantiated SkeletonAnimation</returns>
-	public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) {
-		return SkeletonRenderer.AddSpineComponent<SkeletonAnimation>(gameObject, skeletonDataAsset);
-	}
+		/// <summary>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.</summary>
+		#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;
 
-	/// <summary>Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it.</summary>
-	/// <returns>The newly instantiated SkeletonAnimation component.</returns>
-	public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) {
-		return SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(skeletonDataAsset);
-	}
-	#endregion
+		/// <summary>
+		/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.</summary>
+		/// <remarks>AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</remarks>
+		#if UNITY_5
+		[Tooltip("The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.")]
+		#endif
+		public float timeScale = 1;
 
-	public override void Initialize (bool overwrite) {
-		if (valid && !overwrite)
-			return;
+		#region Runtime Instantiation
+		/// <summary>Adds and prepares a SkeletonAnimation component to a GameObject at runtime.</summary>
+		/// <returns>The newly instantiated SkeletonAnimation</returns>
+		public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) {
+			return SkeletonRenderer.AddSpineComponent<SkeletonAnimation>(gameObject, skeletonDataAsset);
+		}
 
-		base.Initialize(overwrite);
+		/// <summary>Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it.</summary>
+		/// <returns>The newly instantiated SkeletonAnimation component.</returns>
+		public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) {
+			return SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(skeletonDataAsset);
+		}
+		#endregion
 
-		if (!valid)
-			return;
+		public override void Initialize (bool overwrite) {
+			if (valid && !overwrite)
+				return;
 
-		state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
+			base.Initialize(overwrite);
 
-		#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);
+			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);
 			}
-			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 (float deltaTime) {
-		if (!valid)
-			return;
+		public virtual void Update () {
+			Update(Time.deltaTime);
+		}
 
-		deltaTime *= timeScale;
-		skeleton.Update(deltaTime);
-		state.Update(deltaTime);
-		state.Apply(skeleton);
+		public virtual void Update (float deltaTime) {
+			if (!valid)
+				return;
 
-		if (_UpdateLocal != null)
-			_UpdateLocal(this);
+			deltaTime *= timeScale;
+			skeleton.Update(deltaTime);
+			state.Update(deltaTime);
+			state.Apply(skeleton);
 
-		skeleton.UpdateWorldTransform();
+			if (_UpdateLocal != null)
+				_UpdateLocal(this);
 
-		if (_UpdateWorld != null) {
-			_UpdateWorld(this);
 			skeleton.UpdateWorldTransform();
-		}
 
-		if (_UpdateComplete != null) {
-			_UpdateComplete(this);
+			if (_UpdateWorld != null) {
+				_UpdateWorld(this);
+				skeleton.UpdateWorldTransform();
+			}
+
+			if (_UpdateComplete != null) {
+				_UpdateComplete(this);
+			}
 		}
+
 	}
-		
+
 }

+ 149 - 149
spine-unity/Assets/spine-unity/SkeletonAnimator.cs

@@ -5,211 +5,211 @@
  * 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 UpdateLocal {
+			add { _UpdateLocal += value; }
+			remove { _UpdateLocal -= value; }
+		}
 
-	public event UpdateBonesDelegate UpdateWorld {
-		add { _UpdateWorld += value; }
-		remove { _UpdateWorld -= value; }
-	}
+		public event UpdateBonesDelegate UpdateWorld {
+			add { _UpdateWorld += value; }
+			remove { _UpdateWorld -= value; }
+		}
 
-	public event UpdateBonesDelegate UpdateComplete {
-		add { _UpdateComplete += value; }
-		remove { _UpdateComplete -= value; }
-	}
+		public event UpdateBonesDelegate UpdateComplete {
+			add { _UpdateComplete += value; }
+			remove { _UpdateComplete -= value; }
+		}
 
-	protected event UpdateBonesDelegate _UpdateLocal;
-	protected event UpdateBonesDelegate _UpdateWorld;
-	protected event UpdateBonesDelegate _UpdateComplete;
+		protected event UpdateBonesDelegate _UpdateLocal;
+		protected event UpdateBonesDelegate _UpdateWorld;
+		protected event UpdateBonesDelegate _UpdateComplete;
 
-	public Skeleton Skeleton {
-		get {
-			return this.skeleton;
+		public Skeleton Skeleton {
+			get {
+				return this.skeleton;
+			}
 		}
-	}
-
-	readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>();
-	readonly Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>();
-	Animator animator;
-	float lastTime;
 
-	public override void Initialize (bool overwrite) {
-		if (valid && !overwrite)
-			return;
-		
-		base.Initialize(overwrite);
+		readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>();
+		readonly Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>();
+		Animator animator;
+		float lastTime;
 
-		if (!valid)
-			return;
+		public override void Initialize (bool overwrite) {
+			if (valid && !overwrite)
+				return;
 
-		animationTable.Clear();
-		clipNameHashCodeTable.Clear();
+			base.Initialize(overwrite);
 
-		var data = skeletonDataAsset.GetSkeletonData(true);
+			if (!valid)
+				return;
 
-		foreach (var a in data.Animations) {
-			animationTable.Add(a.Name.GetHashCode(), a);
-		}
+			animationTable.Clear();
+			clipNameHashCodeTable.Clear();
 
-		animator = GetComponent<Animator>();
+			var data = skeletonDataAsset.GetSkeletonData(true);
 
-		lastTime = Time.time;
-	}
+			foreach (var a in data.Animations) {
+				animationTable.Add(a.Name.GetHashCode(), a);
+			}
 
-	void Update () {
-		if (!valid)
-			return;
+			animator = GetComponent<Animator>();
 
-		if (layerMixModes.Length != animator.layerCount) {
-			System.Array.Resize<MixMode>(ref layerMixModes, animator.layerCount);
+			lastTime = Time.time;
 		}
-		float deltaTime = Time.time - lastTime;
 
-		skeleton.Update(Time.deltaTime);
+		void Update () {
+			if (!valid)
+				return;
 
-		//apply
-		int layerCount = animator.layerCount;
+			if (layerMixModes.Length != animator.layerCount) {
+				System.Array.Resize<MixMode>(ref layerMixModes, animator.layerCount);
+			}
+			float deltaTime = Time.time - lastTime;
 
-		for (int i = 0; i < layerCount; i++) {
+			skeleton.Update(Time.deltaTime);
 
-			float layerWeight = animator.GetLayerWeight(i);
-			if (i == 0)
-				layerWeight = 1;
+			//apply
+			int layerCount = animator.layerCount;
 
-			var stateInfo = animator.GetCurrentAnimatorStateInfo(i);
-			var nextStateInfo = animator.GetNextAnimatorStateInfo(i);
+			for (int i = 0; i < layerCount; 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];
+				float layerWeight = animator.GetLayerWeight(i);
+				if (i == 0)
+					layerWeight = 1;
 
-			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;
+				var stateInfo = animator.GetCurrentAnimatorStateInfo(i);
+				var nextStateInfo = animator.GetNextAnimatorStateInfo(i);
 
-					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];
+				#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;
-
-				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;
-				}
-
-				//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)].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++) {
+					#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;
 
 							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);
 						}
 					}
+				} else if (mode >= MixMode.MixNext) {
+					//apply first non-zero weighted clip
+					int c = 0;
+
+					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;
+					}
 
 					//mix the rest
-					for (; c < nextClipInfo.Length; c++) {
-						var info = nextClipInfo[c];
+					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);
+						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;
+							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 (_UpdateComplete != null) {
-			_UpdateComplete(this);
-		}
+			if (_UpdateWorld != null) {
+				_UpdateWorld(this);
+				skeleton.UpdateWorldTransform();
+			}
 
-		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);
+			lastTime = Time.time;
 		}
 
-		return clipNameHashCode;
+		private int GetAnimationClipNameHashCode (AnimationClip clip) {
+			int clipNameHashCode;
+			if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) {
+				clipNameHashCode = clip.name.GetHashCode();
+				clipNameHashCodeTable.Add(clip, clipNameHashCode);
+			}
+
+			return clipNameHashCode;
+		}
 	}
 }

+ 121 - 149
spine-unity/Assets/spine-unity/SkeletonExtensions.cs

@@ -6,153 +6,125 @@
 *****************************************************************************/
 
 using UnityEngine;
-using System.Collections;
 using Spine;
-
-public static class SkeletonExtensions {
-
-	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);	}
-
-	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) {
-		// 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
-	/// <summary>
-	/// 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.</summary>
-	/// <param name = "skeleton">The skeleton to pose.</param>
-	/// <param name="animationName">The name of the animation to use.</param>
-	/// <param name = "time">The time of the pose within the animation.</param>
-	/// <param name = "loop">Wraps the time around if it is longer than the duration of the animation.</param>
-	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
-
-}
+using Spine.Unity;
+
+namespace Spine.Unity {
+	public static class SkeletonExtensions {
+
+		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);	}
+
+		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
+		/// <summary>
+		/// 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.</summary>
+		/// <param name = "skeleton">The skeleton to pose.</param>
+		/// <param name="animationName">The name of the animation to use.</param>
+		/// <param name = "time">The time of the pose within the animation.</param>
+		/// <param name = "loop">Wraps the time around if it is longer than the duration of the animation.</param>
+		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
+	}
+}

+ 826 - 663
spine-unity/Assets/spine-unity/SkeletonRenderer.cs

@@ -28,505 +28,521 @@
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
+#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;
-
-/// <summary>Renders a skeleton.</summary>
-[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer)), DisallowMultipleComponent]
-public class SkeletonRenderer : MonoBehaviour {
-
-	public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer);
-	public SkeletonRendererDelegate OnRebuild;
-
-	public SkeletonDataAsset skeletonDataAsset;
-	public String initialSkinName;
-
-	#region Advanced
-	public bool calculateNormals, calculateTangents;
-	public float zSpacing;
-	public bool renderMeshes = true, immutableTriangles;
-	public bool frontFacing;
-	public bool logErrors = false;
-
-	// Submesh Separation
-	[SpineSlot] public string[] submeshSeparators = new string[0];
-	[HideInInspector] public List<Slot> submeshSeparatorSlots = new List<Slot>();
-
-	// Custom Slot Material
-	[System.NonSerialized] private readonly Dictionary<Slot, Material> customSlotMaterials = new Dictionary<Slot, Material>();
-	public Dictionary<Slot, Material> CustomSlotMaterials { get { return customSlotMaterials; } }
-	#endregion
-
-	[System.NonSerialized] public bool valid;
-	[System.NonSerialized] public Skeleton skeleton;
-
-	private MeshRenderer meshRenderer;
-	private MeshFilter meshFilter;
-
-	private Mesh mesh1, mesh2;
-	private bool useMesh1;
-
-	private float[] tempVertices = new float[8];
-	private Vector3[] vertices;
-	private Color32[] colors;
-	private Vector2[] uvs;
-	private Material[] sharedMaterials = new Material[0];
-
-	private MeshState meshState = new MeshState();
-	private readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
-	private readonly ExposedList<Submesh> submeshes = new ExposedList<Submesh>();
-	private SkeletonUtilitySubmeshRenderer[] submeshRenderers;
-
-	#region Runtime Instantiation
-	/// <summary>Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime.</summary>
-	/// <typeparam name="T">T should be SkeletonRenderer or any of its derived classes.</typeparam>
-	public static T AddSpineComponent<T> (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
-		
-		var c = gameObject.AddComponent<T>();
-
-		if (skeletonDataAsset != null) {
-			c.skeletonDataAsset = skeletonDataAsset;
-			c.Initialize(false);
+using Spine.Unity.MeshGeneration;
+
+namespace Spine.Unity {
+	/// <summary>Renders a skeleton.</summary>
+	[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 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<Slot> separatorSlots = new List<Slot>();
+
+		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
 
-		return c;
-	}
+		#if SPINE_OPTIONAL_SUBMESHRENDERER
+		private Spine.Unity.Modules.SkeletonUtilitySubmeshRenderer[] submeshRenderers;
+		#endif
 
-	public static T NewSpineGameObject<T> (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
-		return SkeletonRenderer.AddSpineComponent<T>(new GameObject("New Spine GameObject"), skeletonDataAsset);
-	}
-	#endregion
+		// Custom Slot Material
+		[System.NonSerialized] readonly Dictionary<Slot, Material> customSlotMaterials = new Dictionary<Slot, Material>();
+		public Dictionary<Slot, Material> CustomSlotMaterials { get { return customSlotMaterials; } }
+		#endregion
 
-	public virtual void Awake () {
-		Initialize(false);
-	}
+		[System.NonSerialized] public bool valid;
+		[System.NonSerialized] public Skeleton skeleton;
 
-	public virtual void Initialize (bool overwrite) {
-		if (valid && !overwrite)
-			return;
+		MeshRenderer meshRenderer;
+		MeshFilter meshFilter;
 
-		// Clear
-		{
-			if (meshFilter != null)
-				meshFilter.sharedMesh = null;
+		Spine.Unity.DoubleBuffered<SkeletonRenderer.SmartMesh> doubleBufferedMesh;
+		readonly SmartMesh.Instruction currentInstructions = new SmartMesh.Instruction();
+		readonly ExposedList<SubmeshTriangleBuffer> submeshes = new ExposedList<SubmeshTriangleBuffer>();
 
-			meshRenderer = GetComponent<MeshRenderer>();
-			if (meshRenderer != null) meshRenderer.sharedMaterial = null;
-
-			meshState = new MeshState();
-			mesh1 = null;
-			mesh2 = null;
-			vertices = null;
-			colors = null;
-			uvs = null;
-			sharedMaterials = new Material[0];
-			submeshMaterials.Clear();
-			submeshes.Clear();
-			skeleton = null;
-
-			valid = false;
-		}
+		float[] tempVertices = new float[8];
+		Vector3[] vertices;
+		Color32[] colors;
+		Vector2[] uvs;
 
-		if (!skeletonDataAsset) {
-			if (logErrors)
-				Debug.LogError("Missing SkeletonData asset.", this);
+		readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
+		Material[] sharedMaterials = new Material[0];
 
-			return;
+		#if SPINE_OPTIONAL_NORMALS
+		Vector3[] normals;
+		Vector4[] tangents;
+		#endif
+
+		#region Runtime Instantiation
+		public static T NewSpineGameObject<T> (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
+			return SkeletonRenderer.AddSpineComponent<T>(new GameObject("New Spine GameObject"), skeletonDataAsset);
 		}
-		SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false);
-		if (skeletonData == null)
-			return;
-		valid = true;
-
-		meshFilter = GetComponent<MeshFilter>();
-		meshRenderer = GetComponent<MeshRenderer>();
-		mesh1 = Spine.Unity.SpineMesh.NewMesh();
-		mesh2 = Spine.Unity.SpineMesh.NewMesh();
-		vertices = new Vector3[0];
-
-		skeleton = new Skeleton(skeletonData);
-		if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default")
-			skeleton.SetSkin(initialSkinName);
-
-		submeshSeparatorSlots.Clear();
-		for (int i = 0; i < submeshSeparators.Length; i++) {
-			submeshSeparatorSlots.Add(skeleton.FindSlot(submeshSeparators[i]));
+
+		/// <summary>Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime.</summary>
+		/// <typeparam name="T">T should be SkeletonRenderer or any of its derived classes.</typeparam>
+		public static T AddSpineComponent<T> (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
+			var c = gameObject.AddComponent<T>();
+			if (skeletonDataAsset != null) {
+				c.skeletonDataAsset = skeletonDataAsset;
+				c.Initialize(false);
+			}
+			return c;
 		}
+		#endregion
 
-		CollectSubmeshRenderers();
+		public virtual void Awake () {
+			Initialize(false);
+		}
 
-		LateUpdate();
+		public virtual void Initialize (bool overwrite) {
+			if (valid && !overwrite)
+				return;
 
-		if (OnRebuild != null)
-			OnRebuild(this);
-	}
+			// Clear
+			{
+				if (meshFilter != null)
+					meshFilter.sharedMesh = null;
 
-	public void CollectSubmeshRenderers () {
-		submeshRenderers = GetComponentsInChildren<SkeletonUtilitySubmeshRenderer>();
-	}
+				meshRenderer = GetComponent<MeshRenderer>();
+				if (meshRenderer != null) meshRenderer.sharedMaterial = null;
 
-	public virtual void LateUpdate () {
-		if (!valid)
-			return;
+				currentInstructions.Clear();
+				vertices = null;
+				colors = null;
+				uvs = null;
+				sharedMaterials = new Material[0];
+				submeshMaterials.Clear();
+				submeshes.Clear();
+				skeleton = null;
 
-		// Exit early if there is nothing to render
-		if (!meshRenderer.enabled && submeshRenderers.Length == 0)
-			return;
+				valid = false;
+			}
 
-		// This method caches several .Items arrays. Whenever it does, there should be no mutations done on the overlying ExposedList object.
+			if (!skeletonDataAsset) {
+				if (logErrors)
+					Debug.LogError("Missing SkeletonData asset.", this);
 
-		// Count vertices and submesh triangles.
-		int vertexCount = 0;
+				return;
+			}
+			SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false);
+			if (skeletonData == null)
+				return;
+			valid = true;
 
-		int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0;
-		Material lastMaterial = null;
-		ExposedList<Slot> drawOrder = skeleton.drawOrder;
-		var drawOrderItems = drawOrder.Items;
-		int drawOrderCount = drawOrder.Count;
-		int submeshSeparatorSlotsCount = submeshSeparatorSlots.Count;
-		bool renderMeshes = this.renderMeshes;
+			meshFilter = GetComponent<MeshFilter>();
+			meshRenderer = GetComponent<MeshRenderer>();
+			doubleBufferedMesh = new DoubleBuffered<SmartMesh>();
+			vertices = new Vector3[0];
 
-		// Clear last state of attachments and submeshes
-		MeshState.SingleMeshState workingState = meshState.buffer;
-		var workingAttachments = workingState.attachments;
-		workingAttachments.Clear(true);
-		workingState.UpdateAttachmentCount(drawOrderCount);
-		var workingAttachmentsItems = workingAttachments.Items;
+			skeleton = new Skeleton(skeletonData);
+			if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default")
+				skeleton.SetSkin(initialSkinName);
 
-		var workingFlips = workingState.attachmentsFlipState;
-		var workingFlipsItems = workingState.attachmentsFlipState.Items;
+			separatorSlots.Clear();
+			for (int i = 0; i < separatorSlotNames.Length; i++)
+				separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i]));
 
-		var workingSubmeshArguments = workingState.addSubmeshArguments;	// Items array should not be cached. There is dynamic writing to this object.
-		workingSubmeshArguments.Clear(false);
+			#if SPINE_OPTIONAL_SUBMESHRENDERER
+			submeshRenderers = GetComponentsInChildren<Spine.Unity.Modules.SkeletonUtilitySubmeshRenderer>();
+			#endif
 
-		MeshState.SingleMeshState storedState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2;
-		var storedAttachments = storedState.attachments;
-		var storedAttachmentsItems = storedAttachments.Items;
+			LateUpdate();
 
-		var storedFlips = storedState.attachmentsFlipState;
-		var storedFlipsItems = storedFlips.Items;
+			if (OnRebuild != null)
+				OnRebuild(this);
+		}
 
-		bool mustUpdateMeshStructure = storedState.requiresUpdate ||	// Force update if the mesh was cleared. (prevents flickering due to incorrect state)
-			drawOrderCount != storedAttachments.Count ||				// Number of slots changed (when does this happen?)
-			immutableTriangles != storedState.immutableTriangles;		// Immutable Triangles flag changed.
+		public virtual void LateUpdate () {
+			if (!valid)
+				return;
 
-		bool isCustomMaterialsPopulated = customSlotMaterials.Count > 0;
+			if (
+				(
+					!meshRenderer.enabled
 
-		for (int i = 0; i < drawOrderCount; i++) {
-			Slot slot = drawOrderItems[i];
-			Bone bone = slot.bone;
-			Attachment attachment = slot.attachment;
+				)
+				#if SPINE_OPTIONAL_RENDEROVERRIDE
+				&& this.generateMeshOverride == null
+				#endif
 
-			object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object.
-			int attachmentVertexCount, attachmentTriangleCount;
+				#if SPINE_OPTIONAL_SUBMESHRENDERER
+				&& submeshRenderers.Length > 0
+				#endif
 
-			// Handle flipping for triangle winding (for lighting?).           
-			bool flip = frontFacing && (bone.WorldSignX != bone.WorldSignY);
+			)
+				return;
 
-			workingFlipsItems[i] = flip;
-			workingAttachmentsItems[i] = attachment;
 
-			mustUpdateMeshStructure = mustUpdateMeshStructure ||	// Always prefer short circuited or. || and not |=.
-				(attachment != storedAttachmentsItems[i]) || 		// Attachment order changed. // This relies on the drawOrder.Count != storedAttachments.Count check above as a bounds check.
-				(flip != storedFlipsItems[i]);						// Flip states changed.
 
-			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;
+			// STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes.
+
+			// This method caches several .Items arrays.
+			// Never mutate their overlying ExposedList objects.
+			ExposedList<Slot> 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;
+
+				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 {
-					var skinnedMeshAttachment = attachment as WeightedMeshAttachment;
-					if (skinnedMeshAttachment != null) {
-						rendererObject = skinnedMeshAttachment.RendererObject;
-						attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
-						attachmentTriangleCount = skinnedMeshAttachment.triangles.Length;
-					} 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
+				#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;
+				if (isCustomMaterialsPopulated) {
+					if (!customSlotMaterials.TryGetValue(slot, out material)) {
+						material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
+					}
+				} else {
 					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
+				#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
+						}
+					);
 
-			// Populate submesh when material changes. (or when forced to separate by a submeshSeparator)
-			if ((vertexCount > 0 && lastMaterial.GetInstanceID() != material.GetInstanceID()) ||
-				(submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) {
-
-				workingSubmeshArguments.Add(
-					new MeshState.AddSubmeshArguments {
-						material = lastMaterial,
-						startSlot = submeshStartSlotIndex,
-						endSlot = i,
-						triangleCount = submeshTriangleCount,
-						firstVertex = submeshFirstVertex,
-						isLastSubmesh = false
-					}
-				);
+					submeshTriangleCount = 0;
+					submeshVertexCount = 0;
+					submeshFirstVertex = vertexCount;
+					submeshStartSlotIndex = i;
+				}
+				lastMaterial = material;
 
-				submeshTriangleCount = 0;
-				submeshFirstVertex = vertexCount;
-				submeshStartSlotIndex = i;
+				submeshTriangleCount += attachmentTriangleCount;
+				vertexCount += attachmentVertexCount;
+				submeshVertexCount += attachmentVertexCount;
 			}
-			lastMaterial = material;
 
-			submeshTriangleCount += attachmentTriangleCount;
-			vertexCount += attachmentVertexCount;
-		}
-
-
-		workingSubmeshArguments.Add(
-			new MeshState.AddSubmeshArguments {
-				material = lastMaterial,
-				startSlot = submeshStartSlotIndex,
-				endSlot = drawOrderCount,
-				triangleCount = submeshTriangleCount,
-				firstVertex = submeshFirstVertex,
-				isLastSubmesh = true
-			}
-		);
-
-		mustUpdateMeshStructure = mustUpdateMeshStructure ||
-			this.sharedMaterials.Length != workingSubmeshArguments.Count ||		// Material array changed in size
-			CheckIfMustUpdateMeshStructure(workingSubmeshArguments);			// Submesh Argument Array changed.
-
-		// CheckIfMustUpdateMaterialArray (workingMaterials, sharedMaterials)
-		if (!mustUpdateMeshStructure) {
-			// Narrow phase material array check.
-			var workingMaterials = workingSubmeshArguments.Items;
-			for (int i = 0, n = sharedMaterials.Length; i < n; i++) {
-				if (this.sharedMaterials[i] != workingMaterials[i].material) {	// Bounds check is implied above.
-					mustUpdateMeshStructure = true;
-					break;
+			workingSubmeshInstructions.Add(
+				new Spine.Unity.MeshGeneration.SubmeshInstruction {
+					skeleton = this.skeleton,
+					material = lastMaterial,
+					startSlot = submeshStartSlotIndex,
+					endSlot = drawOrderCount,
+					triangleCount = submeshTriangleCount,
+					firstVertexIndex = submeshFirstVertex,
+					vertexCount = submeshVertexCount,
+					forceSeparate = false
 				}
-			}
-		}
+			);
 
-		// NOT ELSE
+			workingInstruction.vertexCount = vertexCount;
+			workingInstruction.immutableTriangles = this.immutableTriangles;
+			#if SPINE_OPTIONAL_FRONTFACING
+			workingInstruction.frontFacing = this.frontFacing;
+			#endif
 
-		if (mustUpdateMeshStructure) {
-			this.submeshMaterials.Clear();
+			#if SPINE_OPTIONAL_RENDEROVERRIDE
+			if (this.generateMeshOverride != null) {
+				this.generateMeshOverride(workingInstruction);
 
-			var workingSubmeshArgumentsItems = workingSubmeshArguments.Items;
-			for (int i = 0, n = workingSubmeshArguments.Count; i < n; i++) {
-				AddSubmesh(workingSubmeshArgumentsItems[i], workingFlips);
+				if (disableRenderingOnOverride) {
+					return;
+				}
 			}
+			#endif
 
-			// Set materials.
-			if (submeshMaterials.Count == sharedMaterials.Length)
-				submeshMaterials.CopyTo(sharedMaterials);
-			else
-				sharedMaterials = submeshMaterials.ToArray();
-
-			meshRenderer.sharedMaterials = sharedMaterials;
-		}
-
+			// 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;
+				}
 
-		// Ensure mesh data is the right size.
-		Vector3[] vertices = this.vertices;
-		bool newTriangles = vertexCount > vertices.Length;
-		if (newTriangles) {
-			// Not enough vertices, increase size.
-			this.vertices = vertices = new Vector3[vertexCount];
-			this.colors = new Color32[vertexCount];
-			this.uvs = new Vector2[vertexCount];
-
-			mesh1.Clear();
-			mesh2.Clear();
-			meshState.stateMesh1.requiresUpdate = true;
-			meshState.stateMesh2.requiresUpdate = true;
-
-		} else {
-			// Too many vertices, zero the extra.
-			Vector3 zero = Vector3.zero;
-			for (int i = vertexCount, n = meshState.vertexCount; i < n; i++)
-				vertices[i] = zero;
-		}
-		meshState.vertexCount = vertexCount;
-
-		// Setup mesh.
-		float zSpacing = this.zSpacing;
-		float[] tempVertices = this.tempVertices;
-		Vector2[] uvs = this.uvs;
-		Color32[] colors = this.colors;
-		int vertexIndex = 0;
-		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);
+				// 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 {
-				meshBoundsMin.z = zSpacing * (drawOrderCount - 1);
-				meshBoundsMax.z = 0f;
+				Vector3 zero = Vector3.zero;
+				for (int i = vertexCount, n = vertices.Length; i < n; i++)
+					vertices[i] = zero;
 			}
-			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;
-
-					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;
-					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;
+
+			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 {
-					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];
-						meshAttachment.ComputeWorldVertices(slot, tempVertices);
-
-						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;
-
-						float[] meshUVs = meshAttachment.uvs;
+					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;
-						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;
+						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;
 					} 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);
-
-							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;
+							meshAttachment.ComputeWorldVertices(slot, tempVertices);
+
+							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);
+								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);
+							}
 
-							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];
@@ -546,282 +562,429 @@ 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);
 				}
-			} while (++i < drawOrderCount);
-		}
 
-		// Double buffer mesh.
-		Mesh mesh = useMesh1 ? mesh1 : mesh2;
-		meshFilter.sharedMesh = mesh;
+				currentMesh.subMeshCount = submeshCount;
 
-		mesh.vertices = vertices;
-		mesh.colors32 = colors;
-		mesh.uv = uvs;
+				for (int i = 0; i < submeshCount; ++i)
+					currentMesh.SetTriangles(submeshes.Items[i].triangles, i);
+			}
 
-		if (mustUpdateMeshStructure) {
-			int submeshCount = submeshMaterials.Count;
-			mesh.subMeshCount = submeshCount;
-			for (int i = 0; i < submeshCount; ++i)
-				mesh.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;
+						}
+					}
+				}
 
-			// Done updating mesh.
-			storedState.requiresUpdate = false;
-		}
+				if (mustUpdateRendererMaterials) {
+					if (submeshMaterials.Count == sharedMaterials.Length)
+						submeshMaterials.CopyTo(sharedMaterials);
+					else
+						sharedMaterials = submeshMaterials.ToArray();
 
-		Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin;
-		Vector3 meshBoundsCenter = meshBoundsMin + meshBoundsExtents * 0.5f;
-		mesh.bounds = new Bounds(meshBoundsCenter, meshBoundsExtents);
-
-		if (newTriangles && calculateNormals) {
-			Vector3[] normals = new Vector3[vertexCount];
-			Vector3 normal = new Vector3(0, 0, -1);
-			for (int i = 0; i < vertexCount; i++)
-				normals[i] = normal;
-			(useMesh1 ? mesh2 : mesh1).vertices = vertices; // Set other mesh vertices.
-			mesh1.normals = normals;
-			mesh2.normals = normals;
-
-			if (calculateTangents) {
-				Vector4[] tangents = new Vector4[vertexCount];
-				Vector4 tangent = new Vector4(1, 0, 0, -1);
-				for (int i = 0; i < vertexCount; i++)
-					tangents[i] = tangent;
-				mesh1.tangents = tangents;
-				mesh2.tangents = tangents;
+					meshRenderer.sharedMaterials = sharedMaterials;
+				}
 			}
-		}
-			
-		// Update previous state
-		storedState.immutableTriangles = immutableTriangles;
-
-		storedAttachments.Clear(true);
-		storedAttachments.GrowIfNeeded(workingAttachments.Capacity);
-		storedAttachments.Count = workingAttachments.Count;
-		workingAttachments.CopyTo(storedAttachments.Items);
-
-		storedFlips.GrowIfNeeded(workingFlips.Capacity);
-		storedFlips.Count = workingFlips.Count;
-		workingFlips.CopyTo(storedFlips.Items);
-
-		storedState.addSubmeshArguments.GrowIfNeeded(workingSubmeshArguments.Capacity);
-		storedState.addSubmeshArguments.Count = workingSubmeshArguments.Count;
-		workingSubmeshArguments.CopyTo(storedState.addSubmeshArguments.Items);
-
-
-		// Submesh Renderers
-		if (submeshRenderers.Length > 0) {
-			for (int i = 0; i < submeshRenderers.Length; i++) {
-				SkeletonUtilitySubmeshRenderer submeshRenderer = submeshRenderers[i];
-				if (submeshRenderer.submeshIndex < sharedMaterials.Length) {
-					submeshRenderer.SetMesh(meshRenderer, useMesh1 ? mesh1 : mesh2, sharedMaterials[submeshRenderer.submeshIndex]);
-				} else {
-					submeshRenderer.GetComponent<Renderer>().enabled = false;
+
+			// 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);
+
+
+			// 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<Renderer>().enabled = false;
 				}
 			}
+			#endif
 		}
 
-		useMesh1 = !useMesh1;
-	}
-
-	private bool CheckIfMustUpdateMeshStructure (ExposedList<MeshState.AddSubmeshArguments> workingAddSubmeshArguments) {
-		#if UNITY_EDITOR
-		if (!Application.isPlaying)
-			return true;
-		#endif
+		static bool CheckIfMustUpdateMeshStructure (SmartMesh.Instruction a, SmartMesh.Instruction b) {
 
-		// Check if any mesh settings were changed
-		MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2;
-		ExposedList<MeshState.AddSubmeshArguments> addSubmeshArgumentsCurrentMesh = currentMeshState.addSubmeshArguments;
-		int submeshCount = workingAddSubmeshArguments.Count;
-		if (addSubmeshArgumentsCurrentMesh.Count != submeshCount)
-			return true;
+			#if UNITY_EDITOR
+			if (!Application.isPlaying)
+				return true;
+			#endif
 
-		for (int i = 0; i < submeshCount; i++) {
-			if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref workingAddSubmeshArguments.Items[i]))
+			if (a.vertexCount != b.vertexCount)
 				return true;
-		}
 
-		return false;
-	}
+			if (a.immutableTriangles != b.immutableTriangles)
+				return true;
 
-	private void AddSubmesh (MeshState.AddSubmeshArguments submeshArguments, ExposedList<bool> flipStates) {
-		int submeshIndex = submeshMaterials.Count;
-		submeshMaterials.Add(submeshArguments.material);
+			int attachmentCountB = b.attachments.Count;
+			if (a.attachments.Count != attachmentCountB) // Bounds check for the looped storedAttachments count below.
+				return true;
 
-		if (submeshes.Count <= submeshIndex)
-			submeshes.Add(new Submesh());
-		else if (immutableTriangles)
-			return;
+			var attachmentsA = a.attachments.Items;
+			var attachmentsB = b.attachments.Items;		
+			for (int i = 0; i < attachmentCountB; i++) {
+				if (attachmentsA[i] != attachmentsB[i])
+					return true;
+			}
 
-		Submesh currentSubmesh = submeshes.Items[submeshIndex];
-		int[] triangles = currentSubmesh.triangles;
+			#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;
+				for (int i = 0; i < attachmentCountB; i++) {
+					if (flipsA[i] != flipsB[i])
+						return true;
+				}
+			}
+			#endif
 
-		int triangleCount = submeshArguments.triangleCount;
-		int firstVertex = submeshArguments.firstVertex;
+			// Submesh count changed
+			int submeshCountA = a.submeshInstructions.Count;
+			int submeshCountB = b.submeshInstructions.Count;
+			if (submeshCountA != submeshCountB)
+				return true;
 
-		int trianglesCapacity = triangles.Length;
-		if (submeshArguments.isLastSubmesh && trianglesCapacity > triangleCount) {
-			// Last submesh may have more triangles than required, so zero triangles to the end.
-			for (int i = triangleCount; i < trianglesCapacity; i++) {
-				triangles[i] = 0;
+			// 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;			
 			}
-			currentSubmesh.triangleCount = triangleCount;
 
-		} else if (trianglesCapacity != triangleCount) {
-			// Reallocate triangles when not the exact size needed.
-			currentSubmesh.triangles = triangles = new int[triangleCount];
-			currentSubmesh.triangleCount = 0;
+			return false;
 		}
 
-		if (!this.renderMeshes && !this.frontFacing) {
-			// Use stored triangles if possible.
-			if (currentSubmesh.firstVertex != firstVertex || currentSubmesh.triangleCount < triangleCount) { //|| currentSubmesh.triangleCount == 0
+		#if SPINE_OPTIONAL_FRONTFACING
+		void SetSubmesh (int submeshIndex, Spine.Unity.MeshGeneration.SubmeshInstruction submeshInstructions, ExposedList<bool> 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;
-				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;
-				}
 
+			} else if (trianglesCapacity != triangleCount) {
+				// Reallocate triangles when not the exact size needed.
+				currentSubmesh.triangles = triangles = new int[triangleCount];
+				currentSubmesh.triangleCount = 0;
 			}
-			return;
-		}
 
-		// This method caches several .Items arrays. Whenever it does, there should be no mutations done on the overlying ExposedList object.
-		// Iterate through all slots and store their triangles. 
-		var drawOrderItems = skeleton.DrawOrder.Items;
-		var flipStatesItems = flipStates.Items;
-
-		int triangleIndex = 0; // Modified by loop
-		for (int i = submeshArguments.startSlot, n = submeshArguments.endSlot; i < n; i++) {			
-			Attachment attachment = drawOrderItems[i].attachment;
-
-			bool flip = flipStatesItems[i];
-
-			// Add RegionAttachment triangles
-			if (attachment is RegionAttachment) {
-				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 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;
+					}
+
+					triangleIndex += 6;
+					firstVertex += 4;
+					continue;
+				}
+				#else
+				if (attachment is RegionAttachment) {
+				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;
 
 				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;
-			}
+				// 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 (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];
+				#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 {
+				#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;
+				firstVertex += attachmentVertexCount;
+			}
 		}
-	}
 
-	#if UNITY_EDITOR
-	void OnDrawGizmos () {
-		// Make scene view selection easier by drawing a clear gizmo over the skeleton.
-		meshFilter = GetComponent<MeshFilter>();
-		if (meshFilter == null) return;
+		#if UNITY_EDITOR
+		void OnDrawGizmos () {
+			// Make scene view selection easier by drawing a clear gizmo over the skeleton.
+			meshFilter = GetComponent<MeshFilter>();
+			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
 
-		Mesh mesh = meshFilter.sharedMesh;
-		if (mesh == null) return;
+		///<summary>This is a Mesh that also stores the instructions SkeletonRenderer generated for it.</summary>
+		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<Attachment> attachments = new ExposedList<Attachment>();
+				public readonly ExposedList<Spine.Unity.MeshGeneration.SubmeshInstruction> submeshInstructions = new ExposedList<Spine.Unity.MeshGeneration.SubmeshInstruction>();
+
+				#if SPINE_OPTIONAL_FRONTFACING
+				public bool frontFacing;
+				public readonly ExposedList<bool> attachmentFlips = new ExposedList<bool>();
+				#endif
+
+				public void Clear () {
+					this.attachments.Clear(false);
+					this.submeshInstructions.Clear(false);
+
+					#if SPINE_OPTIONAL_FRONTFACING
+					this.attachmentFlips.Clear(false);
+					#endif
+				}
 
-		Bounds meshBounds = mesh.bounds;
-		Gizmos.color = Color.clear;
-		Gizmos.matrix = transform.localToWorldMatrix;
-		Gizmos.DrawCube(meshBounds.center, meshBounds.size);
-	}
-	#endif
-
-	private class MeshState {
-		public int vertexCount;
-		public readonly SingleMeshState buffer = new SingleMeshState();
-		public readonly SingleMeshState stateMesh1 = new SingleMeshState();
-		public readonly SingleMeshState stateMesh2 = new SingleMeshState();
-
-		public class SingleMeshState {
-			public bool immutableTriangles;
-			public bool requiresUpdate;
-			public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>();
-			public readonly ExposedList<bool> attachmentsFlipState = new ExposedList<bool>();
-			public readonly ExposedList<AddSubmeshArguments> addSubmeshArguments = new ExposedList<AddSubmeshArguments>();
-
-			public void UpdateAttachmentCount (int attachmentCount) {
-				attachmentsFlipState.GrowIfNeeded(attachmentCount);
-				attachmentsFlipState.Count = attachmentCount;
-
-				attachments.GrowIfNeeded(attachmentCount);
-				attachments.Count = attachmentCount;
+				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);
+				}
 			}
 		}
 
-		public struct AddSubmeshArguments {
-			public Material material;
-			public int startSlot;
-			public int endSlot;
+		class SubmeshTriangleBuffer {
+			public int[] triangles = new int[0];
+
+			// These two fields are used when renderMeshes == false
 			public int triangleCount;
-			public int firstVertex;
-			public bool isLastSubmesh;
-
-			public bool Equals (ref AddSubmeshArguments other) {
-				return
-					//!ReferenceEquals(material, null) &&
-					//!ReferenceEquals(other.material, null) &&
-					//material.GetInstanceID() == other.material.GetInstanceID() &&
-					startSlot == other.startSlot &&
-					endSlot == other.endSlot &&
-					triangleCount == other.triangleCount &&
-					firstVertex == other.firstVertex;
-			}
+			public int firstVertex = -1;
 		}
 	}
-}
-
-class Submesh {
-	public int[] triangles = new int[0];
-	public int triangleCount;
-	public int firstVertex = -1;
-}
+}

+ 272 - 269
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<Slot, List<BoundingBoxAttachment>> boundingBoxTable = new Dictionary<Slot, List<BoundingBoxAttachment>>();
-	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<Slot, List<BoundingBoxAttachment>> boundingBoxTable = new Dictionary<Slot, List<BoundingBoxAttachment>>();
+		string currentSkinName = "";
 
-		boundingBoxTable.Clear();
+		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");
 
-		if (multiObject)
-			return;
+			// MITCH
+			//		flip = this.serializedObject.FindProperty("flip");
+			//		flipX = this.serializedObject.FindProperty("flipX");
 
-		if (utilityBone.bone == null)
-			return;
+			EvaluateFlags();
 
-		var skeleton = utilityBone.bone.Skeleton;
-		int slotCount = skeleton.Slots.Count;
-		Skin skin = skeleton.Skin;
-		if (skeleton.Skin == null)
-			skin = skeleton.Data.DefaultSkin;
+			if (utilityBone.valid == false && skeletonUtility != null && skeletonUtility.skeletonRenderer != null) {
+				skeletonUtility.skeletonRenderer.Initialize(false);
+			}
 
-		currentSkinName = skin.Name;
-		for(int i = 0; i < slotCount; i++){
-			Slot slot = skeletonUtility.skeletonRenderer.skeleton.Slots.Items[i];
-			if (slot.Bone == utilityBone.bone) {
-				List<Attachment> attachments = new List<Attachment>();
-				
-					
-				skin.FindAttachmentsForSlot(skeleton.FindSlotIndex(slot.Data.Name), attachments);
+			canCreateHingeChain = CanCreateHingeChain();
 
-				List<BoundingBoxAttachment> boundingBoxes = new List<BoundingBoxAttachment>();
-				foreach (var att in attachments) {
-					if (att is BoundingBoxAttachment) {
-						boundingBoxes.Add((BoundingBoxAttachment)att);
-					}
-				}
+			boundingBoxTable.Clear();
 
-				if (boundingBoxes.Count > 0) {
-					boundingBoxTable.Add(slot, boundingBoxes);
-				}
-			}
-		}
-		
-	}
+			if (multiObject)
+				return;
 
-	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<SkeletonUtilityBone>();
-					if (sub != null) {
-						boneCount++;
-						if (sub.mode == SkeletonUtilityBone.Mode.Follow)
-							containsFollows = true;
-						if (sub.mode == SkeletonUtilityBone.Mode.Override)
-							containsOverrides = true;
+			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<Attachment> attachments = new List<Attachment>();
+
+
+					skin.FindAttachmentsForSlot(skeleton.FindSlotIndex(slot.Data.Name), attachments);
+
+					List<BoundingBoxAttachment> boundingBoxes = new List<BoundingBoxAttachment>();
+					foreach (var att in attachments) {
+						if (att is BoundingBoxAttachment) {
+							boundingBoxes.Add((BoundingBoxAttachment)att);
+						}
+					}
+
+					if (boundingBoxes.Count > 0) {
+						boundingBoxTable.Add(slot, boundingBoxes);
 					}
 				}
 			}
-			
-			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 = "<None>";
-			if (multiObject)
-				str = "<Multiple>";
+		void EvaluateFlags () {
+			utilityBone = (SkeletonUtilityBone)target;
+			skeletonUtility = utilityBone.skeletonUtility;
 
-			GUILayout.BeginHorizontal();
-			EditorGUILayout.PrefixLabel("Bone");
+			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<SkeletonUtilityBone>();
+						if (sub != null) {
+							boneCount++;
+							if (sub.mode == SkeletonUtilityBone.Mode.Follow)
+								containsFollows = true;
+							if (sub.mode == SkeletonUtilityBone.Mode.Override)
+								containsOverrides = true;
+						}
+					}
+				}
 
-			if (GUILayout.Button(str, EditorStyles.popup)) {
-				BoneSelectorContextMenu(str, ((SkeletonUtilityBone)target).skeletonUtility.skeletonRenderer.skeleton.Bones, "<None>", TargetBoneSelected);
+				if (boneCount > 1)
+					multiObject = true;
 			}
-
-			GUILayout.EndHorizontal();
 		}
-		EditorGUI.EndDisabledGroup();
 
-		EditorGUILayout.PropertyField(zPosition);
-		EditorGUILayout.PropertyField(position);
-		EditorGUILayout.PropertyField(rotation);
-		EditorGUILayout.PropertyField(scale);
-		// MITCH
-//		EditorGUILayout.PropertyField(flip);
+		public override void OnInspectorGUI () {
+			serializedObject.Update();
 
-		EditorGUI.BeginDisabledGroup(containsFollows);
-		{
-			EditorGUILayout.PropertyField(overrideAlpha);
-			EditorGUILayout.PropertyField(parentReference);
+			EditorGUI.BeginChangeCheck();
+			EditorGUILayout.PropertyField(mode);
+			if (EditorGUI.EndChangeCheck()) {
+				containsOverrides = mode.enumValueIndex == 1;
+				containsFollows = mode.enumValueIndex == 0;
+			}
 
-			// MITCH
-//			EditorGUI.BeginDisabledGroup(multiObject || !flip.boolValue);
-//			{
-//				EditorGUI.BeginChangeCheck();
-//				EditorGUILayout.PropertyField(flipX);
-//				if (EditorGUI.EndChangeCheck()) {
-//					FlipX(flipX.boolValue);
-//				}
-//			}
-//			EditorGUI.EndDisabledGroup();
+			EditorGUI.BeginDisabledGroup(multiObject);
+			{
+				string str = boneName.stringValue;
+				if (str == "")
+					str = "<None>";
+				if (multiObject)
+					str = "<Multiple>";
 
-		}
-		EditorGUI.EndDisabledGroup();
+				GUILayout.BeginHorizontal();
+				EditorGUILayout.PrefixLabel("Bone");
 
-		EditorGUILayout.Space();
+				if (GUILayout.Button(str, EditorStyles.popup)) {
+					BoneSelectorContextMenu(str, ((SkeletonUtilityBone)target).skeletonUtility.skeletonRenderer.skeleton.Bones, "<None>", TargetBoneSelected);
+				}
 
-		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, "<Recursively>", SpawnChildBoneSelected);
+				GUILayout.EndHorizontal();
 			}
 			EditorGUI.EndDisabledGroup();
 
-			EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || utilityBone.bone == null || containsOverrides);
+			EditorGUILayout.PropertyField(zPosition);
+			EditorGUILayout.PropertyField(position);
+			EditorGUILayout.PropertyField(rotation);
+			EditorGUILayout.PropertyField(scale);
+			// MITCH
+			//		EditorGUILayout.PropertyField(flip);
+
+			EditorGUI.BeginDisabledGroup(containsFollows);
 			{
-				if (GUILayout.Button(new GUIContent("Add Override", SpineEditorUtilities.Icons.poseBones), GUILayout.Width(150), GUILayout.Height(24)))
-					SpawnOverride();
+				EditorGUILayout.PropertyField(overrideAlpha);
+				EditorGUILayout.PropertyField(parentReference);
+
+				// MITCH
+				//			EditorGUI.BeginDisabledGroup(multiObject || !flip.boolValue);
+				//			{
+				//				EditorGUI.BeginChangeCheck();
+				//				EditorGUILayout.PropertyField(flipX);
+				//				if (EditorGUI.EndChangeCheck()) {
+				//					FlipX(flipX.boolValue);
+				//				}
+				//			}
+				//			EditorGUI.EndDisabledGroup();
+
 			}
 			EditorGUI.EndDisabledGroup();
 
-			EditorGUI.BeginDisabledGroup(multiObject || !utilityBone.valid || !canCreateHingeChain);
+			EditorGUILayout.Space();
+
+			GUILayout.BeginHorizontal();
 			{
-				if (GUILayout.Button(new GUIContent("Create Hinge Chain", SpineEditorUtilities.Icons.hingeChain), GUILayout.Width(150), GUILayout.Height(24)))
-					CreateHingeChain();
-			}
-			EditorGUI.EndDisabledGroup();
+				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, "<Recursively>", SpawnChildBoneSelected);
+				}
+				EditorGUI.EndDisabledGroup();
 
-		}
-		GUILayout.EndHorizontal();
+				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 || boundingBoxTable.Count == 0);
-		EditorGUILayout.LabelField(new GUIContent("Bounding Boxes", SpineEditorUtilities.Icons.boundingBox), EditorStyles.boldLabel);
+				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<PolygonCollider2D>();
+							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);
+						}
 
-		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<PolygonCollider2D>();
-						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();
 				}
-				GUILayout.EndHorizontal();
 			}
-		}
 
-		EditorGUI.EndDisabledGroup();
+			EditorGUI.EndDisabledGroup();
 
-		serializedObject.ApplyModifiedProperties();
-	}
+			serializedObject.ApplyModifiedProperties();
+		}
 
-	// MITCH
-//	void FlipX (bool state) {
-//		utilityBone.FlipX(state);
-//		if (Application.isPlaying == false) {
-//			skeletonUtility.skeletonAnimation.LateUpdate();
-//		}
-//	}
+		// MITCH
+		//	void FlipX (bool state) {
+		//		utilityBone.FlipX(state);
+		//		if (Application.isPlaying == false) {
+		//			skeletonUtility.skeletonAnimation.LateUpdate();
+		//		}
+		//	}
 
-	void BoneSelectorContextMenu (string current, ExposedList<Bone> bones, string topValue, GenericMenu.MenuFunction2 callback) {
-		GenericMenu menu = new GenericMenu();
+		void BoneSelectorContextMenu (string current, ExposedList<Bone> bones, string topValue, GenericMenu.MenuFunction2 callback) {
+			GenericMenu menu = new GenericMenu();
 
-		if (topValue != "")
-			menu.AddItem(new GUIContent(topValue), current == topValue, callback, null);
+			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]);
-		}
+			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();
+			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();
+		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();
+				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<SkeletonUtilityBone>();
-				foreach (SkeletonUtilityBone utilBone in newUtilityBones)
-					SkeletonUtilityInspector.AttachIcon(utilBone);
+		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<SkeletonUtilityBone>();
+					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<SkeletonUtilityBone>());
+				Selection.activeGameObject = go;
+				EditorGUIUtility.PingObject(go);
 			}
-		} else {
-			Bone bone = (Bone)obj;
-			GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
+		}
+
+		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<SkeletonUtilityBone>());
 			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<SkeletonUtilityBone>());
-		Selection.activeGameObject = go;
-		EditorGUIUtility.PingObject(go);
-	}
+		bool CanCreateHingeChain () {
+			if (utilityBone == null)
+				return false;
+			if (utilityBone.GetComponent<Rigidbody>() != null)
+				return false;
+			if (utilityBone.bone != null && utilityBone.bone.Children.Count == 0)
+				return false;
 
-	bool CanCreateHingeChain () {
-		if (utilityBone == null)
-			return false;
-		if (utilityBone.GetComponent<Rigidbody>() != null)
-			return false;
-		if (utilityBone.bone != null && utilityBone.bone.Children.Count == 0)
-			return false;
+			Rigidbody[] rigidbodies = utilityBone.GetComponentsInChildren<Rigidbody>();
 
-		Rigidbody[] rigidbodies = utilityBone.GetComponentsInChildren<Rigidbody>();
+			if (rigidbodies.Length > 0)
+				return false;
 
-		if (rigidbodies.Length > 0)
-			return false;
-
-		return true;
-	}
+			return true;
+		}
 
-	void CreateHingeChain () {
-		var utilBoneArr = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
+		void CreateHingeChain () {
+			var utilBoneArr = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
 
-		foreach (var utilBone in utilBoneArr) {
-			AttachRigidbody(utilBone);
-		}
+			foreach (var utilBone in utilBoneArr) {
+				AttachRigidbody(utilBone);
+			}
 
-		utilityBone.GetComponent<Rigidbody>().isKinematic = true;
+			utilityBone.GetComponent<Rigidbody>().isKinematic = true;
 
-		foreach (var utilBone in utilBoneArr) {
-			if (utilBone == utilityBone)
-				continue;
+			foreach (var utilBone in utilBoneArr) {
+				if (utilBone == utilityBone)
+					continue;
 
-			utilBone.mode = SkeletonUtilityBone.Mode.Override;
+				utilBone.mode = SkeletonUtilityBone.Mode.Override;
 
-			HingeJoint joint = utilBone.gameObject.AddComponent<HingeJoint>();
-			joint.axis = Vector3.forward;
-			joint.connectedBody = utilBone.transform.parent.GetComponent<Rigidbody>();
-			joint.useLimits = true;
-			JointLimits limits = new JointLimits();
-			limits.min = -20;
-			limits.max = 20;
-			joint.limits = limits;
-			utilBone.GetComponent<Rigidbody>().mass = utilBone.transform.parent.GetComponent<Rigidbody>().mass * 0.75f;
-		}
-	}
-	
-	static void AttachRigidbody (SkeletonUtilityBone utilBone) {
-		if (utilBone.GetComponent<Collider>() == null) {
-			if (utilBone.bone.Data.Length == 0) {
-				SphereCollider sphere = utilBone.gameObject.AddComponent<SphereCollider>();
-				sphere.radius = 0.1f;
-			} else {
-				float length = utilBone.bone.Data.Length;
-				BoxCollider box = utilBone.gameObject.AddComponent<BoxCollider>();
-				box.size = new Vector3(length, length / 3, 0.2f);
-				box.center = new Vector3(length / 2, 0, 0);
+				HingeJoint joint = utilBone.gameObject.AddComponent<HingeJoint>();
+				joint.axis = Vector3.forward;
+				joint.connectedBody = utilBone.transform.parent.GetComponent<Rigidbody>();
+				joint.useLimits = true;
+				JointLimits limits = new JointLimits();
+				limits.min = -20;
+				limits.max = 20;
+				joint.limits = limits;
+				utilBone.GetComponent<Rigidbody>().mass = utilBone.transform.parent.GetComponent<Rigidbody>().mass * 0.75f;
 			}
 		}
 
-		utilBone.gameObject.AddComponent<Rigidbody>();
+		static void AttachRigidbody (SkeletonUtilityBone utilBone) {
+			if (utilBone.GetComponent<Collider>() == null) {
+				if (utilBone.bone.Data.Length == 0) {
+					SphereCollider sphere = utilBone.gameObject.AddComponent<SphereCollider>();
+					sphere.radius = 0.1f;
+				} else {
+					float length = utilBone.bone.Data.Length;
+					BoxCollider box = utilBone.gameObject.AddComponent<BoxCollider>();
+					box.size = new Vector3(length, length / 3, 0.2f);
+					box.center = new Vector3(length / 2, 0, 0);
+				}
+			}
+
+			utilBone.gameObject.AddComponent<Rigidbody>();
+		}
 	}
+
 }

+ 196 - 193
spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs

@@ -18,265 +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
+			});
 		}
-		
-		
-		
-		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<SkeletonUtilityBone>();
-			foreach (var utilBone in utilityBones) {
-				AttachIcon(utilBone);
+		static void AttachIconsToChildren (Transform root) {
+			if (root != null) {
+				var utilityBones = root.GetComponentsInChildren<SkeletonUtilityBone>();
+				foreach (var utilBone in utilityBones) {
+					AttachIcon(utilBone);
+				}
 			}
 		}
-	}
 
-	static SkeletonUtilityInspector () {
+		static SkeletonUtilityInspector () {
+			#if UNITY_4_3
+			showSlots = false;
+			#else
+			showSlots = new AnimBool(false);
+			#endif
+		}
+
+		SkeletonUtility skeletonUtility;
+		Skeleton skeleton;
+		SkeletonRenderer skeletonRenderer;
+		Transform transform;
+		bool isPrefab;
+		Dictionary<Slot, List<Attachment>> attachmentTable = new Dictionary<Slot, List<Attachment>>();
+
+
+		//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<Slot, List<Attachment>> attachmentTable = new Dictionary<Slot, List<Attachment>>();
+		void OnEnable () {
+			skeletonUtility = (SkeletonUtility)target;
+			skeletonRenderer = skeletonUtility.GetComponent<SkeletonRenderer>();
+			skeleton = skeletonRenderer.skeleton;
+			transform = skeletonRenderer.transform;
 
+			if (skeleton == null) {
+				skeletonRenderer.Initialize(false);
+				skeletonRenderer.LateUpdate();
 
-	//GUI stuff
-#if UNITY_4_3
-	static bool showSlots;
-#else
-	static AnimBool showSlots;
-#endif
+				skeleton = skeletonRenderer.skeleton;
+			}
 
-	void OnEnable () {
-		skeletonUtility = (SkeletonUtility)target;
-		skeletonRenderer = skeletonUtility.GetComponent<SkeletonRenderer>();
-		skeleton = skeletonRenderer.skeleton;
-		transform = skeletonRenderer.transform;
+			UpdateAttachments();
 
-		if (skeleton == null) {
-			skeletonRenderer.Initialize(false);
-			skeletonRenderer.LateUpdate();
+			if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
+				isPrefab = true;
 
-			skeleton = skeletonRenderer.skeleton;
 		}
 
-		UpdateAttachments();
+		void OnSceneGUI () {
+			if (skeleton == null) {
+				OnEnable();
+				return;
+			}
 
-		if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
-			isPrefab = true;
+			// 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;
 
-	void OnSceneGUI () {
-		if (skeleton == null) {
-			OnEnable();
-			return;
+				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));
+			}
 		}
 
-		// MITCH
-		//float flipRotation = skeleton.FlipX ? -1 : 1;
-		const float flipRotation = 1;
+		void UpdateAttachments () {
+			attachmentTable = new Dictionary<Slot, List<Attachment>>();
+			Skin skin = skeleton.Skin;
 
-		foreach (Bone b in skeleton.Bones) {
-			Vector3 vec = transform.TransformPoint(new Vector3(b.WorldX, b.WorldY, 0));
+			if (skin == null) {
+				skin = skeletonRenderer.skeletonDataAsset.GetSkeletonData(true).DefaultSkin;
+			}
 
-			// MITCH
-			Quaternion rot = Quaternion.Euler(0, 0, b.WorldRotationX * flipRotation);
-			Vector3 forward = transform.TransformDirection(rot * Vector3.right);
-			forward *= flipRotation;
+			for (int i = skeleton.Slots.Count-1; i >= 0; i--) {
+				List<Attachment> attachments = new List<Attachment>();
+				skin.FindAttachmentsForSlot(i, attachments);
 
-			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));
+				attachmentTable.Add(skeleton.Slots.Items[i], attachments);
+			}
 		}
-	}
 
-	void UpdateAttachments () {
-		attachmentTable = new Dictionary<Slot, List<Attachment>>();
-		Skin skin = skeleton.Skin;
+		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<SkeletonRenderer>();
 
-		if (skin == null) {
-			skin = skeletonRenderer.skeletonDataAsset.GetSkeletonData(true).DefaultSkin;
-		}
+				if (skeletonUtility.boneRoot != null) {
+					return;
+				}
 
-		for (int i = skeleton.Slots.Count-1; i >= 0; i--) {
-			List<Attachment> attachments = new List<Attachment>();
-			skin.FindAttachmentsForSlot(i, attachments);
+				skeletonUtility.SpawnHierarchy(mode, pos, rot, sca);
 
-			attachmentTable.Add(skeleton.Slots.Items[i], attachments);
+				SkeletonUtilityBone[] boneComps = skeletonUtility.GetComponentsInChildren<SkeletonUtilityBone>();
+				foreach (SkeletonUtilityBone b in boneComps) 
+					AttachIcon(b);
+			}
 		}
-	}
 
-	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<SkeletonRenderer>();
-
-			if (skeletonUtility.boneRoot != null) {
+		public override void OnInspectorGUI () {
+			if (isPrefab) {
+				GUILayout.Label(new GUIContent("Cannot edit Prefabs", SpineEditorUtilities.Icons.warning));
 				return;
 			}
 
-			skeletonUtility.SpawnHierarchy(mode, pos, rot, sca);
+			skeletonUtility.boneRoot = (Transform)EditorGUILayout.ObjectField("Bone Root", skeletonUtility.boneRoot, typeof(Transform), true);
 
-			SkeletonUtilityBone[] boneComps = skeletonUtility.GetComponentsInChildren<SkeletonUtilityBone>();
-			foreach (SkeletonUtilityBone b in boneComps) 
-				AttachIcon(b);
-		}
-	}
+			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();
 
-	public override void OnInspectorGUI () {
-		if (isPrefab) {
-			GUILayout.Label(new GUIContent("Cannot edit Prefabs", SpineEditorUtilities.Icons.warning));
-			return;
-		}
+			//		if (GUILayout.Button(new GUIContent("Spawn Submeshes", SpineEditorUtilities.Icons.subMeshRenderer), GUILayout.Width(150), GUILayout.Height(24)))
+			//			skeletonUtility.SpawnSubRenderers(true);
 
-		skeletonUtility.boneRoot = (Transform)EditorGUILayout.ObjectField("Bone Root", skeletonUtility.boneRoot, typeof(Transform), true);
+			GUILayout.EndHorizontal();
 
-		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();
-		}
+			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<Slot, List<Attachment>> pair in attachmentTable) {
+			#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<Slot, List<Attachment>> pair in attachmentTable) {
 
-				Slot slot = pair.Key;
+					Slot slot = pair.Key;
 
-				EditorGUILayout.BeginHorizontal();
-				EditorGUI.indentLevel = 1;
-				EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, SpineEditorUtilities.Icons.slot), GUILayout.ExpandWidth(false));
+					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));
+					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 (EditorGUI.EndChangeCheck()) {
+						slot.SetColor(c);
+						skeletonRenderer.LateUpdate();
+					}
 
-				EditorGUILayout.EndHorizontal();
+					EditorGUILayout.EndHorizontal();
 
 
 
-				foreach (Attachment attachment in pair.Value) {
+					foreach (Attachment attachment in pair.Value) {
 
-					if (slot.Attachment == attachment) {
-						GUI.contentColor = Color.white;
-					} else {
-						GUI.contentColor = Color.grey;
-					}
+						if (slot.Attachment == attachment) {
+							GUI.contentColor = Color.white;
+						} else {
+							GUI.contentColor = Color.grey;
+						}
 
-					EditorGUI.indentLevel = 2;
-					bool isAttached = attachment == slot.Attachment;
+						EditorGUI.indentLevel = 2;
+						bool isAttached = attachment == slot.Attachment;
 
-					Texture2D icon = null;
+						Texture2D icon = null;
 
-					if (attachment is MeshAttachment || attachment is WeightedMeshAttachment)
-						icon = SpineEditorUtilities.Icons.mesh;
-					else
-						icon = SpineEditorUtilities.Icons.image;
+						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);
+						bool swap = EditorGUILayout.ToggleLeft(new GUIContent(attachment.Name, icon), attachment == slot.Attachment);
 
-					if (!isAttached && swap) {
-						slot.Attachment = attachment;
-						skeletonRenderer.LateUpdate();
-					} else if (isAttached && !swap) {
+						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
+				#if UNITY_4_3
 
-#else
+				#else
+			}
+			EditorGUILayout.EndFadeGroup();
+			if (showSlots.isAnimating)
+				Repaint();
+				#endif
 		}
-		EditorGUILayout.EndFadeGroup();
-		if (showSlots.isAnimating)
-			Repaint();
-#endif
-	}
 
-	void SpawnHierarchyContextMenu () {
-		GenericMenu menu = new GenericMenu();
+		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.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();
-	}
+			menu.ShowAsContext();
+		}
 
-	void SpawnFollowHierarchy () {
-		Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true);
-		AttachIconsToChildren(skeletonUtility.boneRoot);
-	}
+		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 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);
+		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);
+		}
 	}
+
 }

+ 11 - 41
spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilitySubmeshRendererInspector.cs

@@ -28,52 +28,22 @@
  * 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 System;
-using System.Collections;
-using System.Reflection;
-
-[CustomEditor(typeof(SkeletonUtilitySubmeshRenderer))]
-public class SkeletonUtilitySubmeshRendererInspector : Editor {
-
-	private static MethodInfo EditorGUILayoutSortingLayerField;
-	protected SerializedObject rendererSerializedObject;
-	protected SerializedProperty sortingLayerIDProperty;
-
-	SkeletonUtilitySubmeshRenderer component;
-
-	void OnEnable () {
-		component = (SkeletonUtilitySubmeshRenderer)target;
+using Spine.Unity.Editor;
 
-		if (EditorGUILayoutSortingLayerField == null)
-			EditorGUILayoutSortingLayerField = typeof(EditorGUILayout).GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(GUIContent), typeof(SerializedProperty), typeof(GUIStyle) }, null);
+namespace Spine.Unity.Modules {
+	[CustomEditor(typeof(SkeletonUtilitySubmeshRenderer))]
+	public class SkeletonUtilitySubmeshRendererInspector : UnityEditor.Editor {
+		public SpineInspectorUtility.SerializedSortingProperties sorting;
 
-		rendererSerializedObject = new SerializedObject(((SkeletonUtilitySubmeshRenderer)target).GetComponent<Renderer>());
-		sortingLayerIDProperty = rendererSerializedObject.FindProperty("m_SortingLayerID");
-	}
-
-	public override void OnInspectorGUI () {
-		// Sorting Layers
-		{
-			var renderer = component.GetComponent<Renderer>();
-			if (renderer != null) {
-				EditorGUI.BeginChangeCheck();
-
-				if (EditorGUILayoutSortingLayerField != null && sortingLayerIDProperty != null) {
-					EditorGUILayoutSortingLayerField.Invoke(null, new object[] { new GUIContent("Sorting Layer"), sortingLayerIDProperty, EditorStyles.popup });
-				} else {
-					renderer.sortingLayerID = EditorGUILayout.IntField("Sorting Layer ID", renderer.sortingLayerID);
-				}
-
-				renderer.sortingOrder = EditorGUILayout.IntField("Order in Layer", renderer.sortingOrder);
+		void OnEnable () {			
+			sorting = new SpineInspectorUtility.SerializedSortingProperties((target as Component).GetComponent<Renderer>());
+		}
 
-				if (EditorGUI.EndChangeCheck()) {
-					rendererSerializedObject.ApplyModifiedProperties();
-					EditorUtility.SetDirty(renderer);
-				}
-			}
+		public override void OnInspectorGUI () {
+			EditorGUILayout.HelpBox("SkeletonUtilitySubmeshRenderer is now obsolete. We recommend using SkeletonRenderSeparator.", MessageType.Info);
+			SpineInspectorUtility.SortingPropertyFields(sorting, true);
 		}
 	}
 }

+ 264 - 281
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 {
-
-	public static T GetInParent<T> (Transform origin) where T : Component {
-#if UNITY_4_3
-		Transform parent = origin.parent;
-		while(parent.GetComponent<T>() == null){
+namespace Spine.Unity {
+	[RequireComponent(typeof(ISkeletonAnimation))]
+	[ExecuteInEditMode]
+	public class SkeletonUtility : MonoBehaviour {
+
+		public static T GetInParent<T> (Transform origin) where T : Component {
+			#if UNITY_4_3
+			Transform parent = origin.parent;
+			while(parent.GetComponent<T>() == null){
 			parent = parent.parent;
 			if(parent == null)
-				return default(T);
+			return default(T);
+			}
+
+			return parent.GetComponent<T>();
+			#else
+			return origin.GetComponentInParent<T>();
+			#endif
 		}
 
-		return parent.GetComponent<T>();
-#else
-		return origin.GetComponentInParent<T>();
-#endif
-	}
+		public static PolygonCollider2D AddBoundingBox (Skeleton skeleton, string skinName, string slotName, string attachmentName, Transform parent, bool isTrigger = true) {
+			// List<Attachment> attachments = new List<Attachment>();
+			Skin skin;
+
+			if (skinName == "")
+				skinName = skeleton.Data.DefaultSkin.Name;
+
+			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<PolygonCollider2D>();
+				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];
+				}
 
-	public static PolygonCollider2D AddBoundingBox (Skeleton skeleton, string skinName, string slotName, string attachmentName, Transform parent, bool isTrigger = true) {
-		// List<Attachment> attachments = new List<Attachment>();
-		Skin skin;
+				collider.SetPath(0, verts);
 
-		if (skinName == "")
-			skinName = skeleton.Data.DefaultSkin.Name;
+				return collider;
 
-		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<PolygonCollider2D>();
+		public static PolygonCollider2D AddBoundingBoxAsComponent (BoundingBoxAttachment boundingBox, GameObject gameObject, bool isTrigger = true) {
+			if (boundingBox == null)
+				return null;
+
+			var collider = gameObject.AddComponent<PolygonCollider2D>();
 			collider.isTrigger = isTrigger;
-			var boundingBox = (BoundingBoxAttachment)attachment;
 			float[] floats = boundingBox.Vertices;
 			int floatCount = floats.Length;
 			int vertCount = floatCount / 2;
@@ -67,338 +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<PolygonCollider2D>();
-		collider.isTrigger = isTrigger;
-		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];
-		}
+			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;
 
-		collider.SetPath(0, verts);
+			return bounds;
+		}
 
-		return collider;
-	}
+		public delegate void SkeletonUtilityDelegate ();
 
-	public static Bounds GetBoundingBoxBounds (BoundingBoxAttachment boundingBox, float depth = 0) {
-		float[] floats = boundingBox.Vertices;
-		int floatCount = floats.Length;
+		public event SkeletonUtilityDelegate OnReset;
 
-		Bounds bounds = new Bounds();
+		public Transform boneRoot;
 
-		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;
+		void Update () {
+			if (boneRoot != null && skeletonRenderer.skeleton != null) {
+				Vector3 flipScale = Vector3.one;
+				if (skeletonRenderer.skeleton.FlipX)
+					flipScale.x = -1;
 
-		return bounds;
-	}
+				if (skeletonRenderer.skeleton.FlipY)
+					flipScale.y = -1;
 
-	public delegate void SkeletonUtilityDelegate ();
+				boneRoot.localScale = flipScale;
+			}
+		}
 
-	public event SkeletonUtilityDelegate OnReset;
+		[HideInInspector]
+		public SkeletonRenderer skeletonRenderer;
+		[HideInInspector]
+		public ISkeletonAnimation skeletonAnimation;
+		[System.NonSerialized]
+		public List<SkeletonUtilityBone> utilityBones = new List<SkeletonUtilityBone>();
+		[System.NonSerialized]
+		public List<SkeletonUtilityConstraint> utilityConstraints = new List<SkeletonUtilityConstraint>();
+		//	Dictionary<Bone, SkeletonUtilityBone> utilityBoneTable;
+
+		protected bool hasTransformBones;
+		protected bool hasUtilityConstraints;
+		protected bool needToReprocessBones;
+
+		void OnEnable () {
+			if (skeletonRenderer == null) {
+				skeletonRenderer = GetComponent<SkeletonRenderer>();
+			}
 
-	public Transform boneRoot;
+			if (skeletonAnimation == null) {
+				skeletonAnimation = GetComponent<SkeletonAnimation>();
+				if (skeletonAnimation == null)
+					skeletonAnimation = GetComponent<SkeletonAnimator>();
+			}
 
-	void Update () {
-		if (boneRoot != null && skeletonRenderer.skeleton != null) {
-			Vector3 flipScale = Vector3.one;
-			if (skeletonRenderer.skeleton.FlipX)
-				flipScale.x = -1;
+			skeletonRenderer.OnRebuild -= HandleRendererReset;
+			skeletonRenderer.OnRebuild += HandleRendererReset;
 
-			if (skeletonRenderer.skeleton.FlipY)
-				flipScale.y = -1;
+			if (skeletonAnimation != null) {
+				skeletonAnimation.UpdateLocal -= UpdateLocal;
+				skeletonAnimation.UpdateLocal += UpdateLocal;
+			}
 
-			boneRoot.localScale = flipScale;
-		}
-	}
 
-	[HideInInspector]
-	public SkeletonRenderer skeletonRenderer;
-	[HideInInspector]
-	public ISkeletonAnimation skeletonAnimation;
-	[System.NonSerialized]
-	public List<SkeletonUtilityBone> utilityBones = new List<SkeletonUtilityBone>();
-	[System.NonSerialized]
-	public List<SkeletonUtilityConstraint> utilityConstraints = new List<SkeletonUtilityConstraint>();
-	//	Dictionary<Bone, SkeletonUtilityBone> utilityBoneTable;
-
-	protected bool hasTransformBones;
-	protected bool hasUtilityConstraints;
-	protected bool needToReprocessBones;
-
-	void OnEnable () {
-		if (skeletonRenderer == null) {
-			skeletonRenderer = GetComponent<SkeletonRenderer>();
+			CollectBones();
 		}
 
-		if (skeletonAnimation == null) {
-			skeletonAnimation = GetComponent<SkeletonAnimation>();
-			if (skeletonAnimation == null)
-				skeletonAnimation = GetComponent<SkeletonAnimator>();
+		void Start () {
+			//recollect because order of operations failure when switching between game mode and edit mode...
+			//		CollectBones();
 		}
 
-		skeletonRenderer.OnRebuild -= HandleRendererReset;
-		skeletonRenderer.OnRebuild += HandleRendererReset;
+		void OnDisable () {
+			skeletonRenderer.OnRebuild -= HandleRendererReset;
 
-		if (skeletonAnimation != null) {
-			skeletonAnimation.UpdateLocal -= UpdateLocal;
-			skeletonAnimation.UpdateLocal += UpdateLocal;
+			if (skeletonAnimation != null) {
+				skeletonAnimation.UpdateLocal -= UpdateLocal;
+				skeletonAnimation.UpdateWorld -= UpdateWorld;
+				skeletonAnimation.UpdateComplete -= UpdateComplete;
+			}
 		}
 
+		void HandleRendererReset (SkeletonRenderer r) {
+			if (OnReset != null)
+				OnReset();
 
-		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;
+			CollectBones();
 		}
-	}
-
-	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 RegisterBone (SkeletonUtilityBone bone) {
-		if (utilityBones.Contains(bone))
-			return;
-		else {
-			utilityBones.Add(bone);
-			needToReprocessBones = true;
+		public void UnregisterBone (SkeletonUtilityBone bone) {
+			utilityBones.Remove(bone);
 		}
-	}
 
-	public void UnregisterBone (SkeletonUtilityBone bone) {
-		utilityBones.Remove(bone);
-	}
+		public void RegisterConstraint (SkeletonUtilityConstraint constraint) {
 
-	public void RegisterConstraint (SkeletonUtilityConstraint constraint) {
+			if (utilityConstraints.Contains(constraint))
+				return;
+			else {
+				utilityConstraints.Add(constraint);
+				needToReprocessBones = true;
+			}
+		}
 
-		if (utilityConstraints.Contains(constraint))
-			return;
-		else {
-			utilityConstraints.Add(constraint);
-			needToReprocessBones = true;
+		public void UnregisterConstraint (SkeletonUtilityConstraint constraint) {
+			utilityConstraints.Remove(constraint);
 		}
-	}
 
-	public void UnregisterConstraint (SkeletonUtilityConstraint constraint) {
-		utilityConstraints.Remove(constraint);
-	}
+		public void CollectBones () {
+			if (skeletonRenderer.skeleton == null)
+				return;
 
-	public void CollectBones () {
-		if (skeletonRenderer.skeleton == null)
-			return;
+			if (boneRoot != null) {
+				List<string> constraintTargetNames = new List<string>();
 
-		if (boneRoot != null) {
-			List<string> constraintTargetNames = new List<string>();
+				ExposedList<IkConstraint> ikConstraints = skeletonRenderer.skeleton.IkConstraints;
+				for (int i = 0, n = ikConstraints.Count; i < n; i++)
+					constraintTargetNames.Add(ikConstraints.Items[i].Target.Data.Name);
 
-			ExposedList<IkConstraint> 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;
+					}
 
-			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 (constraintTargetNames.Contains(b.bone.Data.Name)) {
+				if (utilityConstraints.Count > 0)
 					hasUtilityConstraints = true;
-				}
-			}
 
-			if (utilityConstraints.Count > 0)
-				hasUtilityConstraints = true;
+				if (skeletonAnimation != null) {
+					skeletonAnimation.UpdateWorld -= UpdateWorld;
+					skeletonAnimation.UpdateComplete -= UpdateComplete;
 
-			if (skeletonAnimation != null) {
-				skeletonAnimation.UpdateWorld -= UpdateWorld;
-				skeletonAnimation.UpdateComplete -= UpdateComplete;
+					if (hasTransformBones || hasUtilityConstraints) {
+						skeletonAnimation.UpdateWorld += UpdateWorld;
+					}
 
-				if (hasTransformBones || hasUtilityConstraints) {
-					skeletonAnimation.UpdateWorld += UpdateWorld;
+					if (hasUtilityConstraints) {
+						skeletonAnimation.UpdateComplete += UpdateComplete;
+					}
 				}
 
-				if (hasUtilityConstraints) {
-					skeletonAnimation.UpdateComplete += UpdateComplete;
-				}
+				needToReprocessBones = false;
+			} else {
+				utilityBones.Clear();
+				utilityConstraints.Clear();
 			}
 
-			needToReprocessBones = false;
-		} else {
-			utilityBones.Clear();
-			utilityConstraints.Clear();
 		}
 
-	}
+		void UpdateLocal (ISkeletonAnimation anim) {
 
-	void UpdateLocal (ISkeletonAnimation anim) {
+			if (needToReprocessBones)
+				CollectBones();
 
-		if (needToReprocessBones)
-			CollectBones();
+			if (utilityBones == null)
+				return;
 
-		if (utilityBones == null)
-			return;
+			foreach (SkeletonUtilityBone b in utilityBones) {
+				b.transformLerpComplete = false;
+			}
 
-		foreach (SkeletonUtilityBone b in utilityBones) {
-			b.transformLerpComplete = false;
+			UpdateAllBones();
 		}
 
-		UpdateAllBones();
-	}
-
-	void UpdateWorld (ISkeletonAnimation anim) {
-		UpdateAllBones();
+		void UpdateWorld (ISkeletonAnimation anim) {
+			UpdateAllBones();
 
-		foreach (SkeletonUtilityConstraint c in utilityConstraints)
-			c.DoUpdate();
-	}
-
-	void UpdateComplete (ISkeletonAnimation anim) {
-		UpdateAllBones();
-	}
-
-	void UpdateAllBones () {
-		if (boneRoot == null) {
-			CollectBones();
+			foreach (SkeletonUtilityConstraint c in utilityConstraints)
+				c.DoUpdate();
 		}
 
-		if (utilityBones == null)
-			return;
-
-		foreach (SkeletonUtilityBone b in utilityBones) {
-			b.DoUpdate();
+		void UpdateComplete (ISkeletonAnimation anim) {
+			UpdateAllBones();
 		}
-	}
 
-	public Transform GetBoneRoot () {
-		if (boneRoot != null)
-			return boneRoot;
+		void UpdateAllBones () {
+			if (boneRoot == null) {
+				CollectBones();
+			}
 
-		boneRoot = new GameObject("SkeletonUtility-Root").transform;
-		boneRoot.parent = transform;
-		boneRoot.localPosition = Vector3.zero;
-		boneRoot.localRotation = Quaternion.identity;
-		boneRoot.localScale = Vector3.one;
+			if (utilityBones == null)
+				return;
 
-		return boneRoot;
-	}
+			foreach (SkeletonUtilityBone b in utilityBones) {
+				b.DoUpdate();
+			}
+		}
 
-	public GameObject SpawnRoot (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
-		GetBoneRoot();
-		Skeleton skeleton = this.skeletonRenderer.skeleton;
+		public Transform GetBoneRoot () {
+			if (boneRoot != null)
+				return boneRoot;
 
-		GameObject go = SpawnBone(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
+			boneRoot = new GameObject("SkeletonUtility-Root").transform;
+			boneRoot.parent = transform;
+			boneRoot.localPosition = Vector3.zero;
+			boneRoot.localRotation = Quaternion.identity;
+			boneRoot.localScale = Vector3.one;
 
-		CollectBones();
+			return boneRoot;
+		}
 
-		return go;
-	}
+		public GameObject SpawnRoot (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
+			GetBoneRoot();
+			Skeleton skeleton = this.skeletonRenderer.skeleton;
 
-	public GameObject SpawnHierarchy (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
-		GetBoneRoot();
+			GameObject go = SpawnBone(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
 
-		Skeleton skeleton = this.skeletonRenderer.skeleton;
+			CollectBones();
 
-		GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
+			return go;
+		}
 
-		CollectBones();
+		public GameObject SpawnHierarchy (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
+			GetBoneRoot();
 
-		return go;
-	}
+			Skeleton skeleton = this.skeletonRenderer.skeleton;
 
-	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);
+			GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
 
-		ExposedList<Bone> 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);
+			CollectBones();
+
+			return go;
 		}
 
-		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);
 
-	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<SkeletonUtilityBone>();
-		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);
-		}
+			ExposedList<Bone> 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;
-	}
+			return go;
+		}
 
-	public void SpawnSubRenderers (bool disablePrimaryRenderer) {
-		int submeshCount = GetComponent<MeshFilter>().sharedMesh.subMeshCount;
+		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;
 
-		for (int i = 0; i < submeshCount; i++) {
-			GameObject go = new GameObject("Submesh " + i, typeof(MeshFilter), typeof(MeshRenderer));
-			go.transform.parent = transform;
-			go.transform.localPosition = Vector3.zero;
-			go.transform.localRotation = Quaternion.identity;
-			go.transform.localScale = Vector3.one;
+			SkeletonUtilityBone b = go.AddComponent<SkeletonUtilityBone>();
+			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);
+			}
 
-			SkeletonUtilitySubmeshRenderer s = go.AddComponent<SkeletonUtilitySubmeshRenderer>();
-			s.GetComponent<Renderer>().sortingOrder = i * 10;
-			s.submeshIndex = i;
+			return go;
 		}
 
-		skeletonRenderer.CollectSubmeshRenderers();
-
-		if (disablePrimaryRenderer)
-			GetComponent<Renderer>().enabled = false;
 	}
+
 }

+ 226 - 224
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs

@@ -11,274 +11,276 @@ using System.Collections.Generic;
 using UnityEngine;
 using Spine;
 
-/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
-[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;
-
-	/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
-	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; }
-	}
+namespace Spine.Unity {
+	/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
+	[ExecuteInEditMode]
+	[AddComponentMenu("Spine/SkeletonUtilityBone")]
+	public class SkeletonUtilityBone : MonoBehaviour {
+
+		public enum Mode {
+			Follow,
+			Override
+		}
 
-	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();
-	}
+		[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;
 
-	void OnEnable () {
-		skeletonUtility = SkeletonUtility.GetInParent<SkeletonUtility>(transform);
+		/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
+		public String boneName;
+		public Transform parentReference;
 
-		if (skeletonUtility == null)
-			return;
+		[System.NonSerialized]
+		public bool transformLerpComplete;
 
-		skeletonUtility.RegisterBone(this);
-		skeletonUtility.OnReset += HandleOnReset;
-	}
+		protected Transform cachedTransform;
+		protected Transform skeletonTransform;
 
-	void HandleOnReset () {
-		Reset();
-	}
+		// MITCH : nonuniform scale
+		//	private bool nonUniformScaleWarning;
+		//	public bool NonUniformScaleWarning {
+		//		get { return nonUniformScaleWarning; }
+		//	}
+
+		private bool disableInheritScaleWarning;
+		public bool DisableInheritScaleWarning {
+			get { return disableInheritScaleWarning; }
+		}
 
-	void OnDisable () {
-		if (skeletonUtility != null) {
+		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.UnregisterBone(this);
+			skeletonUtility.OnReset += HandleOnReset;
+			DoUpdate();
 		}
-	}
 
-	public void DoUpdate () {
-		if (!valid) {
-			Reset();
-			return;
+		void OnEnable () {
+			skeletonUtility = SkeletonUtility.GetInParent<SkeletonUtility>(transform);
+
+			if (skeletonUtility == null)
+				return;
+
+			skeletonUtility.RegisterBone(this);
+			skeletonUtility.OnReset += HandleOnReset;
 		}
 
-		Spine.Skeleton skeleton = skeletonUtility.skeletonRenderer.skeleton;
+		void HandleOnReset () {
+			Reset();
+		}
 
-		if (bone == null) {
-			if (boneName == null || boneName.Length == 0)
-				return;
-			
-			bone = skeleton.FindBone(boneName);
+		void OnDisable () {
+			if (skeletonUtility != null) {
+				skeletonUtility.OnReset -= HandleOnReset;
+				skeletonUtility.UnregisterBone(this);
+			}
+		}
 
-			if (bone == null) {
-				Debug.LogError("Bone not found: " + boneName, this);
+		public void DoUpdate () {
+			if (!valid) {
+				Reset();
 				return;
 			}
-		}
 
-		float skeletonFlipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f;
+			Spine.Skeleton skeleton = skeletonUtility.skeletonRenderer.skeleton;
 
-		// MITCH : remove flipX
-//		float flipCompensation = 0;
-//		if (flip && (flipX || (flipX != bone.flipX)) && bone.parent != null) {
-//			flipCompensation = bone.parent.WorldRotation * -2;
-//		}
+			if (bone == null) {
+				if (boneName == null || boneName.Length == 0)
+					return;
 
-		if (mode == Mode.Follow) {
-			// MITCH : remove flipX
-//			if (flip)
-//				flipX = bone.flipX;
-				
-			if (position)
-				cachedTransform.localPosition = new Vector3(bone.x, bone.y, 0);
-
-			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));
+				bone = skeleton.FindBone(boneName);
+
+				if (bone == null) {
+					Debug.LogError("Bone not found: " + boneName, this);
+					return;
 				}
 			}
 
-			if (scale) {
-				cachedTransform.localScale = new Vector3(bone.scaleX, bone.scaleY, bone.WorldSignX);
-				// MITCH : nonuniform scale
-				//nonUniformScaleWarning = (bone.scaleX != bone.scaleY);
-				disableInheritScaleWarning = !bone.data.inheritScale;
-			}
+			float skeletonFlipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f;
 
-		} else if (mode == Mode.Override) {
-			if (transformLerpComplete)
-				return;
+			// MITCH : remove flipX
+			//		float flipCompensation = 0;
+			//		if (flip && (flipX || (flipX != bone.flipX)) && bone.parent != null) {
+			//			flipCompensation = bone.parent.WorldRotation * -2;
+			//		}
 
-			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 (mode == Mode.Follow) {
+				// MITCH : remove flipX
+				//			if (flip)
+				//				flipX = bone.flipX;
 
-				if (rotation) {
-					float angle = Mathf.LerpAngle(bone.Rotation, cachedTransform.localRotation.eulerAngles.z, overrideAlpha);
+				if (position)
+					cachedTransform.localPosition = new Vector3(bone.x, bone.y, 0);
 
-					// 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 (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 (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);
+					}
+
+					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
-				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
+					//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
-//					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 (flip)
+					//	bone.flipX = flipX;
 				}
-					
-				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;
+				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);
-	}
+		// 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 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");		
+			if (DisableInheritScaleWarning)
+				Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning");		
+		}
+		#endif
 	}
-	#endif
-}
+}

+ 14 - 13
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<SkeletonUtilityBone>();
+			skeletonUtility = SkeletonUtility.GetInParent<SkeletonUtility>(transform);
+			skeletonUtility.RegisterConstraint(this);
+		}
 
-	protected virtual void OnEnable () {
-		utilBone = GetComponent<SkeletonUtilityBone>();
-		skeletonUtility = SkeletonUtility.GetInParent<SkeletonUtility>(transform);
-		skeletonUtility.RegisterConstraint(this);
-	}
+		protected virtual void OnDisable () {
+			skeletonUtility.UnregisterConstraint(this);
+		}
 
-	protected virtual void OnDisable () {
-		skeletonUtility.UnregisterConstraint(this);
+		public abstract void DoUpdate ();
 	}
-
-	public abstract void DoUpdate ();
 }

+ 32 - 31
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtilitySubmeshRenderer.cs

@@ -28,43 +28,44 @@
  * 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;
 
-[ExecuteInEditMode]
-public class SkeletonUtilitySubmeshRenderer : MonoBehaviour {
-	[System.NonSerialized]
-	public Mesh mesh;
-	public int submeshIndex = 0;
-	public Material hiddenPassMaterial;
-	Renderer cachedRenderer;
-	MeshFilter filter;
-	Material[] sharedMaterials;
+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<Renderer>();
-		filter = GetComponent<MeshFilter>();
-		sharedMaterials = new Material[0];
-	}
+		void Awake () {
+			cachedRenderer = GetComponent<Renderer>();
+			filter = GetComponent<MeshFilter>();
+			sharedMaterials = new Material[0];
+		}
 
-	public void SetMesh (Renderer parentRenderer, Mesh mesh, Material mat) {
-		if (cachedRenderer == null)
-			return;
+		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;
-		}
+			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;
-		}
+			for (int i = 0; i < sharedMaterials.Length; i++) {
+				if (i == submeshIndex)
+					sharedMaterials[i] = mat;
+				else
+					sharedMaterials[i] = hiddenPassMaterial;
+			}
 
-		cachedRenderer.sharedMaterials = sharedMaterials;
+			cachedRenderer.sharedMaterials = sharedMaterials;
+		}
 	}
+
 }

+ 144 - 141
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 = "";
-}
+namespace Spine.Unity {
+	public abstract class SpineAttributeBase : PropertyAttribute {
+		public string dataField = "";
+		public string startsWith = "";
+	}
 
-public class SpineSlot : SpineAttributeBase {
-	public bool containsBoundingBoxes = false;
-
-	/// <summary>
-	/// Smart popup menu for Spine Slots
-	/// </summary>
-	/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
-	/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
-	/// </param>
-	/// <param name="containsBoundingBoxes">Disables popup results that don't contain bounding box attachments when true.</param>
-	public SpineSlot(string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) {
-		this.startsWith = startsWith;
-		this.dataField = dataField;
-		this.containsBoundingBoxes = containsBoundingBoxes;
+	public class SpineSlot : SpineAttributeBase {
+		public bool containsBoundingBoxes = false;
+
+		/// <summary>
+		/// Smart popup menu for Spine Slots
+		/// </summary>
+		/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
+		/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
+		/// </param>
+		/// <param name="containsBoundingBoxes">Disables popup results that don't contain bounding box attachments when true.</param>
+		public SpineSlot(string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) {
+			this.startsWith = startsWith;
+			this.dataField = dataField;
+			this.containsBoundingBoxes = containsBoundingBoxes;
+		}
 	}
-}
 
-public class SpineEvent : SpineAttributeBase {
-	/// <summary>
-	/// Smart popup menu for Spine Events (Spine.EventData)
-	/// </summary>
-	/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
-	/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
-	/// </param>
-	public SpineEvent(string startsWith = "", string dataField = "") {
-		this.startsWith = startsWith;
-		this.dataField = dataField;
+	public class SpineEvent : SpineAttributeBase {
+		/// <summary>
+		/// Smart popup menu for Spine Events (Spine.EventData)
+		/// </summary>
+		/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
+		/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
+		/// </param>
+		public SpineEvent(string startsWith = "", string dataField = "") {
+			this.startsWith = startsWith;
+			this.dataField = dataField;
+		}
 	}
-}
 
-public class SpineSkin : SpineAttributeBase {
-	/// <summary>
-	/// Smart popup menu for Spine Skins
-	/// </summary>
-	/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
-	/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
-	/// </param>
-	public SpineSkin(string startsWith = "", string dataField = "") {
-		this.startsWith = startsWith;
-		this.dataField = dataField;
+	public class SpineSkin : SpineAttributeBase {
+		/// <summary>
+		/// Smart popup menu for Spine Skins
+		/// </summary>
+		/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
+		/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
+		/// </param>
+		public SpineSkin(string startsWith = "", string dataField = "") {
+			this.startsWith = startsWith;
+			this.dataField = dataField;
+		}
 	}
-}
-public class SpineAnimation : SpineAttributeBase {
-	/// <summary>
-	/// Smart popup menu for Spine Animations
-	/// </summary>
-	/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
-	/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
-	/// </param>
-	public SpineAnimation(string startsWith = "", string dataField = "") {
-		this.startsWith = startsWith;
-		this.dataField = dataField;
+	public class SpineAnimation : SpineAttributeBase {
+		/// <summary>
+		/// Smart popup menu for Spine Animations
+		/// </summary>
+		/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
+		/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
+		/// </param>
+		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 = "";
-
-	/// <summary>
-	/// Smart popup menu for Spine Attachments
-	/// </summary>
-	/// <param name="currentSkinOnly">Filters popup results to only include the current Skin.  Only valid when a SkeletonRenderer is the data source.</param>
-	/// <param name="returnAttachmentPath">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.</param>
-	/// <param name="placeholdersOnly">Filters popup results to exclude attachments that are not children of Skin Placeholders</param>
-	/// <param name="slotField">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</param>
-	/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
-	/// </param>
-	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 class SpineAttachment : SpineAttributeBase {
+		public bool returnAttachmentPath = false;
+		public bool currentSkinOnly = false;
+		public bool placeholdersOnly = false;
+		public string slotField = "";
+
+		/// <summary>
+		/// Smart popup menu for Spine Attachments
+		/// </summary>
+		/// <param name="currentSkinOnly">Filters popup results to only include the current Skin.  Only valid when a SkeletonRenderer is the data source.</param>
+		/// <param name="returnAttachmentPath">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.</param>
+		/// <param name="placeholdersOnly">Filters popup results to exclude attachments that are not children of Skin Placeholders</param>
+		/// <param name="slotField">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</param>
+		/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
+		/// </param>
+		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 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;
+		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);
-	}
+			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));
-	}
+		public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) {
+			return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true));
+		}
 
-	/// <summary>
-	/// A struct that represents 3 strings that help identify and locate an attachment in a skeleton.</summary>
-	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 = "";
+		/// <summary>
+		/// A struct that represents 3 strings that help identify and locate an attachment in a skeleton.</summary>
+		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 {
-	/// <summary>
-	/// Smart popup menu for Spine Bones
-	/// </summary>
-	/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
-	/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
-	/// </param>
-	public SpineBone(string startsWith = "", string dataField = "") {
-		this.startsWith = startsWith;
-		this.dataField = dataField;
-	}
+	public class SpineBone : SpineAttributeBase {
+		/// <summary>
+		/// Smart popup menu for Spine Bones
+		/// </summary>
+		/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
+		/// <param name="dataField">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<SkeletonRenderer>() will be called as a fallback.
+		/// </param>
+		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.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.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.
 	}
-}
 
-public class SpineAtlasRegion : PropertyAttribute {
-	//TODO:  Standardize with Skeleton attributes
-	//NOTE:  For now, relies on locally scoped field named "atlasAsset" for source.
 }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно