瀏覽代碼

FFD for spine-c and spine-sfml.

NathanSweet 11 年之前
父節點
當前提交
99a8de4bb4

+ 30 - 6
spine-c/include/spine/Animation.h

@@ -32,6 +32,7 @@
 #define SPINE_ANIMATION_H_
 
 #include <spine/Event.h>
+#include <spine/Attachment.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -81,7 +82,8 @@ typedef enum {
 	SP_TIMELINE_COLOR,
 	SP_TIMELINE_ATTACHMENT,
 	SP_TIMELINE_EVENT,
-	SP_TIMELINE_DRAWORDER
+	SP_TIMELINE_DRAWORDER,
+	SP_TIMELINE_FFD
 } spTimelineType;
 
 struct spTimeline {
@@ -135,7 +137,7 @@ typedef spCurveTimeline CurveTimeline;
 
 typedef struct spBaseTimeline {
 	spCurveTimeline super;
-	int const framesLength;
+	int const framesCount;
 	float* const frames; /* time, angle, ... for rotate. time, x, y, ... for translate and scale. */
 	int boneIndex;
 } spRotateTimeline;
@@ -182,7 +184,7 @@ typedef spScaleTimeline ScaleTimeline;
 
 typedef struct {
 	spCurveTimeline super;
-	int const framesLength;
+	int const framesCount;
 	float* const frames; /* time, r, g, b, a, ... */
 	int slotIndex;
 } spColorTimeline;
@@ -201,7 +203,7 @@ typedef spColorTimeline ColorTimeline;
 
 typedef struct {
 	spTimeline super;
-	int const framesLength;
+	int const framesCount;
 	float* const frames; /* time, ... */
 	int slotIndex;
 	const char** const attachmentNames;
@@ -222,7 +224,7 @@ typedef spAttachmentTimeline AttachmentTimeline;
 
 typedef struct {
 	spTimeline super;
-	int const framesLength;
+	int const framesCount;
 	float* const frames; /* time, ... */
 	spEvent** const events;
 } spEventTimeline;
@@ -241,7 +243,7 @@ typedef spEventTimeline EventTimeline;
 
 typedef struct {
 	spTimeline super;
-	int const framesLength;
+	int const framesCount;
 	float* const frames; /* time, ... */
 	const int** const drawOrders;
 	int const slotCount;
@@ -257,6 +259,28 @@ typedef spDrawOrderTimeline DrawOrderTimeline;
 #define DrawOrderTimeline_setFrame(...) spDrawOrderTimeline_setFrame(__VA_ARGS__)
 #endif
 
+/**/
+
+typedef struct {
+	spCurveTimeline super;
+	int const framesCount;
+	float* const frames; /* time, ... */
+	int const frameVerticesCount;
+	const float** const frameVertices;
+	int slotIndex;
+	spAttachment* attachment;
+} spFFDTimeline;
+
+spFFDTimeline* spFFDTimeline_create (int frameCount, int frameVerticesCount);
+
+void spFFDTimeline_setFrame (spFFDTimeline* self, int frameIndex, float time, float* vertices);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spFFDTimeline FFDTimeline;
+#define FFDTimeline_create(...) spFFDTimeline_create(__VA_ARGS__)
+#define FFDTimeline_setFrame(...) spFFDTimeline_setFrame(__VA_ARGS__)
+#endif
+
 #ifdef __cplusplus
 }
 #endif

+ 1 - 0
spine-c/include/spine/Slot.h

@@ -48,6 +48,7 @@ typedef struct spSlot {
 	float r, g, b, a;
 	spAttachment* const attachment;
 
+	int attachmentVerticesCapacity;
 	int attachmentVerticesCount;
 	float* attachmentVertices;
 } spSlot;

+ 113 - 36
spine-c/src/spine/Animation.c

@@ -233,8 +233,8 @@ struct spBaseTimeline* _spBaseTimeline_create (int frameCount, spTimelineType ty
 	struct spBaseTimeline* self = NEW(struct spBaseTimeline);
 	_spCurveTimeline_init(SUPER(self), type, frameCount, _spBaseTimeline_dispose, apply);
 
-	CONST_CAST(int, self->framesLength) = frameCount * frameSize;
-	CONST_CAST(float*, self->frames) = CALLOC(float, self->framesLength);
+	CONST_CAST(int, self->framesCount) = frameCount * frameSize;
+	CONST_CAST(float*, self->frames) = CALLOC(float, self->framesCount);
 
 	return self;
 }
@@ -256,8 +256,8 @@ void _spRotateTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
 
 	bone = skeleton->bones[self->boneIndex];
 
-	if (time >= self->frames[self->framesLength - 2]) { /* Time is after last frame. */
-		float amount = bone->data->rotation + self->frames[self->framesLength - 1] - bone->rotation;
+	if (time >= self->frames[self->framesCount - 2]) { /* Time is after last frame. */
+		float amount = bone->data->rotation + self->frames[self->framesCount - 1] - bone->rotation;
 		while (amount > 180)
 			amount -= 360;
 		while (amount < -180)
@@ -266,8 +266,8 @@ void _spRotateTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton,
 		return;
 	}
 
-	/* Interpolate between the last frame and the current frame. */
-	frameIndex = binarySearch(self->frames, self->framesLength, time, 2);
+	/* Interpolate between the previous frame and the current frame. */
+	frameIndex = binarySearch(self->frames, self->framesCount, time, 2);
 	lastFrameValue = self->frames[frameIndex - 1];
 	frameTime = self->frames[frameIndex];
 	percent = 1 - (time - frameTime) / (self->frames[frameIndex + ROTATE_LAST_FRAME_TIME] - frameTime);
@@ -314,14 +314,14 @@ void _spTranslateTimeline_apply (const spTimeline* timeline, spSkeleton* skeleto
 
 	bone = skeleton->bones[self->boneIndex];
 
-	if (time >= self->frames[self->framesLength - 3]) { /* Time is after last frame. */
-		bone->x += (bone->data->x + self->frames[self->framesLength - 2] - bone->x) * alpha;
-		bone->y += (bone->data->y + self->frames[self->framesLength - 1] - bone->y) * alpha;
+	if (time >= self->frames[self->framesCount - 3]) { /* Time is after last frame. */
+		bone->x += (bone->data->x + self->frames[self->framesCount - 2] - bone->x) * alpha;
+		bone->y += (bone->data->y + self->frames[self->framesCount - 1] - bone->y) * alpha;
 		return;
 	}
 
-	/* Interpolate between the last frame and the current frame. */
-	frameIndex = binarySearch(self->frames, self->framesLength, time, 3);
+	/* Interpolate between the previous frame and the current frame. */
+	frameIndex = binarySearch(self->frames, self->framesCount, time, 3);
 	lastFrameX = self->frames[frameIndex - 2];
 	lastFrameY = self->frames[frameIndex - 1];
 	frameTime = self->frames[frameIndex];
@@ -358,14 +358,14 @@ void _spScaleTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, f
 	if (time < self->frames[0]) return; /* Time is before first frame. */
 
 	bone = skeleton->bones[self->boneIndex];
-	if (time >= self->frames[self->framesLength - 3]) { /* Time is after last frame. */
-		bone->scaleX += (bone->data->scaleX - 1 + self->frames[self->framesLength - 2] - bone->scaleX) * alpha;
-		bone->scaleY += (bone->data->scaleY - 1 + self->frames[self->framesLength - 1] - bone->scaleY) * alpha;
+	if (time >= self->frames[self->framesCount - 3]) { /* Time is after last frame. */
+		bone->scaleX += (bone->data->scaleX - 1 + self->frames[self->framesCount - 2] - bone->scaleX) * alpha;
+		bone->scaleY += (bone->data->scaleY - 1 + self->frames[self->framesCount - 1] - bone->scaleY) * alpha;
 		return;
 	}
 
-	/* Interpolate between the last frame and the current frame. */
-	frameIndex = binarySearch(self->frames, self->framesLength, time, 3);
+	/* Interpolate between the previous frame and the current frame. */
+	frameIndex = binarySearch(self->frames, self->framesCount, time, 3);
 	lastFrameX = self->frames[frameIndex - 2];
 	lastFrameY = self->frames[frameIndex - 1];
 	frameTime = self->frames[frameIndex];
@@ -406,16 +406,16 @@ void _spColorTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, f
 
 	slot = skeleton->slots[self->slotIndex];
 
-	if (time >= self->frames[self->framesLength - 5]) {
+	if (time >= self->frames[self->framesCount - 5]) {
 		/* Time is after last frame. */
-		int i = self->framesLength - 1;
+		int i = self->framesCount - 1;
 		r = self->frames[i - 3];
 		g = self->frames[i - 2];
 		b = self->frames[i - 1];
 		a = self->frames[i];
 	} else {
-		/* Interpolate between the last frame and the current frame. */
-		frameIndex = binarySearch(self->frames, self->framesLength, time, 5);
+		/* Interpolate between the previous frame and the current frame. */
+		frameIndex = binarySearch(self->frames, self->framesCount, time, 5);
 		lastFrameR = self->frames[frameIndex - 4];
 		lastFrameG = self->frames[frameIndex - 3];
 		lastFrameB = self->frames[frameIndex - 2];
@@ -465,10 +465,10 @@ void _spAttachmentTimeline_apply (const spTimeline* timeline, spSkeleton* skelet
 
 	if (time < self->frames[0]) return; /* Time is before first frame. */
 
-	if (time >= self->frames[self->framesLength - 1]) /* Time is after last frame. */
-		frameIndex = self->framesLength - 1;
+	if (time >= self->frames[self->framesCount - 1]) /* Time is after last frame. */
+		frameIndex = self->framesCount - 1;
 	else
-		frameIndex = binarySearch(self->frames, self->framesLength, time, 1) - 1;
+		frameIndex = binarySearch(self->frames, self->framesCount, time, 1) - 1;
 
 	attachmentName = self->attachmentNames[frameIndex];
 	spSlot_setAttachment(skeleton->slots[self->slotIndex],
@@ -481,7 +481,7 @@ void _spAttachmentTimeline_dispose (spTimeline* timeline) {
 
 	_spTimeline_deinit(timeline);
 
-	for (i = 0; i < self->framesLength; ++i)
+	for (i = 0; i < self->framesCount; ++i)
 		FREE(self->attachmentNames[i]);
 	FREE(self->attachmentNames);
 	FREE(self->frames);
@@ -492,7 +492,7 @@ spAttachmentTimeline* spAttachmentTimeline_create (int frameCount) {
 	spAttachmentTimeline* self = NEW(spAttachmentTimeline);
 	_spTimeline_init(SUPER(self), SP_TIMELINE_ATTACHMENT, _spAttachmentTimeline_dispose, _spAttachmentTimeline_apply);
 
-	CONST_CAST(int, self->framesLength) = frameCount;
+	CONST_CAST(int, self->framesCount) = frameCount;
 	CONST_CAST(float*, self->frames) = CALLOC(float, frameCount);
 	CONST_CAST(char**, self->attachmentNames) = CALLOC(char*, frameCount);
 
@@ -521,22 +521,22 @@ void _spEventTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, f
 	if (lastTime > time) { /* Fire events after last time for looped animations. */
 		_spEventTimeline_apply(timeline, skeleton, lastTime, (float)INT_MAX, firedEvents, eventCount, alpha);
 		lastTime = -1;
-	} else if (lastTime >= self->frames[self->framesLength - 1]) /* Last time is after last frame. */
-		return;
+	} else if (lastTime >= self->frames[self->framesCount - 1]) /* Last time is after last frame. */
+	return;
 	if (time < self->frames[0]) return; /* Time is before first frame. */
 
 	if (lastTime < self->frames[0])
 		frameIndex = 0;
 	else {
 		float frame;
-		frameIndex = binarySearch(self->frames, self->framesLength, lastTime, 1);
+		frameIndex = binarySearch(self->frames, self->framesCount, lastTime, 1);
 		frame = self->frames[frameIndex];
 		while (frameIndex > 0) { /* Fire multiple events with the same frame. */
 			if (self->frames[frameIndex - 1] != frame) break;
 			frameIndex--;
 		}
 	}
-	for (; frameIndex < self->framesLength && time >= self->frames[frameIndex]; frameIndex++) {
+	for (; frameIndex < self->framesCount && time >= self->frames[frameIndex]; frameIndex++) {
 		firedEvents[*eventCount] = self->events[frameIndex];
 		(*eventCount)++;
 	}
@@ -548,7 +548,7 @@ void _spEventTimeline_dispose (spTimeline* timeline) {
 
 	_spTimeline_deinit(timeline);
 
-	for (i = 0; i < self->framesLength; ++i)
+	for (i = 0; i < self->framesCount; ++i)
 		spEvent_dispose(self->events[i]);
 	FREE(self->events);
 	FREE(self->frames);
@@ -559,7 +559,7 @@ spEventTimeline* spEventTimeline_create (int frameCount) {
 	spEventTimeline* self = NEW(spEventTimeline);
 	_spTimeline_init(SUPER(self), SP_TIMELINE_EVENT, _spEventTimeline_dispose, _spEventTimeline_apply);
 
-	CONST_CAST(int, self->framesLength) = frameCount;
+	CONST_CAST(int, self->framesCount) = frameCount;
 	CONST_CAST(float*, self->frames) = CALLOC(float, frameCount);
 	CONST_CAST(spEvent**, self->events) = CALLOC(spEvent*, frameCount);
 
@@ -584,10 +584,10 @@ void _spDrawOrderTimeline_apply (const spTimeline* timeline, spSkeleton* skeleto
 
 	if (time < self->frames[0]) return; /* Time is before first frame. */
 
-	if (time >= self->frames[self->framesLength - 1]) /* Time is after last frame. */
-		frameIndex = self->framesLength - 1;
+	if (time >= self->frames[self->framesCount - 1]) /* Time is after last frame. */
+		frameIndex = self->framesCount - 1;
 	else
-		frameIndex = binarySearch(self->frames, self->framesLength, time, 1) - 1;
+		frameIndex = binarySearch(self->frames, self->framesCount, time, 1) - 1;
 
 	drawOrderToSetupIndex = self->drawOrders[frameIndex];
 	if (!drawOrderToSetupIndex)
@@ -604,7 +604,7 @@ void _spDrawOrderTimeline_dispose (spTimeline* timeline) {
 
 	_spTimeline_deinit(timeline);
 
-	for (i = 0; i < self->framesLength; ++i)
+	for (i = 0; i < self->framesCount; ++i)
 		FREE(self->drawOrders[i]);
 	FREE(self->drawOrders);
 	FREE(self->frames);
@@ -615,7 +615,7 @@ spDrawOrderTimeline* spDrawOrderTimeline_create (int frameCount, int slotCount)
 	spDrawOrderTimeline* self = NEW(spDrawOrderTimeline);
 	_spTimeline_init(SUPER(self), SP_TIMELINE_DRAWORDER, _spDrawOrderTimeline_dispose, _spDrawOrderTimeline_apply);
 
-	CONST_CAST(int, self->framesLength) = frameCount;
+	CONST_CAST(int, self->framesCount) = frameCount;
 	CONST_CAST(float*, self->frames) = CALLOC(float, frameCount);
 	CONST_CAST(int**, self->drawOrders) = CALLOC(int*, frameCount);
 	CONST_CAST(int, self->slotCount) = slotCount;
@@ -634,3 +634,80 @@ void spDrawOrderTimeline_setFrame (spDrawOrderTimeline* self, int frameIndex, fl
 		memcpy(CONST_CAST(int*, self->drawOrders[frameIndex]), drawOrder, self->slotCount * sizeof(int));
 	}
 }
+
+/**/
+
+void _spFFDTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventCount, float alpha) {
+	int frameIndex, i;
+	float percent, frameTime;
+	spFFDTimeline* self = (spFFDTimeline*)timeline;
+
+	spSlot *slot = skeleton->slots[self->slotIndex];
+	if (slot->attachment != self->attachment) return;
+
+	if (time < self->frames[0]) {
+		slot->attachmentVerticesCount = 0;
+		return; /* Time is before first frame. */
+	}
+
+	if (slot->attachmentVerticesCount < self->frameVerticesCount) {
+		if (slot->attachmentVerticesCapacity < self->frameVerticesCount) {
+			FREE(slot->attachmentVertices);
+			slot->attachmentVertices = MALLOC(float, self->frameVerticesCount);
+			slot->attachmentVerticesCapacity = self->frameVerticesCount;
+		}
+		slot->attachmentVerticesCount = self->frameVerticesCount;
+	}
+
+	if (time >= self->frames[self->framesCount - 1]) {
+		/* Time is after last frame. */
+		const float* lastVertices = self->frameVertices[self->framesCount - 1];
+		if (alpha < 1) {
+			for (i = 0; i < self->frameVerticesCount; i++)
+				slot->attachmentVertices[i] += (lastVertices[i] - slot->attachmentVertices[i]) * alpha;
+		} else
+			memcpy(slot->attachmentVertices, lastVertices, self->frameVerticesCount);
+		return;
+	}
+
+	/* Interpolate between the previous frame and the current frame. */
+	frameIndex = binarySearch(self->frames, self->framesCount, time, 1);
+	frameTime = self->frames[frameIndex];
+	percent = 1 - (time - frameTime) / (self->frames[frameIndex - 1] - frameTime);
+	percent = spCurveTimeline_getCurvePercent(SUPER(self), frameIndex - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+
+	const float* prevVertices = self->frameVertices[frameIndex - 1];
+	const float* nextVertices = self->frameVertices[frameIndex];
+
+	if (alpha < 1) {
+		for (i = 0; i < self->frameVerticesCount; i++) {
+			float prev = prevVertices[i];
+			slot->attachmentVertices[i] += (prev + (nextVertices[i] - prev) * percent - slot->attachmentVertices[i]) * alpha;
+		}
+	} else {
+		for (i = 0; i < self->frameVerticesCount; i++) {
+			float prev = prevVertices[i];
+			slot->attachmentVertices[i] = prev + (nextVertices[i] - prev) * percent;
+		}
+	}
+}
+
+spFFDTimeline* spFFDTimeline_create (int frameCount, int frameVerticesCount) {
+	spFFDTimeline* self = SUB_CAST(spFFDTimeline, _spBaseTimeline_create(frameCount, SP_TIMELINE_FFD, 1, _spFFDTimeline_apply));
+	CONST_CAST(float**, self->frameVertices) = CALLOC(float*, frameCount);
+	CONST_CAST(int, self->frameVerticesCount) = frameVerticesCount;
+	return self;
+}
+
+void spFFDTimeline_setFrame (spFFDTimeline* self, int frameIndex, float time, float* vertices) {
+	self->frames[frameIndex] = time;
+
+	FREE(self->frameVertices[frameIndex]);
+	if (!vertices)
+		self->frameVertices[frameIndex] = 0;
+	else {
+		self->frameVertices[frameIndex] = MALLOC(float, self->frameVerticesCount);
+		memcpy(CONST_CAST(float*, self->frameVertices[frameIndex]), vertices, self->frameVerticesCount * sizeof(float));
+	}
+}

+ 1 - 1
spine-c/src/spine/Json.c

@@ -111,7 +111,7 @@ static const char* parse_string (Json *item, const char* str) {
 	while (*ptr != '\"' && *ptr && ++len)
 		if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
 
-	out = (char*)malloc(len + 1); /* This is how long we need for the string, roughly. */
+	out = MALLOC(char, len + 1); /* The length needed for the string, roughly. */
 	if (!out) return 0;
 
 	ptr = str + 1;

+ 2 - 3
spine-c/src/spine/MeshAttachment.c

@@ -74,13 +74,12 @@ void spMeshAttachment_updateUVs (spMeshAttachment* self) {
 void spMeshAttachment_computeWorldVertices (spMeshAttachment* self, float x, float y, spSlot* slot, float* worldVertices) {
 	int i;
 	float* vertices = self->vertices;
-	spBone* bone = slot->bone;
+	const spBone* bone = slot->bone;
 	x += bone->worldX;
 	y += bone->worldY;
 	if (slot->attachmentVerticesCount == self->verticesCount) vertices = slot->attachmentVertices;
 	for (i = 0; i < self->verticesCount; i += 2) {
-		float vx = vertices[i];
-		float vy = vertices[i + 1];
+		const float vx = vertices[i], vy = vertices[i + 1];
 		worldVertices[i] = vx * bone->m00 + vy * bone->m01 + x;
 		worldVertices[i + 1] = vx * bone->m10 + vy * bone->m11 + y;
 	}

+ 1 - 1
spine-c/src/spine/RegionAttachment.c

@@ -105,7 +105,7 @@ void spRegionAttachment_updateOffset (spRegionAttachment* self) {
 }
 
 void spRegionAttachment_computeWorldVertices (spRegionAttachment* self, float x, float y, spBone* bone, float* vertices) {
-	float* offset = self->offset;
+	const float* offset = self->offset;
 	x += bone->worldX;
 	y += bone->worldY;
 	vertices[SP_VERTEX_X1] = offset[SP_VERTEX_X1] * bone->m00 + offset[SP_VERTEX_Y1] * bone->m01 + x;

+ 144 - 81
spine-c/src/spine/SkeletonJson.c

@@ -104,18 +104,24 @@ static void readCurve (spCurveTimeline* timeline, int frameIndex, Json* frame) {
 static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* root, spSkeletonData *skeletonData) {
 	int i;
 	spAnimation* animation;
+	Json* frame;
+	float duration;
 
 	Json* bones = Json_getItem(root, "bones");
 	Json* slots = Json_getItem(root, "slots");
+	Json* ffd = Json_getItem(root, "ffd");
 	Json* drawOrder = Json_getItem(root, "draworder");
 	Json* events = Json_getItem(root, "events");
-	Json *boneMap, *slotMap;
+	Json *boneMap, *slotMap, *ffdMap;
 
 	int timelineCount = 0;
 	for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next)
 		timelineCount += boneMap->size;
 	for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next)
 		timelineCount += slotMap->size;
+	for (ffdMap = ffd ? ffd->child : 0; ffdMap; ffdMap = ffdMap->next)
+		for (slotMap = ffdMap->child; slotMap; slotMap = slotMap->next)
+			timelineCount += slotMap->size;
 	if (events) ++timelineCount;
 	if (drawOrder) ++timelineCount;
 
@@ -124,6 +130,50 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 	skeletonData->animations[skeletonData->animationCount] = animation;
 	++skeletonData->animationCount;
 
+	for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next) {
+		Json *timelineArray;
+
+		int slotIndex = spSkeletonData_findSlotIndex(skeletonData, slotMap->name);
+		if (slotIndex == -1) {
+			spAnimation_dispose(animation);
+			_spSkeletonJson_setError(self, root, "Slot not found: ", slotMap->name);
+			return 0;
+		}
+
+		for (timelineArray = slotMap->child; timelineArray; timelineArray = timelineArray->next) {
+			if (strcmp(timelineArray->name, "color") == 0) {
+				spColorTimeline *timeline = spColorTimeline_create(timelineArray->size);
+				timeline->slotIndex = slotIndex;
+				for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) {
+					const char* s = Json_getString(frame, "color", 0);
+					spColorTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2),
+							toColor(s, 3));
+					readCurve(SUPER(timeline), i, frame);
+				}
+				animation->timelines[animation->timelineCount++] = SUPER_CAST(spTimeline, timeline);
+				duration = timeline->frames[timelineArray->size * 5 - 5];
+				if (duration > animation->duration) animation->duration = duration;
+
+			} else if (strcmp(timelineArray->name, "attachment") == 0) {
+				spAttachmentTimeline *timeline = spAttachmentTimeline_create(timelineArray->size);
+				timeline->slotIndex = slotIndex;
+				for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) {
+					Json* name = Json_getItem(frame, "name");
+					spAttachmentTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0),
+							name->type == Json_NULL ? 0 : name->valueString);
+				}
+				animation->timelines[animation->timelineCount++] = SUPER_CAST(spTimeline, timeline);
+				duration = timeline->frames[timelineArray->size - 1];
+				if (duration > animation->duration) animation->duration = duration;
+
+			} else {
+				spAnimation_dispose(animation);
+				_spSkeletonJson_setError(self, 0, "Invalid timeline type for a slot: ", timelineArray->name);
+				return 0;
+			}
+		}
+	}
+
 	for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next) {
 		Json *timelineArray;
 
@@ -135,9 +185,6 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 		}
 
 		for (timelineArray = boneMap->child; timelineArray; timelineArray = timelineArray->next) {
-			Json* frame;
-			float duration;
-
 			if (strcmp(timelineArray->name, "rotate") == 0) {
 				spRotateTimeline *timeline = spRotateTimeline_create(timelineArray->size);
 				timeline->boneIndex = boneIndex;
@@ -145,7 +192,7 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 					spRotateTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "angle", 0));
 					readCurve(SUPER(timeline), i, frame);
 				}
-				animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
+				animation->timelines[animation->timelineCount++] = SUPER_CAST(spTimeline, timeline);
 				duration = timeline->frames[timelineArray->size * 2 - 2];
 				if (duration > animation->duration) animation->duration = duration;
 
@@ -161,7 +208,7 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 								Json_getFloat(frame, "y", 0) * scale);
 						readCurve(SUPER(timeline), i, frame);
 					}
-					animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
+					animation->timelines[animation->timelineCount++] = SUPER_CAST(spTimeline, timeline);
 					duration = timeline->frames[timelineArray->size * 3 - 3];
 					if (duration > animation->duration) animation->duration = duration;
 				} else {
@@ -173,83 +220,75 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 		}
 	}
 
-	for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next) {
-		Json *timelineArray;
-
-		int slotIndex = spSkeletonData_findSlotIndex(skeletonData, slotMap->name);
-		if (slotIndex == -1) {
-			spAnimation_dispose(animation);
-			_spSkeletonJson_setError(self, root, "Slot not found: ", slotMap->name);
-			return 0;
-		}
-
-		for (timelineArray = slotMap->child; timelineArray; timelineArray = timelineArray->next) {
-			Json* frame;
-			float duration;
+	for (ffdMap = ffd ? ffd->child : 0; ffdMap; ffdMap = ffdMap->next) {
+		spSkin* skin = spSkeletonData_findSkin(skeletonData, ffdMap->name);
+		for (slotMap = ffdMap->child; slotMap; slotMap = slotMap->next) {
+			int slotIndex = spSkeletonData_findSlotIndex(skeletonData, slotMap->name);
+			Json* timelineArray;
+			for (timelineArray = slotMap->child; timelineArray; timelineArray = timelineArray->next) {
+				Json* frame;
+				int verticesCount;
+				float* tempVertices;
+				spFFDTimeline *timeline;
+
+				spAttachment* attachment = spSkin_getAttachment(skin, slotIndex, timelineArray->name);
+				if (!attachment) {
+					spAnimation_dispose(animation);
+					_spSkeletonJson_setError(self, 0, "Attachment not found: ", timelineArray->name);
+					return 0;
+				}
+				if (attachment->type == SP_ATTACHMENT_MESH)
+					verticesCount = SUB_CAST(spMeshAttachment, attachment)->verticesCount;
+				else if (attachment->type == SP_ATTACHMENT_SKINNED_MESH)
+					verticesCount = SUB_CAST(spSkinnedMeshAttachment, attachment)->weightsCount / 3 * 2;
 
-			if (strcmp(timelineArray->name, "color") == 0) {
-				spColorTimeline *timeline = spColorTimeline_create(timelineArray->size);
+				timeline = spFFDTimeline_create(timelineArray->size, verticesCount);
 				timeline->slotIndex = slotIndex;
+				timeline->attachment = attachment;
+
+				tempVertices = MALLOC(float, verticesCount);
 				for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) {
-					const char* s = Json_getString(frame, "color", 0);
-					spColorTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2),
-							toColor(s, 3));
+					Json* vertices = Json_getItem(frame, "vertices");
+					float* frameVertices;
+					if (!vertices) {
+						if (attachment->type == SP_ATTACHMENT_MESH)
+							frameVertices = SUB_CAST(spMeshAttachment, attachment)->vertices;
+						else {
+							frameVertices = tempVertices;
+							memset(frameVertices, 0, sizeof(float) * verticesCount);
+						}
+					} else {
+						int v, start = Json_getInt(frame, "offset", 0);
+						Json* vertex;
+						frameVertices = tempVertices;
+						memset(frameVertices, 0, sizeof(float) * start);
+						if (self->scale == 1) {
+							for (vertex = vertices->child, v = start; vertex; vertex = vertex->next, ++v)
+								frameVertices[v] = vertex->valueFloat;
+						} else {
+							for (vertex = vertices->child, v = start; vertex; vertex = vertex->next, ++v)
+								frameVertices[v] = vertex->valueFloat * self->scale;
+						}
+						memset(frameVertices + v, 0, sizeof(float) * (verticesCount - v));
+						if (attachment->type == SP_ATTACHMENT_MESH) {
+							float* meshVertices = SUB_CAST(spMeshAttachment, attachment)->vertices;
+							for (v = 0; v < verticesCount; ++v)
+								frameVertices[v] += meshVertices[v];
+						}
+					}
+					spFFDTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), frameVertices);
 					readCurve(SUPER(timeline), i, frame);
 				}
-				animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
-				duration = timeline->frames[timelineArray->size * 5 - 5];
-				if (duration > animation->duration) animation->duration = duration;
+				FREE(tempVertices);
 
-			} else if (strcmp(timelineArray->name, "attachment") == 0) {
-				spAttachmentTimeline *timeline = spAttachmentTimeline_create(timelineArray->size);
-				timeline->slotIndex = slotIndex;
-				for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) {
-					Json* name = Json_getItem(frame, "name");
-					spAttachmentTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0),
-							name->type == Json_NULL ? 0 : name->valueString);
-				}
-				animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
+				animation->timelines[animation->timelineCount++] = SUPER_CAST(spTimeline, timeline);
 				duration = timeline->frames[timelineArray->size - 1];
 				if (duration > animation->duration) animation->duration = duration;
-
-			} else {
-				spAnimation_dispose(animation);
-				_spSkeletonJson_setError(self, 0, "Invalid timeline type for a slot: ", timelineArray->name);
-				return 0;
 			}
 		}
 	}
 
-	if (events) {
-		Json* frame;
-		float duration;
-
-		spEventTimeline* timeline = spEventTimeline_create(events->size);
-		for (frame = events->child, i = 0; frame; frame = frame->next, ++i) {
-			spEvent* event;
-			const char* stringValue;
-			spEventData* eventData = spSkeletonData_findEvent(skeletonData, Json_getString(frame, "name", 0));
-			if (!eventData) {
-				spAnimation_dispose(animation);
-				_spSkeletonJson_setError(self, 0, "Event not found: ", Json_getString(frame, "name", 0));
-				return 0;
-			}
-			event = spEvent_create(eventData);
-			event->intValue = Json_getInt(frame, "int", eventData->intValue);
-			event->floatValue = Json_getFloat(frame, "float", eventData->floatValue);
-			stringValue = Json_getString(frame, "string", eventData->stringValue);
-			if (stringValue) MALLOC_STR(event->stringValue, stringValue);
-			spEventTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), event);
-		}
-		animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
-		duration = timeline->frames[events->size - 1];
-		if (duration > animation->duration) animation->duration = duration;
-	}
-
 	if (drawOrder) {
-		Json* frame;
-		float duration;
-
 		spDrawOrderTimeline* timeline = spDrawOrderTimeline_create(drawOrder->size, skeletonData->slotCount);
 		for (frame = drawOrder->child, i = 0; frame; frame = frame->next, ++i) {
 			int ii;
@@ -289,11 +328,36 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 			spDrawOrderTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), drawOrder);
 			FREE(drawOrder);
 		}
-		animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
+		animation->timelines[animation->timelineCount++] = SUPER_CAST(spTimeline, timeline);
 		duration = timeline->frames[drawOrder->size - 1];
 		if (duration > animation->duration) animation->duration = duration;
 	}
 
+	if (events) {
+		Json* frame;
+
+		spEventTimeline* timeline = spEventTimeline_create(events->size);
+		for (frame = events->child, i = 0; frame; frame = frame->next, ++i) {
+			spEvent* event;
+			const char* stringValue;
+			spEventData* eventData = spSkeletonData_findEvent(skeletonData, Json_getString(frame, "name", 0));
+			if (!eventData) {
+				spAnimation_dispose(animation);
+				_spSkeletonJson_setError(self, 0, "Event not found: ", Json_getString(frame, "name", 0));
+				return 0;
+			}
+			event = spEvent_create(eventData);
+			event->intValue = Json_getInt(frame, "int", eventData->intValue);
+			event->floatValue = Json_getFloat(frame, "float", eventData->floatValue);
+			stringValue = Json_getString(frame, "string", eventData->stringValue);
+			if (stringValue) MALLOC_STR(event->stringValue, stringValue);
+			spEventTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), event);
+		}
+		animation->timelines[animation->timelineCount++] = SUPER_CAST(spTimeline, timeline);
+		duration = timeline->frames[events->size - 1];
+		if (duration > animation->duration) animation->duration = duration;
+	}
+
 	return animation;
 }
 
@@ -446,7 +510,7 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 
 					switch (attachment->type) {
 					case SP_ATTACHMENT_REGION: {
-						spRegionAttachment* region = (spRegionAttachment*)attachment;
+						spRegionAttachment* region = SUB_CAST(spRegionAttachment, attachment);
 						if (path) MALLOC_STR(region->path, path);
 						region->x = Json_getFloat(attachmentMap, "x", 0) * self->scale;
 						region->y = Json_getFloat(attachmentMap, "y", 0) * self->scale;
@@ -468,7 +532,7 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 						break;
 					}
 					case SP_ATTACHMENT_MESH: {
-						spMeshAttachment* mesh = (spMeshAttachment*)attachment;
+						spMeshAttachment* mesh = SUB_CAST(spMeshAttachment, attachment);
 
 						MALLOC_STR(mesh->path, path);
 
@@ -515,7 +579,7 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 						break;
 					}
 					case SP_ATTACHMENT_SKINNED_MESH: {
-						spSkinnedMeshAttachment* mesh = (spSkinnedMeshAttachment*)attachment;
+						spSkinnedMeshAttachment* mesh = SUB_CAST(spSkinnedMeshAttachment, attachment);
 						int verticesCount, b, w, nn;
 						float* vertices;
 
@@ -545,12 +609,11 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 						for (i = 0, b = 0, w = 0; i < verticesCount;) {
 							int boneCount = (int)vertices[i++];
 							mesh->bones[b++] = boneCount;
-							for (nn = i + boneCount * 4; i < nn;) {
-								mesh->bones[b++] = (int)vertices[i];
-								mesh->weights[w++] = vertices[i + 1] * self->scale;
-								mesh->weights[w++] = vertices[i + 2] * self->scale;
-								mesh->weights[w++] = vertices[i + 3];
-								i += 4;
+							for (nn = i + boneCount * 4; i < nn; i += 4, ++b, w += 3) {
+								mesh->bones[b] = (int)vertices[i];
+								mesh->weights[w] = vertices[i + 1] * self->scale;
+								mesh->weights[w + 1] = vertices[i + 2] * self->scale;
+								mesh->weights[w + 2] = vertices[i + 3];
 							}
 						}
 
@@ -587,7 +650,7 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 						break;
 					}
 					case SP_ATTACHMENT_BOUNDING_BOX: {
-						spBoundingBoxAttachment* box = (spBoundingBoxAttachment*)attachment;
+						spBoundingBoxAttachment* box = SUB_CAST(spBoundingBoxAttachment, attachment);
 						entry = Json_getItem(attachmentMap, "vertices");
 						box->verticesCount = entry->size;
 						box->vertices = MALLOC(float, entry->size);

+ 7 - 7
spine-c/src/spine/SkinnedMeshAttachment.c

@@ -79,11 +79,11 @@ void spSkinnedMeshAttachment_computeWorldVertices (spSkinnedMeshAttachment* self
 	if (slot->attachmentVerticesCount == 0) {
 		for (; v < self->bonesCount; w += 2) {
 			float wx = 0, wy = 0;
-			int nn = self->bones[v] + v;
+			const int nn = self->bones[v] + v;
 			v++;
 			for (; v <= nn; v++, b += 3) {
-				spBone* bone = skeletonBones[self->bones[v]];
-				float vx = self->weights[b], vy = self->weights[b + 1], weight = self->weights[b + 2];
+				const spBone* bone = skeletonBones[self->bones[v]];
+				const float vx = self->weights[b], vy = self->weights[b + 1], weight = self->weights[b + 2];
 				wx += (vx * bone->m00 + vy * bone->m01 + bone->worldX) * weight;
 				wy += (vx * bone->m10 + vy * bone->m11 + bone->worldY) * weight;
 			}
@@ -91,14 +91,14 @@ void spSkinnedMeshAttachment_computeWorldVertices (spSkinnedMeshAttachment* self
 			worldVertices[w + 1] = wy + y;
 		}
 	} else {
-		float* ffd = slot->attachmentVertices;
+		const float* ffd = slot->attachmentVertices;
 		for (; v < self->bonesCount; w += 2) {
 			float wx = 0, wy = 0;
-			int nn = self->bones[v] + v;
+			const int nn = self->bones[v] + v;
 			v++;
 			for (; v <= nn; v++, b += 3, f += 2) {
-				spBone* bone = skeletonBones[self->bones[v]];
-				float vx = self->weights[b] + ffd[f], vy = self->weights[b + 1] + ffd[f + 1], weight = self->weights[b + 2];
+				const spBone* bone = skeletonBones[self->bones[v]];
+				const float vx = self->weights[b] + ffd[f], vy = self->weights[b + 1] + ffd[f + 1], weight = self->weights[b + 2];
 				wx += (vx * bone->m00 + vy * bone->m01 + bone->worldX) * weight;
 				wy += (vx * bone->m10 + vy * bone->m11 + bone->worldY) * weight;
 			}

+ 3 - 3
spine-sfml/example/main.cpp

@@ -135,7 +135,7 @@ void spineboy () {
 }
 
 void goblins () {
-// Load atlas, skeleton, and animations.
+	// Load atlas, skeleton, and animations.
 	Atlas* atlas = Atlas_readAtlasFile("../data/goblins-ffd.atlas");
 	SkeletonJson* json = SkeletonJson_create(atlas);
 	SkeletonData *skeletonData = SkeletonJson_readSkeletonDataFile(json, "../data/goblins-ffd.json");
@@ -154,7 +154,7 @@ void goblins () {
 	skeleton->flipY = false;
 	Skeleton_setSkinByName(skeleton, "goblin");
 	Skeleton_setSlotsToSetupPose(skeleton);
-//	Skeleton_setAttachment(skeleton, "left hand item", "dagger");
+	//Skeleton_setAttachment(skeleton, "left hand item", "dagger");
 
 	skeleton->x = 320;
 	skeleton->y = 590;
@@ -185,6 +185,6 @@ void goblins () {
 }
 
 int main () {
-//	spineboy();
+	spineboy();
 	goblins();
 }