Forráskód Böngészése

Merge branch '3.8-beta' of https://github.com/EsotericSoftware/spine-runtimes into 3.8-beta

badlogic 6 éve
szülő
commit
03fe2c4779

+ 13 - 3
spine-csharp/src/Animation.cs

@@ -336,7 +336,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			Bone bone = skeleton.bones.Items[boneIndex];
-
+			if (!bone.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				switch (blend) {
@@ -438,7 +438,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			Bone bone = skeleton.bones.Items[boneIndex];
-
+			if (!bone.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				switch (blend) {
@@ -501,7 +501,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			Bone bone = skeleton.bones.Items[boneIndex];
-
+			if (!bone.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				switch (blend) {
@@ -606,6 +606,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			Bone bone = skeleton.bones.Items[boneIndex];
+			if (!bone.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				switch (blend) {
@@ -699,6 +700,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			Slot slot = skeleton.slots.Items[slotIndex];
+			if (!slot.bone.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				var slotData = slot.data;
@@ -816,6 +818,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			Slot slot = skeleton.slots.Items[slotIndex];
+			if (!slot.bone.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				var slotData = slot.data;
@@ -960,6 +963,7 @@ namespace Spine {
 							MixDirection direction) {
 			string attachmentName;
 			Slot slot = skeleton.slots.Items[slotIndex];
+			if (!slot.bone.active) return;
 			if (direction == MixDirection.Out && blend == MixBlend.Setup) {
 				attachmentName = slot.data.attachmentName;
 				slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
@@ -1033,6 +1037,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			Slot slot = skeleton.slots.Items[slotIndex];
+			if (!slot.bone.active) return;
 			VertexAttachment vertexAttachment = slot.attachment as VertexAttachment;
 			if (vertexAttachment == null || vertexAttachment.DeformAttachment != attachment) return;
 
@@ -1393,6 +1398,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
+			if (!constraint.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				switch (blend) {
@@ -1508,6 +1514,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];
+			if (!constraint.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				TransformConstraintData data = constraint.data;
@@ -1608,6 +1615,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
+			if (!constraint.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				switch (blend) {
@@ -1654,6 +1662,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha, MixBlend blend,
 									MixDirection direction) {
 			PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
+			if (!constraint.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				switch (blend) {
@@ -1731,6 +1740,7 @@ namespace Spine {
 		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
 									MixDirection direction) {
 			PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
+			if (!constraint.active) return;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 				switch (blend) {

+ 7 - 5
spine-csharp/src/AnimationState.cs

@@ -351,18 +351,20 @@ namespace Spine {
 			return mix;
 		}
 
-		static private void ApplyRotateTimeline (RotateTimeline rotateTimeline, Skeleton skeleton, float time, float alpha, MixBlend blend,
+		static private void ApplyRotateTimeline (RotateTimeline timeline, Skeleton skeleton, float time, float alpha, MixBlend blend,
 			float[] timelinesRotation, int i, bool firstFrame) {
 
 			if (firstFrame) timelinesRotation[i] = 0;
 
 			if (alpha == 1) {
-				rotateTimeline.Apply(skeleton, 0, time, null, 1, blend, MixDirection.In);
+				timeline.Apply(skeleton, 0, time, null, 1, blend, MixDirection.In);
 				return;
 			}
 
-			Bone bone = skeleton.bones.Items[rotateTimeline.boneIndex];
-			float[] frames = rotateTimeline.frames;
+			Bone bone = skeleton.bones.Items[timeline.boneIndex];
+			if (!bone.active) return;
+
+			float[] frames = timeline.frames;
 			float r1, r2;
 			if (time < frames[0]) { // Time is before first frame.
 				switch (blend) {
@@ -385,7 +387,7 @@ namespace Spine {
 					int frame = Animation.BinarySearch(frames, time, RotateTimeline.ENTRIES);
 					float prevRotation = frames[frame + RotateTimeline.PREV_ROTATION];
 					float frameTime = frames[frame];
-					float percent = rotateTimeline.GetCurvePercent((frame >> 1) - 1,
+					float percent = timeline.GetCurvePercent((frame >> 1) - 1,
 						1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime));
 
 					r2 = frames[frame + RotateTimeline.ROTATION] - prevRotation;

+ 4 - 1
spine-csharp/src/Bone.cs

@@ -52,12 +52,15 @@ namespace Spine {
 		internal float a, b, worldX;
 		internal float c, d, worldY;
 
-		internal bool sorted, update;
+		internal bool sorted, active;
 
 		public BoneData Data { get { return data; } }
 		public Skeleton Skeleton { get { return skeleton; } }
 		public Bone Parent { get { return parent; } }
 		public ExposedList<Bone> Children { get { return children; } }
+		/// <summary>Returns false when the bone has not been computed because <see cref="BoneData.SkinRequired"/> is true and the
+		/// <see cref="Skeleton.Skin">active skin</see> does not <see cref="Skin.Bones">contain</see> this bone.</summary>
+		public bool Active { get { return active; } }
 		/// <summary>The local X translation.</summary>
 		public float X { get { return x; } set { x = value; } }
 		/// <summary>The local Y translation.</summary>

+ 8 - 0
spine-csharp/src/IUpdatable.cs

@@ -28,7 +28,15 @@
  *****************************************************************************/
 
 namespace Spine {
+
+	///<summary>The interface for items updated by <see cref="Skeleton.UpdateWorldTransform()"/>.</summary>
 	public interface IUpdatable {
 		void Update ();
+
+		///<summary>Returns false when this item has not been updated because a skin is required and the <see cref="Skeleton.Skin">active
+		/// skin</see> does not contain this item.</summary>
+		/// <seealso cref="Skin.Bones"/>
+		/// <seealso cref="Skin.Constraints"/>
+		bool Active { get; }
 	}
 }

+ 6 - 0
spine-csharp/src/IkConstraint.cs

@@ -45,6 +45,8 @@ namespace Spine {
 		internal bool compress, stretch;
 		internal float mix = 1;
 
+		internal bool active;
+
 		public IkConstraint (IkConstraintData data, Skeleton skeleton) {
 			if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
 			if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
@@ -131,6 +133,10 @@ namespace Spine {
 			set { stretch = value; }
 		}
 
+		public bool Active {
+			get { return active; }
+		}
+
 		/// <summary>The IK constraint's setup pose data.</summary>
 		public IkConstraintData Data {
 			get { return data; }

+ 3 - 0
spine-csharp/src/PathConstraint.cs

@@ -47,6 +47,8 @@ namespace Spine {
 		internal Slot target;
 		internal float position, spacing, rotateMix, translateMix;
 
+		internal bool active;
+
 		internal ExposedList<float> spaces = new ExposedList<float>(), positions = new ExposedList<float>();
 		internal ExposedList<float> world = new ExposedList<float>(), curves = new ExposedList<float>(), lengths = new ExposedList<float>();
 		internal float[] segments = new float[10];
@@ -458,6 +460,7 @@ namespace Spine {
 		public ExposedList<Bone> Bones { get { return bones; } }
 		/// <summary>The slot whose path attachment will be used to constrained the bones.</summary>
 		public Slot Target { get { return target; } set { target = value; } }
+		public bool Active { get { return active; } }
 		/// <summary>The path constraint's setup pose data.</summary>
 		public PathConstraintData Data { get { return data; } }
 	}

+ 27 - 9
spine-csharp/src/Skeleton.cs

@@ -118,8 +118,8 @@ namespace Spine {
 			UpdateWorldTransform();
 		}
 
-		/// <summary>Caches information about bones and constraints. Must be called if the skin is modified or if bones, constraints, or
-		/// weighted path attachments are added or removed.</summary>
+		/// <summary>Caches information about bones and constraints. Must be called if the <see cref="Skin"/> is modified or if bones, constraints, or
+		/// constraints, or weighted path attachments are added or removed.</summary>
 		public void UpdateCache () {
 			var updateCache = this.updateCache;
 			updateCache.Clear();
@@ -129,8 +129,19 @@ namespace Spine {
 			var bones = this.bones;
 			for (int i = 0; i < boneCount; i++) {
 				Bone bone = bones.Items[i];
-				bone.update = !bone.data.skinRequired || (skin != null && skin.bones.Contains(bone.data));
-				bone.sorted = !bone.update;
+				bone.sorted = bone.data.skinRequired;
+				bone.active = !bone.sorted;
+			}
+			if (skin != null) {
+				Object[] skinBones = skin.bones.Items;
+				for (int i = 0, n = skin.bones.Count; i < n; i++) {
+					Bone bone = (Bone)bones.Items[((BoneData)skinBones[i]).index];
+					do {
+						bone.sorted = false;
+						bone.active = true;
+						bone = bone.parent;
+					} while (bone != null);
+				}
 			}
 
 			int ikCount = this.ikConstraints.Count, transformCount = this.transformConstraints.Count, pathCount = this.pathConstraints.Count;
@@ -169,7 +180,9 @@ namespace Spine {
 		}
 
 		private void SortIkConstraint (IkConstraint constraint) {
-			if (constraint.data.skinRequired && (skin == null || !skin.constraints.Contains(constraint.data))) return;
+			constraint.active = constraint.target.active
+				&& (!constraint.data.skinRequired || (skin != null && skin.constraints.Contains(constraint.data)));
+			if (!constraint.active) return;
 
 			Bone target = constraint.target;
 			SortBone(target);
@@ -191,7 +204,9 @@ namespace Spine {
 		}
 
 		private void SortPathConstraint (PathConstraint constraint) {
-			if (constraint.data.skinRequired && (skin == null || !skin.constraints.Contains(constraint.data))) return;
+			constraint.active = constraint.target.bone.active
+				&& (!constraint.data.skinRequired || (skin != null && skin.constraints.Contains(constraint.data)));
+			if (!constraint.active) return;
 
 			Slot slot = constraint.target;
 			int slotIndex = slot.data.index;
@@ -217,7 +232,9 @@ namespace Spine {
 		}
 
 		private void SortTransformConstraint (TransformConstraint constraint) {
-			if (constraint.data.skinRequired && (skin == null || !skin.constraints.Contains(constraint.data))) return;
+			constraint.active = constraint.target.active
+				&& (!constraint.data.skinRequired || (skin != null && skin.constraints.Contains(constraint.data)));
+			if (!constraint.active) return;
 
 			SortBone(constraint.target);
 
@@ -277,7 +294,7 @@ namespace Spine {
 			var bonesItems = bones.Items;
 			for (int i = 0, n = bones.Count; i < n; i++) {
 				Bone bone = bonesItems[i];
-				if (!bone.update) continue;
+				if (!bone.active) continue;
 				if (bone.sorted) SortReset(bone.children);
 				bone.sorted = false;
 			}
@@ -574,8 +591,9 @@ namespace Spine {
 			temp = temp ?? new float[8];
 			var drawOrderItems = this.drawOrder.Items;
 			float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
-			for (int i = 0, n = this.drawOrder.Count; i < n; i++) {
+			for (int i = 0, n = drawOrderItems.Length; i < n; i++) {
 				Slot slot = drawOrderItems[i];
+				if (!slot.bone.active) continue;
 				int verticesLength = 0;
 				float[] vertices = null;
 				Attachment attachment = slot.attachment;

+ 3 - 0
spine-csharp/src/TransformConstraint.cs

@@ -42,6 +42,8 @@ namespace Spine {
 		internal ExposedList<Bone> bones;
 		internal Bone target;
 		internal float rotateMix, translateMix, scaleMix, shearMix;
+		
+		internal bool active;
 
 		public TransformConstraint (TransformConstraintData data, Skeleton skeleton) {
 			if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
@@ -300,6 +302,7 @@ namespace Spine {
 		public float ScaleMix { get { return scaleMix; } set { scaleMix = value; } }
 		/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained scales.</summary>
 		public float ShearMix { get { return shearMix; } set { shearMix = value; } }
+		public bool Active { get { return active; } }
 		/// <summary>The transform constraint's setup pose data.</summary>
 		public TransformConstraintData Data { get { return data; } }
 

+ 1 - 0
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java

@@ -666,6 +666,7 @@ public class Skeleton {
 		float minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE;
 		for (int i = 0, n = drawOrder.size; i < n; i++) {
 			Slot slot = drawOrder.get(i);
+			if (!slot.bone.active) continue;
 			int verticesLength = 0;
 			float[] vertices = null;
 			Attachment attachment = slot.attachment;

+ 87 - 45
spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/JsonRollback.java

@@ -33,10 +33,12 @@ import com.badlogic.gdx.files.FileHandle;
 import com.badlogic.gdx.utils.Array;
 import com.badlogic.gdx.utils.Json;
 import com.badlogic.gdx.utils.JsonValue;
+import com.badlogic.gdx.utils.JsonValue.ValueType;
 import com.badlogic.gdx.utils.JsonWriter.OutputType;
 
-/** Takes Spine JSON data and transforms it to work with an older version of Spine. It supports going from version 3.3.xx to
- * 2.1.27.
+/** Takes Spine JSON data and transforms it to work with an older version of Spine. Target versions:<br>
+ * 2.1: supports going from version 3.3.xx to 2.1.27.<br>
+ * 3.7: supports going from version 3.8.xx to 3.7.94.
  * <p>
  * Data can be exported from a Spine project, processed with JsonRollback, then imported into an older version of Spine. However,
  * JsonRollback may remove data for features not supported by the older Spine version. Because of this, JsonRollback is only
@@ -47,67 +49,107 @@ import com.badlogic.gdx.utils.JsonWriter.OutputType;
  * the runtime is updated to support a newer Spine version should animators update their Spine editor version to match. */
 public class JsonRollback {
 	static public void main (String[] args) throws Exception {
-		if (args.length == 0) {
-			System.out.println("Usage: <inputFile> [outputFile]");
+		if (args.length != 2 && args.length != 3) {
+			System.out.println("Usage: <inputFile> <targetVersion> [outputFile]");
+			System.exit(0);
+		}
+
+		String version = args[1];
+		if (!version.equals("2.1") && !version.equals("3.7")) {
+			System.out.println("ERROR: Target version must be: 2.1 or 3.7");
+			System.out.println("Usage: <inputFile> <toVersion> [outputFile]");
 			System.exit(0);
 		}
 
 		JsonValue root = new Json().fromJson(null, new FileHandle(args[0]));
 
-		// In 3.2 skinnedmesh was renamed to weightedmesh.
-		setValue(root, "skinnedmesh", "skins", "*", "*", "*", "type", "weightedmesh");
+		if (version.equals("2.1")) {
+			// In 3.2 skinnedmesh was renamed to weightedmesh.
+			setValue(root, "skinnedmesh", "skins", "*", "*", "*", "type", "weightedmesh");
 
-		// In 3.2 shear was added.
-		delete(root, "animations", "*", "bones", "*", "shear");
+			// In 3.2 shear was added.
+			delete(root, "animations", "*", "bones", "*", "shear");
 
-		// In 3.3 ffd was renamed to deform.
-		rename(root, "ffd", "animations", "*", "deform");
+			// In 3.3 ffd was renamed to deform.
+			rename(root, "ffd", "animations", "*", "deform");
 
-		// In 3.3 mesh is now a single type, previously they were skinnedmesh if they had weights.
-		for (JsonValue value : find(root, new Array<JsonValue>(), 0, "skins", "*", "*", "*", "type", "mesh"))
-			if (value.parent.get("uvs").size != value.parent.get("vertices").size) value.set("skinnedmesh");
+			// In 3.3 mesh is now a single type, previously they were skinnedmesh if they had weights.
+			for (JsonValue value : find(root, new Array<JsonValue>(), 0, "skins", "*", "*", "*", "type", "mesh"))
+				if (value.parent.get("uvs").size != value.parent.get("vertices").size) value.set("skinnedmesh");
 
-		// In 3.3 linkedmesh is now a single type, previously they were linkedweightedmesh if they had weights.
-		for (JsonValue value : find(root, new Array<JsonValue>(), 0, "skins", "*", "*", "*", "type", "linkedmesh")) {
-			String slot = value.parent.parent.name.replaceAll("", "");
-			String skinName = value.parent.getString("skin", "default");
-			String parentName = value.parent.getString("parent");
-			if (find(root, new Array<JsonValue>(), 0,
-				("skins~~" + skinName + "~~" + slot + "~~" + parentName + "~~type~~skinnedmesh").split("~~")).size > 0)
-				value.set("weightedlinkedmesh");
-		}
+			// In 3.3 linkedmesh is now a single type, previously they were linkedweightedmesh if they had weights.
+			for (JsonValue value : find(root, new Array<JsonValue>(), 0, "skins", "*", "*", "*", "type", "linkedmesh")) {
+				String slot = value.parent.parent.name.replaceAll("", "");
+				String skinName = value.parent.getString("skin", "default");
+				String parentName = value.parent.getString("parent");
+				if (find(root, new Array<JsonValue>(), 0,
+					("skins~~" + skinName + "~~" + slot + "~~" + parentName + "~~type~~skinnedmesh").split("~~")).size > 0)
+					value.set("weightedlinkedmesh");
+			}
 
-		// In 3.3 bounding boxes can be weighted.
-		for (JsonValue value : find(root, new Array<JsonValue>(), 0, "skins", "*", "*", "*", "type", "boundingbox"))
-			if (value.parent.getInt("vertexCount") * 2 != value.parent.get("vertices").size)
-				value.parent.parent.remove(value.parent.name);
-
-		// In 3.3 paths were added.
-		for (JsonValue value : find(root, new Array<JsonValue>(), 0, "skins", "*", "*", "*", "type", "path")) {
-			String attachment = value.parent.name;
-			value.parent.parent.remove(attachment);
-			String slot = value.parent.parent.name;
-			// Also remove path deform timelines.
-			delete(root, "animations", "*", "ffd", "*", slot, attachment);
-		}
+			// In 3.3 bounding boxes can be weighted.
+			for (JsonValue value : find(root, new Array<JsonValue>(), 0, "skins", "*", "*", "*", "type", "boundingbox"))
+				if (value.parent.getInt("vertexCount") * 2 != value.parent.get("vertices").size)
+					value.parent.parent.remove(value.parent.name);
+
+			// In 3.3 paths were added.
+			for (JsonValue value : find(root, new Array<JsonValue>(), 0, "skins", "*", "*", "*", "type", "path")) {
+				String attachment = value.parent.name;
+				value.parent.parent.remove(attachment);
+				String slot = value.parent.parent.name;
+				// Also remove path deform timelines.
+				delete(root, "animations", "*", "ffd", "*", slot, attachment);
+			}
 
-		// In 3.3 IK constraint timelines no longer require bendPositive.
-		for (JsonValue value : find(root, new Array<JsonValue>(), 0, "animations", "*", "ik", "*"))
-			for (JsonValue child = value.child; child != null; child = child.next)
-				if (!child.has("bendPositive")) child.addChild("bendPositive", new JsonValue(true));
+			// In 3.3 IK constraint timelines no longer require bendPositive.
+			for (JsonValue value : find(root, new Array<JsonValue>(), 0, "animations", "*", "ik", "*"))
+				for (JsonValue child = value.child; child != null; child = child.next)
+					if (!child.has("bendPositive")) child.addChild("bendPositive", new JsonValue(true));
 
-		// In 3.3 transform constraints can have more than 1 bone.
-		for (JsonValue child = root.getChild("transform"); child != null; child = child.next) {
-			JsonValue bones = child.remove("bones");
-			if (bones != null) child.addChild("bone", new JsonValue(bones.child.asString()));
+			// In 3.3 transform constraints can have more than 1 bone.
+			for (JsonValue child = root.getChild("transform"); child != null; child = child.next) {
+				JsonValue bones = child.remove("bones");
+				if (bones != null) child.addChild("bone", new JsonValue(bones.child.asString()));
+			}
+		} else if (version.equals("3.7")) {
+			JsonValue skins = root.get("skins");
+			if (skins != null && skins.isArray()) {
+				JsonValue newSkins = new JsonValue(ValueType.object);
+				for (JsonValue skinMap = skins.child; skinMap != null; skinMap = skinMap.next)
+					newSkins.addChild(skinMap.getString("name"), skinMap.get("attachments"));
+				root.remove("skins");
+				root.addChild("skins", newSkins);
+			}
+
+			rollbackCurves(root.get("animations"));
 		}
 
-		if (args.length > 1)
-			new FileHandle(args[1]).writeString(root.prettyPrint(OutputType.json, 130), false, "UTF-8");
+		if (args.length == 3)
+			new FileHandle(args[2]).writeString(root.prettyPrint(OutputType.json, 130), false, "UTF-8");
 		else
 			System.out.println(root.prettyPrint(OutputType.json, 130));
 	}
 
+	static private void rollbackCurves (JsonValue map) {
+		if (map == null) return;
+		JsonValue curve = map.get("curve");
+		if (curve == null) {
+			for (map = map.child; map != null; map = map.next)
+				rollbackCurves(map);
+			return;
+		}
+		if (curve.isNumber()) {
+			curve.addChild(new JsonValue(curve.asFloat()));
+			curve.setType(ValueType.array);
+			curve.addChild(new JsonValue(map.getFloat("c2", 0)));
+			curve.addChild(new JsonValue(map.getFloat("c3", 0)));
+			curve.addChild(new JsonValue(map.getFloat("c4", 0)));
+			map.remove("c2");
+			map.remove("c3");
+			map.remove("c4");
+		}
+	}
+
 	static void setValue (JsonValue root, String newValue, String... path) {
 		for (JsonValue value : find(root, new Array<JsonValue>(), 0, path))
 			value.set(newValue);

+ 7 - 2
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonDebugWindow.cs

@@ -481,7 +481,10 @@ namespace Spine.Unity.Editor {
 							if (Application.isPlaying) {
 								foreach (var slot in skeleton.DrawOrder) {
 									if (skeletonRenderer.separatorSlots.Contains(slot))	EditorGUILayout.LabelField(SeparatorString);
-									EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
+
+									using (new EditorGUI.DisabledScope(!slot.Bone.Active)) {
+										EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
+									}
 								}
 							} else {
 								foreach (var slot in skeleton.DrawOrder) {
@@ -492,7 +495,9 @@ namespace Spine.Unity.Editor {
 											break;
 										}
 									}
-									EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
+									using (new EditorGUI.DisabledScope(!slot.Bone.Active)) {
+										EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
+									}
 								}
 							}
 								

+ 2 - 0
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs

@@ -2294,6 +2294,7 @@ namespace Spine.Unity.Editor {
 		public static void DrawBoneNames (Transform transform, Skeleton skeleton, float positionScale = 1f) {
 			GUIStyle style = BoneNameStyle;
 			foreach (Bone b in skeleton.Bones) {
+				if (!b.Active) continue;
 				var pos = new Vector3(b.WorldX * positionScale, b.WorldY * positionScale, 0) + (new Vector3(b.A, b.C) * (b.Data.Length * 0.5f));
 				pos = transform.TransformPoint(pos);
 				Handles.Label(pos, b.Data.Name, style);
@@ -2305,6 +2306,7 @@ namespace Spine.Unity.Editor {
 			DrawCrosshairs2D(skeleton.Bones.Items[0].GetWorldPosition(transform), 0.08f, positionScale);
 
 			foreach (Bone b in skeleton.Bones) {
+				if (!b.Active) continue;
 				DrawBone(transform, b, boneScale, positionScale);
 				boneScale = 1f;
 			}

+ 21 - 9
spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SpineMesh.cs

@@ -211,6 +211,7 @@ namespace Spine.Unity {
 			var drawOrderItems = drawOrder.Items;
 			for (int i = 0; i < drawOrderCount; i++) {
 				Slot slot = drawOrderItems[i];
+				if (!slot.bone.active) continue;
 				Attachment attachment = slot.attachment;
 
 				workingAttachmentsItems[i] = attachment;
@@ -285,6 +286,7 @@ namespace Spine.Unity {
 			var drawOrderItems = drawOrder.Items;
 			for (int i = 0; i < drawOrderCount; i++) {
 				Slot slot = drawOrderItems[i];
+				if (!slot.bone.active) continue;
 				Attachment attachment = slot.attachment;
 				#if SPINE_TRIANGLECHECK
 				workingAttachmentsItems[i] = attachment;
@@ -495,6 +497,7 @@ namespace Spine.Unity {
 
 			for (int slotIndex = instruction.startSlot; slotIndex < instruction.endSlot; slotIndex++) {
 				var slot = drawOrderItems[slotIndex];
+				if (!slot.bone.active) continue;
 				var attachment = slot.attachment;
 				float z = zSpacing * slotIndex;
 
@@ -701,7 +704,7 @@ namespace Spine.Unity {
 			for (int si = 0, n = instruction.submeshInstructions.Count; si < n; si++) {
 				var submesh = instruction.submeshInstructions.Items[si];
 				var skeleton = submesh.skeleton;
-				var skeletonDrawOrderItems = skeleton.drawOrder.Items;
+				var drawOrderItems = skeleton.drawOrder.Items;
 				float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
 
 				int endSlot = submesh.endSlot;
@@ -729,7 +732,8 @@ namespace Spine.Unity {
 					var uv3i = uv3.Items;
 
 					for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
-						var slot = skeletonDrawOrderItems[slotIndex];
+						var slot = drawOrderItems[slotIndex];
+						if (!slot.bone.active) continue;
 						var attachment = slot.attachment;
 
 						rg.x = slot.r2; //r
@@ -756,7 +760,8 @@ namespace Spine.Unity {
 				}
 
 				for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
-					var slot = skeletonDrawOrderItems[slotIndex];
+					var slot = drawOrderItems[slotIndex];
+					if (!slot.bone.active) continue;
 					var attachment = slot.attachment;
 					float z = slotIndex * settings.zSpacing;
 
@@ -909,9 +914,12 @@ namespace Spine.Unity {
 					var tris = currentSubmeshBuffer.Items;
 					int triangleIndex = 0;
 					var skeleton = submeshInstruction.skeleton;
-					var skeletonDrawOrderItems = skeleton.drawOrder.Items;
-					for (int a = submeshInstruction.startSlot, endSlot = submeshInstruction.endSlot; a < endSlot; a++) {			
-						var attachment = skeletonDrawOrderItems[a].attachment;
+					var drawOrderItems = skeleton.drawOrder.Items;
+					for (int slotIndex = submeshInstruction.startSlot, endSlot = submeshInstruction.endSlot; slotIndex < endSlot; slotIndex++) {
+						var slot = drawOrderItems[slotIndex];
+						if (!slot.bone.active) continue;
+
+						var attachment = drawOrderItems[slotIndex].attachment;
 						if (attachment is RegionAttachment) {
 							tris[triangleIndex] = attachmentFirstVertex;
 							tris[triangleIndex + 1] = attachmentFirstVertex + 2;
@@ -1475,9 +1483,13 @@ namespace Spine.Unity {
 			attachments.Resize(attachmentCount);
 			var attachmentsItems = attachments.Items;
 
-			var drawOrder = instructionsItems[0].skeleton.drawOrder.Items;
-			for (int i = 0; i < attachmentCount; i++)
-			attachmentsItems[i] = drawOrder[startSlot + i].attachment;
+			var drawOrderItems = instructionsItems[0].skeleton.drawOrder.Items;
+			for (int i = 0; i < attachmentCount; i++) {
+				Slot slot = drawOrderItems[startSlot + i];
+				if (!slot.bone.active) continue;
+				attachmentsItems[i] = slot.attachment;
+			}
+			
 			#endif
 		}