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

[cpp] More 4.0 porting, SkeletonJson.

badlogic 4 жил өмнө
parent
commit
4375342576

+ 2 - 0
spine-cpp/spine-cpp/include/spine/Json.h

@@ -54,6 +54,8 @@ public:
 	/* Get item "string" from object. Case insensitive. */
 	static Json *getItem(Json *object, const char *string);
 
+	static Json *getItem(Json *object, int childIndex);
+
 	static const char *getString(Json *object, const char *name, const char *defaultValue);
 
 	static float getFloat(Json *object, const char *name, float defaultValue);

+ 14 - 2
spine-cpp/spine-cpp/include/spine/SkeletonJson.h

@@ -35,8 +35,14 @@
 #include <spine/SpineString.h>
 
 namespace spine {
+class Timeline;
+
 class CurveTimeline;
 
+class CurveTimeline1;
+
+class CurveTimeline2;
+
 class VertexAttachment;
 
 class Animation;
@@ -76,9 +82,15 @@ private:
 	const bool _ownsLoader;
 	String _error;
 
-	static float toColor(const char *value, size_t index);
+    static 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);
+
+	static int readCurve(Json *curve, CurveTimeline *timeline, int bezier, int frame, int value, float time1, float time2,
+                          float value1, float value2, float scale);
+
+	static Timeline *readTimeline (Json *keyMap, CurveTimeline1 *timeline, float defaultValue, float scale);
 
-	static void readCurve(Json *frame, CurveTimeline *timeline, size_t frameIndex);
+    static Timeline *readTimeline (Json *keyMap, CurveTimeline2 *timeline, const char* name1, const char *name2, float defaultValue, float scale);
 
 	Animation *readAnimation(Json *root, SkeletonData *skeletonData);
 

+ 0 - 1
spine-cpp/spine-cpp/include/spine/spine.h

@@ -102,7 +102,6 @@
 #include <spine/TransformMode.h>
 #include <spine/TranslateTimeline.h>
 #include <spine/Triangulator.h>
-#include <spine/TwoColorTimeline.h>
 #include <spine/Updatable.h>
 #include <spine/Vector.h>
 #include <spine/VertexAttachment.h>

+ 2 - 0
spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp

@@ -56,6 +56,8 @@ AttachmentTimeline::AttachmentTimeline(size_t frameCount, int slotIndex) : Timel
 	}
 }
 
+AttachmentTimeline::~AttachmentTimeline() {}
+
 void AttachmentTimeline::setAttachment(Skeleton& skeleton, Slot& slot, String* attachmentName) {
     slot.setAttachment(attachmentName == NULL || attachmentName->isEmpty() ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName));
 }

+ 9 - 0
spine-cpp/spine-cpp/src/spine/Json.cpp

@@ -65,6 +65,15 @@ Json *Json::getItem(Json *object, const char *string) {
 	return c;
 }
 
+Json *Json::getItem(Json *object, int childIndex) {
+    Json *current = object->_child;
+    while (current != NULL && childIndex > 0) {
+        childIndex--;
+        current = current->_next;
+    }
+    return current;
+}
+
 const char *Json::getString(Json *object, const char *name, const char *defaultValue) {
 	object = getItem(object, name);
 	if (object) {

+ 6 - 0
spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp

@@ -51,6 +51,8 @@ ScaleTimeline::ScaleTimeline(size_t frameCount, size_t bezierCount, int boneInde
     setPropertyIds(ids, 2);
 }
 
+ScaleTimeline::~ScaleTimeline() {}
+
 void ScaleTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
 	MixBlend blend, MixDirection direction
 ) {
@@ -164,6 +166,8 @@ ScaleXTimeline::ScaleXTimeline(size_t frameCount, size_t bezierCount, int boneIn
     setPropertyIds(ids, 1);
 }
 
+ScaleXTimeline::~ScaleXTimeline() {}
+
 void ScaleXTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
                           MixBlend blend, MixDirection direction
 ) {
@@ -235,6 +239,8 @@ ScaleYTimeline::ScaleYTimeline(size_t frameCount, size_t bezierCount, int boneIn
     setPropertyIds(ids, 1);
 }
 
+ScaleYTimeline::~ScaleYTimeline() {}
+
 void ScaleYTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
                            MixBlend blend, MixDirection direction
 ) {

+ 413 - 281
spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp

@@ -79,6 +79,31 @@
 
 using namespace spine;
 
+static float toColor(const char *value, size_t index) {
+    char digits[3];
+    char *error;
+    int color;
+
+    if (index >= strlen(value) / 2) return -1;
+
+    value += index * 2;
+
+    digits[0] = *value;
+    digits[1] = *(value + 1);
+    digits[2] = '\0';
+    color = (int) strtoul(digits, &error, 16);
+    if (*error != 0) return -1;
+
+    return color / (float) 255;
+}
+
+static void toColor(Color &color, const char *value, bool hasAlpha) {
+    color.r = toColor(value, 0);
+    color.g = toColor(value, 1);
+    color.g = toColor(value, 2);
+    if (hasAlpha) color.a = toColor(value, 3);
+}
+
 SkeletonJson::SkeletonJson(Atlas *atlas) : _attachmentLoader(new(__FILE__, __LINE__) AtlasAttachmentLoader(atlas)),
 	_scale(1), _ownsLoader(true)
 {}
@@ -132,11 +157,6 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 	if (skeleton) {
 		skeletonData->_hash = Json::getString(skeleton, "hash", 0);
 		skeletonData->_version = Json::getString(skeleton, "spine", 0);
-		if ("3.8.75" == skeletonData->_version) {
-            delete skeletonData;
-            setError(root, "Unsupported skeleton data, please export with a newer version of Spine.", "");
-            return NULL;
-        }
 		skeletonData->_x = Json::getFloat(skeleton, "x", 0);
 		skeletonData->_y = Json::getFloat(skeleton, "y", 0);
 		skeletonData->_width = Json::getFloat(skeleton, "width", 0);
@@ -329,10 +349,12 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 			data->_offsetScaleY = Json::getFloat(constraintMap, "scaleY", 0);
 			data->_offsetShearY = Json::getFloat(constraintMap, "shearY", 0);
 
-			data->_rotateMix = Json::getFloat(constraintMap, "rotateMix", 1);
-			data->_translateMix = Json::getFloat(constraintMap, "translateMix", 1);
-			data->_scaleMix = Json::getFloat(constraintMap, "scaleMix", 1);
-			data->_shearMix = Json::getFloat(constraintMap, "shearMix", 1);
+            data->_mixRotate = Json::getFloat(constraintMap, "mixRotate", 1);
+            data->_mixX = Json::getFloat(constraintMap, "mixX", 1);
+            data->_mixY = Json::getFloat(constraintMap, "mixY", data->_mixX);
+            data->_mixScaleX = Json::getFloat(constraintMap, "mixScaleX", 1);
+            data->_mixScaleY = Json::getFloat(constraintMap, "mixScaleY", data->_mixScaleX);
+            data->_mixShearY = Json::getFloat(constraintMap, "mixShearY", 1);
 
 			skeletonData->_transformConstraints[i] = data;
 		}
@@ -394,8 +416,9 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 			if (data->_positionMode == PositionMode_Fixed) data->_position *= _scale;
 			data->_spacing = Json::getFloat(constraintMap, "spacing", 0);
 			if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) data->_spacing *= _scale;
-			data->_rotateMix = Json::getFloat(constraintMap, "rotateMix", 1);
-			data->_translateMix = Json::getFloat(constraintMap, "translateMix", 1);
+			data->_mixRotate = Json::getFloat(constraintMap, "mixRotate", 1);
+			data->_mixX = Json::getFloat(constraintMap, "mixX", 1);
+            data->_mixY = Json::getFloat(constraintMap, "mixY", 1);
 
 			skeletonData->_pathConstraints[i] = data;
 		}
@@ -725,46 +748,75 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 	return skeletonData;
 }
 
-float SkeletonJson::toColor(const char *value, size_t index) {
-	char digits[3];
-	char *error;
-	int color;
-
-	if (index >= strlen(value) / 2) return -1;
-
-	value += index * 2;
+void SkeletonJson::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);
+}
 
-	digits[0] = *value;
-	digits[1] = *(value + 1);
-	digits[2] = '\0';
-	color = (int) strtoul(digits, &error, 16);
-	if (*error != 0) return -1;
+int SkeletonJson::readCurve (Json *curve, CurveTimeline *timeline, int bezier, int frame, int value, float time1, float time2,
+               float value1, float value2, float scale) {
+    if (curve->_type == Json::JSON_STRING && strcmp(curve->_valueString, "stepped") == 0) {
+        timeline->setStepped(frame);
+    } else {
+        curve = Json::getItem(curve, value << 2);
+        float cx1 = curve->_valueFloat;
+        curve = curve->_next;
+        float cy1 = curve->_valueFloat * scale;
+        curve = curve->_next;
+        float cx2 = curve->_valueFloat;
+        curve = curve->_next;
+        float cy2 = curve->_valueFloat * scale;
+        setBezier(timeline, frame, value, bezier++, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
+    }
+    return bezier;
+}
 
-	return color / (float) 255;
+Timeline *SkeletonJson::readTimeline (Json *keyMap, CurveTimeline1 *timeline, float defaultValue, float scale) {
+    float time = Json::getFloat(keyMap, "time", 0);
+    float value = Json::getFloat(keyMap, "value", defaultValue) * scale;
+    int bezier = 0;
+    for (int frame = 0;; frame++) {
+        timeline->setFrame(frame, time, value);
+        Json* nextMap = keyMap->_next;
+        if (nextMap == NULL) break;
+        float time2 = Json::getFloat(nextMap, "time", 0);
+        float value2 = Json::getFloat(nextMap, "value", defaultValue) * scale;
+        Json* curve = Json::getItem(keyMap, "curve");
+        if (curve != NULL) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value, value2, scale);
+        time = time2;
+        value = value2;
+        keyMap = nextMap;
+    }
+    return timeline;
 }
 
-void SkeletonJson::readCurve(Json *frame, CurveTimeline *timeline, size_t frameIndex) {
-	Json *curve = Json::getItem(frame, "curve");
-	if (!curve) return;
-	if (curve->_type == Json::JSON_STRING && strcmp(curve->_valueString, "stepped") == 0)
-		timeline->setStepped(frameIndex);
-	else {
-		float c1 = Json::getFloat(frame, "curve", 0);
-		float c2 = Json::getFloat(frame, "c2", 0);
-		float c3 = Json::getFloat(frame, "c3", 1);
-		float c4 = Json::getFloat(frame, "c4", 1);
-		timeline->setCurve(frameIndex, c1, c2, c3, c4);
-	}
+Timeline *SkeletonJson::readTimeline (Json *keyMap, CurveTimeline2 *timeline, const char *name1, const char *name2, float defaultValue, float scale) {
+    float time = Json::getFloat(keyMap, "time", 0);
+    float value1 = Json::getFloat(keyMap, name1, defaultValue) * scale;
+    float value2 = Json::getFloat(keyMap, name2, defaultValue) * scale;
+    int bezier = 0;
+    for (int frame = 0;; frame++) {
+        timeline->setFrame(frame, time, value1, value2);
+        Json *nextMap = keyMap->_next;
+        if (nextMap == NULL) break;
+        float time2 = Json::getFloat(nextMap, "time", 0);
+        float nvalue1 = Json::getFloat(nextMap, name1, defaultValue) * scale;
+        float nvalue2 = Json::getFloat(nextMap, name2, defaultValue) * scale;
+        Json *curve = Json::getItem(keyMap, "curve");
+        if (curve != NULL) {
+            bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale);
+            bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale);
+        }
+        time = time2;
+        value1 = nvalue1;
+        value2 = nvalue2;
+        keyMap = nextMap;
+    }
+    return timeline;
 }
 
 Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 	Vector<Timeline *> timelines;
-	float duration = 0;
-
-	size_t frameIndex;
-	Json *valueMap;
-	int timelinesCount = 0;
-
 	Json *bones = Json::getItem(root, "bones");
 	Json *slots = Json::getItem(root, "slots");
 	Json *ik = Json::getItem(root, "ik");
@@ -773,33 +825,12 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 	Json *deform = Json::getItem(root, "deform");
 	Json *drawOrder = Json::getItem(root, "drawOrder");
 	Json *events = Json::getItem(root, "events");
-	Json *boneMap, *slotMap, *constraintMap;
-	if (!drawOrder) drawOrder = Json::getItem(root, "draworder");
-
-	for (boneMap = bones ? bones->_child : NULL; boneMap; boneMap = boneMap->_next)
-		timelinesCount += boneMap->_size;
-
-	for (slotMap = slots ? slots->_child : NULL; slotMap; slotMap = slotMap->_next)
-		timelinesCount += slotMap->_size;
-
-	timelinesCount += ik ? ik->_size : 0;
-	timelinesCount += transform ? transform->_size : 0;
-
-	for (constraintMap = paths ? paths->_child : NULL; constraintMap; constraintMap = constraintMap->_next)
-		timelinesCount += constraintMap->_size;
-
-	for (constraintMap = deform ? deform->_child : NULL; constraintMap; constraintMap = constraintMap->_next)
-		for (slotMap = constraintMap->_child; slotMap; slotMap = slotMap->_next)
-			timelinesCount += slotMap->_size;
-
-	if (drawOrder) ++timelinesCount;
-
-	if (events) ++timelinesCount;
+	Json *boneMap, *slotMap, *constraintMap, *keyMap, *nextMap, *curve;
+	int frame, bezier;
+    Color color, color2, newColor, newColor2;
 
 	/** Slot timelines. */
 	for (slotMap = slots ? slots->_child : 0; slotMap; slotMap = slotMap->_next) {
-		Json *timelineMap;
-
 		int slotIndex = skeletonData->findSlotIndex(slotMap->_name);
 		if (slotIndex == -1) {
 			ContainerUtil::cleanUpVectorOfPointers(timelines);
@@ -807,52 +838,131 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 			return NULL;
 		}
 
-		for (timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) {
+		for (Json *timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) {
 			if (strcmp(timelineMap->_name, "attachment") == 0) {
-				AttachmentTimeline *timeline = new(__FILE__, __LINE__) AttachmentTimeline(timelineMap->_size);
-
-				timeline->_slotIndex = slotIndex;
-
-				for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
-					Json *name = Json::getItem(valueMap, "name");
-					String attachmentName = name->_type == Json::JSON_NULL ? "" : name->_valueString;
-					timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), attachmentName);
-				}
-				timelines.add(timeline);
-				timelinesCount++;
-				duration = MathUtil::max(duration, timeline->_frames[timelineMap->_size - 1]);
-
-			} else if (strcmp(timelineMap->_name, "color") == 0) {
-				ColorTimeline *timeline = new(__FILE__, __LINE__) ColorTimeline(timelineMap->_size);
-
-				timeline->_slotIndex = slotIndex;
-
-				for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
-					const char *s = Json::getString(valueMap, "color", 0);
-					timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1),
-						toColor(s, 2), toColor(s, 3));
-					readCurve(valueMap, timeline, frameIndex);
+			    int frameCount = timelineMap->_size;
+				AttachmentTimeline *timeline = new(__FILE__, __LINE__) AttachmentTimeline(frameCount, slotIndex);
+				for (keyMap = timelineMap->_child, frame = 0; keyMap; keyMap = keyMap->_next, ++frame) {
+					timeline->setFrame(frame, Json::getFloat(keyMap, "time", 0), Json::getItem(keyMap, "name")->_valueString);
 				}
 				timelines.add(timeline);
-				timelinesCount++;
-				duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * ColorTimeline::ENTRIES]);
-
-			} else if (strcmp(timelineMap->_name, "twoColor") == 0) {
-				TwoColorTimeline *timeline = new(__FILE__, __LINE__) TwoColorTimeline(timelineMap->_size);
-
-				timeline->_slotIndex = slotIndex;
 
-				for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
-					const char *s = Json::getString(valueMap, "light", 0);
-					const char *ds = Json::getString(valueMap, "dark", 0);
-					timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1),
-						toColor(s, 2), toColor(s, 3), toColor(ds, 0), toColor(ds, 1), toColor(ds, 2));
-					readCurve(valueMap, timeline, frameIndex);
+			} else if (strcmp(timelineMap->_name, "rgba") == 0) {
+                int frameCount = timelineMap->_size;
+                int bezierCount = frameCount << 2;
+				RGBATimeline *timeline = new(__FILE__, __LINE__) RGBATimeline(frameCount, bezierCount, slotIndex);
+                keyMap = timelineMap->_child;
+                float time = Json::getFloat(keyMap, "time", 0);
+                toColor(color, Json::getString(keyMap, "color", 0), true);
+
+				for (frame = 0, bezier = 0;;++frame) {
+					timeline->setFrame(frame, time, color.r, color.g, color.b, color.a);
+					nextMap = keyMap->_next;
+					if (!keyMap) break;
+					float time2 = Json::getFloat(nextMap, "time", 0);
+                    toColor(newColor, Json::getString(nextMap, "color", 0), true);
+                    curve = Json::getItem(keyMap, "curve");
+                    if (curve) {
+                        bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color.a, newColor.a, 1);
+                    }
+                    time = time2;
+                    color = newColor;
+                    keyMap = nextMap;
 				}
 				timelines.add(timeline);
-				timelinesCount++;
-				duration = MathUtil::max(duration,
-										 timeline->_frames[(timelineMap->_size - 1) * TwoColorTimeline::ENTRIES]);
+			} else if (strcmp(timelineMap->_name, "rgb") == 0) {
+                int frameCount = timelineMap->_size;
+                int bezierCount = frameCount * 3;
+                RGBTimeline *timeline = new(__FILE__, __LINE__) RGBTimeline(frameCount, bezierCount, slotIndex);
+                keyMap = timelineMap->_child;
+                float time = Json::getFloat(keyMap, "time", 0);
+                toColor(color, Json::getString(keyMap, "color", 0), false);
+
+                for (frame = 0, bezier = 0;;++frame) {
+                    timeline->setFrame(frame, time, color.r, color.g, color.b);
+                    nextMap = keyMap->_next;
+                    if (!keyMap) break;
+                    float time2 = Json::getFloat(nextMap, "time", 0);
+                    toColor(newColor, Json::getString(nextMap, "color", 0), false);
+                    curve = Json::getItem(keyMap, "curve");
+                    if (curve) {
+                        bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+                    }
+                    time = time2;
+                    color = newColor;
+                    keyMap = nextMap;
+                }
+                timelines.add(timeline);
+            } else if (strcmp(timelineMap->_name, "alpha") == 0) {
+                timelines.add(readTimeline(timelineMap, new (__FILE__, __LINE__) AlphaTimeline(timelineMap->_size, timelineMap->_size, slotIndex), 0, 1));
+            } else if (strcmp(timelineMap->_name, "rgba2") == 0) {
+                int frameCount = timelineMap->_size;
+                int bezierCount = frameCount * 7;
+                RGBA2Timeline *timeline = new(__FILE__, __LINE__) RGBA2Timeline(frameCount, bezierCount, slotIndex);
+                keyMap = timelineMap->_child;
+                float time = Json::getFloat(keyMap, "time", 0);
+                toColor(color, Json::getString(keyMap, "light", 0), true);
+                toColor(color2, Json::getString(keyMap, "dark", 0), false);
+
+                for (frame = 0, bezier = 0;;++frame) {
+                    timeline->setFrame(frame, time, color.r, color.g, color.b, color.a, color2.g, color2.g, color2.b);
+                    nextMap = keyMap->_next;
+                    if (!keyMap) break;
+                    float time2 = Json::getFloat(nextMap, "time", 0);
+                    toColor(newColor, Json::getString(nextMap, "light", 0), true);
+                    toColor(newColor2, Json::getString(nextMap, "dark", 0), false);
+                    curve = Json::getItem(keyMap, "curve");
+                    if (curve) {
+                        bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color.a, newColor.a, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, color2.r, newColor2.r, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, color2.g, newColor2.g, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 6, time, time2, color2.b, newColor2.b, 1);
+                    }
+                    time = time2;
+                    color = newColor;
+                    color2 = newColor2;
+                    keyMap = nextMap;
+                }
+                timelines.add(timeline);
+            } else if (strcmp(timelineMap->_name, "rgb2") == 0) {
+                int frameCount = timelineMap->_size;
+                int bezierCount = frameCount * 7;
+                RGBA2Timeline *timeline = new(__FILE__, __LINE__) RGBA2Timeline(frameCount, bezierCount, slotIndex);
+                keyMap = timelineMap->_child;
+                float time = Json::getFloat(keyMap, "time", 0);
+                toColor(color, Json::getString(keyMap, "light", 0), false);
+                toColor(color2, Json::getString(keyMap, "dark", 0), false);
+
+                for (frame = 0, bezier = 0;;++frame) {
+                    timeline->setFrame(frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b);
+                    nextMap = keyMap->_next;
+                    if (!keyMap) break;
+                    float time2 = Json::getFloat(nextMap, "time", 0);
+                    toColor(newColor, Json::getString(nextMap, "light", 0), false);
+                    toColor(newColor2, Json::getString(nextMap, "dark", 0), false);
+                    curve = Json::getItem(keyMap, "curve");
+                    if (curve) {
+                        bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color2.r, newColor2.r, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, color2.g, newColor2.g, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, color2.b, newColor2.b, 1);
+                    }
+                    time = time2;
+                    color = newColor;
+                    color2 = newColor2;
+                    keyMap = nextMap;
+                }
+                timelines.add(timeline);
 			} else {
 				ContainerUtil::cleanUpVectorOfPointers(timelines);
 				setError(NULL, "Invalid timeline type for a slot: ", timelineMap->_name);
@@ -873,161 +983,184 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 		}
 
 		for (timelineMap = boneMap->_child; timelineMap; timelineMap = timelineMap->_next) {
-			if (strcmp(timelineMap->_name, "rotate") == 0) {
-				RotateTimeline *timeline = new(__FILE__, __LINE__) RotateTimeline(timelineMap->_size);
-
-				timeline->_boneIndex = boneIndex;
-
-				for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
-					timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "angle", 0));
-					readCurve(valueMap, timeline, frameIndex);
-				}
-				timelines.add(timeline);
-				timelinesCount++;
-				duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * RotateTimeline::ENTRIES]);
-			} else {
-				int isScale = strcmp(timelineMap->_name, "scale") == 0;
-				int isTranslate = strcmp(timelineMap->_name, "translate") == 0;
-				int isShear = strcmp(timelineMap->_name, "shear") == 0;
-				if (isScale || isTranslate || isShear) {
-					float timelineScale = isTranslate ? _scale : 1;
-					float defaultValue = 0;
-					TranslateTimeline *timeline = 0;
-					if (isScale) {
-						timeline = new(__FILE__, __LINE__) ScaleTimeline(timelineMap->_size);
-						defaultValue = 1;
-					} else if (isTranslate) {
-						timeline = new(__FILE__, __LINE__) TranslateTimeline(timelineMap->_size);
-					} else if (isShear) {
-						timeline = new(__FILE__, __LINE__) ShearTimeline(timelineMap->_size);
-					}
-					timeline->_boneIndex = boneIndex;
-
-					for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
-						timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0),
-							Json::getFloat(valueMap, "x", defaultValue) * timelineScale,
-							Json::getFloat(valueMap, "y", defaultValue) * timelineScale);
-						readCurve(valueMap, timeline, frameIndex);
-					}
-
-					timelines.add(timeline);
-					timelinesCount++;
-					duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * TranslateTimeline::ENTRIES]);
-				} else {
-					ContainerUtil::cleanUpVectorOfPointers(timelines);
-					setError(NULL, "Invalid timeline type for a bone: ", timelineMap->_name);
-					return NULL;
-				}
-			}
+		    if (timelineMap->_size == 0) continue;
+
+            if (strcmp(timelineMap->_name, "rotate") == 0) {
+                timelines.add(readTimeline(timelineMap, new RotateTimeline(timelineMap->_size, timelineMap->_size, boneIndex), 0, 1));
+            } else if (strcmp(timelineMap->_name, "translate") == 0) {
+                TranslateTimeline *timeline = new TranslateTimeline(timelineMap->_size, timelineMap->_size << 1, boneIndex);
+                timelines.add(readTimeline(timelineMap, timeline, "x", "y", 0, _scale));
+            } else if (strcmp(timelineMap->_name, "translatex") == 0) {
+                TranslateXTimeline *timeline = new TranslateXTimeline(timelineMap->_size, timelineMap->_size, boneIndex);
+                timelines.add(readTimeline(timelineMap, timeline, 0, _scale));
+            } else if (strcmp(timelineMap->_name, "translatey") == 0) {
+                TranslateYTimeline *timeline = new TranslateYTimeline(timelineMap->_size, timelineMap->_size, boneIndex);
+                timelines.add(readTimeline(timelineMap, timeline, 0, _scale));
+            } else if (strcmp(timelineMap->_name, "scale") == 0) {
+                ScaleTimeline *timeline = new (__FILE__, __LINE__) ScaleTimeline(timelineMap->_size, timelineMap->_size << 1, boneIndex);
+                timelines.add(readTimeline(timelineMap, timeline, "x", "y", 1, 1));
+            } else if (strcmp(timelineMap->_name, "scalex") == 0) {
+                ScaleXTimeline *timeline = new (__FILE__, __LINE__) ScaleXTimeline(timelineMap->_size, timelineMap->_size, boneIndex);
+                timelines.add(readTimeline(timelineMap, timeline, 1, 1));
+            } else if (strcmp(timelineMap->_name, "scaley") == 0) {
+                ScaleYTimeline *timeline = new (__FILE__, __LINE__) ScaleYTimeline(timelineMap->_size, timelineMap->_size, boneIndex);
+                timelines.add(readTimeline(timelineMap, timeline, 1, 1));
+            } else if (strcmp(timelineMap->_name, "shear") == 0) {
+                ShearTimeline *timeline = new (__FILE__, __LINE__) ShearTimeline(timelineMap->_size, timelineMap->_size << 1, boneIndex);
+                timelines.add(readTimeline(timelineMap, timeline, "x", "y", 0, 1));
+            } else if (strcmp(timelineMap->_name, "shearx") == 0) {
+                ShearXTimeline *timeline = new (__FILE__, __LINE__) ShearXTimeline(timelineMap->_size, timelineMap->_size, boneIndex);
+                timelines.add(readTimeline(timelineMap, timeline, 0, 1));
+            } else if (strcmp(timelineMap->_name, "sheary") == 0) {
+                ShearYTimeline *timeline = new (__FILE__, __LINE__) ShearYTimeline(timelineMap->_size, timelineMap->_size, boneIndex);
+                timelines.add(readTimeline(timelineMap, timeline, 0, 1));
+            } else {
+                ContainerUtil::cleanUpVectorOfPointers(timelines);
+                setError(NULL, "Invalid timeline type for a bone: ", timelineMap->_name);
+                return NULL;
+            }
 		}
 	}
 
 	/** IK constraint timelines. */
 	for (constraintMap = ik ? ik->_child : 0; constraintMap; constraintMap = constraintMap->_next) {
+	    keyMap = constraintMap->_child;
+        if (keyMap == NULL) continue;
+
 		IkConstraintData *constraint = skeletonData->findIkConstraint(constraintMap->_name);
-		IkConstraintTimeline *timeline = new(__FILE__, __LINE__) IkConstraintTimeline(constraintMap->_size);
+		int constraintIndex = skeletonData->_ikConstraints.indexOf(constraint);
+		IkConstraintTimeline *timeline = new(__FILE__, __LINE__) IkConstraintTimeline(constraintMap->_size, constraintMap->_size << 1, constraintIndex);
+
+        float time = Json::getFloat(keyMap, "time", 0);
+        float mix = Json::getFloat(keyMap, "mix", 1);
+        float softness = Json::getFloat(keyMap, "softness", 0) * _scale;
+
+        for (frame = 0, bezier = 0;; frame++) {
+            timeline->setFrame(frame, time, mix, softness, Json::getBoolean(keyMap, "bendPositive", true) ? 1 : -1, Json::getBoolean(keyMap, "compress", false), Json::getBoolean(keyMap, "stretch", false));
+            nextMap = keyMap->_next;
+            if (!nextMap) break;
+
+            float time2 = Json::getFloat(nextMap, "time", 0);
+            float mix2 = Json::getFloat(nextMap, "mix", 1);
+            float softness2 = Json::getFloat(nextMap, "softness", 0) * _scale;
+            curve = Json::getItem(keyMap, "curve");
+            if (curve) {
+                bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2, 1);
+                bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2, _scale);
+            }
+
+            time = time2;
+            mix = mix2;
+            softness = softness2;
+            keyMap = nextMap;
+        }
 
-		for (frameIndex = 0; frameIndex < skeletonData->_ikConstraints.size(); ++frameIndex) {
-			if (constraint == skeletonData->_ikConstraints[frameIndex]) {
-				timeline->_ikConstraintIndex = frameIndex;
-				break;
-			}
-		}
-		for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
-			timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "mix", 1),
-				Json::getFloat(valueMap, "softness", 0) * _scale, Json::getInt(valueMap, "bendPositive", 1) ? 1 : -1,
-				Json::getInt(valueMap, "compress", 0) ? true : false, Json::getInt(valueMap, "stretch", 0) ? true : false);
-			readCurve(valueMap, timeline, frameIndex);
-		}
 		timelines.add(timeline);
-		timelinesCount++;
-		duration = MathUtil::max(duration, timeline->_frames[(constraintMap->_size - 1) * IkConstraintTimeline::ENTRIES]);
 	}
 
 	/** Transform constraint timelines. */
 	for (constraintMap = transform ? transform->_child : 0; constraintMap; constraintMap = constraintMap->_next) {
-		TransformConstraintData *constraint = skeletonData->findTransformConstraint(constraintMap->_name);
-		TransformConstraintTimeline *timeline = new(__FILE__, __LINE__) TransformConstraintTimeline(constraintMap->_size);
+        keyMap = constraintMap->_child;
+        if (keyMap == NULL) continue;
+
+        TransformConstraintData *constraint = skeletonData->findTransformConstraint(constraintMap->_name);
+        int constraintIndex = skeletonData->_transformConstraints.indexOf(constraint);
+        TransformConstraintTimeline *timeline = new(__FILE__, __LINE__) TransformConstraintTimeline(constraintMap->_size, constraintMap->_size << 2, constraintIndex);
+
+        float time = Json::getFloat(keyMap, "time", 0);
+        float mixRotate = Json::getFloat(keyMap, "mixRotate", 1);
+        float mixShearY = Json::getFloat(keyMap, "mixShearY", 1);
+        float mixX = Json::getFloat(keyMap, "mixX", 1);
+        float mixY = Json::getFloat(keyMap, "mixY", mixX);
+        float mixScaleX = Json::getFloat(keyMap, "mixScaleX", 1);
+        float mixScaleY = Json::getFloat(keyMap, "mixScaleY", mixScaleX);
+
+        for (frame = 0, bezier = 0;; frame++) {
+            timeline->setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
+            nextMap = keyMap->_next;
+            if (!nextMap) break;
+
+            float time2 = Json::getFloat(nextMap, "time", 0);
+            float mixRotate2 = Json::getFloat(nextMap, "mixRotate", 1);
+            float mixShearY2 = Json::getFloat(nextMap, "mixShearY", 1);
+            float mixX2 = Json::getFloat(nextMap, "mixX", 1);
+            float mixY2 = Json::getFloat(nextMap, "mixY", mixX2);
+            float mixScaleX2 = Json::getFloat(nextMap, "mixScaleX", 1);
+            float mixScaleY2 = Json::getFloat(nextMap, "mixScaleY", mixScaleX2);
+            curve = Json::getItem(keyMap, "curve");
+            if (curve) {
+                bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+                bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
+                bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
+                bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
+                bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
+                bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, mixShearY, mixShearY2, 1);
+            }
+
+            time = time2;
+            mixRotate = mixRotate2;
+            mixX = mixX2;
+            mixY = mixY2;
+            mixScaleX = mixScaleX2;
+            mixScaleY = mixScaleY2;
+            mixScaleX = mixScaleX2;
+            keyMap = nextMap;
+        }
 
-		for (frameIndex = 0; frameIndex < skeletonData->_transformConstraints.size(); ++frameIndex) {
-			if (constraint == skeletonData->_transformConstraints[frameIndex]) {
-				timeline->_transformConstraintIndex = frameIndex;
-				break;
-			}
-		}
-		for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
-			timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0),
-				Json::getFloat(valueMap, "rotateMix", 1), Json::getFloat(valueMap, "translateMix", 1),
-				Json::getFloat(valueMap, "scaleMix", 1), Json::getFloat(valueMap, "shearMix", 1));
-			readCurve(valueMap, timeline, frameIndex);
-		}
-		timelines.add(timeline);
-		timelinesCount++;
-		duration = MathUtil::max(duration, timeline->_frames[(constraintMap->_size - 1) * TransformConstraintTimeline::ENTRIES]);
+        timelines.add(timeline);
 	}
 
 	/** Path constraint timelines. */
 	for (constraintMap = paths ? paths->_child : 0; constraintMap; constraintMap = constraintMap->_next) {
-		size_t constraintIndex = 0, i;
-		Json *timelineMap;
-
-		PathConstraintData *data = skeletonData->findPathConstraint(constraintMap->_name);
-		if (!data) {
-			ContainerUtil::cleanUpVectorOfPointers(timelines);
-			setError(NULL, "Path constraint not found: ", constraintMap->_name);
-			return NULL;
-		}
-
-		for (i = 0; i < skeletonData->_pathConstraints.size(); i++) {
-			if (skeletonData->_pathConstraints[i] == data) {
-				constraintIndex = i;
-				break;
-			}
-		}
-
-		for (timelineMap = constraintMap->_child; timelineMap; timelineMap = timelineMap->_next) {
-			const char *timelineName = timelineMap->_name;
-			if (strcmp(timelineName, "position") == 0 || strcmp(timelineName, "spacing") == 0) {
-				PathConstraintPositionTimeline *timeline;
-				float timelineScale = 1;
-				if (strcmp(timelineName, "spacing") == 0) {
-					timeline = new(__FILE__, __LINE__) PathConstraintSpacingTimeline(timelineMap->_size);
-
-					if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) {
-						timelineScale = _scale;
-					}
-				} else {
-					timeline = new(__FILE__, __LINE__) PathConstraintPositionTimeline(timelineMap->_size);
-
-					if (data->_positionMode == PositionMode_Fixed) {
-						timelineScale = _scale;
-					}
-				}
-
-				timeline->_pathConstraintIndex = constraintIndex;
-				for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
-					timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0),
-						Json::getFloat(valueMap, timelineName, 0) * timelineScale);
-					readCurve(valueMap, timeline, frameIndex);
-				}
-				timelines.add(timeline);
-				timelinesCount++;
-				duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) *
-																	 PathConstraintPositionTimeline::ENTRIES]);
-			} else if (strcmp(timelineName, "mix") == 0) {
-				PathConstraintMixTimeline *timeline = new(__FILE__, __LINE__) PathConstraintMixTimeline(timelineMap->_size);
-				timeline->_pathConstraintIndex = constraintIndex;
-				for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
-					timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0),
-						Json::getFloat(valueMap, "rotateMix", 1),
-						Json::getFloat(valueMap, "translateMix", 1));
-					readCurve(valueMap, timeline, frameIndex);
-				}
-				timelines.add(timeline);
-				timelinesCount++;
-				duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * PathConstraintMixTimeline::ENTRIES]);
-			}
-		}
+        PathConstraintData *data = skeletonData->findPathConstraint(constraintMap->_name);
+        if (!data) {
+            ContainerUtil::cleanUpVectorOfPointers(timelines);
+            setError(NULL, "Path constraint not found: ", constraintMap->_name);
+            return NULL;
+        }
+        int index = skeletonData->_pathConstraints.indexOf(data);
+        for (Json *timelineMap = constraintMap->_child; timelineMap; timelineMap = timelineMap->_next) {
+            keyMap = timelineMap->_child;
+            if (keyMap == NULL) continue;
+            const char *timelineName = timelineMap->_name;
+            if (strcmp(timelineName, "position") == 0 ) {
+                PathConstraintPositionTimeline *timeline = new (__FILE__, __LINE__) PathConstraintPositionTimeline(timelineMap->_size, timelineMap->_size, index);
+                timelines.add(readTimeline(keyMap, timeline, 0, data->_positionMode == PositionMode_Fixed ? _scale : 1));
+            } else if (strcmp(timelineName, "spacing") == 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 (strcmp(timelineName, "mix") == 0) {
+                PathConstraintMixTimeline *timeline = new PathConstraintMixTimeline(timelineMap->_size, timelineMap->_size * 3, index);
+                float time = Json::getFloat(keyMap, "time", 0);
+                float mixRotate = Json::getFloat(keyMap, "mixRotate", 1);
+                float mixX = Json::getFloat(keyMap, "mixX", 1);
+                float mixY = Json::getFloat(keyMap, "mixY", mixX);
+                for (frame = 0, bezier = 0;; frame++) {
+                    timeline->setFrame(frame, time, mixRotate, mixX, mixY);
+                    nextMap = keyMap->_next;
+                    if (nextMap == NULL) {
+                        break;
+                    }
+                    float time2 = Json::getFloat(nextMap, "time", 0);
+                    float mixRotate2 = Json::getFloat(nextMap, "mixRotate", 1);
+                    float mixX2 = Json::getFloat(nextMap, "mixX", 1);
+                    float mixY2 = Json::getFloat(nextMap, "mixY", mixX2);
+                    curve = Json::getItem(keyMap, "curve");
+                    if (curve != NULL) {
+                        bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
+                        bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
+                    }
+                    time = time2;
+                    mixRotate = mixRotate2;
+                    mixX = mixX2;
+                    mixY = mixY2;
+                    keyMap = nextMap;
+                }
+                timelines.add(timeline);
+            }
+        }
 	}
 
 	/** Deform timelines. */
@@ -1037,30 +1170,25 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 			int slotIndex = skeletonData->findSlotIndex(slotMap->_name);
 			Json *timelineMap;
 			for (timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) {
-				DeformTimeline *timeline;
-				int weighted, deformLength;
+			    keyMap = timelineMap->_child;
+			    if (keyMap == NULL) continue;
 
 				Attachment *baseAttachment = skin->getAttachment(slotIndex, timelineMap->_name);
-
 				if (!baseAttachment) {
 					ContainerUtil::cleanUpVectorOfPointers(timelines);
 					setError(NULL, "Attachment not found: ", timelineMap->_name);
 					return NULL;
 				}
-
 				VertexAttachment *attachment = static_cast<VertexAttachment *>(baseAttachment);
 
-				weighted = attachment->_bones.size() != 0;
+				bool weighted = attachment->_bones.size() != 0;
 				Vector<float> &verts = attachment->_vertices;
-				deformLength = weighted ? verts.size() / 3 * 2 : verts.size();
-
-				timeline = new(__FILE__, __LINE__) DeformTimeline(timelineMap->_size);
-
-				timeline->_slotIndex = slotIndex;
-				timeline->_attachment = attachment;
+				int deformLength = weighted ? verts.size() / 3 * 2 : verts.size();
 
-				for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
-					Json *vertices = Json::getItem(valueMap, "vertices");
+				DeformTimeline *timeline = new(__FILE__, __LINE__) DeformTimeline(timelineMap->_size, timelineMap->_size, slotIndex, attachment);
+                float time = Json::getFloat(keyMap, "time", 0);
+				for (frame = 0, bezier = 0;; frame++) {
+					Json *vertices = Json::getItem(keyMap, "vertices");
 					Vector<float> deformed;
 					if (!vertices) {
 						if (weighted) {
@@ -1069,7 +1197,7 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 							deformed.clearAndAddAll(attachment->_vertices);
 						}
 					} else {
-						int v, start = Json::getInt(valueMap, "offset", 0);
+						int v, start = Json::getInt(keyMap, "offset", 0);
 						Json *vertex;
 						deformed.setSize(deformLength, 0);
 						if (_scale == 1) {
@@ -1088,13 +1216,18 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 							}
 						}
 					}
-					timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), deformed);
-					readCurve(valueMap, timeline, frameIndex);
+                    timeline->setFrame(frame, time, deformed);
+                    nextMap = keyMap->_next;
+                    if (!nextMap) break;
+                    float time2 = Json::getFloat(nextMap, "time", 0);
+                    curve = Json::getItem(keyMap, "curve");
+                    if (curve) {
+                        bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1);
+                    }
+                    time = time2;
+                    keyMap = nextMap;
 				}
-
 				timelines.add(timeline);
-				timelinesCount++;
-				duration = MathUtil::max(duration, timeline->_frames[timelineMap->_size - 1]);
 			}
 		}
 	}
@@ -1103,10 +1236,10 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 	if (drawOrder) {
 		DrawOrderTimeline *timeline = new(__FILE__, __LINE__) DrawOrderTimeline(drawOrder->_size);
 
-		for (valueMap = drawOrder->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
+		for (keyMap = drawOrder->_child, frame = 0; keyMap; keyMap = keyMap->_next, ++frame) {
 			int ii;
 			Vector<int> drawOrder2;
-			Json *offsets = Json::getItem(valueMap, "offsets");
+			Json *offsets = Json::getItem(keyMap, "offsets");
 			if (offsets) {
 				Json *offsetMap;
 				Vector<int> unchanged;
@@ -1140,41 +1273,40 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 				for (ii = (int)skeletonData->_slots.size() - 1; ii >= 0; ii--)
 					if (drawOrder2[ii] == -1) drawOrder2[ii] = unchanged[--unchangedIndex];
 			}
-			timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), drawOrder2);
+			timeline->setFrame(frame, Json::getFloat(keyMap, "time", 0), drawOrder2);
 		}
 		timelines.add(timeline);
-		timelinesCount++;
-		duration = MathUtil::max(duration, timeline->_frames[drawOrder->_size - 1]);
 	}
 
 	/** Event timeline. */
 	if (events) {
 		EventTimeline *timeline = new(__FILE__, __LINE__) EventTimeline(events->_size);
 
-		for (valueMap = events->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
+		for (keyMap = events->_child, frame = 0; keyMap; keyMap = keyMap->_next, ++frame) {
 			Event *event;
-			EventData *eventData = skeletonData->findEvent(Json::getString(valueMap, "name", 0));
+			EventData *eventData = skeletonData->findEvent(Json::getString(keyMap, "name", 0));
 			if (!eventData) {
 				ContainerUtil::cleanUpVectorOfPointers(timelines);
-				setError(NULL, "Event not found: ", Json::getString(valueMap, "name", 0));
+				setError(NULL, "Event not found: ", Json::getString(keyMap, "name", 0));
 				return NULL;
 			}
 
-			event = new(__FILE__, __LINE__) Event(Json::getFloat(valueMap, "time", 0), *eventData);
-			event->_intValue = Json::getInt(valueMap, "int", eventData->_intValue);
-			event->_floatValue = Json::getFloat(valueMap, "float", eventData->_floatValue);
-			event->_stringValue = Json::getString(valueMap, "string", eventData->_stringValue.buffer());
+			event = new(__FILE__, __LINE__) Event(Json::getFloat(keyMap, "time", 0), *eventData);
+			event->_intValue = Json::getInt(keyMap, "int", eventData->_intValue);
+			event->_floatValue = Json::getFloat(keyMap, "float", eventData->_floatValue);
+			event->_stringValue = Json::getString(keyMap, "string", eventData->_stringValue.buffer());
 			if (!eventData->_audioPath.isEmpty()) {
-				event->_volume = Json::getFloat(valueMap, "volume", 1);
-				event->_balance = Json::getFloat(valueMap, "balance", 0);
+				event->_volume = Json::getFloat(keyMap, "volume", 1);
+				event->_balance = Json::getFloat(keyMap, "balance", 0);
 			}
-			timeline->setFrame(frameIndex, event);
+			timeline->setFrame(frame, event);
 		}
 		timelines.add(timeline);
-		timelinesCount++;
-		duration = MathUtil::max(duration, timeline->_frames[events->_size - 1]);
 	}
 
+	float duration = 0;
+	for (size_t i = 0; i < timelines.size(); i++)
+	    duration = MathUtil::max(duration, timelines[i]->getDuration());
 	return new(__FILE__, __LINE__) Animation(String(root->_name), timelines, duration);
 }