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

Restructured timelines to have a curve per value.

EsotericSoftware/spine-editor#118
NathanSweet 5 жил өмнө
parent
commit
952a34bc08

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 334 - 350
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java


+ 160 - 90
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java

@@ -43,6 +43,9 @@ import com.badlogic.gdx.utils.SerializationException;
 
 import com.esotericsoftware.spine.Animation.AttachmentTimeline;
 import com.esotericsoftware.spine.Animation.ColorTimeline;
+import com.esotericsoftware.spine.Animation.CurveTimeline;
+import com.esotericsoftware.spine.Animation.CurveTimeline1;
+import com.esotericsoftware.spine.Animation.CurveTimeline2;
 import com.esotericsoftware.spine.Animation.DeformTimeline;
 import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
 import com.esotericsoftware.spine.Animation.EventTimeline;
@@ -50,8 +53,6 @@ import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
 import com.esotericsoftware.spine.Animation.PathConstraintMixTimeline;
 import com.esotericsoftware.spine.Animation.PathConstraintPositionTimeline;
 import com.esotericsoftware.spine.Animation.PathConstraintSpacingTimeline;
-import com.esotericsoftware.spine.Animation.PercentCurveTimeline;
-import com.esotericsoftware.spine.Animation.PercentCurveTimeline2;
 import com.esotericsoftware.spine.Animation.RotateTimeline;
 import com.esotericsoftware.spine.Animation.ScaleTimeline;
 import com.esotericsoftware.spine.Animation.ShearTimeline;
@@ -59,7 +60,6 @@ import com.esotericsoftware.spine.Animation.Timeline;
 import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
 import com.esotericsoftware.spine.Animation.TranslateTimeline;
 import com.esotericsoftware.spine.Animation.TwoColorTimeline;
-import com.esotericsoftware.spine.Animation.ValueCurveTimeline;
 import com.esotericsoftware.spine.BoneData.TransformMode;
 import com.esotericsoftware.spine.PathConstraintData.PositionMode;
 import com.esotericsoftware.spine.PathConstraintData.RotateMode;
@@ -100,11 +100,9 @@ public class SkeletonBinary {
 	static public final int CURVE_STEPPED = 1;
 	static public final int CURVE_BEZIER = 2;
 
-	static private final Color tempColor1 = new Color(), tempColor2 = new Color();
-
 	private final AttachmentLoader attachmentLoader;
 	private float scale = 1;
-	private Array<LinkedMesh> linkedMeshes = new Array();
+	private final Array<LinkedMesh> linkedMeshes = new Array();
 
 	public SkeletonBinary (TextureAtlas atlas) {
 		attachmentLoader = new AtlasAttachmentLoader(atlas);
@@ -585,19 +583,39 @@ public class SkeletonBinary {
 				switch (timelineType) {
 				case SLOT_ATTACHMENT: {
 					AttachmentTimeline timeline = new AttachmentTimeline(frameCount, slotIndex);
-					for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
-						timeline.setFrame(frameIndex, input.readFloat(), input.readStringRef());
+					for (int frame = 0; frame < frameCount; frame++)
+						timeline.setFrame(frame, input.readFloat(), input.readStringRef());
 					timelines.add(timeline);
 					break;
 				}
 				case SLOT_COLOR: {
 					ColorTimeline timeline = new ColorTimeline(frameCount, input.readInt(true), slotIndex);
 					float time = input.readFloat();
-					for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) {
-						Color.rgba8888ToColor(tempColor1, input.readInt());
-						timeline.setFrame(frameIndex, time, tempColor1.r, tempColor1.g, tempColor1.b, tempColor1.a);
-						if (frameIndex == frameLast) break;
-						bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat());
+					int c1 = input.readInt();
+					float r = ((c1 & 0xff000000) >> 24) / 255f, g = ((c1 & 0x00ff0000) >> 16) / 255f;
+					float b = ((c1 & 0x0000ff00) >> 8) / 255f, a = (c1 & 0x000000ff) / 255f;
+					for (int frame = 0, bezier = 0;; frame++) {
+						timeline.setFrame(frame, time, r, g, b, a);
+						if (frame == frameLast) break;
+						float time2 = input.readFloat();
+						c1 = input.readInt();
+						float r2 = ((c1 & 0xff000000) >> 24) / 255f, g2 = ((c1 & 0x00ff0000) >> 16) / 255f;
+						float b2 = ((c1 & 0x0000ff00) >> 8) / 255f, a2 = (c1 & 0x000000ff) / 255f;
+						switch (input.readByte()) {
+						case CURVE_STEPPED:
+							timeline.setStepped(frame);
+							break;
+						case CURVE_BEZIER:
+							setBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2);
+							setBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2);
+							setBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2);
+							setBezier(input, timeline, bezier++, frame, 3, time, time2, a, a2);
+						}
+						time = time2;
+						r = r2;
+						g = g2;
+						b = b2;
+						a = a2;
 					}
 					timelines.add(timeline);
 					break;
@@ -605,13 +623,42 @@ public class SkeletonBinary {
 				case SLOT_TWO_COLOR: {
 					TwoColorTimeline timeline = new TwoColorTimeline(frameCount, input.readInt(true), slotIndex);
 					float time = input.readFloat();
-					for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) {
-						Color.rgba8888ToColor(tempColor1, input.readInt());
-						Color.rgb888ToColor(tempColor2, input.readInt());
-						timeline.setFrame(frameIndex, time, tempColor1.r, tempColor1.g, tempColor1.b, tempColor1.a, tempColor2.r,
-							tempColor2.g, tempColor2.b);
-						if (frameIndex == frameLast) break;
-						bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat());
+					int c1 = input.readInt(), c2 = input.readInt();
+					float r = ((c1 & 0xff000000) >> 24) / 255f, g = ((c1 & 0x00ff0000) >> 16) / 255f;
+					float b = ((c1 & 0x0000ff00) >> 8) / 255f, a = (c1 & 0x000000ff) / 255f;
+					float r2 = ((c2 & 0xff000000) >> 24) / 255f, g2 = ((c2 & 0x00ff0000) >> 16) / 255f;
+					float b2 = ((c2 & 0x0000ff00) >> 8) / 255f;
+					for (int frame = 0, bezier = 0;; frame++) {
+						timeline.setFrame(frame, time, r, g, b, a, r2, g2, b2);
+						if (frame == frameLast) break;
+						float time2 = input.readFloat();
+						c1 = input.readInt();
+						c2 = input.readInt();
+						float nr = ((c1 & 0xff000000) >> 24) / 255f, ng = ((c1 & 0x00ff0000) >> 16) / 255f;
+						float nb = ((c1 & 0x0000ff00) >> 8) / 255f, na = (c1 & 0x000000ff) / 255f;
+						float nr2 = ((c2 & 0xff000000) >> 24) / 255f, ng2 = ((c2 & 0x00ff0000) >> 16) / 255f;
+						float nb2 = ((c2 & 0x0000ff00) >> 8) / 255f;
+						switch (input.readByte()) {
+						case CURVE_STEPPED:
+							timeline.setStepped(frame);
+							break;
+						case CURVE_BEZIER:
+							setBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr);
+							setBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng);
+							setBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb);
+							setBezier(input, timeline, bezier++, frame, 3, time, time2, a, na);
+							setBezier(input, timeline, bezier++, frame, 4, time, time2, r2, nr2);
+							setBezier(input, timeline, bezier++, frame, 5, time, time2, g2, ng2);
+							setBezier(input, timeline, bezier++, frame, 6, time, time2, b2, nb2);
+						}
+						time = time2;
+						r = nr;
+						g = ng;
+						b = nb;
+						a = na;
+						r2 = nr2;
+						g2 = ng2;
+						b2 = nb2;
 					}
 					timelines.add(timeline);
 					break;
@@ -625,23 +672,18 @@ public class SkeletonBinary {
 			int boneIndex = input.readInt(true);
 			for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
 				switch (input.readByte()) {
-				case BONE_ROTATE: {
+				case BONE_ROTATE:
 					timelines.add(readTimeline(input, new RotateTimeline(input.readInt(true), input.readInt(true), boneIndex), 1));
 					break;
-				}
-				case BONE_TRANSLATE: {
+				case BONE_TRANSLATE:
 					timelines
 						.add(readTimeline(input, new TranslateTimeline(input.readInt(true), input.readInt(true), boneIndex), scale));
 					break;
-				}
-				case BONE_SCALE: {
+				case BONE_SCALE:
 					timelines.add(readTimeline(input, new ScaleTimeline(input.readInt(true), input.readInt(true), boneIndex), 1));
 					break;
-				}
-				case BONE_SHEAR: {
+				case BONE_SHEAR:
 					timelines.add(readTimeline(input, new ShearTimeline(input.readInt(true), input.readInt(true), boneIndex), 1));
-					break;
-				}
 				}
 			}
 		}
@@ -650,12 +692,22 @@ public class SkeletonBinary {
 		for (int i = 0, n = input.readInt(true); i < n; i++) {
 			int index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1;
 			IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index);
-			float time = input.readFloat();
-			for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) {
-				timeline.setFrame(frameIndex, time, input.readFloat(), input.readFloat() * scale, input.readByte(),
-					input.readBoolean(), input.readBoolean());
-				if (frameIndex == frameLast) break;
-				bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat());
+			float time = input.readFloat(), mix = input.readFloat(), softness = input.readFloat() * scale;
+			for (int frame = 0, bezier = 0;; frame++) {
+				timeline.setFrame(frame, time, mix, softness, input.readByte(), input.readBoolean(), input.readBoolean());
+				if (frame == frameLast) break;
+				float time2 = input.readFloat(), mix2 = input.readFloat() * scale, softness2 = input.readFloat() * scale;
+				switch (input.readByte()) {
+				case CURVE_STEPPED:
+					timeline.setStepped(frame);
+					break;
+				case CURVE_BEZIER:
+					setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2);
+					setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2);
+				}
+				time = time2;
+				mix = mix2;
+				softness = softness2;
 			}
 			timelines.add(timeline);
 		}
@@ -664,11 +716,28 @@ public class SkeletonBinary {
 		for (int i = 0, n = input.readInt(true); i < n; i++) {
 			int index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1;
 			TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index);
-			float time = input.readFloat();
-			for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) {
-				timeline.setFrame(frameIndex, time, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat());
-				if (frameIndex == frameLast) break;
-				bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat());
+			float time = input.readFloat(), rotateMix = input.readFloat(), translateMix = input.readFloat(),
+				scaleMix = input.readFloat(), shearMix = input.readFloat();
+			for (int frame = 0, bezier = 0;; frame++) {
+				timeline.setFrame(frame, time, rotateMix, translateMix, scaleMix, shearMix);
+				if (frame == frameLast) break;
+				float time2 = input.readFloat(), rotateMix2 = input.readFloat(), translateMix2 = input.readFloat(),
+					scaleMix2 = input.readFloat(), shearMix2 = input.readFloat();
+				switch (input.readByte()) {
+				case CURVE_STEPPED:
+					timeline.setStepped(frame);
+					break;
+				case CURVE_BEZIER:
+					setBezier(input, timeline, bezier++, frame, 0, time, time2, rotateMix, rotateMix2);
+					setBezier(input, timeline, bezier++, frame, 1, time, time2, translateMix, translateMix2);
+					setBezier(input, timeline, bezier++, frame, 2, time, time2, scaleMix, scaleMix2);
+					setBezier(input, timeline, bezier++, frame, 3, time, time2, shearMix, shearMix2);
+				}
+				time = time2;
+				rotateMix = rotateMix2;
+				translateMix = translateMix2;
+				scaleMix = scaleMix2;
+				shearMix = shearMix2;
 			}
 			timelines.add(timeline);
 		}
@@ -679,23 +748,19 @@ public class SkeletonBinary {
 			PathConstraintData data = skeletonData.pathConstraints.get(index);
 			for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
 				switch (input.readByte()) {
-				case PATH_POSITION: {
+				case PATH_POSITION:
 					timelines
 						.add(readTimeline(input, new PathConstraintSpacingTimeline(input.readInt(true), input.readInt(true), index),
 							data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1));
 					break;
-				}
-				case PATH_SPACING: {
+				case PATH_SPACING:
 					timelines
 						.add(readTimeline(input, new PathConstraintPositionTimeline(input.readInt(true), input.readInt(true), index),
 							data.positionMode == PositionMode.fixed ? scale : 1));
 					break;
-				}
-				case PATH_MIX: {
+				case PATH_MIX:
 					timelines
 						.add(readTimeline(input, new PathConstraintMixTimeline(input.readInt(true), input.readInt(true), index), 1));
-					break;
-				}
 				}
 			}
 		}
@@ -709,13 +774,13 @@ public class SkeletonBinary {
 					VertexAttachment attachment = (VertexAttachment)skin.getAttachment(slotIndex, input.readStringRef());
 					boolean weighted = attachment.getBones() != null;
 					float[] vertices = attachment.getVertices();
-					int deformLength = weighted ? vertices.length / 3 * 2 : vertices.length;
+					int deformLength = weighted ? (vertices.length / 3) << 1 : vertices.length;
 
 					int frameCount = input.readInt(true), frameLast = frameCount - 1;
 					DeformTimeline timeline = new DeformTimeline(frameCount, input.readInt(true), slotIndex, attachment);
 
 					float time = input.readFloat();
-					for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) {
+					for (int frame = 0, bezier = 0;; frame++) {
 						float[] deform;
 						int end = input.readInt(true);
 						if (end == 0)
@@ -736,9 +801,17 @@ public class SkeletonBinary {
 									deform[v] += vertices[v];
 							}
 						}
-						timeline.setFrame(frameIndex, time, deform);
-						if (frameIndex == frameLast) break;
-						bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat());
+						timeline.setFrame(frame, time, deform);
+						if (frame == frameLast) break;
+						float time2 = input.readFloat();
+						switch (input.readByte()) {
+						case CURVE_STEPPED:
+							timeline.setStepped(frame);
+							break;
+						case CURVE_BEZIER:
+							setBezier(input, timeline, bezier++, frame, 0, time, time2, 0, 1);
+						}
+						time = time2;
 					}
 					timelines.add(timeline);
 				}
@@ -803,53 +876,50 @@ public class SkeletonBinary {
 		return new Animation(name, timelines, duration);
 	}
 
-	private Timeline readTimeline (SkeletonInput input, ValueCurveTimeline timeline, float scale) throws IOException {
+	private Timeline readTimeline (SkeletonInput input, CurveTimeline1 timeline, float scale) throws IOException {
 		float time = input.readFloat(), value = input.readFloat() * scale;
-		for (int frameIndex = 0, bezierIndex = 0, frameLast = timeline.getFrameCount() - 1;; frameIndex++) {
-			timeline.setFrame(frameIndex, time, value);
-			if (frameIndex == frameLast) break;
-			bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, value, time = input.readFloat(),
-				value = input.readFloat() * scale);
+		for (int frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1;; frame++) {
+			timeline.setFrame(frame, time, value);
+			if (frame == frameLast) break;
+			float time2 = input.readFloat(), value2 = input.readFloat() * scale;
+			switch (input.readByte()) {
+			case CURVE_STEPPED:
+				timeline.setStepped(frame);
+				break;
+			case CURVE_BEZIER:
+				setBezier(input, timeline, bezier++, frame, 0, time, time2, value, value2);
+			}
+			time = time2;
+			value = value2;
 		}
 		return timeline;
 	}
 
-	private Timeline readTimeline (SkeletonInput input, PercentCurveTimeline2 timeline, float scale) throws IOException {
-		float time = input.readFloat();
-		for (int frameIndex = 0, bezierIndex = 0, frameLast = timeline.getFrameCount() - 1;; frameIndex++) {
-			timeline.setFrame(frameIndex, time, input.readFloat() * scale, input.readFloat() * scale);
-			if (frameIndex == frameLast) break;
-			bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat());
+	private Timeline readTimeline (SkeletonInput input, CurveTimeline2 timeline, float scale) throws IOException {
+		float time = input.readFloat(), value1 = input.readFloat() * scale, value2 = input.readFloat() * scale;
+		for (int frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1;; frame++) {
+			timeline.setFrame(frame, time, value1, value2);
+			if (frame == frameLast) break;
+			float time2 = input.readFloat(), nvalue1 = input.readFloat() * scale, nvalue2 = input.readFloat() * scale;
+			switch (input.readByte()) {
+			case CURVE_STEPPED:
+				timeline.setStepped(frame);
+				break;
+			case CURVE_BEZIER:
+				setBezier(input, timeline, bezier++, frame, 0, time, time2, value1, nvalue1);
+				setBezier(input, timeline, bezier++, frame, 1, time, time2, value2, nvalue2);
+			}
+			time = time2;
+			value1 = nvalue1;
+			value2 = nvalue2;
 		}
 		return timeline;
 	}
 
-	int readCurve (SkeletonInput input, PercentCurveTimeline timeline, int frameIndex, int bezierIndex, float time1, float time2)
-		throws IOException {
-		switch (input.readByte()) {
-		case CURVE_STEPPED:
-			timeline.setStepped(frameIndex);
-			break;
-		case CURVE_BEZIER:
-			timeline.setBezier(frameIndex, bezierIndex++, time1, input.readFloat(), input.readFloat(), input.readFloat(),
-				input.readFloat(), time2);
-			break;
-		}
-		return bezierIndex;
-	}
-
-	int readCurve (SkeletonInput input, ValueCurveTimeline timeline, int frameIndex, int bezierIndex, float time1, float value1,
-		float time2, float value2) throws IOException {
-		switch (input.readByte()) {
-		case CURVE_STEPPED:
-			timeline.setStepped(frameIndex);
-			break;
-		case CURVE_BEZIER:
-			timeline.setBezier(frameIndex, bezierIndex++, time1, value1, input.readFloat(), input.readFloat(), input.readFloat(),
-				input.readFloat(), time2, value2);
-			break;
-		}
-		return bezierIndex;
+	void setBezier (SkeletonInput input, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2,
+		float value1, float value2) throws IOException {
+		timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat(), input.readFloat(),
+			input.readFloat(), time2, value2);
 	}
 
 	static class Vertices {

+ 190 - 97
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java

@@ -43,6 +43,9 @@ import com.badlogic.gdx.utils.SerializationException;
 
 import com.esotericsoftware.spine.Animation.AttachmentTimeline;
 import com.esotericsoftware.spine.Animation.ColorTimeline;
+import com.esotericsoftware.spine.Animation.CurveTimeline;
+import com.esotericsoftware.spine.Animation.CurveTimeline1;
+import com.esotericsoftware.spine.Animation.CurveTimeline2;
 import com.esotericsoftware.spine.Animation.DeformTimeline;
 import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
 import com.esotericsoftware.spine.Animation.EventTimeline;
@@ -50,8 +53,6 @@ import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
 import com.esotericsoftware.spine.Animation.PathConstraintMixTimeline;
 import com.esotericsoftware.spine.Animation.PathConstraintPositionTimeline;
 import com.esotericsoftware.spine.Animation.PathConstraintSpacingTimeline;
-import com.esotericsoftware.spine.Animation.PercentCurveTimeline;
-import com.esotericsoftware.spine.Animation.PercentCurveTimeline2;
 import com.esotericsoftware.spine.Animation.RotateTimeline;
 import com.esotericsoftware.spine.Animation.ScaleTimeline;
 import com.esotericsoftware.spine.Animation.ShearTimeline;
@@ -59,7 +60,6 @@ import com.esotericsoftware.spine.Animation.Timeline;
 import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
 import com.esotericsoftware.spine.Animation.TranslateTimeline;
 import com.esotericsoftware.spine.Animation.TwoColorTimeline;
-import com.esotericsoftware.spine.Animation.ValueCurveTimeline;
 import com.esotericsoftware.spine.BoneData.TransformMode;
 import com.esotericsoftware.spine.PathConstraintData.PositionMode;
 import com.esotericsoftware.spine.PathConstraintData.RotateMode;
@@ -84,7 +84,7 @@ import com.esotericsoftware.spine.attachments.VertexAttachment;
 public class SkeletonJson {
 	private final AttachmentLoader attachmentLoader;
 	private float scale = 1;
-	private Array<LinkedMesh> linkedMeshes = new Array();
+	private final Array<LinkedMesh> linkedMeshes = new Array();
 
 	public SkeletonJson (TextureAtlas atlas) {
 		attachmentLoader = new AtlasAttachmentLoader(atlas);
@@ -415,7 +415,7 @@ public class SkeletonJson {
 			mesh.setRegionUVs(uvs);
 			mesh.updateUVs();
 
-			if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt() * 2);
+			if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt() << 1);
 			if (map.has("edges")) mesh.setEdges(map.require("edges").asShortArray());
 			return mesh;
 		}
@@ -512,39 +512,94 @@ public class SkeletonJson {
 
 				if (timelineName.equals("attachment")) {
 					AttachmentTimeline timeline = new AttachmentTimeline(timelineMap.size, slot.index);
-					for (int frameIndex = 0; keyMap != null; keyMap = keyMap.next, frameIndex++)
-						timeline.setFrame(frameIndex, keyMap.getFloat("time", 0), keyMap.getString("name"));
+					for (int frame = 0; keyMap != null; keyMap = keyMap.next, frame++)
+						timeline.setFrame(frame, keyMap.getFloat("time", 0), keyMap.getString("name"));
 					timelines.add(timeline);
 
 				} else if (timelineName.equals("color")) {
-					ColorTimeline timeline = new ColorTimeline(timelineMap.size, timelineMap.size, slot.index);
-					float time = timelineMap.child.getFloat("time", 0);
-					for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) {
-						Color color = Color.valueOf(keyMap.getString("color"));
-						timeline.setFrame(frameIndex, time, color.r, color.g, color.b, color.a);
+					ColorTimeline timeline = new ColorTimeline(timelineMap.size, timelineMap.size << 2, slot.index);
+					float time = keyMap.getFloat("time", 0);
+					String color = keyMap.getString("color");
+					float r = Integer.parseInt(color.substring(0, 2), 16) / 255f;
+					float g = Integer.parseInt(color.substring(2, 4), 16) / 255f;
+					float b = Integer.parseInt(color.substring(4, 6), 16) / 255f;
+					float a = Integer.parseInt(color.substring(6, 8), 16) / 255f;
+					for (int frame = 0, bezier = 0;; frame++) {
+						timeline.setFrame(frame, time, r, g, b, a);
 						JsonValue nextMap = keyMap.next;
 						if (nextMap == null) {
-							timeline.shrink(bezierIndex);
+							timeline.shrink(bezier);
 							break;
 						}
-						bezierIndex = readCurve(keyMap, timeline, frameIndex, bezierIndex, time, time = nextMap.getFloat("time", 0));
+						float time2 = nextMap.getFloat("time", 0);
+						color = nextMap.getString("color");
+						float nr = Integer.parseInt(color.substring(0, 2), 16) / 255f;
+						float ng = Integer.parseInt(color.substring(2, 4), 16) / 255f;
+						float nb = Integer.parseInt(color.substring(4, 6), 16) / 255f;
+						float na = Integer.parseInt(color.substring(6, 8), 16) / 255f;
+						JsonValue curve = keyMap.get("curve");
+						if (curve != null) {
+							bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr);
+							bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng);
+							bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb);
+							bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, a, na);
+						}
+						time = time2;
+						r = nr;
+						g = ng;
+						b = nb;
+						a = na;
 						keyMap = nextMap;
 					}
 					timelines.add(timeline);
 
 				} else if (timelineName.equals("twoColor")) {
-					TwoColorTimeline timeline = new TwoColorTimeline(timelineMap.size, timelineMap.size, slot.index);
-					float time = timelineMap.child.getFloat("time", 0);
-					for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) {
-						Color light = Color.valueOf(keyMap.getString("light")), dark = Color.valueOf(keyMap.getString("dark"));
-						timeline.setFrame(frameIndex, time, light.r, light.g, light.b, light.a, //
-							dark.r, dark.g, dark.b);
+					TwoColorTimeline timeline = new TwoColorTimeline(timelineMap.size, timelineMap.size * 7, slot.index);
+					float time = keyMap.getFloat("time", 0);
+					String color = keyMap.getString("light");
+					float r = Integer.parseInt(color.substring(0, 2), 16) / 255f;
+					float g = Integer.parseInt(color.substring(2, 4), 16) / 255f;
+					float b = Integer.parseInt(color.substring(4, 6), 16) / 255f;
+					float a = Integer.parseInt(color.substring(6, 8), 16) / 255f;
+					color = keyMap.getString("dark");
+					float r2 = Integer.parseInt(color.substring(0, 2), 16) / 255f;
+					float g2 = Integer.parseInt(color.substring(2, 4), 16) / 255f;
+					float b2 = Integer.parseInt(color.substring(4, 6), 16) / 255f;
+					for (int frame = 0, bezier = 0;; frame++) {
+						timeline.setFrame(frame, time, r, g, b, a, r2, g2, b2);
 						JsonValue nextMap = keyMap.next;
 						if (nextMap == null) {
-							timeline.shrink(bezierIndex);
+							timeline.shrink(bezier);
 							break;
 						}
-						bezierIndex = readCurve(keyMap, timeline, frameIndex, bezierIndex, time, time = nextMap.getFloat("time", 0));
+						float time2 = nextMap.getFloat("time", 0);
+						color = nextMap.getString("light");
+						float nr = Integer.parseInt(color.substring(0, 2), 16) / 255f;
+						float ng = Integer.parseInt(color.substring(2, 4), 16) / 255f;
+						float nb = Integer.parseInt(color.substring(4, 6), 16) / 255f;
+						float na = Integer.parseInt(color.substring(6, 8), 16) / 255f;
+						color = nextMap.getString("dark");
+						float nr2 = Integer.parseInt(color.substring(8, 10), 16) / 255f;
+						float ng2 = Integer.parseInt(color.substring(10, 12), 16) / 255f;
+						float nb2 = Integer.parseInt(color.substring(12, 14), 16) / 255f;
+						JsonValue curve = keyMap.get("curve");
+						if (curve != null) {
+							bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr);
+							bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng);
+							bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb);
+							bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, a, na);
+							bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, r2, nr2);
+							bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, g2, ng2);
+							bezier = readCurve(curve, timeline, bezier, frame, 6, time, time2, b2, nb2);
+						}
+						time = time2;
+						r = nr;
+						g = ng;
+						b = nb;
+						a = na;
+						r2 = nr2;
+						g2 = ng2;
+						b2 = nb2;
 						keyMap = nextMap;
 					}
 					timelines.add(timeline);
@@ -566,13 +621,15 @@ public class SkeletonJson {
 				if (timelineName.equals("rotate"))
 					timelines.add(readTimeline(keyMap, new RotateTimeline(timelineMap.size, timelineMap.size, bone.index), 0, 1));
 				else if (timelineName.equals("translate")) {
-					timelines
-						.add(readTimeline(keyMap, new TranslateTimeline(timelineMap.size, timelineMap.size, bone.index), 0, scale));
-				} else if (timelineName.equals("scale"))
-					timelines.add(readTimeline(keyMap, new ScaleTimeline(timelineMap.size, timelineMap.size, bone.index), 1, 1));
-				else if (timelineName.equals("shear"))
-					timelines.add(readTimeline(keyMap, new ShearTimeline(timelineMap.size, timelineMap.size, bone.index), 0, 1));
-				else
+					TranslateTimeline timeline = new TranslateTimeline(timelineMap.size, timelineMap.size << 1, bone.index);
+					timelines.add(readTimeline(keyMap, timeline, "x", "y", 0, scale));
+				} else if (timelineName.equals("scale")) {
+					ScaleTimeline timeline = new ScaleTimeline(timelineMap.size, timelineMap.size << 1, bone.index);
+					timelines.add(readTimeline(keyMap, timeline, "x", "y", 1, 1));
+				} else if (timelineName.equals("shear")) {
+					ShearTimeline timeline = new ShearTimeline(timelineMap.size, timelineMap.size << 1, bone.index);
+					timelines.add(readTimeline(keyMap, timeline, "x", "y", 0, 1));
+				} else
 					throw new RuntimeException("Invalid timeline type for a bone: " + timelineName + " (" + boneMap.name + ")");
 			}
 		}
@@ -582,19 +639,28 @@ public class SkeletonJson {
 			JsonValue keyMap = timelineMap.child;
 			if (keyMap == null) continue;
 			IkConstraintData constraint = skeletonData.findIkConstraint(timelineMap.name);
-			IkConstraintTimeline timeline = new IkConstraintTimeline(timelineMap.size, timelineMap.size,
+			IkConstraintTimeline timeline = new IkConstraintTimeline(timelineMap.size, timelineMap.size << 1,
 				skeletonData.getIkConstraints().indexOf(constraint, true));
 			float time = keyMap.getFloat("time", 0);
-			for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) {
-				timeline.setFrame(frameIndex, time, keyMap.getFloat("mix", 1), keyMap.getFloat("softness", 0) * scale,
-					keyMap.getBoolean("bendPositive", true) ? 1 : -1, keyMap.getBoolean("compress", false),
-					keyMap.getBoolean("stretch", false));
+			float mix = keyMap.getFloat("mix", 1), softness = keyMap.getFloat("softness", 0) * scale;
+			for (int frame = 0, bezier = 0;; frame++) {
+				timeline.setFrame(frame, time, mix, softness, keyMap.getBoolean("bendPositive", true) ? 1 : -1,
+					keyMap.getBoolean("compress", false), keyMap.getBoolean("stretch", false));
 				JsonValue nextMap = keyMap.next;
 				if (nextMap == null) {
-					timeline.shrink(bezierIndex);
+					timeline.shrink(bezier);
 					break;
 				}
-				bezierIndex = readCurve(keyMap, timeline, frameIndex, bezierIndex, time, time = nextMap.getFloat("time", 0));
+				float time2 = nextMap.getFloat("time", 0);
+				float mix2 = nextMap.getFloat("mix", 1), softness2 = nextMap.getFloat("softness", 0) * scale;
+				JsonValue curve = keyMap.get("curve");
+				if (curve != null) {
+					bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2);
+					bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2);
+				}
+				time = time2;
+				mix = mix2;
+				softness = softness2;
 				keyMap = nextMap;
 			}
 			timelines.add(timeline);
@@ -605,18 +671,33 @@ public class SkeletonJson {
 			JsonValue keyMap = timelineMap.child;
 			if (keyMap == null) continue;
 			TransformConstraintData constraint = skeletonData.findTransformConstraint(timelineMap.name);
-			TransformConstraintTimeline timeline = new TransformConstraintTimeline(timelineMap.size, timelineMap.size,
+			TransformConstraintTimeline timeline = new TransformConstraintTimeline(timelineMap.size, timelineMap.size << 2,
 				skeletonData.getTransformConstraints().indexOf(constraint, true));
 			float time = keyMap.getFloat("time", 0);
-			for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) {
-				timeline.setFrame(frameIndex, time, keyMap.getFloat("rotateMix", 1), keyMap.getFloat("translateMix", 1),
-					keyMap.getFloat("scaleMix", 1), keyMap.getFloat("shearMix", 1));
+			float rotateMix = keyMap.getFloat("rotateMix", 1), translateMix = keyMap.getFloat("translateMix", 1);
+			float scaleMix = keyMap.getFloat("scaleMix", 1), shearMix = keyMap.getFloat("shearMix", 1);
+			for (int frame = 0, bezier = 0;; frame++) {
+				timeline.setFrame(frame, time, rotateMix, translateMix, scaleMix, shearMix);
 				JsonValue nextMap = keyMap.next;
 				if (nextMap == null) {
-					timeline.shrink(bezierIndex);
+					timeline.shrink(bezier);
 					break;
 				}
-				bezierIndex = readCurve(keyMap, timeline, frameIndex, bezierIndex, time, time = nextMap.getFloat("time", 0));
+				float time2 = nextMap.getFloat("time", 0);
+				float rotateMix2 = nextMap.getFloat("rotateMix", 1), translateMix2 = nextMap.getFloat("translateMix", 1);
+				float scaleMix2 = nextMap.getFloat("scaleMix", 1), shearMix2 = nextMap.getFloat("shearMix", 1);
+				JsonValue curve = keyMap.get("curve");
+				if (curve != null) {
+					bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, rotateMix, rotateMix2);
+					bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, translateMix, translateMix2);
+					bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, scaleMix, scaleMix2);
+					bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, shearMix, shearMix2);
+				}
+				time = time2;
+				rotateMix = rotateMix2;
+				translateMix = translateMix2;
+				scaleMix = scaleMix2;
+				shearMix = shearMix2;
 				keyMap = nextMap;
 			}
 			timelines.add(timeline);
@@ -631,16 +712,16 @@ public class SkeletonJson {
 				JsonValue keyMap = timelineMap.child;
 				if (keyMap == null) continue;
 				String timelineName = timelineMap.name;
-
 				if (timelineName.equals("position")) {
-					timelines.add(readTimeline(keyMap, new PathConstraintPositionTimeline(timelineMap.size, timelineMap.size, index),
-						0, data.positionMode == PositionMode.fixed ? scale : 1));
+					CurveTimeline1 timeline = new PathConstraintPositionTimeline(timelineMap.size, timelineMap.size, index);
+					timelines.add(readTimeline(keyMap, timeline, 0, data.positionMode == PositionMode.fixed ? scale : 1));
 				} else if (timelineName.equals("spacing")) {
-					timelines.add(readTimeline(keyMap, new PathConstraintSpacingTimeline(timelineMap.size, timelineMap.size, index), 0,
+					CurveTimeline1 timeline = new PathConstraintSpacingTimeline(timelineMap.size, timelineMap.size, index);
+					timelines.add(readTimeline(keyMap, timeline, 0,
 						data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1));
 				} else if (timelineName.equals("mix")) {
-					timelines
-						.add(readTimeline(keyMap, new PathConstraintMixTimeline(timelineMap.size, timelineMap.size, index), 1, 1));
+					CurveTimeline2 timeline = new PathConstraintMixTimeline(timelineMap.size, timelineMap.size << 1, index);
+					timelines.add(readTimeline(keyMap, timeline, "rotateMix", "translateMix", 1, 1));
 				}
 			}
 		}
@@ -660,11 +741,11 @@ public class SkeletonJson {
 					if (attachment == null) throw new SerializationException("Deform attachment not found: " + timelineMap.name);
 					boolean weighted = attachment.getBones() != null;
 					float[] vertices = attachment.getVertices();
-					int deformLength = weighted ? vertices.length / 3 * 2 : vertices.length;
+					int deformLength = weighted ? (vertices.length / 3) << 1 : vertices.length;
 
 					DeformTimeline timeline = new DeformTimeline(timelineMap.size, timelineMap.size, slot.index, attachment);
 					float time = keyMap.getFloat("time", 0);
-					for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) {
+					for (int frame = 0, bezier = 0;; frame++) {
 						float[] deform;
 						JsonValue verticesValue = keyMap.get("vertices");
 						if (verticesValue == null)
@@ -683,16 +764,19 @@ public class SkeletonJson {
 							}
 						}
 
-						timeline.setFrame(frameIndex, time, deform);
+						timeline.setFrame(frame, time, deform);
 						JsonValue nextMap = keyMap.next;
 						if (nextMap == null) {
-							timeline.shrink(bezierIndex);
+							timeline.shrink(bezier);
 							break;
 						}
-						bezierIndex = readCurve(keyMap, timeline, frameIndex, bezierIndex, time, time = nextMap.getFloat("time", 0));
+						float time2 = nextMap.getFloat("time", 0);
+						JsonValue curve = keyMap.get("curve");
+						if (curve != null) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1);
+						time = time2;
 						keyMap = nextMap;
 					}
-					//timelines.add(timeline);
+					timelines.add(timeline);
 				}
 			}
 		}
@@ -703,8 +787,8 @@ public class SkeletonJson {
 		if (drawOrdersMap != null) {
 			DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrdersMap.size);
 			int slotCount = skeletonData.slots.size;
-			int frameIndex = 0;
-			for (JsonValue drawOrderMap = drawOrdersMap.child; drawOrderMap != null; drawOrderMap = drawOrderMap.next, frameIndex++) {
+			int frame = 0;
+			for (JsonValue drawOrderMap = drawOrdersMap.child; drawOrderMap != null; drawOrderMap = drawOrderMap.next, frame++) {
 				int[] drawOrder = null;
 				JsonValue offsets = drawOrderMap.get("offsets");
 				if (offsets != null) {
@@ -729,7 +813,7 @@ public class SkeletonJson {
 					for (int i = slotCount - 1; i >= 0; i--)
 						if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
 				}
-				timeline.setFrame(frameIndex, drawOrderMap.getFloat("time", 0), drawOrder);
+				timeline.setFrame(frame, drawOrderMap.getFloat("time", 0), drawOrder);
 			}
 			timelines.add(timeline);
 		}
@@ -738,8 +822,8 @@ public class SkeletonJson {
 		JsonValue eventsMap = map.get("events");
 		if (eventsMap != null) {
 			EventTimeline timeline = new EventTimeline(eventsMap.size);
-			int frameIndex = 0;
-			for (JsonValue eventMap = eventsMap.child; eventMap != null; eventMap = eventMap.next, frameIndex++) {
+			int frame = 0;
+			for (JsonValue eventMap = eventsMap.child; eventMap != null; eventMap = eventMap.next, frame++) {
 				EventData eventData = skeletonData.findEvent(eventMap.getString("name"));
 				if (eventData == null) throw new SerializationException("Event not found: " + eventMap.getString("name"));
 				Event event = new Event(eventMap.getFloat("time", 0), eventData);
@@ -750,7 +834,7 @@ public class SkeletonJson {
 					event.volume = eventMap.getFloat("volume", eventData.volume);
 					event.balance = eventMap.getFloat("balance", eventData.balance);
 				}
-				timeline.setFrame(frameIndex, event);
+				timeline.setFrame(frame, event);
 			}
 			timelines.add(timeline);
 		}
@@ -762,62 +846,71 @@ public class SkeletonJson {
 		skeletonData.animations.add(new Animation(name, timelines, duration));
 	}
 
-	private ValueCurveTimeline readTimeline (JsonValue keyMap, ValueCurveTimeline timeline, float defaultValue, float scale) {
+	private Timeline readTimeline (JsonValue keyMap, CurveTimeline1 timeline, float defaultValue, float scale) {
 		float time = keyMap.getFloat("time", 0), value = keyMap.getFloat("value", defaultValue);
-		int bezierIndex = 0;
-		for (int frameIndex = 0;; frameIndex++) {
-			timeline.setFrame(frameIndex, time, value);
+		int bezier = 0;
+		for (int frame = 0;; frame++) {
+			timeline.setFrame(frame, time, value);
 			JsonValue nextMap = keyMap.next;
 			if (nextMap == null) break;
-			bezierIndex = readCurve(keyMap, timeline, frameIndex, bezierIndex, //
-				time, value, time = nextMap.getFloat("time", 0), value = nextMap.getFloat("value", defaultValue));
+			float time2 = nextMap.getFloat("time", 0);
+			float value2 = nextMap.getFloat("value", defaultValue);
+			JsonValue curve = keyMap.get("curve");
+			if (curve != null) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value, value2);
+			time = time2;
+			value = value2;
 			keyMap = nextMap;
 		}
-		timeline.shrink(bezierIndex);
+		timeline.shrink(bezier);
 		return timeline;
 	}
 
-	private PercentCurveTimeline2 readTimeline (JsonValue keyMap, PercentCurveTimeline2 timeline, float defaultValue,
+	private Timeline readTimeline (JsonValue keyMap, CurveTimeline2 timeline, String name1, String name2, float defaultValue,
 		float scale) {
 		float time = keyMap.getFloat("time", 0);
-		int bezierIndex = 0;
-		for (int frameIndex = 0;; frameIndex++) {
-			float x = keyMap.getFloat("x", defaultValue), y = keyMap.getFloat("y", defaultValue);
-			timeline.setFrame(frameIndex, time, x * scale, y * scale);
+		float value1 = keyMap.getFloat(name1, defaultValue), value2 = keyMap.getFloat(name2, defaultValue);
+		int bezier = 0;
+		for (int frame = 0;; frame++) {
+			timeline.setFrame(frame, time, value1, value2);
 			JsonValue nextMap = keyMap.next;
 			if (nextMap == null) break;
-			bezierIndex = readCurve(keyMap, timeline, frameIndex, bezierIndex, time, time = nextMap.getFloat("time", 0));
+			float time2 = nextMap.getFloat("time", 0);
+			float nvalue1 = nextMap.getFloat(name1, defaultValue), nvalue2 = nextMap.getFloat(name2, defaultValue);
+			JsonValue curve = keyMap.get("curve");
+			if (curve != null) {
+				bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1);
+				bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2);
+			}
+			time = time2;
+			value1 = nvalue1;
+			value2 = nvalue2;
 			keyMap = nextMap;
 		}
-		timeline.shrink(bezierIndex);
+		timeline.shrink(bezier);
 		return timeline;
 	}
 
-	int readCurve (JsonValue map, PercentCurveTimeline timeline, int frameIndex, int bezierIndex, float time1, float time2) {
-		JsonValue curve = map.get("curve");
-		if (curve != null) {
-			if (curve.isString())
-				timeline.setStepped(frameIndex);
-			else {
-				timeline.setBezier(frameIndex, bezierIndex++, time1, curve.asFloat(), map.getFloat("c2", 0), map.getFloat("c3", 1),
-					map.getFloat("c4", 1), time2);
-			}
-		}
-		return bezierIndex;
+	int readCurve (JsonValue curve, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2,
+		float value1, float value2) {
+		if (curve.isString()) {
+			if (value != 0) timeline.setStepped(frame);
+		} else {
+			curve = curve.get(value * 4);
+			float cx1 = curve.asFloat();
+			curve = curve.next;
+			float cy1 = curve.asFloat();
+			curve = curve.next;
+			float cx2 = curve.asFloat();
+			curve = curve.next;
+			float cy2 = curve.asFloat();
+			setBezier(timeline, frame, value, bezier++, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
+		}
+		return bezier;
 	}
 
-	int readCurve (JsonValue map, ValueCurveTimeline timeline, int frameIndex, int bezierIndex, float time1, float value1,
-		float time2, float value2) {
-		JsonValue curve = map.get("curve");
-		if (curve != null) {
-			if (curve.isString())
-				timeline.setStepped(frameIndex);
-			else {
-				timeline.setBezier(frameIndex, bezierIndex++, time1, value1, curve.asFloat(), map.getFloat("c2", 0),
-					map.getFloat("c3", 1), map.getFloat("c4", 1), time2, value2);
-			}
-		}
-		return bezierIndex;
+	void setBezier (CurveTimeline timeline, int frame, int value, int bezier, float time1, float value1, float cx1, float cy1,
+		float cx2, float cy2, float time2, float value2) {
+		timeline.setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
 	}
 
 	static class LinkedMesh {

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