diff --git a/extensions/Android.mk b/extensions/Android.mk index 67441c0820..7053bc5866 100644 --- a/extensions/Android.mk +++ b/extensions/Android.mk @@ -51,7 +51,26 @@ GUI/CCEditBox/CCEditBoxImplAndroid.cpp \ network/HttpClient.cpp \ physics_nodes/CCPhysicsDebugNode.cpp \ physics_nodes/CCPhysicsSprite.cpp \ -LocalStorage/LocalStorageAndroid.cpp +LocalStorage/LocalStorageAndroid.cpp \ +spine/Animation.cpp \ +spine/AnimationState.cpp \ +spine/AnimationStateData.cpp \ +spine/Atlas.cpp \ +spine/AtlasAttachmentLoader.cpp \ +spine/Attachment.cpp \ +spine/AttachmentLoader.cpp \ +spine/Bone.cpp \ +spine/BoneData.cpp \ +spine/Json.cpp \ +spine/RegionAttachment.cpp \ +spine/Skeleton.cpp \ +spine/SkeletonData.cpp \ +spine/SkeletonJson.cpp \ +spine/Skin.cpp \ +spine/Slot.cpp \ +spine/SlotData.cpp \ +spine/extension.cpp \ +spine/spine-cocos2dx.cpp LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static LOCAL_WHOLE_STATIC_LIBRARIES += cocosdenshion_static diff --git a/extensions/spine/Animation.cpp b/extensions/spine/Animation.cpp new file mode 100644 index 0000000000..a06f13ca19 --- /dev/null +++ b/extensions/spine/Animation.cpp @@ -0,0 +1,462 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +Animation* Animation_create (const char* name, int timelineCount) { + Animation* self = NEW(Animation); + MALLOC_STR(self->name, name); + self->timelineCount = timelineCount; + self->timelines = MALLOC(Timeline*, timelineCount); + return self; +} + +void Animation_dispose (Animation* self) { + int i; + for (i = 0; i < self->timelineCount; ++i) + Timeline_dispose(self->timelines[i]); + FREE(self->timelines); + FREE(self); +} + +void Animation_apply (const Animation* self, Skeleton* skeleton, float time, int/*bool*/loop) { + if (loop && self->duration) time = fmodf(time, self->duration); + + int i, n = self->timelineCount; + for (i = 0; i < n; ++i) + Timeline_apply(self->timelines[i], skeleton, time, 1); +} + +void Animation_mix (const Animation* self, Skeleton* skeleton, float time, int/*bool*/loop, float alpha) { + if (loop && self->duration) time = fmodf(time, self->duration); + + int i, n = self->timelineCount; + for (i = 0; i < n; ++i) + Timeline_apply(self->timelines[i], skeleton, time, alpha); +} + +/**/ + +typedef struct _TimelineVtable { + void (*apply) (const Timeline* self, Skeleton* skeleton, float time, float alpha); + void (*dispose) (Timeline* self); +} _TimelineVtable; + +void _Timeline_init (Timeline* self, // + void (*dispose) (Timeline* self), // + void (*apply) (const Timeline* self, Skeleton* skeleton, float time, float alpha)) { + CONST_CAST(_TimelineVtable*, self->vtable) = NEW(_TimelineVtable); + VTABLE(Timeline, self) ->dispose = dispose; + VTABLE(Timeline, self) ->apply = apply; +} + +void _Timeline_deinit (Timeline* self) { + FREE(self->vtable); +} + +void Timeline_dispose (Timeline* self) { + VTABLE(Timeline, self) ->dispose(self); +} + +void Timeline_apply (const Timeline* self, Skeleton* skeleton, float time, float alpha) { + VTABLE(Timeline, self) ->apply(self, skeleton, time, alpha); +} + +/**/ + +static const float CURVE_LINEAR = 0; +static const float CURVE_STEPPED = -1; +static const int CURVE_SEGMENTS = 10; + +void _CurveTimeline_init (CurveTimeline* self, int frameCount, // + void (*dispose) (Timeline* self), // + void (*apply) (const Timeline* self, Skeleton* skeleton, float time, float alpha)) { + _Timeline_init(SUPER(self), dispose, apply); + self->curves = CALLOC(float, (frameCount - 1) * 6); +} + +void _CurveTimeline_deinit (CurveTimeline* self) { + _Timeline_deinit(SUPER(self)); + FREE(self->curves); +} + +void CurveTimeline_setLinear (CurveTimeline* self, int frameIndex) { + self->curves[frameIndex * 6] = CURVE_LINEAR; +} + +void CurveTimeline_setStepped (CurveTimeline* self, int frameIndex) { + self->curves[frameIndex * 6] = CURVE_STEPPED; +} + +void CurveTimeline_setCurve (CurveTimeline* self, int frameIndex, float cx1, float cy1, float cx2, float cy2) { + float subdiv_step = 1.0f / CURVE_SEGMENTS; + float subdiv_step2 = subdiv_step * subdiv_step; + float subdiv_step3 = subdiv_step2 * subdiv_step; + float pre1 = 3 * subdiv_step; + float pre2 = 3 * subdiv_step2; + float pre4 = 6 * subdiv_step2; + float pre5 = 6 * subdiv_step3; + float tmp1x = -cx1 * 2 + cx2; + float tmp1y = -cy1 * 2 + cy2; + float tmp2x = (cx1 - cx2) * 3 + 1; + float tmp2y = (cy1 - cy2) * 3 + 1; + int i = frameIndex * 6; + self->curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3; + self->curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3; + self->curves[i + 2] = tmp1x * pre4 + tmp2x * pre5; + self->curves[i + 3] = tmp1y * pre4 + tmp2y * pre5; + self->curves[i + 4] = tmp2x * pre5; + self->curves[i + 5] = tmp2y * pre5; +} + +float CurveTimeline_getCurvePercent (const CurveTimeline* self, int frameIndex, float percent) { + int curveIndex = frameIndex * 6; + float dfx = self->curves[curveIndex]; + if (dfx == CURVE_LINEAR) return percent; + if (dfx == CURVE_STEPPED) return 0; + float dfy = self->curves[curveIndex + 1]; + float ddfx = self->curves[curveIndex + 2]; + float ddfy = self->curves[curveIndex + 3]; + float dddfx = self->curves[curveIndex + 4]; + float dddfy = self->curves[curveIndex + 5]; + float x = dfx, y = dfy; + int i = CURVE_SEGMENTS - 2; + while (1) { + if (x >= percent) { + float lastX = x - dfx; + float lastY = y - dfy; + return lastY + (y - lastY) * (percent - lastX) / (x - lastX); + } + if (i == 0) break; + i--; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } + return y + (1 - y) * (percent - x) / (1 - x); /* Last point is 1,1. */ +} + +/* @param target After the first and before the last entry. */ +static int binarySearch (float *values, int valuesLength, float target, int step) { + int low = 0; + int high = valuesLength / step - 2; + if (high == 0) return step; + int current = high >> 1; + while (1) { + if (values[(current + 1) * step] <= target) + low = current + 1; + else + high = current; + if (low == high) return (low + 1) * step; + current = (low + high) >> 1; + } + return 0; +} + +/*static int linearSearch (float *values, int valuesLength, float target, int step) { + int i, last = valuesLength - step; + for (i = 0; i <= last; i += step) { + if (values[i] <= target) continue; + return i; + } + return -1; + }*/ + +/**/ + +void _BaseTimeline_dispose (Timeline* timeline) { + struct BaseTimeline* self = SUB_CAST(struct BaseTimeline, timeline); + _CurveTimeline_deinit(SUPER(self)); + FREE(self->frames); + FREE(self); +} + +/* Many timelines have structure identical to struct BaseTimeline and extend CurveTimeline. **/ +struct BaseTimeline* _BaseTimeline_create (int frameCount, int frameSize, // + void (*apply) (const Timeline* self, Skeleton* skeleton, float time, float alpha)) { + + struct BaseTimeline* self = NEW(struct BaseTimeline); + _CurveTimeline_init(SUPER(self), frameCount, _BaseTimeline_dispose, apply); + + CONST_CAST(int, self->framesLength) = frameCount * frameSize; + CONST_CAST(float*, self->frames) = CALLOC(float, self->framesLength); + + return self; +} + +/**/ + +static const int ROTATE_LAST_FRAME_TIME = -2; +static const int ROTATE_FRAME_VALUE = 1; + +void _RotateTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float time, float alpha) { + RotateTimeline* self = SUB_CAST(RotateTimeline, timeline); + + if (time < self->frames[0]) return; /* Time is before first frame. */ + + Bone *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; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone->rotation += amount * alpha; + return; + } + + /* Interpolate between the last frame and the current frame. */ + int frameIndex = binarySearch(self->frames, self->framesLength, time, 2); + float lastFrameValue = self->frames[frameIndex - 1]; + float frameTime = self->frames[frameIndex]; + float percent = 1 - (time - frameTime) / (self->frames[frameIndex + ROTATE_LAST_FRAME_TIME] - frameTime); + percent = CurveTimeline_getCurvePercent(SUPER(self), frameIndex / 2 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + float amount = self->frames[frameIndex + ROTATE_FRAME_VALUE] - lastFrameValue; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + amount = bone->data->rotation + (lastFrameValue + amount * percent) - bone->rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone->rotation += amount * alpha; +} + +RotateTimeline* RotateTimeline_create (int frameCount) { + return _BaseTimeline_create(frameCount, 2, _RotateTimeline_apply); +} + +void RotateTimeline_setFrame (RotateTimeline* self, int frameIndex, float time, float angle) { + frameIndex *= 2; + self->frames[frameIndex] = time; + self->frames[frameIndex + 1] = angle; +} + +/**/ + +static const int TRANSLATE_LAST_FRAME_TIME = -3; +static const int TRANSLATE_FRAME_X = 1; +static const int TRANSLATE_FRAME_Y = 2; + +void _TranslateTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float time, float alpha) { + TranslateTimeline* self = SUB_CAST(TranslateTimeline, timeline); + + if (time < self->frames[0]) return; /* Time is before first frame. */ + + Bone *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; + return; + } + + /* Interpolate between the last frame and the current frame. */ + int frameIndex = binarySearch(self->frames, self->framesLength, time, 3); + float lastFrameX = self->frames[frameIndex - 2]; + float lastFrameY = self->frames[frameIndex - 1]; + float frameTime = self->frames[frameIndex]; + float percent = 1 - (time - frameTime) / (self->frames[frameIndex + TRANSLATE_LAST_FRAME_TIME] - frameTime); + percent = CurveTimeline_getCurvePercent(SUPER(self), frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + bone->x += (bone->data->x + lastFrameX + (self->frames[frameIndex + TRANSLATE_FRAME_X] - lastFrameX) * percent - bone->x) + * alpha; + bone->y += (bone->data->y + lastFrameY + (self->frames[frameIndex + TRANSLATE_FRAME_Y] - lastFrameY) * percent - bone->y) + * alpha; +} + +TranslateTimeline* TranslateTimeline_create (int frameCount) { + return _BaseTimeline_create(frameCount, 3, _TranslateTimeline_apply); +} + +void TranslateTimeline_setFrame (TranslateTimeline* self, int frameIndex, float time, float x, float y) { + frameIndex *= 3; + self->frames[frameIndex] = time; + self->frames[frameIndex + 1] = x; + self->frames[frameIndex + 2] = y; +} + +/**/ + +void _ScaleTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float time, float alpha) { + ScaleTimeline* self = SUB_CAST(ScaleTimeline, timeline); + + if (time < self->frames[0]) return; /* Time is before first frame. */ + + Bone *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; + return; + } + + /* Interpolate between the last frame and the current frame. */ + int frameIndex = binarySearch(self->frames, self->framesLength, time, 3); + float lastFrameX = self->frames[frameIndex - 2]; + float lastFrameY = self->frames[frameIndex - 1]; + float frameTime = self->frames[frameIndex]; + float percent = 1 - (time - frameTime) / (self->frames[frameIndex + TRANSLATE_LAST_FRAME_TIME] - frameTime); + percent = CurveTimeline_getCurvePercent(SUPER(self), frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + bone->scaleX += (bone->data->scaleX - 1 + lastFrameX + (self->frames[frameIndex + TRANSLATE_FRAME_X] - lastFrameX) * percent + - bone->scaleX) * alpha; + bone->scaleY += (bone->data->scaleY - 1 + lastFrameY + (self->frames[frameIndex + TRANSLATE_FRAME_Y] - lastFrameY) * percent + - bone->scaleY) * alpha; +} + +ScaleTimeline* ScaleTimeline_create (int frameCount) { + return _BaseTimeline_create(frameCount, 3, _ScaleTimeline_apply); +} + +void ScaleTimeline_setFrame (ScaleTimeline* self, int frameIndex, float time, float x, float y) { + TranslateTimeline_setFrame(self, frameIndex, time, x, y); +} + +/**/ + +static const int COLOR_LAST_FRAME_TIME = -5; +static const int COLOR_FRAME_R = 1; +static const int COLOR_FRAME_G = 2; +static const int COLOR_FRAME_B = 3; +static const int COLOR_FRAME_A = 4; + +void _ColorTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float time, float alpha) { + ColorTimeline* self = (ColorTimeline*)timeline; + + if (time < self->frames[0]) return; /* Time is before first frame. */ + + Slot *slot = skeleton->slots[self->slotIndex]; + + if (time >= self->frames[self->framesLength - 5]) { /* Time is after last frame. */ + int i = self->framesLength - 1; + slot->r = self->frames[i - 3]; + slot->g = self->frames[i - 2]; + slot->b = self->frames[i - 1]; + slot->a = self->frames[i]; + return; + } + + /* Interpolate between the last frame and the current frame. */ + int frameIndex = binarySearch(self->frames, self->framesLength, time, 5); + float lastFrameR = self->frames[frameIndex - 4]; + float lastFrameG = self->frames[frameIndex - 3]; + float lastFrameB = self->frames[frameIndex - 2]; + float lastFrameA = self->frames[frameIndex - 1]; + float frameTime = self->frames[frameIndex]; + float percent = 1 - (time - frameTime) / (self->frames[frameIndex + COLOR_LAST_FRAME_TIME] - frameTime); + percent = CurveTimeline_getCurvePercent(SUPER(self), frameIndex / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + float r = lastFrameR + (self->frames[frameIndex + COLOR_FRAME_R] - lastFrameR) * percent; + float g = lastFrameG + (self->frames[frameIndex + COLOR_FRAME_G] - lastFrameG) * percent; + float b = lastFrameB + (self->frames[frameIndex + COLOR_FRAME_B] - lastFrameB) * percent; + float a = lastFrameA + (self->frames[frameIndex + COLOR_FRAME_A] - lastFrameA) * percent; + if (alpha < 1) { + slot->r += (r - slot->r) * alpha; + slot->g += (g - slot->g) * alpha; + slot->b += (b - slot->b) * alpha; + slot->a += (a - slot->a) * alpha; + } else { + slot->r = r; + slot->g = g; + slot->b = b; + slot->a = a; + } +} + +ColorTimeline* ColorTimeline_create (int frameCount) { + return (ColorTimeline*)_BaseTimeline_create(frameCount, 5, _ColorTimeline_apply); +} + +void ColorTimeline_setFrame (ColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a) { + frameIndex *= 5; + self->frames[frameIndex] = time; + self->frames[frameIndex + 1] = r; + self->frames[frameIndex + 2] = g; + self->frames[frameIndex + 3] = b; + self->frames[frameIndex + 4] = a; +} + +/**/ + +void _AttachmentTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float time, float alpha) { + AttachmentTimeline* self = (AttachmentTimeline*)timeline; + + if (time < self->frames[0]) return; /* Time is before first frame. */ + + int frameIndex; + if (time >= self->frames[self->framesLength - 1]) /* Time is after last frame. */ + frameIndex = self->framesLength - 1; + else + frameIndex = binarySearch(self->frames, self->framesLength, time, 1) - 1; + + const char* attachmentName = self->attachmentNames[frameIndex]; + Slot_setAttachment(skeleton->slots[self->slotIndex], + attachmentName ? Skeleton_getAttachmentForSlotIndex(skeleton, self->slotIndex, attachmentName) : 0); +} + +void _AttachmentTimeline_dispose (Timeline* timeline) { + _Timeline_deinit(timeline); + AttachmentTimeline* self = (AttachmentTimeline*)timeline; + + int i; + for (i = 0; i < self->framesLength; ++i) + FREE(self->attachmentNames[i]); + FREE(self->attachmentNames); + + FREE(self); +} + +AttachmentTimeline* AttachmentTimeline_create (int frameCount) { + AttachmentTimeline* self = NEW(AttachmentTimeline); + _Timeline_init(SUPER(self), _AttachmentTimeline_dispose, _AttachmentTimeline_apply); + + CONST_CAST(char**, self->attachmentNames) = CALLOC(char*, frameCount); + CONST_CAST(int, self->framesLength) = frameCount; + CONST_CAST(float*, self->frames) = CALLOC(float, frameCount); + + return self; +} + +void AttachmentTimeline_setFrame (AttachmentTimeline* self, int frameIndex, float time, const char* attachmentName) { + self->frames[frameIndex] = time; + FREE(self->attachmentNames[frameIndex]); + if (attachmentName) + MALLOC_STR(self->attachmentNames[frameIndex], attachmentName); + else + self->attachmentNames[frameIndex] = 0; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/Animation.h b/extensions/spine/Animation.h new file mode 100644 index 0000000000..b7d922ca95 --- /dev/null +++ b/extensions/spine/Animation.h @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_ANIMATION_H_ +#define SPINE_ANIMATION_H_ + +namespace cocos2d { namespace extension { + +typedef struct Timeline Timeline; +struct Skeleton; + +typedef struct { + const char* const name; + float duration; + + int timelineCount; + Timeline** timelines; +} Animation; + +Animation* Animation_create (const char* name, int timelineCount); +void Animation_dispose (Animation* self); + +void Animation_apply (const Animation* self, struct Skeleton* skeleton, float time, int/*bool*/loop); +void Animation_mix (const Animation* self, struct Skeleton* skeleton, float time, int/*bool*/loop, float alpha); + +/**/ + +struct Timeline { + const void* const vtable; +}; + +void Timeline_dispose (Timeline* self); +void Timeline_apply (const Timeline* self, struct Skeleton* skeleton, float time, float alpha); + +/**/ + +typedef struct { + Timeline super; + float* curves; /* dfx, dfy, ddfx, ddfy, dddfx, dddfy, ... */ +} CurveTimeline; + +void CurveTimeline_setLinear (CurveTimeline* self, int frameIndex); +void CurveTimeline_setStepped (CurveTimeline* self, int frameIndex); + +/* Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + * the difference between the keyframe's values. */ +void CurveTimeline_setCurve (CurveTimeline* self, int frameIndex, float cx1, float cy1, float cx2, float cy2); +float CurveTimeline_getCurvePercent (const CurveTimeline* self, int frameIndex, float percent); + +/**/ + +typedef struct BaseTimeline { + CurveTimeline super; + int const framesLength; + float* const frames; /* time, angle, ... for rotate. time, x, y, ... for translate and scale. */ + int boneIndex; +} RotateTimeline; + +RotateTimeline* RotateTimeline_create (int frameCount); + +void RotateTimeline_setFrame (RotateTimeline* self, int frameIndex, float time, float angle); + +/**/ + +typedef struct BaseTimeline TranslateTimeline; + +TranslateTimeline* TranslateTimeline_create (int frameCount); + +void TranslateTimeline_setFrame (TranslateTimeline* self, int frameIndex, float time, float x, float y); + +/**/ + +typedef struct BaseTimeline ScaleTimeline; + +ScaleTimeline* ScaleTimeline_create (int frameCount); + +void ScaleTimeline_setFrame (ScaleTimeline* self, int frameIndex, float time, float x, float y); + +/**/ + +typedef struct { + CurveTimeline super; + int const framesLength; + float* const frames; /* time, r, g, b, a, ... */ + int slotIndex; +} ColorTimeline; + +ColorTimeline* ColorTimeline_create (int frameCount); + +void ColorTimeline_setFrame (ColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a); + +/**/ + +typedef struct { + Timeline super; + int const framesLength; + float* const frames; /* time, ... */ + int slotIndex; + const char** const attachmentNames; +} AttachmentTimeline; + +AttachmentTimeline* AttachmentTimeline_create (int frameCount); + +/* @param attachmentName May be 0. */ +void AttachmentTimeline_setFrame (AttachmentTimeline* self, int frameIndex, float time, const char* attachmentName); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_ANIMATION_H_ */ diff --git a/extensions/spine/AnimationState.cpp b/extensions/spine/AnimationState.cpp new file mode 100644 index 0000000000..4cf7307d36 --- /dev/null +++ b/extensions/spine/AnimationState.cpp @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct { + AnimationState super; + Animation *previous; + float previousTime; + int/*bool*/previousLoop; + float mixTime; + float mixDuration; +} _Internal; + +AnimationState* AnimationState_create (AnimationStateData* data) { + AnimationState* self = SUPER(NEW(_Internal)); + CONST_CAST(AnimationStateData*, self->data) = data; + return self; +} + +void AnimationState_dispose (AnimationState* self) { + FREE(self); +} + +void AnimationState_update (AnimationState* self, float delta) { + self->time += delta; + SUB_CAST(_Internal, self) ->previousTime += delta; + SUB_CAST(_Internal, self) ->mixTime += delta; +} + +void AnimationState_apply (AnimationState* self, Skeleton* skeleton) { + if (!self->animation) return; + _Internal* internal = SUB_CAST(_Internal, self); + if (internal->previous) { + Animation_apply(internal->previous, skeleton, internal->previousTime, internal->previousLoop); + float alpha = internal->mixTime / internal->mixDuration; + if (alpha >= 1) { + alpha = 1; + internal->previous = 0; + } + Animation_mix(self->animation, skeleton, self->time, self->loop, alpha); + } else + Animation_apply(self->animation, skeleton, self->time, self->loop); +} + +void AnimationState_setAnimationByName (AnimationState* self, const char* animationName, int/**/loop) { + Animation* animation = SkeletonData_findAnimation(self->data->skeletonData, animationName); + AnimationState_setAnimation(self, animation, loop); +} + +void AnimationState_setAnimation (AnimationState* self, Animation* newAnimation, int/**/loop) { + _Internal* internal = SUB_CAST(_Internal, self); + internal->previous = 0; + if (newAnimation && self->animation && self->data) { + internal->mixDuration = AnimationStateData_getMix(self->data, self->animation, newAnimation); + if (internal->mixDuration > 0) { + internal->mixTime = 0; + internal->previous = self->animation; + internal->previousTime = self->time; + internal->previousLoop = self->loop; + } + } + CONST_CAST(Animation*, self->animation) = newAnimation; + self->loop = loop; + self->time = 0; +} + +void AnimationState_clearAnimation (AnimationState* self) { + SUB_CAST(_Internal, self) ->previous = 0; + CONST_CAST(Animation*, self->animation) = 0; +} + +int/*bool*/AnimationState_isComplete (AnimationState* self) { + return !self->animation || self->time >= self->animation->duration; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/AnimationState.h b/extensions/spine/AnimationState.h new file mode 100644 index 0000000000..97e511adc2 --- /dev/null +++ b/extensions/spine/AnimationState.h @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_ANIMATIONSTATE_H_ +#define SPINE_ANIMATIONSTATE_H_ + +#include + +namespace cocos2d { namespace extension { + +typedef struct { + AnimationStateData* const data; + Animation* const animation; + float time; + int/*bool*/loop; +} AnimationState; + +/* @param data May be 0 for no mixing. */ +AnimationState* AnimationState_create (AnimationStateData* data); +void AnimationState_dispose (AnimationState* self); + +void AnimationState_update (AnimationState* self, float delta); + +void AnimationState_apply (AnimationState* self, struct Skeleton* skeleton); + +/* @param animationName May be 0. */ +void AnimationState_setAnimationByName (AnimationState* self, const char* animationName, int/**/loop); +/* @param animation May be 0. */ +void AnimationState_setAnimation (AnimationState* self, Animation* animation, int/**/loop); +void AnimationState_clearAnimation (AnimationState* self); + +int/*bool*/AnimationState_isComplete (AnimationState* self); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_ANIMATIONSTATE_H_ */ diff --git a/extensions/spine/AnimationStateData.cpp b/extensions/spine/AnimationStateData.cpp new file mode 100644 index 0000000000..738ede33c2 --- /dev/null +++ b/extensions/spine/AnimationStateData.cpp @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct _ToEntry _ToEntry; +struct _ToEntry { + Animation* animation; + float duration; + _ToEntry* next; +}; + +_ToEntry* _ToEntry_create (Animation* to, float duration) { + _ToEntry* self = NEW(_ToEntry); + self->animation = to; + self->duration = duration; + return self; +} + +void _ToEntry_dispose (_ToEntry* self) { + FREE(self); +} + +/**/ + +typedef struct _FromEntry _FromEntry; +struct _FromEntry { + Animation* animation; + _ToEntry* toEntries; + _FromEntry* next; +}; + +_FromEntry* _FromEntry_create (Animation* from) { + _FromEntry* self = NEW(_FromEntry); + self->animation = from; + return self; +} + +void _FromEntry_dispose (_FromEntry* self) { + FREE(self); +} + +/**/ + +AnimationStateData* AnimationStateData_create (SkeletonData* skeletonData) { + AnimationStateData* self = NEW(AnimationStateData); + CONST_CAST(SkeletonData*, self->skeletonData) = skeletonData; + return self; +} + +void AnimationStateData_dispose (AnimationStateData* self) { + _FromEntry* fromEntry = (_FromEntry*)self->entries; + while (fromEntry) { + _ToEntry* toEntry = fromEntry->toEntries; + while (toEntry) { + _ToEntry* next = toEntry->next; + _ToEntry_dispose(toEntry); + toEntry = next; + } + _FromEntry* next = fromEntry->next; + _FromEntry_dispose(fromEntry); + fromEntry = next; + } + + FREE(self); +} + +void AnimationStateData_setMixByName (AnimationStateData* self, const char* fromName, const char* toName, float duration) { + Animation* from = SkeletonData_findAnimation(self->skeletonData, fromName); + if (!from) return; + Animation* to = SkeletonData_findAnimation(self->skeletonData, toName); + if (!to) return; + AnimationStateData_setMix(self, from, to, duration); +} + +void AnimationStateData_setMix (AnimationStateData* self, Animation* from, Animation* to, float duration) { + /* Find existing FromEntry. */ + _FromEntry* fromEntry = (_FromEntry*)self->entries; + while (fromEntry) { + if (fromEntry->animation == from) { + /* Find existing ToEntry. */ + _ToEntry* toEntry = fromEntry->toEntries; + while (toEntry) { + if (toEntry->animation == to) { + toEntry->duration = duration; + return; + } + toEntry = toEntry->next; + } + break; /* Add new ToEntry to the existing FromEntry. */ + } + fromEntry = fromEntry->next; + } + if (!fromEntry) { + fromEntry = _FromEntry_create(from); + fromEntry->next = (_FromEntry*)self->entries; + CONST_CAST(_FromEntry*, self->entries) = fromEntry; + } + _ToEntry* toEntry = _ToEntry_create(to, duration); + toEntry->next = fromEntry->toEntries; + fromEntry->toEntries = toEntry; +} + +float AnimationStateData_getMix (AnimationStateData* self, Animation* from, Animation* to) { + _FromEntry* fromEntry = (_FromEntry*)self->entries; + while (fromEntry) { + if (fromEntry->animation == from) { + _ToEntry* toEntry = fromEntry->toEntries; + while (toEntry) { + if (toEntry->animation == to) return toEntry->duration; + toEntry = toEntry->next; + } + } + fromEntry = fromEntry->next; + } + return 0; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/AnimationStateData.h b/extensions/spine/AnimationStateData.h new file mode 100644 index 0000000000..ea45653ed7 --- /dev/null +++ b/extensions/spine/AnimationStateData.h @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_ANIMATIONSTATEDATA_H_ +#define SPINE_ANIMATIONSTATEDATA_H_ + +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct { + SkeletonData* const skeletonData; + const void* const entries; +} AnimationStateData; + +AnimationStateData* AnimationStateData_create (SkeletonData* skeletonData); +void AnimationStateData_dispose (AnimationStateData* self); + +void AnimationStateData_setMixByName (AnimationStateData* self, const char* fromName, const char* toName, float duration); +void AnimationStateData_setMix (AnimationStateData* self, Animation* from, Animation* to, float duration); +/* Returns 0 if there is no mixing between the animations. */ +float AnimationStateData_getMix (AnimationStateData* self, Animation* from, Animation* to); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_ANIMATIONSTATEDATA_H_ */ diff --git a/extensions/spine/Atlas.cpp b/extensions/spine/Atlas.cpp new file mode 100644 index 0000000000..27ddd37f4b --- /dev/null +++ b/extensions/spine/Atlas.cpp @@ -0,0 +1,327 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +AtlasPage* AtlasPage_create (const char* name) { + AtlasPage* self = NEW(AtlasPage); + MALLOC_STR(self->name, name); + return self; +} + +void AtlasPage_dispose (AtlasPage* self) { + FREE(self->name); + _AtlasPage_disposeTexture(self); +} + +/**/ + +AtlasRegion* AtlasRegion_create () { + return NEW(AtlasRegion) ; +} + +void AtlasRegion_dispose (AtlasRegion* self) { + FREE(self->name); + FREE(self->splits); + FREE(self->pads); + FREE(self); +} + +/**/ + +typedef struct { + const char* begin; + const char* end; +} Str; + +static void trim (Str* str) { + while (isspace(*str->begin) && str->begin < str->end) + (str->begin)++; + if (str->begin == str->end) return; + str->end--; + while (isspace(*str->end) && str->end >= str->begin) + str->end--; + str->end++; +} + +/* Tokenize string without modification. Returns 0 on failure. */ +static int readLine (const char* begin, const char* end, Str* str) { + static const char* nextStart; + if (begin) { + nextStart = begin; + return 1; + } + if (nextStart == end) return 0; + str->begin = nextStart; + + /* Find next delimiter. */ + while (nextStart != end && *nextStart != '\n') + nextStart++; + + str->end = nextStart; + trim(str); + + if (nextStart != end) nextStart++; + return 1; +} + +/* Moves str->begin past the first occurence of c. Returns 0 on failure. */ +static int beginPast (Str* str, char c) { + const char* begin = str->begin; + while (1) { + char lastSkippedChar = *begin; + if (begin == str->end) return 0; + begin++; + if (lastSkippedChar == c) break; + } + str->begin = begin; + return 1; +} + +/* Returns 0 on failure. */ +static int readValue (const char* end, Str* str) { + readLine(0, end, str); + if (!beginPast(str, ':')) return 0; + trim(str); + return 1; +} + +/* Returns the number of tuple values read (2, 4, or 0 for failure). */ +static int readTuple (const char* end, Str tuple[]) { + Str str; + readLine(0, end, &str); + if (!beginPast(&str, ':')) return 0; + int i = 0; + for (i = 0; i < 3; ++i) { + tuple[i].begin = str.begin; + if (!beginPast(&str, ',')) { + if (i == 0) return 0; + break; + } + tuple[i].end = str.begin - 2; + trim(&tuple[i]); + } + tuple[i].begin = str.begin; + tuple[i].end = str.end; + trim(&tuple[i]); + return i + 1; +} + +static char* mallocString (Str* str) { + int length = str->end - str->begin; + char* string = MALLOC(char, length + 1); + memcpy(string, str->begin, length); + string[length] = '\0'; + return string; +} + +static int indexOf (const char** array, int count, Str* str) { + int length = str->end - str->begin; + int i; + for (i = count - 1; i >= 0; i--) + if (strncmp(array[i], str->begin, length) == 0) return i; + return -1; +} + +static int equals (Str* str, const char* other) { + return strncmp(other, str->begin, str->end - str->begin) == 0; +} + +static int toInt (Str* str) { + return strtol(str->begin, (char**)&str->end, 10); +} + +static Atlas* abortAtlas (Atlas* self) { + Atlas_dispose(self); + return 0; +} + +static const char* formatNames[] = {"Alpha", "Intensity", "LuminanceAlpha", "RGB565", "RGBA4444", "RGB888", "RGBA8888"}; +static const char* textureFilterNames[] = {"Nearest", "Linear", "MipMap", "MipMapNearestNearest", "MipMapLinearNearest", + "MipMapNearestLinear", "MipMapLinearLinear"}; + +Atlas* Atlas_readAtlas (const char* begin, int length, const char* dir) { + const char* end = begin + length; + int dirLength = strlen(dir); + int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\'; + + Atlas* self = NEW(Atlas); + + AtlasPage *page = 0; + AtlasPage *lastPage = 0; + AtlasRegion *lastRegion = 0; + Str str; + Str tuple[4]; + readLine(begin, 0, 0); + while (readLine(0, end, &str)) { + if (str.end - str.begin == 0) { + page = 0; + } else if (!page) { + char* name = mallocString(&str); + char* path = MALLOC(char, dirLength + needsSlash + strlen(name) + 1); + memcpy(path, dir, dirLength); + if (needsSlash) path[dirLength] = '/'; + strcpy(path + dirLength + needsSlash, name); + + page = AtlasPage_create(name); + FREE(name); + if (lastPage) + lastPage->next = page; + else + self->pages = page; + lastPage = page; + + if (!readValue(end, &str)) return abortAtlas(self); + page->format = (AtlasFormat)indexOf(formatNames, 7, &str); + + if (!readTuple(end, tuple)) return abortAtlas(self); + page->minFilter = (AtlasFilter)indexOf(textureFilterNames, 7, tuple); + page->magFilter = (AtlasFilter)indexOf(textureFilterNames, 7, tuple + 1); + + if (!readValue(end, &str)) return abortAtlas(self); + if (!equals(&str, "none")) { + page->uWrap = *str.begin == 'x' ? ATLAS_REPEAT : (*str.begin == 'y' ? ATLAS_CLAMPTOEDGE : ATLAS_REPEAT); + page->vWrap = *str.begin == 'x' ? ATLAS_CLAMPTOEDGE : (*str.begin == 'y' ? ATLAS_REPEAT : ATLAS_REPEAT); + } + + _AtlasPage_createTexture(page, path); + FREE(path); + } else { + AtlasRegion *region = AtlasRegion_create(); + if (lastRegion) + lastRegion->next = region; + else + self->regions = region; + lastRegion = region; + + region->page = page; + region->name = mallocString(&str); + + if (!readValue(end, &str)) return abortAtlas(self); + region->rotate = equals(&str, "true"); + + if (readTuple(end, tuple) != 2) return abortAtlas(self); + region->x = toInt(tuple); + region->y = toInt(tuple + 1); + + if (readTuple(end, tuple) != 2) return abortAtlas(self); + region->width = toInt(tuple); + region->height = toInt(tuple + 1); + + region->u = region->x / (float)page->width; + region->v = region->y / (float)page->height; + region->u2 = (region->x + region->width) / (float)page->width; + region->v2 = (region->y + region->height) / (float)page->height; + + int count; + if (!(count = readTuple(end, tuple))) return abortAtlas(self); + if (count == 4) { /* split is optional */ + region->splits = MALLOC(int, 4); + region->splits[0] = toInt(tuple); + region->splits[1] = toInt(tuple + 1); + region->splits[2] = toInt(tuple + 2); + region->splits[3] = toInt(tuple + 3); + + if (!(count = readTuple(end, tuple))) return abortAtlas(self); + if (count == 4) { /* pad is optional, but only present with splits */ + region->pads = MALLOC(int, 4); + region->pads[0] = toInt(tuple); + region->pads[1] = toInt(tuple + 1); + region->pads[2] = toInt(tuple + 2); + region->pads[3] = toInt(tuple + 3); + + if (!readTuple(end, tuple)) return abortAtlas(self); + } + } + + region->originalWidth = toInt(tuple); + region->originalHeight = toInt(tuple + 1); + + readTuple(end, tuple); + region->offsetX = (float)toInt(tuple); + region->offsetY = (float)toInt(tuple + 1); + + if (!readValue(end, &str)) return abortAtlas(self); + region->index = toInt(&str); + } + } + + return self; +} + +Atlas* Atlas_readAtlasFile (const char* path) { + Atlas* atlas = 0; + + /* Get directory from atlas path. */ + const char* lastForwardSlash = strrchr(path, '/'); + const char* lastBackwardSlash = strrchr(path, '\\'); + const char* lastSlash = lastForwardSlash > lastBackwardSlash ? lastForwardSlash : lastBackwardSlash; + if (lastSlash == path) lastSlash++; /* Never drop starting slash. */ + int dirLength = lastSlash ? lastSlash - path : 0; + char* dir = MALLOC(char, dirLength + 1); + memcpy(dir, path, dirLength); + dir[dirLength] = '\0'; + + int length; + const char* data = _Util_readFile(path, &length); + if (data) atlas = Atlas_readAtlas(data, length, dir); + + FREE(data); + FREE(dir); + return atlas; +} + +void Atlas_dispose (Atlas* self) { + AtlasPage* page = self->pages; + while (page) { + AtlasPage* nextPage = page->next; + AtlasPage_dispose(page); + page = nextPage; + } + + AtlasRegion* region = self->regions; + while (region) { + AtlasRegion* nextRegion = region->next; + AtlasRegion_dispose(region); + region = nextRegion; + } + + FREE(self); +} + +AtlasRegion* Atlas_findRegion (const Atlas* self, const char* name) { + AtlasRegion* region = self->regions; + while (region) { + if (strcmp(region->name, name) == 0) return region; + region = region->next; + } + return 0; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/Atlas.h b/extensions/spine/Atlas.h new file mode 100644 index 0000000000..a7aa3e39cf --- /dev/null +++ b/extensions/spine/Atlas.h @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_ATLAS_H_ +#define SPINE_ATLAS_H_ + +namespace cocos2d { namespace extension { + +typedef enum { + ATLAS_ALPHA, ATLAS_INTENSITY, ATLAS_LUMINANCE_ALPHA, ATLAS_RGB565, ATLAS_RGBA4444, ATLAS_RGB888, ATLAS_RGBA8888 +} AtlasFormat; + +typedef enum { + ATLAS_NEAREST, + ATLAS_LINEAR, + ATLAS_MIPMAP, + ATLAS_MIPMAP_NEAREST_NEAREST, + ATLAS_MIPMAP_LINEAR_NEAREST, + ATLAS_MIPMAP_NEAREST_LINEAR, + ATLAS_MIPMAP_LINEAR_LINEAR +} AtlasFilter; + +typedef enum { + ATLAS_MIRROREDREPEAT, ATLAS_CLAMPTOEDGE, ATLAS_REPEAT +} AtlasWrap; + +typedef struct AtlasPage AtlasPage; +struct AtlasPage { + const char* name; + AtlasFormat format; + AtlasFilter minFilter, magFilter; + AtlasWrap uWrap, vWrap; + + void* texture; + int width, height; + + AtlasPage* next; +}; + +AtlasPage* AtlasPage_create (const char* name); +void AtlasPage_dispose (AtlasPage* self); + +/**/ + +typedef struct AtlasRegion AtlasRegion; +struct AtlasRegion { + const char* name; + int x, y, width, height; + float u, v, u2, v2; + float offsetX, offsetY; + int originalWidth, originalHeight; + int index; + int/*bool*/rotate; + int/*bool*/flip; + int* splits; + int* pads; + + AtlasPage* page; + + AtlasRegion* next; +}; + +AtlasRegion* AtlasRegion_create (); +void AtlasRegion_dispose (AtlasRegion* self); + +/**/ + +typedef struct { + AtlasPage* pages; + AtlasRegion* regions; +} Atlas; + +/* Image files referenced in the atlas file will be prefixed with dir. */ +Atlas* Atlas_readAtlas (const char* data, int length, const char* dir); +/* Image files referenced in the atlas file will be prefixed with the directory containing the atlas file. */ +Atlas* Atlas_readAtlasFile (const char* path); +void Atlas_dispose (Atlas* atlas); + +/* Returns 0 if the region was not found. */ +AtlasRegion* Atlas_findRegion (const Atlas* self, const char* name); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_ATLAS_H_ */ diff --git a/extensions/spine/AtlasAttachmentLoader.cpp b/extensions/spine/AtlasAttachmentLoader.cpp new file mode 100644 index 0000000000..c0f613a6ef --- /dev/null +++ b/extensions/spine/AtlasAttachmentLoader.cpp @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +namespace cocos2d { namespace extension { + +Attachment* _AtlasAttachmentLoader_newAttachment (AttachmentLoader* loader, Skin* skin, AttachmentType type, const char* name) { + AtlasAttachmentLoader* self = SUB_CAST(AtlasAttachmentLoader, loader); + switch (type) { + case ATTACHMENT_REGION: { + AtlasRegion* region = Atlas_findRegion(self->atlas, name); + if (!region) { + _AttachmentLoader_setError(loader, "Region not found: ", name); + return 0; + } + RegionAttachment* attachment = RegionAttachment_create(name); + attachment->region = region; + return SUPER(attachment); + } + default: + _AttachmentLoader_setUnknownTypeError(loader, type); + return 0; + } +} + +AtlasAttachmentLoader* AtlasAttachmentLoader_create (Atlas* atlas) { + AtlasAttachmentLoader* self = NEW(AtlasAttachmentLoader); + _AttachmentLoader_init(SUPER(self), _AttachmentLoader_deinit, _AtlasAttachmentLoader_newAttachment); + self->atlas = atlas; + return self; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/AtlasAttachmentLoader.h b/extensions/spine/AtlasAttachmentLoader.h new file mode 100644 index 0000000000..20e90464e2 --- /dev/null +++ b/extensions/spine/AtlasAttachmentLoader.h @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_ATLASATTACHMENTLOADER_H_ +#define SPINE_ATLASATTACHMENTLOADER_H_ + +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct { + AttachmentLoader super; + Atlas* atlas; +} AtlasAttachmentLoader; + +AtlasAttachmentLoader* AtlasAttachmentLoader_create (Atlas* atlas); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_ATLASATTACHMENTLOADER_H_ */ diff --git a/extensions/spine/Attachment.cpp b/extensions/spine/Attachment.cpp new file mode 100644 index 0000000000..2201592f81 --- /dev/null +++ b/extensions/spine/Attachment.cpp @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct _AttachmentVtable { + void (*dispose) (Attachment* self); +} _AttachmentVtable; + +void _Attachment_init (Attachment* self, const char* name, AttachmentType type, // + void (*dispose) (Attachment* self)) { + + CONST_CAST(_AttachmentVtable*, self->vtable) = NEW(_AttachmentVtable); + VTABLE(Attachment, self) ->dispose = dispose; + + MALLOC_STR(self->name, name); + self->type = type; +} + +void _Attachment_deinit (Attachment* self) { + FREE(self->vtable); + FREE(self->name); +} + +void Attachment_dispose (Attachment* self) { + VTABLE(Attachment, self) ->dispose(self); +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/Attachment.h b/extensions/spine/Attachment.h new file mode 100644 index 0000000000..7be7cf0dc5 --- /dev/null +++ b/extensions/spine/Attachment.h @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_ATTACHMENT_H_ +#define SPINE_ATTACHMENT_H_ + +namespace cocos2d { namespace extension { + +struct Slot; + +typedef enum { + ATTACHMENT_REGION, ATTACHMENT_REGION_SEQUENCE +} AttachmentType; + +typedef struct Attachment Attachment; +struct Attachment { + const char* const name; + AttachmentType type; + + const void* const vtable; +}; + +void Attachment_dispose (Attachment* self); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_ATTACHMENT_H_ */ diff --git a/extensions/spine/AttachmentLoader.cpp b/extensions/spine/AttachmentLoader.cpp new file mode 100644 index 0000000000..a454dd8791 --- /dev/null +++ b/extensions/spine/AttachmentLoader.cpp @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct _AttachmentLoaderVtable { + Attachment* (*newAttachment) (AttachmentLoader* self, Skin* skin, AttachmentType type, const char* name); + void (*dispose) (AttachmentLoader* self); +} _AttachmentLoaderVtable; + +void _AttachmentLoader_init (AttachmentLoader* self, // + void (*dispose) (AttachmentLoader* self), // + Attachment* (*newAttachment) (AttachmentLoader* self, Skin* skin, AttachmentType type, const char* name)) { + CONST_CAST(_AttachmentLoaderVtable*, self->vtable) = NEW(_AttachmentLoaderVtable); + VTABLE(AttachmentLoader, self) ->dispose = dispose; + VTABLE(AttachmentLoader, self) ->newAttachment = newAttachment; +} + +void _AttachmentLoader_deinit (AttachmentLoader* self) { + FREE(self->vtable); + FREE(self->error1); + FREE(self->error2); +} + +void AttachmentLoader_dispose (AttachmentLoader* self) { + VTABLE(AttachmentLoader, self) ->dispose(self); +} + +Attachment* AttachmentLoader_newAttachment (AttachmentLoader* self, Skin* skin, AttachmentType type, const char* name) { + FREE(self->error1); + FREE(self->error2); + self->error1 = 0; + self->error2 = 0; + return VTABLE(AttachmentLoader, self) ->newAttachment(self, skin, type, name); +} + +void _AttachmentLoader_setError (AttachmentLoader* self, const char* error1, const char* error2) { + FREE(self->error1); + FREE(self->error2); + MALLOC_STR(self->error1, error1); + MALLOC_STR(self->error2, error2); +} + +void _AttachmentLoader_setUnknownTypeError (AttachmentLoader* self, AttachmentType type) { + char buffer[16]; + sprintf(buffer, "%d", type); + _AttachmentLoader_setError(self, "Unknown attachment type: ", buffer); +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/AttachmentLoader.h b/extensions/spine/AttachmentLoader.h new file mode 100644 index 0000000000..8d6e202960 --- /dev/null +++ b/extensions/spine/AttachmentLoader.h @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_ATTACHMENTLOADER_H_ +#define SPINE_ATTACHMENTLOADER_H_ + +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct AttachmentLoader AttachmentLoader; +struct AttachmentLoader { + const char* error1; + const char* error2; + + const void* const vtable; +}; + +void AttachmentLoader_dispose (AttachmentLoader* self); + +/* Returns 0 to not load an attachment. If 0 is returned and AttachmentLoader.error1 is set, an error occurred. */ +Attachment* AttachmentLoader_newAttachment (AttachmentLoader* self, Skin* skin, AttachmentType type, const char* name); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_ATTACHMENTLOADER_H_ */ diff --git a/extensions/spine/Bone.cpp b/extensions/spine/Bone.cpp new file mode 100644 index 0000000000..2d0c85fa79 --- /dev/null +++ b/extensions/spine/Bone.cpp @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +static int yDown; + +void Bone_setYDown (int value) { + yDown = value; +} + +Bone* Bone_create (BoneData* data, Bone* parent) { + Bone* self = NEW(Bone); + CONST_CAST(BoneData*, self->data) = data; + CONST_CAST(Bone*, self->parent) = parent; + Bone_setToBindPose(self); + return self; +} + +void Bone_dispose (Bone* self) { + FREE(self); +} + +void Bone_setToBindPose (Bone* self) { + self->x = self->data->x; + self->y = self->data->y; + self->rotation = self->data->rotation; + self->scaleX = self->data->scaleX; + self->scaleY = self->data->scaleY; +} + +void Bone_updateWorldTransform (Bone* self, int flipX, int flipY) { + if (self->parent) { + CONST_CAST(float, self->worldX) = self->x * self->parent->m00 + self->y * self->parent->m01 + self->parent->worldX; + CONST_CAST(float, self->worldY) = self->x * self->parent->m10 + self->y * self->parent->m11 + self->parent->worldY; + CONST_CAST(float, self->worldScaleX) = self->parent->worldScaleX * self->scaleX; + CONST_CAST(float, self->worldScaleY) = self->parent->worldScaleY * self->scaleY; + CONST_CAST(float, self->worldRotation) = self->parent->worldRotation + self->rotation; + } else { + CONST_CAST(float, self->worldX) = self->x; + CONST_CAST(float, self->worldY) = self->y; + CONST_CAST(float, self->worldScaleX) = self->scaleX; + CONST_CAST(float, self->worldScaleY) = self->scaleY; + CONST_CAST(float, self->worldRotation) = self->rotation; + } + float radians = (float)(self->worldRotation * 3.1415926535897932385 / 180); + float cosine = cosf(radians); + float sine = sinf(radians); + CONST_CAST(float, self->m00) = cosine * self->worldScaleX; + CONST_CAST(float, self->m10) = sine * self->worldScaleX; + CONST_CAST(float, self->m01) = -sine * self->worldScaleY; + CONST_CAST(float, self->m11) = cosine * self->worldScaleY; + if (flipX) { + CONST_CAST(float, self->m00) = -self->m00; + CONST_CAST(float, self->m01) = -self->m01; + } + if (flipY) { + CONST_CAST(float, self->m10) = -self->m10; + CONST_CAST(float, self->m11) = -self->m11; + } + if (yDown) { + CONST_CAST(float, self->m10) = -self->m10; + CONST_CAST(float, self->m11) = -self->m11; + } +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/Bone.h b/extensions/spine/Bone.h new file mode 100644 index 0000000000..6471bfc7d9 --- /dev/null +++ b/extensions/spine/Bone.h @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_BONE_H_ +#define SPINE_BONE_H_ + +#include + +namespace cocos2d { namespace extension { + +typedef struct Bone Bone; +struct Bone { + BoneData* const data; + Bone* const parent; + float x, y; + float rotation; + float scaleX, scaleY; + + float const m00, m01, worldX; /* a b x */ + float const m10, m11, worldY; /* c d y */ + float const worldRotation; + float const worldScaleX, worldScaleY; +}; + +void Bone_setYDown (int/*bool*/yDown); + +/* @param parent May be 0. */ +Bone* Bone_create (BoneData* data, Bone* parent); +void Bone_dispose (Bone* self); + +void Bone_setToBindPose (Bone* self); + +void Bone_updateWorldTransform (Bone* self, int/*bool*/flipX, int/*bool*/flipY); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_BONE_H_ */ diff --git a/extensions/spine/BoneData.cpp b/extensions/spine/BoneData.cpp new file mode 100644 index 0000000000..d41e32e2d0 --- /dev/null +++ b/extensions/spine/BoneData.cpp @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +namespace cocos2d { namespace extension { + +BoneData* BoneData_create (const char* name, BoneData* parent) { + BoneData* self = NEW(BoneData); + MALLOC_STR(self->name, name); + CONST_CAST(BoneData*, self->parent) = parent; + self->scaleX = 1; + self->scaleY = 1; + return self; +} + +void BoneData_dispose (BoneData* self) { + FREE(self->name); + FREE(self); +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/BoneData.h b/extensions/spine/BoneData.h new file mode 100644 index 0000000000..54126a700d --- /dev/null +++ b/extensions/spine/BoneData.h @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_BONEDATA_H_ +#define SPINE_BONEDATA_H_ + +namespace cocos2d { namespace extension { + +typedef struct BoneData BoneData; +struct BoneData { + const char* const name; + BoneData* const parent; + float length; + float x, y; + float rotation; + float scaleX, scaleY; +}; + +BoneData* BoneData_create (const char* name, BoneData* parent); +void BoneData_dispose (BoneData* self); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_BONEDATA_H_ */ diff --git a/extensions/spine/Json.cpp b/extensions/spine/Json.cpp new file mode 100644 index 0000000000..b2cda20d5a --- /dev/null +++ b/extensions/spine/Json.cpp @@ -0,0 +1,375 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, dispose of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/* Json */ +/* JSON parser in C. */ + +#include "Json.h" +#include +#include +#include +#include + +namespace cocos2d { namespace extension { + +static const char* ep; + +const char* Json_getError (void) { + return ep; +} + +static int Json_strcasecmp (const char* s1, const char* s2) { + if (!s1) return (s1 == s2) ? 0 : 1; + if (!s2) return 1; + for (; tolower(*s1) == tolower(*s2); ++s1, ++s2) + if (*s1 == 0) return 0; + return tolower(*(const unsigned char*)s1) - tolower(*(const unsigned char*)s2); +} + +/* Internal constructor. */ +static Json *Json_new (void) { + return (Json*)CALLOC(Json, 1); +} + +/* Delete a Json structure. */ +void Json_dispose (Json *c) { + Json *next; + while (c) { + next = c->next; + if (c->child) Json_dispose(c->child); + if (c->valuestring) FREE(c->valuestring); + if (c->name) FREE(c->name); + FREE(c); + c = next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char* parse_number (Json *item, const char* num) { + float n = 0, sign = 1, scale = 0; + int subscale = 0, signsubscale = 1; + + /* Could use sscanf for this? */ + if (*num == '-') sign = -1, num++; /* Has sign? */ + if (*num == '0') num++; /* is zero */ + if (*num >= '1' && *num <= '9') do + n = (n * 10.0f) + (*num++ - '0'); + while (*num >= '0' && *num <= '9'); /* Number? */ + if (*num == '.' && num[1] >= '0' && num[1] <= '9') { + num++; + do + n = (n * 10.0f) + (*num++ - '0'), scale--; + while (*num >= '0' && *num <= '9'); + } /* Fractional part? */ + if (*num == 'e' || *num == 'E') /* Exponent? */ + { + num++; + if (*num == '+') + num++; + else if (*num == '-') signsubscale = -1, num++; /* With sign? */ + while (*num >= '0' && *num <= '9') + subscale = (subscale * 10) + (*num++ - '0'); /* Number? */ + } + + n = sign * n * pow(10.0f, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuefloat = n; + item->valueint = (int)n; + item->type = Json_Number; + return num; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC}; +static const char* parse_string (Json *item, const char* str) { + const char* ptr = str + 1; + char* ptr2; + char* out; + int len = 0; + unsigned uc, uc2; + if (*str != '\"') { + ep = str; + return 0; + } /* not a string! */ + + 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. */ + if (!out) return 0; + + ptr = str + 1; + ptr2 = out; + while (*ptr != '\"' && *ptr) { + if (*ptr != '\\') + *ptr2++ = *ptr++; + else { + ptr++; + switch (*ptr) { + case 'b': + *ptr2++ = '\b'; + break; + case 'f': + *ptr2++ = '\f'; + break; + case 'n': + *ptr2++ = '\n'; + break; + case 'r': + *ptr2++ = '\r'; + break; + case 't': + *ptr2++ = '\t'; + break; + case 'u': /* transcode utf16 to utf8. */ + sscanf(ptr + 1, "%4x", &uc); + ptr += 4; /* get the unicode char. */ + + if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) break; /* check for invalid. */ + + if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */ + { + if (ptr[1] != '\\' || ptr[2] != 'u') break; /* missing second-half of surrogate. */ + sscanf(ptr + 3, "%4x", &uc2); + ptr += 6; + if (uc2 < 0xDC00 || uc2 > 0xDFFF) break; /* invalid second-half of surrogate. */ + uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); + } + + len = 4; + if (uc < 0x80) + len = 1; + else if (uc < 0x800) + len = 2; + else if (uc < 0x10000) len = 3; + ptr2 += len; + + switch (len) { + case 4: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 3: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 2: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 1: + *--ptr2 = (uc | firstByteMark[len]); + } + ptr2 += len; + break; + default: + *ptr2++ = *ptr; + break; + } + ptr++; + } + } + *ptr2 = 0; + if (*ptr == '\"') ptr++; + item->valuestring = out; + item->type = Json_String; + return ptr; +} + +/* Predeclare these prototypes. */ +static const char* parse_value (Json *item, const char* value); +static const char* parse_array (Json *item, const char* value); +static const char* parse_object (Json *item, const char* value); + +/* Utility to jump whitespace and cr/lf */ +static const char* skip (const char* in) { + while (in && *in && (unsigned char)*in <= 32) + in++; + return in; +} + +/* Parse an object - create a new root, and populate. */ +Json *Json_create (const char* value) { + const char* end = 0; + Json *c = Json_new(); + ep = 0; + if (!c) return 0; /* memory fail */ + + end = parse_value(c, skip(value)); + if (!end) { + Json_dispose(c); + return 0; + } /* parse failure. ep is set. */ + + return c; +} + +/* Parser core - when encountering text, process appropriately. */ +static const char* parse_value (Json *item, const char* value) { + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value, "null", 4)) { + item->type = Json_NULL; + return value + 4; + } + if (!strncmp(value, "false", 5)) { + item->type = Json_False; + return value + 5; + } + if (!strncmp(value, "true", 4)) { + item->type = Json_True; + item->valueint = 1; + return value + 4; + } + if (*value == '\"') { + return parse_string(item, value); + } + if (*value == '-' || (*value >= '0' && *value <= '9')) { + return parse_number(item, value); + } + if (*value == '[') { + return parse_array(item, value); + } + if (*value == '{') { + return parse_object(item, value); + } + + ep = value; + return 0; /* failure. */ +} + +/* Build an array from input text. */ +static const char* parse_array (Json *item, const char* value) { + Json *child; + if (*value != '[') { + ep = value; + return 0; + } /* not an array! */ + + item->type = Json_Array; + value = skip(value + 1); + if (*value == ']') return value + 1; /* empty array. */ + + item->child = child = Json_new(); + if (!item->child) return 0; /* memory fail */ + value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value == ',') { + Json *new_item; + if (!(new_item = Json_new())) return 0; /* memory fail */ + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip(parse_value(child, skip(value + 1))); + if (!value) return 0; /* memory fail */ + } + + if (*value == ']') return value + 1; /* end of array */ + ep = value; + return 0; /* malformed. */ +} + +/* Build an object from the text. */ +static const char* parse_object (Json *item, const char* value) { + Json *child; + if (*value != '{') { + ep = value; + return 0; + } /* not an object! */ + + item->type = Json_Object; + value = skip(value + 1); + if (*value == '}') return value + 1; /* empty array. */ + + item->child = child = Json_new(); + if (!item->child) return 0; + value = skip(parse_string(child, skip(value))); + if (!value) return 0; + child->name = child->valuestring; + child->valuestring = 0; + if (*value != ':') { + ep = value; + return 0; + } /* fail! */ + value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value == ',') { + Json *new_item; + if (!(new_item = Json_new())) return 0; /* memory fail */ + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip(parse_string(child, skip(value + 1))); + if (!value) return 0; + child->name = child->valuestring; + child->valuestring = 0; + if (*value != ':') { + ep = value; + return 0; + } /* fail! */ + value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value == '}') return value + 1; /* end of array */ + ep = value; + return 0; /* malformed. */ +} + +/* Get Array size/item / object item. */ +int Json_getSize (Json *array) { + Json *c = array->child; + int i = 0; + while (c) + i++, c = c->next; + return i; +} + +Json *Json_getItemAt (Json *array, int item) { + Json *c = array->child; + while (c && item > 0) + item--, c = c->next; + return c; +} + +Json *Json_getItem (Json *object, const char* string) { + Json *c = object->child; + while (c && Json_strcasecmp(c->name, string)) + c = c->next; + return c; +} + +const char* Json_getString (Json* object, const char* name, const char* defaultValue) { + object = Json_getItem(object, name); + if (object) return object->valuestring; + return defaultValue; +} + +float Json_getFloat (Json* value, const char* name, float defaultValue) { + value = Json_getItem(value, name); + return value ? value->valuefloat : defaultValue; +} + +int Json_getInt (Json* value, const char* name, int defaultValue) { + value = Json_getItem(value, name); + return value ? (int)value->valuefloat : defaultValue; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/Json.h b/extensions/spine/Json.h new file mode 100644 index 0000000000..35cccd6655 --- /dev/null +++ b/extensions/spine/Json.h @@ -0,0 +1,77 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, dispose of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/* Esoteric Software: Removed everything except parsing, shorter method names, more get methods, double to float, formatted. */ + +#ifndef SPINE_JSON_H_ +#define SPINE_JSON_H_ + +namespace cocos2d { namespace extension { + +/* Json Types: */ +#define Json_False 0 +#define Json_True 1 +#define Json_NULL 2 +#define Json_Number 3 +#define Json_String 4 +#define Json_Array 5 +#define Json_Object 6 + +/* The Json structure: */ +typedef struct Json { + struct Json* next; + struct Json* prev; /* next/prev allow you to walk array/object chains. Alternatively, use getSize/getItemAt/getItem */ + struct Json* child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + const char* valuestring; /* The item's string, if type==Json_String */ + int valueint; /* The item's number, if type==Json_Number */ + float valuefloat; /* The item's number, if type==Json_Number */ + + const char* name; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} Json; + +/* Supply a block of JSON, and this returns a Json object you can interrogate. Call Json_dispose when finished. */ +Json* Json_create (const char* value); + +/* Delete a Json entity and all subentities. */ +void Json_dispose (Json* json); + +/* Returns the number of items in an array (or object). */ +int Json_getSize (Json* json); + +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +Json* Json_getItemAt (Json* json, int item); + +/* Get item "string" from object. Case insensitive. */ +Json* Json_getItem (Json* json, const char* string); +const char* Json_getString (Json* json, const char* name, const char* defaultValue); +float Json_getFloat (Json* json, const char* name, float defaultValue); +int Json_getInt (Json* json, const char* name, int defaultValue); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Json_create() returns 0. 0 when Json_create() succeeds. */ +const char* Json_getError (void); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_JSON_H_ */ diff --git a/extensions/spine/RegionAttachment.cpp b/extensions/spine/RegionAttachment.cpp new file mode 100644 index 0000000000..cc26532e64 --- /dev/null +++ b/extensions/spine/RegionAttachment.cpp @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +RegionAttachment* RegionAttachment_create (const char* name) { + RegionAttachment* self = NEW(RegionAttachment); + self->scaleX = 1; + self->scaleY = 1; + _Attachment_init(SUPER(self), name, ATTACHMENT_REGION, _Attachment_deinit); + return self; +} + +void RegionAttachment_updateOffset (RegionAttachment* self) { + float localX2 = self->width / 2; + float localY2 = self->height / 2; + float localX = -localX2; + float localY = -localY2; + if (self->region->rotate) { + localX += self->region->offsetX / self->region->originalWidth * self->height; + localY += self->region->offsetY / self->region->originalHeight * self->width; + localX2 -= (self->region->originalWidth - self->region->offsetX - self->region->height) / self->region->originalWidth * self->width; + localY2 -= (self->region->originalHeight - self->region->offsetY - self->region->width) / self->region->originalHeight * self->height; + } else { + localX += self->region->offsetX / self->region->originalWidth * self->width; + localY += self->region->offsetY / self->region->originalHeight * self->height; + localX2 -= (self->region->originalWidth - self->region->offsetX - self->region->width) / self->region->originalWidth * self->width; + localY2 -= (self->region->originalHeight - self->region->offsetY - self->region->height) / self->region->originalHeight * self->height; + } + localX *= self->scaleX; + localY *= self->scaleY; + localX2 *= self->scaleX; + localY2 *= self->scaleY; + float radians = (float)(self->rotation * 3.1415926535897932385 / 180); + float cosine = cosf(radians); + float sine = sinf(radians); + float localXCos = localX * cosine + self->x; + float localXSin = localX * sine; + float localYCos = localY * cosine + self->y; + float localYSin = localY * sine; + float localX2Cos = localX2 * cosine + self->x; + float localX2Sin = localX2 * sine; + float localY2Cos = localY2 * cosine + self->y; + float localY2Sin = localY2 * sine; + self->offset[VERTEX_X1] = localXCos - localYSin; + self->offset[VERTEX_Y1] = localYCos + localXSin; + self->offset[VERTEX_X2] = localXCos - localY2Sin; + self->offset[VERTEX_Y2] = localY2Cos + localXSin; + self->offset[VERTEX_X3] = localX2Cos - localY2Sin; + self->offset[VERTEX_Y3] = localY2Cos + localX2Sin; + self->offset[VERTEX_X4] = localX2Cos - localYSin; + self->offset[VERTEX_Y4] = localYCos + localX2Sin; +} + +void RegionAttachment_updateVertices (RegionAttachment* self, Slot* slot) { + float* offset = self->offset; + Bone* bone = slot->bone; + self->vertices[VERTEX_X1] = offset[VERTEX_X1] * bone->m00 + offset[VERTEX_Y1] * bone->m01 + bone->worldX; + self->vertices[VERTEX_Y1] = offset[VERTEX_X1] * bone->m10 + offset[VERTEX_Y1] * bone->m11 + bone->worldY; + self->vertices[VERTEX_X2] = offset[VERTEX_X2] * bone->m00 + offset[VERTEX_Y2] * bone->m01 + bone->worldX; + self->vertices[VERTEX_Y2] = offset[VERTEX_X2] * bone->m10 + offset[VERTEX_Y2] * bone->m11 + bone->worldY; + self->vertices[VERTEX_X3] = offset[VERTEX_X3] * bone->m00 + offset[VERTEX_Y3] * bone->m01 + bone->worldX; + self->vertices[VERTEX_Y3] = offset[VERTEX_X3] * bone->m10 + offset[VERTEX_Y3] * bone->m11 + bone->worldY; + self->vertices[VERTEX_X4] = offset[VERTEX_X4] * bone->m00 + offset[VERTEX_Y4] * bone->m01 + bone->worldX; + self->vertices[VERTEX_Y4] = offset[VERTEX_X4] * bone->m10 + offset[VERTEX_Y4] * bone->m11 + bone->worldY; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/RegionAttachment.h b/extensions/spine/RegionAttachment.h new file mode 100644 index 0000000000..b866ef0d5f --- /dev/null +++ b/extensions/spine/RegionAttachment.h @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_REGIONATTACHMENT_H_ +#define SPINE_REGIONATTACHMENT_H_ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +typedef enum { + VERTEX_X1 = 0, VERTEX_Y1, VERTEX_X2, VERTEX_Y2, VERTEX_X3, VERTEX_Y3, VERTEX_X4, VERTEX_Y4 +} VertexIndex; + +typedef struct RegionAttachment RegionAttachment; +struct RegionAttachment { + Attachment super; + float x, y, scaleX, scaleY, rotation, width, height; + AtlasRegion* region; + float offset[8]; + float vertices[8]; + float uvs[8]; +}; + +RegionAttachment* RegionAttachment_create (const char* name); + +void RegionAttachment_updateOffset (RegionAttachment* self); +void RegionAttachment_updateVertices (RegionAttachment* self, Slot* slot); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_REGIONATTACHMENT_H_ */ diff --git a/extensions/spine/Skeleton.cpp b/extensions/spine/Skeleton.cpp new file mode 100644 index 0000000000..2098860dc5 --- /dev/null +++ b/extensions/spine/Skeleton.cpp @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +Skeleton* Skeleton_create (SkeletonData* data) { + Skeleton* self = NEW(Skeleton); + CONST_CAST(SkeletonData*, self->data) = data; + + self->boneCount = self->data->boneCount; + self->bones = MALLOC(Bone*, self->boneCount); + int i, ii; + for (i = 0; i < self->boneCount; ++i) { + BoneData* boneData = self->data->bones[i]; + Bone* parent = 0; + if (boneData->parent) { + /* Find parent bone. */ + for (ii = 0; ii < self->boneCount; ++ii) { + if (data->bones[ii] == boneData->parent) { + parent = self->bones[ii]; + break; + } + } + } + self->bones[i] = Bone_create(boneData, parent); + } + CONST_CAST(Bone*, self->root) = self->bones[0]; + + self->slotCount = data->slotCount; + self->slots = MALLOC(Slot*, self->slotCount); + for (i = 0; i < self->slotCount; ++i) { + SlotData *slotData = data->slots[i]; + + /* Find bone for the slotData's boneData. */ + Bone *bone; + for (ii = 0; ii < self->boneCount; ++ii) { + if (data->bones[ii] == slotData->boneData) { + bone = self->bones[ii]; + break; + } + } + + self->slots[i] = Slot_create(slotData, self, bone); + } + + self->drawOrder = MALLOC(Slot*, self->slotCount); + memcpy(self->drawOrder, self->slots, sizeof(Slot*) * self->slotCount); + + self->r = 1; + self->g = 1; + self->b = 1; + self->a = 1; + + return self; +} + +void Skeleton_dispose (Skeleton* self) { + int i; + for (i = 0; i < self->boneCount; ++i) + Bone_dispose(self->bones[i]); + FREE(self->bones); + + for (i = 0; i < self->slotCount; ++i) + Slot_dispose(self->slots[i]); + FREE(self->slots); + + FREE(self->drawOrder); +} + +void Skeleton_updateWorldTransform (const Skeleton* self) { + int i; + for (i = 0; i < self->boneCount; ++i) + Bone_updateWorldTransform(self->bones[i], self->flipX, self->flipY); +} + +void Skeleton_setToBindPose (const Skeleton* self) { + Skeleton_setBonesToBindPose(self); + Skeleton_setSlotsToBindPose(self); +} + +void Skeleton_setBonesToBindPose (const Skeleton* self) { + int i; + for (i = 0; i < self->boneCount; ++i) + Bone_setToBindPose(self->bones[i]); +} + +void Skeleton_setSlotsToBindPose (const Skeleton* self) { + int i; + for (i = 0; i < self->slotCount; ++i) + Slot_setToBindPose(self->slots[i]); +} + +Bone* Skeleton_findBone (const Skeleton* self, const char* boneName) { + int i; + for (i = 0; i < self->boneCount; ++i) + if (strcmp(self->data->bones[i]->name, boneName) == 0) return self->bones[i]; + return 0; +} + +int Skeleton_findBoneIndex (const Skeleton* self, const char* boneName) { + int i; + for (i = 0; i < self->boneCount; ++i) + if (strcmp(self->data->bones[i]->name, boneName) == 0) return i; + return -1; +} + +Slot* Skeleton_findSlot (const Skeleton* self, const char* slotName) { + int i; + for (i = 0; i < self->slotCount; ++i) + if (strcmp(self->data->slots[i]->name, slotName) == 0) return self->slots[i]; + return 0; +} + +int Skeleton_findSlotIndex (const Skeleton* self, const char* slotName) { + int i; + for (i = 0; i < self->slotCount; ++i) + if (strcmp(self->data->slots[i]->name, slotName) == 0) return i; + return -1; +} + +int Skeleton_setSkinByName (Skeleton* self, const char* skinName) { + if (!skinName) { + Skeleton_setSkin(self, 0); + return 1; + } + Skin *skin = SkeletonData_findSkin(self->data, skinName); + if (!skin) return 0; + Skeleton_setSkin(self, skin); + return 1; +} + +void Skeleton_setSkin (Skeleton* self, Skin* newSkin) { + if (self->skin && newSkin) Skin_attachAll(newSkin, self, self->skin); + CONST_CAST(Skin*, self->skin) = newSkin; +} + +Attachment* Skeleton_getAttachmentForSlotName (const Skeleton* self, const char* slotName, const char* attachmentName) { + int slotIndex = SkeletonData_findSlotIndex(self->data, slotName); + return Skeleton_getAttachmentForSlotIndex(self, slotIndex, attachmentName); +} + +Attachment* Skeleton_getAttachmentForSlotIndex (const Skeleton* self, int slotIndex, const char* attachmentName) { + if (slotIndex == -1) return 0; + if (self->skin) { + Attachment *attachment = Skin_getAttachment(self->skin, slotIndex, attachmentName); + if (attachment) return attachment; + } + if (self->data->defaultSkin) { + Attachment *attachment = Skin_getAttachment(self->data->defaultSkin, slotIndex, attachmentName); + if (attachment) return attachment; + } + return 0; +} + +int Skeleton_setAttachment (Skeleton* self, const char* slotName, const char* attachmentName) { + int i; + for (i = 0; i < self->slotCount; ++i) { + Slot *slot = self->slots[i]; + if (strcmp(slot->data->name, slotName) == 0) { + Attachment* attachment = Skeleton_getAttachmentForSlotIndex(self, i, attachmentName); + if (!attachment) return 0; + Slot_setAttachment(slot, attachment); + return 1; + } + } + return 0; +} + +void Skeleton_update (Skeleton* self, float deltaTime) { + self->time += deltaTime; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/Skeleton.h b/extensions/spine/Skeleton.h new file mode 100644 index 0000000000..2ebf45260c --- /dev/null +++ b/extensions/spine/Skeleton.h @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_SKELETON_H_ +#define SPINE_SKELETON_H_ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct Skeleton Skeleton; +struct Skeleton { + SkeletonData* const data; + + int boneCount; + Bone** bones; + Bone* const root; + + int slotCount; + Slot** slots; + Slot** drawOrder; + + Skin* const skin; + float r, g, b, a; + float time; + int/*bool*/flipX, flipY; +}; + +Skeleton* Skeleton_create (SkeletonData* data); +void Skeleton_dispose (Skeleton* self); + +void Skeleton_updateWorldTransform (const Skeleton* self); + +void Skeleton_setToBindPose (const Skeleton* self); +void Skeleton_setBonesToBindPose (const Skeleton* self); +void Skeleton_setSlotsToBindPose (const Skeleton* self); + +/* Returns 0 if the bone was not found. */ +Bone* Skeleton_findBone (const Skeleton* self, const char* boneName); +/* Returns -1 if the bone was not found. */ +int Skeleton_findBoneIndex (const Skeleton* self, const char* boneName); + +/* Returns 0 if the slot was not found. */ +Slot* Skeleton_findSlot (const Skeleton* self, const char* slotName); +/* Returns -1 if the slot was not found. */ +int Skeleton_findSlotIndex (const Skeleton* self, const char* slotName); + +/* Sets the skin used to look up attachments not found in the SkeletonData defaultSkin. Attachments from the new skin are + * attached if the corresponding attachment from the old skin was attached. + * @param skin May be 0.*/ +void Skeleton_setSkin (Skeleton* self, Skin* skin); +/* Returns 0 if the skin was not found. See Skeleton_setSkin. + * @param skinName May be 0. */ +int Skeleton_setSkinByName (Skeleton* self, const char* skinName); + +/* Returns 0 if the slot or attachment was not found. */ +Attachment* Skeleton_getAttachmentForSlotName (const Skeleton* self, const char* slotName, const char* attachmentName); +/* Returns 0 if the slot or attachment was not found. */ +Attachment* Skeleton_getAttachmentForSlotIndex (const Skeleton* self, int slotIndex, const char* attachmentName); +/* Returns 0 if the slot or attachment was not found. */ +int Skeleton_setAttachment (Skeleton* self, const char* slotName, const char* attachmentName); + +void Skeleton_update (Skeleton* self, float deltaTime); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_SKELETON_H_*/ diff --git a/extensions/spine/SkeletonData.cpp b/extensions/spine/SkeletonData.cpp new file mode 100644 index 0000000000..e7ab6f4466 --- /dev/null +++ b/extensions/spine/SkeletonData.cpp @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +SkeletonData* SkeletonData_create () { + return NEW(SkeletonData); +} + +void SkeletonData_dispose (SkeletonData* self) { + int i; + for (i = 0; i < self->boneCount; ++i) + BoneData_dispose(self->bones[i]); + FREE(self->bones); + + for (i = 0; i < self->slotCount; ++i) + SlotData_dispose(self->slots[i]); + FREE(self->slots); + + for (i = 0; i < self->skinCount; ++i) + Skin_dispose(self->skins[i]); + FREE(self->skins); + + for (i = 0; i < self->animationCount; ++i) + Animation_dispose(self->animations[i]); + FREE(self->animations); + + FREE(self); +} + +BoneData* SkeletonData_findBone (const SkeletonData* self, const char* boneName) { + int i; + for (i = 0; i < self->boneCount; ++i) + if (strcmp(self->bones[i]->name, boneName) == 0) return self->bones[i]; + return 0; +} + +int SkeletonData_findBoneIndex (const SkeletonData* self, const char* boneName) { + int i; + for (i = 0; i < self->boneCount; ++i) + if (strcmp(self->bones[i]->name, boneName) == 0) return i; + return 0; +} + +SlotData* SkeletonData_findSlot (const SkeletonData* self, const char* slotName) { + int i; + for (i = 0; i < self->slotCount; ++i) + if (strcmp(self->slots[i]->name, slotName) == 0) return self->slots[i]; + return 0; +} + +int SkeletonData_findSlotIndex (const SkeletonData* self, const char* slotName) { + int i; + for (i = 0; i < self->slotCount; ++i) + if (strcmp(self->slots[i]->name, slotName) == 0) return i; + return 0; +} + +Skin* SkeletonData_findSkin (const SkeletonData* self, const char* skinName) { + int i; + for (i = 0; i < self->skinCount; ++i) + if (strcmp(self->skins[i]->name, skinName) == 0) return self->skins[i]; + return 0; +} + +Animation* SkeletonData_findAnimation (const SkeletonData* self, const char* animationName) { + int i; + for (i = 0; i < self->animationCount; ++i) + if (strcmp(self->animations[i]->name, animationName) == 0) return self->animations[i]; + return 0; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/SkeletonData.h b/extensions/spine/SkeletonData.h new file mode 100644 index 0000000000..b05bc4574a --- /dev/null +++ b/extensions/spine/SkeletonData.h @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_SKELETONDATA_H_ +#define SPINE_SKELETONDATA_H_ + +#include +#include +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct { + int boneCount; + BoneData** bones; + + int slotCount; + SlotData** slots; + + int skinCount; + Skin** skins; + Skin* defaultSkin; + + int animationCount; + Animation** animations; +} SkeletonData; + +SkeletonData* SkeletonData_create (); +void SkeletonData_dispose (SkeletonData* self); + +BoneData* SkeletonData_findBone (const SkeletonData* self, const char* boneName); +int SkeletonData_findBoneIndex (const SkeletonData* self, const char* boneName); + +SlotData* SkeletonData_findSlot (const SkeletonData* self, const char* slotName); +int SkeletonData_findSlotIndex (const SkeletonData* self, const char* slotName); + +Skin* SkeletonData_findSkin (const SkeletonData* self, const char* skinName); + +Animation* SkeletonData_findAnimation (const SkeletonData* self, const char* animationName); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_SKELETONDATA_H_ */ diff --git a/extensions/spine/SkeletonJson.cpp b/extensions/spine/SkeletonJson.cpp new file mode 100644 index 0000000000..acc5a51654 --- /dev/null +++ b/extensions/spine/SkeletonJson.cpp @@ -0,0 +1,392 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include +#include +#include "Json.h" +#include +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct { + SkeletonJson super; + int ownsLoader; +} _Internal; + +SkeletonJson* SkeletonJson_createWithLoader (AttachmentLoader* attachmentLoader) { + SkeletonJson* self = SUPER(NEW(_Internal)); + self->scale = 1; + self->attachmentLoader = attachmentLoader; + return self; +} + +SkeletonJson* SkeletonJson_create (Atlas* atlas) { + AtlasAttachmentLoader* attachmentLoader = AtlasAttachmentLoader_create(atlas); + SkeletonJson* self = SkeletonJson_createWithLoader(SUPER(attachmentLoader)); + SUB_CAST(_Internal, self) ->ownsLoader = 1; + return self; +} + +void SkeletonJson_dispose (SkeletonJson* self) { + if (SUB_CAST(_Internal, self) ->ownsLoader) AttachmentLoader_dispose(self->attachmentLoader); + FREE(self->error); + FREE(self); +} + +void _SkeletonJson_setError (SkeletonJson* self, Json* root, const char* value1, const char* value2) { + FREE(self->error); + char message[256]; + strcpy(message, value1); + int length = strlen(value1); + if (value2) strncat(message + length, value2, 256 - length); + MALLOC_STR(self->error, message); + if (root) Json_dispose(root); +} + +static float toColor (const char* value, int index) { + if (strlen(value) != 8) return -1; + value += index * 2; + char digits[3]; + digits[0] = *value; + digits[1] = *(value + 1); + digits[2] = '\0'; + char* error; + int color = strtoul(digits, &error, 16); + if (*error != 0) return -1; + return color / (float)255; +} + +static void readCurve (CurveTimeline* timeline, int frameIndex, Json* frame) { + Json* curve = Json_getItem(frame, "curve"); + if (!curve) return; + if (curve->type == Json_String && strcmp(curve->valuestring, "stepped") == 0) + CurveTimeline_setStepped(timeline, frameIndex); + else if (curve->type == Json_Array) { + CurveTimeline_setCurve(timeline, frameIndex, Json_getItemAt(curve, 0)->valuefloat, Json_getItemAt(curve, 1)->valuefloat, + Json_getItemAt(curve, 2)->valuefloat, Json_getItemAt(curve, 3)->valuefloat); + } +} + +static Animation* _SkeletonJson_readAnimation (SkeletonJson* self, Json* root, SkeletonData *skeletonData) { + Json* bones = Json_getItem(root, "bones"); + int boneCount = bones ? Json_getSize(bones) : 0; + + Json* slots = Json_getItem(root, "slots"); + int slotCount = slots ? Json_getSize(slots) : 0; + + int timelineCount = 0; + int i, ii, iii; + for (i = 0; i < boneCount; ++i) + timelineCount += Json_getSize(Json_getItemAt(bones, i)); + for (i = 0; i < slotCount; ++i) + timelineCount += Json_getSize(Json_getItemAt(slots, i)); + Animation* animation = Animation_create(root->name, timelineCount); + animation->timelineCount = 0; + skeletonData->animations[skeletonData->animationCount] = animation; + skeletonData->animationCount++; + + for (i = 0; i < boneCount; ++i) { + Json* boneMap = Json_getItemAt(bones, i); + + const char* boneName = boneMap->name; + + int boneIndex = SkeletonData_findBoneIndex(skeletonData, boneName); + if (boneIndex == -1) { + Animation_dispose(animation); + _SkeletonJson_setError(self, root, "Bone not found: ", boneName); + return 0; + } + + int timelineCount = Json_getSize(boneMap); + for (ii = 0; ii < timelineCount; ++ii) { + Json* timelineArray = Json_getItemAt(boneMap, ii); + int frameCount = Json_getSize(timelineArray); + const char* timelineType = timelineArray->name; + + if (strcmp(timelineType, "rotate") == 0) { + RotateTimeline *timeline = RotateTimeline_create(frameCount); + timeline->boneIndex = boneIndex; + for (iii = 0; iii < frameCount; ++iii) { + Json* frame = Json_getItemAt(timelineArray, iii); + RotateTimeline_setFrame(timeline, iii, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "angle", 0)); + readCurve(SUPER(timeline), iii, frame); + } + animation->timelines[animation->timelineCount++] = (Timeline*)timeline; + float duration = timeline->frames[frameCount * 2 - 2]; + if (duration > animation->duration) animation->duration = duration; + + } else { + int isScale = strcmp(timelineType, "scale") == 0; + if (isScale || strcmp(timelineType, "translate") == 0) { + TranslateTimeline *timeline = isScale ? ScaleTimeline_create(frameCount) : TranslateTimeline_create(frameCount); + float scale = isScale ? 1 : self->scale; + timeline->boneIndex = boneIndex; + for (iii = 0; iii < frameCount; ++iii) { + Json* frame = Json_getItemAt(timelineArray, iii); + TranslateTimeline_setFrame(timeline, iii, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "x", 0) * scale, + Json_getFloat(frame, "y", 0) * scale); + readCurve(SUPER(timeline), iii, frame); + } + animation->timelines[animation->timelineCount++] = (Timeline*)timeline; + float duration = timeline->frames[frameCount * 3 - 3]; + if (duration > animation->duration) animation->duration = duration; + } else { + Animation_dispose(animation); + _SkeletonJson_setError(self, 0, "Invalid timeline type for a bone: ", timelineType); + return 0; + } + } + } + } + + for (i = 0; i < slotCount; ++i) { + Json* slotMap = Json_getItemAt(slots, i); + const char* slotName = slotMap->name; + + int slotIndex = SkeletonData_findSlotIndex(skeletonData, slotName); + if (slotIndex == -1) { + Animation_dispose(animation); + _SkeletonJson_setError(self, root, "Slot not found: ", slotName); + return 0; + } + + int timelineCount = Json_getSize(slotMap); + for (ii = 0; ii < timelineCount; ++ii) { + Json* timelineArray = Json_getItemAt(slotMap, ii); + int frameCount = Json_getSize(timelineArray); + const char* timelineType = timelineArray->name; + + if (strcmp(timelineType, "color") == 0) { + ColorTimeline *timeline = ColorTimeline_create(frameCount); + timeline->slotIndex = slotIndex; + for (iii = 0; iii < frameCount; ++iii) { + Json* frame = Json_getItemAt(timelineArray, iii); + const char* s = Json_getString(frame, "color", 0); + ColorTimeline_setFrame(timeline, iii, Json_getFloat(frame, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2), + toColor(s, 3)); + readCurve(SUPER(timeline), iii, frame); + } + animation->timelines[animation->timelineCount++] = (Timeline*)timeline; + float duration = timeline->frames[frameCount * 5 - 5]; + if (duration > animation->duration) animation->duration = duration; + + } else if (strcmp(timelineType, "attachment") == 0) { + AttachmentTimeline *timeline = AttachmentTimeline_create(frameCount); + timeline->slotIndex = slotIndex; + for (iii = 0; iii < frameCount; ++iii) { + Json* frame = Json_getItemAt(timelineArray, iii); + Json* name = Json_getItem(frame, "name"); + AttachmentTimeline_setFrame(timeline, iii, Json_getFloat(frame, "time", 0), + name->type == Json_NULL ? 0 : name->valuestring); + } + animation->timelines[animation->timelineCount++] = (Timeline*)timeline; + float duration = timeline->frames[frameCount - 1]; + if (duration > animation->duration) animation->duration = duration; + + } else { + Animation_dispose(animation); + _SkeletonJson_setError(self, 0, "Invalid timeline type for a slot: ", timelineType); + return 0; + } + } + } + + return animation; +} + +SkeletonData* SkeletonJson_readSkeletonDataFile (SkeletonJson* self, const char* path) { + int length; + const char* json = _Util_readFile(path, &length); + if (!json) { + _SkeletonJson_setError(self, 0, "Unable to read skeleton file: ", path); + return 0; + } + SkeletonData* skeletonData = SkeletonJson_readSkeletonData(self, json); + FREE(json); + return skeletonData; +} + +SkeletonData* SkeletonJson_readSkeletonData (SkeletonJson* self, const char* json) { + FREE(self->error); + CONST_CAST(char*, self->error) = 0; + + Json* root = Json_create(json); + if (!root) { + _SkeletonJson_setError(self, 0, "Invalid skeleton JSON: ", Json_getError()); + return 0; + } + + SkeletonData* skeletonData = SkeletonData_create(); + int i, ii, iii; + + Json* bones = Json_getItem(root, "bones"); + int boneCount = Json_getSize(bones); + skeletonData->bones = MALLOC(BoneData*, boneCount); + for (i = 0; i < boneCount; ++i) { + Json* boneMap = Json_getItemAt(bones, i); + + const char* boneName = Json_getString(boneMap, "name", 0); + + BoneData* parent = 0; + const char* parentName = Json_getString(boneMap, "parent", 0); + if (parentName) { + parent = SkeletonData_findBone(skeletonData, parentName); + if (!parent) { + SkeletonData_dispose(skeletonData); + _SkeletonJson_setError(self, root, "Parent bone not found: ", parentName); + return 0; + } + } + + BoneData* boneData = BoneData_create(boneName, parent); + boneData->length = Json_getFloat(boneMap, "length", 0) * self->scale; + boneData->x = Json_getFloat(boneMap, "x", 0) * self->scale; + boneData->y = Json_getFloat(boneMap, "y", 0) * self->scale; + boneData->rotation = Json_getFloat(boneMap, "rotation", 0); + boneData->scaleX = Json_getFloat(boneMap, "scaleX", 1); + boneData->scaleY = Json_getFloat(boneMap, "scaleY", 1); + + skeletonData->bones[i] = boneData; + skeletonData->boneCount++; + } + + Json* slots = Json_getItem(root, "slots"); + if (slots) { + int slotCount = Json_getSize(slots); + skeletonData->slots = MALLOC(SlotData*, slotCount); + for (i = 0; i < slotCount; ++i) { + Json* slotMap = Json_getItemAt(slots, i); + + const char* slotName = Json_getString(slotMap, "name", 0); + + const char* boneName = Json_getString(slotMap, "bone", 0); + BoneData* boneData = SkeletonData_findBone(skeletonData, boneName); + if (!boneData) { + SkeletonData_dispose(skeletonData); + _SkeletonJson_setError(self, root, "Slot bone not found: ", boneName); + return 0; + } + + SlotData* slotData = SlotData_create(slotName, boneData); + + const char* color = Json_getString(slotMap, "color", 0); + if (color) { + slotData->r = toColor(color, 0); + slotData->g = toColor(color, 1); + slotData->b = toColor(color, 2); + slotData->a = toColor(color, 3); + } + + Json *attachmentItem = Json_getItem(slotMap, "attachment"); + if (attachmentItem) SlotData_setAttachmentName(slotData, attachmentItem->valuestring); + + skeletonData->slots[i] = slotData; + skeletonData->slotCount++; + } + } + + Json* skinsMap = Json_getItem(root, "skins"); + if (skinsMap) { + int skinCount = Json_getSize(skinsMap); + skeletonData->skins = MALLOC(Skin*, skinCount); + for (i = 0; i < skinCount; ++i) { + Json* slotMap = Json_getItemAt(skinsMap, i); + const char* skinName = slotMap->name; + Skin *skin = Skin_create(skinName); + skeletonData->skins[i] = skin; + skeletonData->skinCount++; + if (strcmp(skinName, "default") == 0) skeletonData->defaultSkin = skin; + + int slotNameCount = Json_getSize(slotMap); + for (ii = 0; ii < slotNameCount; ++ii) { + Json* attachmentsMap = Json_getItemAt(slotMap, ii); + const char* slotName = attachmentsMap->name; + int slotIndex = SkeletonData_findSlotIndex(skeletonData, slotName); + + int attachmentCount = Json_getSize(attachmentsMap); + for (iii = 0; iii < attachmentCount; ++iii) { + Json* attachmentMap = Json_getItemAt(attachmentsMap, iii); + const char* skinAttachmentName = attachmentMap->name; + const char* attachmentName = Json_getString(attachmentMap, "name", skinAttachmentName); + + const char* typeString = Json_getString(attachmentMap, "type", "region"); + AttachmentType type; + if (strcmp(typeString, "region") == 0) + type = ATTACHMENT_REGION; + else if (strcmp(typeString, "regionSequence") == 0) + type = ATTACHMENT_REGION_SEQUENCE; + else { + SkeletonData_dispose(skeletonData); + _SkeletonJson_setError(self, root, "Unknown attachment type: ", typeString); + return 0; + } + + Attachment* attachment = AttachmentLoader_newAttachment(self->attachmentLoader, skin, type, attachmentName); + if (!attachment) { + if (self->attachmentLoader->error1) { + SkeletonData_dispose(skeletonData); + _SkeletonJson_setError(self, root, self->attachmentLoader->error1, self->attachmentLoader->error2); + return 0; + } + continue; + } + + if (attachment->type == ATTACHMENT_REGION || attachment->type == ATTACHMENT_REGION_SEQUENCE) { + RegionAttachment* regionAttachment = (RegionAttachment*)attachment; + regionAttachment->x = Json_getFloat(attachmentMap, "x", 0) * self->scale; + regionAttachment->y = Json_getFloat(attachmentMap, "y", 0) * self->scale; + regionAttachment->scaleX = Json_getFloat(attachmentMap, "scaleX", 1); + regionAttachment->scaleY = Json_getFloat(attachmentMap, "scaleY", 1); + regionAttachment->rotation = Json_getFloat(attachmentMap, "rotation", 0); + regionAttachment->width = Json_getFloat(attachmentMap, "width", 32) * self->scale; + regionAttachment->height = Json_getFloat(attachmentMap, "height", 32) * self->scale; + RegionAttachment_updateOffset(regionAttachment); + } + + Skin_addAttachment(skin, slotIndex, skinAttachmentName, attachment); + } + } + } + } + + Json* animations = Json_getItem(root, "animations"); + if (animations) { + int animationCount = Json_getSize(animations); + skeletonData->animations = MALLOC(Animation*, animationCount); + for (i = 0; i < animationCount; ++i) { + Json* animationMap = Json_getItemAt(animations, i); + _SkeletonJson_readAnimation(self, animationMap, skeletonData); + } + } + + Json_dispose(root); + return skeletonData; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/SkeletonJson.h b/extensions/spine/SkeletonJson.h new file mode 100644 index 0000000000..1fd9b2875a --- /dev/null +++ b/extensions/spine/SkeletonJson.h @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_SKELETONJSON_H_ +#define SPINE_SKELETONJSON_H_ + +#include +#include +#include +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct { + float scale; + AttachmentLoader* attachmentLoader; + const char* const error; +} SkeletonJson; + +SkeletonJson* SkeletonJson_createWithLoader (AttachmentLoader* attachmentLoader); +SkeletonJson* SkeletonJson_create (Atlas* atlas); +void SkeletonJson_dispose (SkeletonJson* self); + +SkeletonData* SkeletonJson_readSkeletonData (SkeletonJson* self, const char* json); +SkeletonData* SkeletonJson_readSkeletonDataFile (SkeletonJson* self, const char* path); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_SKELETONJSON_H_ */ diff --git a/extensions/spine/Skin.cpp b/extensions/spine/Skin.cpp new file mode 100644 index 0000000000..4045bb98e9 --- /dev/null +++ b/extensions/spine/Skin.cpp @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct _Entry _Entry; +struct _Entry { + int slotIndex; + const char* name; + Attachment* attachment; + _Entry* next; +}; + +_Entry* _Entry_create (int slotIndex, const char* name, Attachment* attachment) { + _Entry* self = NEW(_Entry); + self->slotIndex = slotIndex; + MALLOC_STR(self->name, name); + self->attachment = attachment; + return self; +} + +void _Entry_dispose (_Entry* self) { + Attachment_dispose(self->attachment); + FREE(self->name); + FREE(self); +} + +/**/ + +typedef struct { + Skin super; + _Entry* entries; +} _Internal; + +Skin* Skin_create (const char* name) { + Skin* self = SUPER(NEW(_Internal)); + MALLOC_STR(self->name, name); + return self; +} + +void Skin_dispose (Skin* self) { + _Entry* entry = SUB_CAST(_Internal, self)->entries; + while (entry) { + _Entry* nextEtry = entry->next; + _Entry_dispose(entry); + entry = nextEtry; + } + + FREE(self->name); + FREE(self); +} +#include +void Skin_addAttachment (Skin* self, int slotIndex, const char* name, Attachment* attachment) { + _Entry* newEntry = _Entry_create(slotIndex, name, attachment); + newEntry->next = SUB_CAST(_Internal, self)->entries; + SUB_CAST(_Internal, self)->entries = newEntry; +} + +Attachment* Skin_getAttachment (const Skin* self, int slotIndex, const char* name) { + const _Entry* entry = SUB_CAST(_Internal, self) ->entries; + while (entry) { + if (entry->slotIndex == slotIndex && strcmp(entry->name, name) == 0) return entry->attachment; + entry = entry->next; + } + return 0; +} + +void Skin_attachAll (const Skin* self, Skeleton* skeleton, const Skin* oldSkin) { + const _Entry *entry = SUB_CAST(_Internal, oldSkin) ->entries; + while (entry) { + Slot *slot = skeleton->slots[entry->slotIndex]; + if (slot->attachment == entry->attachment) { + Attachment *attachment = Skin_getAttachment(self, entry->slotIndex, entry->name); + if (attachment) Slot_setAttachment(slot, attachment); + } + entry = entry->next; + } +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/Skin.h b/extensions/spine/Skin.h new file mode 100644 index 0000000000..72b8a44ca9 --- /dev/null +++ b/extensions/spine/Skin.h @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_SKIN_H_ +#define SPINE_SKIN_H_ + +#include + +namespace cocos2d { namespace extension { + +struct Skeleton; + +typedef struct { + const char* const name; +} Skin; + +Skin* Skin_create (const char* name); +void Skin_dispose (Skin* self); + +/* The Skin owns the attachment. */ +void Skin_addAttachment (Skin* self, int slotIndex, const char* name, Attachment* attachment); +/* Returns 0 if the attachment was not found. */ +Attachment* Skin_getAttachment (const Skin* self, int slotIndex, const char* name); + +/** Attach each attachment in this skin if the corresponding attachment in oldSkin is currently attached. */ +void Skin_attachAll (const Skin* self, struct Skeleton* skeleton, const Skin* oldSkin); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_SKIN_H_ */ diff --git a/extensions/spine/Slot.cpp b/extensions/spine/Slot.cpp new file mode 100644 index 0000000000..eb582a7d3c --- /dev/null +++ b/extensions/spine/Slot.cpp @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +typedef struct { + Slot super; + float attachmentTime; +} _Internal; + +Slot* Slot_create (SlotData* data, Skeleton* skeleton, Bone* bone) { + Slot* self = SUPER(NEW(_Internal)); + CONST_CAST(SlotData*, self->data) = data; + CONST_CAST(Skeleton*, self->skeleton) = skeleton; + CONST_CAST(Bone*, self->bone) = bone; + Slot_setToBindPose(self); + return self; +} + +void Slot_dispose (Slot* self) { + FREE(self); +} + +void Slot_setAttachment (Slot* self, Attachment* attachment) { + CONST_CAST(Attachment*, self->attachment) = attachment; + SUB_CAST(_Internal, self) ->attachmentTime = self->skeleton->time; +} + +void Slot_setAttachmentTime (Slot* self, float time) { + SUB_CAST(_Internal, self) ->attachmentTime = self->skeleton->time - time; +} + +float Slot_getAttachmentTime (const Slot* self) { + return self->skeleton->time - SUB_CAST(_Internal, self) ->attachmentTime; +} + +void Slot_setToBindPose (Slot* self) { + self->r = self->data->r; + self->g = self->data->g; + self->b = self->data->b; + self->a = self->data->a; + + Attachment* attachment = 0; + if (self->data->attachmentName) { + /* Find slot index. */ + int i; + for (i = 0; i < self->skeleton->data->slotCount; ++i) { + if (self->data == self->skeleton->data->slots[i]) { + attachment = Skeleton_getAttachmentForSlotIndex(self->skeleton, i, self->data->attachmentName); + break; + } + } + } + Slot_setAttachment(self, attachment); +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/Slot.h b/extensions/spine/Slot.h new file mode 100644 index 0000000000..aa8d7d768e --- /dev/null +++ b/extensions/spine/Slot.h @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_SLOT_H_ +#define SPINE_SLOT_H_ + +#include +#include +#include + +namespace cocos2d { namespace extension { + +struct Skeleton; + +typedef struct Slot { + SlotData* const data; + struct Skeleton* const skeleton; + Bone* const bone; + float r, g, b, a; + Attachment* const attachment; +} Slot; + +Slot* Slot_create (SlotData* data, struct Skeleton* skeleton, Bone* bone); +void Slot_dispose (Slot* self); + +/* @param attachment May be 0 to clear the attachment for the slot. */ +void Slot_setAttachment (Slot* self, Attachment* attachment); + +void Slot_setAttachmentTime (Slot* self, float time); +float Slot_getAttachmentTime (const Slot* self); + +void Slot_setToBindPose (Slot* self); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_SLOT_H_ */ diff --git a/extensions/spine/SlotData.cpp b/extensions/spine/SlotData.cpp new file mode 100644 index 0000000000..e25c9ebc64 --- /dev/null +++ b/extensions/spine/SlotData.cpp @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +namespace cocos2d { namespace extension { + +SlotData* SlotData_create (const char* name, BoneData* boneData) { + SlotData* self = NEW(SlotData); + MALLOC_STR(self->name, name); + CONST_CAST(BoneData*, self->boneData) = boneData; + self->r = 1; + self->g = 1; + self->b = 1; + self->a = 1; + return self; +} + +void SlotData_dispose (SlotData* self) { + FREE(self->name); + FREE(self->attachmentName); + FREE(self); +} + +void SlotData_setAttachmentName (SlotData* self, const char* attachmentName) { + FREE(self->attachmentName); + if (attachmentName) + MALLOC_STR(self->attachmentName, attachmentName); + else + CONST_CAST(char*, self->attachmentName) = 0; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/SlotData.h b/extensions/spine/SlotData.h new file mode 100644 index 0000000000..9c431ff93e --- /dev/null +++ b/extensions/spine/SlotData.h @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_SLOTDATA_H_ +#define SPINE_SLOTDATA_H_ + +#include + +namespace cocos2d { namespace extension { + +typedef struct { + const char* const name; + const BoneData* const boneData; + const char* const attachmentName; + float r, g, b, a; +} SlotData; + +SlotData* SlotData_create (const char* name, BoneData* boneData); +void SlotData_dispose (SlotData* self); + +/* @param attachmentName May be 0 for no bind pose attachment. */ +void SlotData_setAttachmentName (SlotData* self, const char* attachmentName); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_SLOTDATA_H_ */ diff --git a/extensions/spine/extension.cpp b/extensions/spine/extension.cpp new file mode 100644 index 0000000000..5cf307540c --- /dev/null +++ b/extensions/spine/extension.cpp @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +namespace cocos2d { namespace extension { + +static void* (*mallocFunc) (size_t size) = malloc; +static void (*freeFunc) (void* ptr) = free; + +void* _malloc (size_t size) { + return mallocFunc(size); +} +void* _calloc (size_t num, size_t size) { + void* ptr = mallocFunc(size); + if (ptr) memset(ptr, 0, size); + return ptr; +} +void _free (void* ptr) { + freeFunc(ptr); +} + +void _setMalloc (void* (*malloc) (size_t size)) { + mallocFunc = malloc; +} +void _setFree (void (*free) (void* ptr)) { + freeFunc = free; +} + +char* _readFile (const char* path, int* length) { + FILE *file = fopen(path, "rb"); + if (!file) return 0; + + fseek(file, 0, SEEK_END); + *length = ftell(file); + fseek(file, 0, SEEK_SET); + + char* data = MALLOC(char, *length); + fread(data, 1, *length, file); + fclose(file); + + return data; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/extension.h b/extensions/spine/extension.h new file mode 100644 index 0000000000..3847ed6eb3 --- /dev/null +++ b/extensions/spine/extension.h @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +/* + Implementation notes: + + - An OOP style is used where each "class" is made up of a struct and a number of functions prefixed with the struct name. + + - struct fields that are const are readonly. Either they are set in a create function and can never be changed, or they can only + be changed by calling a function. + + - Inheritance is done using a struct field named "super" as the first field, allowing the struct to be cast to its "super class". + This works because a pointer to a struct is guaranteed to be a pointer to the first struct field. + + - Classes intended for inheritance provide init/deinit functions which subclasses must call in their create/dispose functions. + + - Polymorphism is done by a base class providing function pointers in its init function. The public API delegates to this + function. + + - Subclasses do not provide a dispose function, instead the base class' dispose function should be used, which will delegate to + a dispose function. + + - Classes not designed for inheritance cannot be extended. They may use an internal subclass to hide private data and don't + expose function pointers. + + - The public API hides implementation details such init/deinit functions. An internal API is exposed in extension.h to allow + classes to be extended. Internal functions begin with underscore (_). + + - OOP in C tends to lose type safety. Macros are provided in extension.h to give context for why a cast is being done. + */ + +#ifndef SPINE_EXTENSION_H_ +#define SPINE_EXTENSION_H_ + +/* All allocation uses these. */ +#define MALLOC(TYPE,COUNT) ((TYPE*)_malloc(sizeof(TYPE) * COUNT)) +#define CALLOC(TYPE,COUNT) ((TYPE*)_calloc(1, sizeof(TYPE) * COUNT)) +#define NEW(TYPE) CALLOC(TYPE,1) + +/* Gets the direct super class. Type safe. */ +#define SUPER(VALUE) (&VALUE->super) + +/* Cast to a super class. Not type safe, use with care. Prefer SUPER() where possible. */ +#define SUPER_CAST(TYPE,VALUE) ((TYPE*)VALUE) + +/* Cast to a sub class. Not type safe, use with care. */ +#define SUB_CAST(TYPE,VALUE) ((TYPE*)VALUE) + +/* Casts away const. Can be used as an lvalue. Not type safe, use with care. */ +#define CONST_CAST(TYPE,VALUE) (*(TYPE*)&VALUE) + +/* Gets the vtable for the specified type. Not type safe, use with care. */ +#define VTABLE(TYPE,VALUE) ((_##TYPE##Vtable*)((TYPE*)VALUE)->vtable) + +/* Frees memory. Can be used on const types. */ +#define FREE(VALUE) _free((void*)VALUE) + +/* Allocates a new char[], assigns it to TO, and copies FROM to it. Can be used on const types. */ +#define MALLOC_STR(TO,FROM) strcpy(CONST_CAST(char*, TO) = (char*)malloc(strlen(FROM) + 1), FROM) + +#include +#include +#include +#include +#include +#include +#include + +namespace cocos2d { namespace extension { + +/* + * Functions that must be implemented: + */ + +void _AtlasPage_createTexture (AtlasPage* self, const char* path); +void _AtlasPage_disposeTexture (AtlasPage* self); +char* _Util_readFile (const char* path, int* length); + +/* + * Internal API available for extension: + */ + +void* _malloc (size_t size); +void* _calloc (size_t num, size_t size); +void _free (void* ptr); + +void _setMalloc (void* (*_malloc) (size_t size)); +void _setFree (void (*_free) (void* ptr)); + +char* _readFile (const char* path, int* length); + +/**/ + +void _AttachmentLoader_init (AttachmentLoader* self, // + void (*dispose) (AttachmentLoader* self), // + Attachment* (*newAttachment) (AttachmentLoader* self, Skin* skin, AttachmentType type, const char* name)); +void _AttachmentLoader_deinit (AttachmentLoader* self); +void _AttachmentLoader_setError (AttachmentLoader* self, const char* error1, const char* error2); +void _AttachmentLoader_setUnknownTypeError (AttachmentLoader* self, AttachmentType type); + +/**/ + +void _Attachment_init (Attachment* self, const char* name, AttachmentType type, // + void (*dispose) (Attachment* self)); +void _Attachment_deinit (Attachment* self); + +/**/ + +void _Timeline_init (Timeline* self, // + void (*dispose) (Timeline* self), // + void (*apply) (const Timeline* self, Skeleton* skeleton, float time, float alpha)); +void _Timeline_deinit (Timeline* self); + +/**/ + +void _CurveTimeline_init (CurveTimeline* self, int frameCount, // + void (*dispose) (Timeline* self), // + void (*apply) (const Timeline* self, Skeleton* skeleton, float time, float alpha)); +void _CurveTimeline_deinit (CurveTimeline* self); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_EXTENSION_H_ */ diff --git a/extensions/spine/spine-cocos2dx.cpp b/extensions/spine/spine-cocos2dx.cpp new file mode 100644 index 0000000000..d2bd54c245 --- /dev/null +++ b/extensions/spine/spine-cocos2dx.cpp @@ -0,0 +1,307 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +USING_NS_CC; +using std::min; +using std::max; +namespace cocos2d { namespace extension { + +void _AtlasPage_createTexture (AtlasPage* self, const char* path) { + CCTexture2D* texture = CCTextureCache::sharedTextureCache()->addImage(path); + CCTextureAtlas* textureAtlas = CCTextureAtlas::createWithTexture(texture, 4); + textureAtlas->retain(); + self->texture = textureAtlas; + + // Using getContentSize to make it supports the strategy of loading resources in cocos2d-x. + +// self->width = texture->getPixelsWide(); +// self->height = texture->getPixelsHigh(); + + self->width = texture->getContentSize().width; + self->height = texture->getContentSize().height; +} + +void _AtlasPage_disposeTexture (AtlasPage* self) { + ((CCTextureAtlas*)self->texture)->release(); +} + +char* _Util_readFile (const char* path, int* length) { + unsigned long size; + char* data = reinterpret_cast(CCFileUtils::sharedFileUtils()->getFileData(path, "r", &size)); + *length = size; + return data; +} + +/**/ + +void RegionAttachment_updateQuad (RegionAttachment* self, Slot* slot, ccV3F_C4B_T2F_Quad* quad) { + RegionAttachment_updateVertices(self, slot); + + GLubyte r = slot->skeleton->r * slot->r * 255; + GLubyte g = slot->skeleton->g * slot->g * 255; + GLubyte b = slot->skeleton->b * slot->b * 255; + GLubyte a = slot->skeleton->a * slot->a * 255; + quad->bl.colors.r = r; + quad->bl.colors.g = g; + quad->bl.colors.b = b; + quad->bl.colors.a = a; + quad->tl.colors.r = r; + quad->tl.colors.g = g; + quad->tl.colors.b = b; + quad->tl.colors.a = a; + quad->tr.colors.r = r; + quad->tr.colors.g = g; + quad->tr.colors.b = b; + quad->tr.colors.a = a; + quad->br.colors.r = r; + quad->br.colors.g = g; + quad->br.colors.b = b; + quad->br.colors.a = a; + + quad->bl.vertices.x = self->vertices[VERTEX_X1]; + quad->bl.vertices.y = self->vertices[VERTEX_Y1]; + quad->tl.vertices.x = self->vertices[VERTEX_X2]; + quad->tl.vertices.y = self->vertices[VERTEX_Y2]; + quad->tr.vertices.x = self->vertices[VERTEX_X3]; + quad->tr.vertices.y = self->vertices[VERTEX_Y3]; + quad->br.vertices.x = self->vertices[VERTEX_X4]; + quad->br.vertices.y = self->vertices[VERTEX_Y4]; + + if (self->region->rotate) { + quad->tl.texCoords.u = self->region->u; + quad->tl.texCoords.v = self->region->v2; + quad->tr.texCoords.u = self->region->u; + quad->tr.texCoords.v = self->region->v; + quad->br.texCoords.u = self->region->u2; + quad->br.texCoords.v = self->region->v; + quad->bl.texCoords.u = self->region->u2; + quad->bl.texCoords.v = self->region->v2; + } else { + quad->bl.texCoords.u = self->region->u; + quad->bl.texCoords.v = self->region->v2; + quad->tl.texCoords.u = self->region->u; + quad->tl.texCoords.v = self->region->v; + quad->tr.texCoords.u = self->region->u2; + quad->tr.texCoords.v = self->region->v; + quad->br.texCoords.u = self->region->u2; + quad->br.texCoords.v = self->region->v2; + } +} + +/**/ + +CCSkeleton* CCSkeleton::createWithFile (const char* skeletonDataFile, Atlas* atlas, float scale) { + SkeletonJson* json = SkeletonJson_create(atlas); + json->scale = scale; + SkeletonData* skeletonData = SkeletonJson_readSkeletonDataFile(json, skeletonDataFile); + SkeletonJson_dispose(json); + CCSkeleton* node = skeletonData ? createWithData(skeletonData) : 0; + node->ownsSkeleton = true; + return node; +} + +CCSkeleton* CCSkeleton::createWithFile (const char* skeletonDataFile, const char* atlasFile, float scale) { + Atlas* atlas = Atlas_readAtlasFile(atlasFile); + if (!atlas) return 0; + SkeletonJson* json = SkeletonJson_create(atlas); + json->scale = scale; + SkeletonData* skeletonData = SkeletonJson_readSkeletonDataFile(json, skeletonDataFile); + SkeletonJson_dispose(json); + if (!skeletonData) { + Atlas_dispose(atlas); + return 0; + } + CCSkeleton* node = createWithData(skeletonData); + node->ownsSkeleton = true; + node->atlas = atlas; + return node; +} + +CCSkeleton* CCSkeleton::createWithData (SkeletonData* skeletonData, AnimationStateData* stateData) { + CCSkeleton* node = new CCSkeleton(skeletonData, stateData); + node->autorelease(); + return node; +} + +CCSkeleton::CCSkeleton (SkeletonData *skeletonData, AnimationStateData *stateData) : + ownsSkeleton(false), ownsStateData(false), atlas(0), + skeleton(0), state(0), debugSlots(false), debugBones(false) { + CONST_CAST(Skeleton*, skeleton) = Skeleton_create(skeletonData); + + if (!stateData) { + stateData = AnimationStateData_create(skeletonData); + ownsStateData = true; + } + CONST_CAST(AnimationState*, state) = AnimationState_create(stateData); + + blendFunc.src = GL_ONE; + blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA; + + timeScale = 1; + + setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor)); + scheduleUpdate(); +} + +CCSkeleton::~CCSkeleton () { + if (ownsSkeleton) Skeleton_dispose(skeleton); + if (ownsStateData) AnimationStateData_dispose(state->data); + if (atlas) Atlas_dispose(atlas); + AnimationState_dispose(state); +} + +void CCSkeleton::update (float deltaTime) { + Skeleton_update(skeleton, deltaTime); + AnimationState_update(state, deltaTime * timeScale); + AnimationState_apply(state, skeleton); + Skeleton_updateWorldTransform(skeleton); +} + +void CCSkeleton::draw () { + CC_NODE_DRAW_SETUP(); + + ccGLBlendFunc(blendFunc.src, blendFunc.dst); + ccColor3B color = getColor(); + skeleton->r = color.r / (float)255; + skeleton->g = color.g / (float)255; + skeleton->b = color.b / (float)255; + skeleton->a = getOpacity() / (float)255; + + CCTextureAtlas* textureAtlas = 0; + ccV3F_C4B_T2F_Quad quad; + quad.tl.vertices.z = 0; + quad.tr.vertices.z = 0; + quad.bl.vertices.z = 0; + quad.br.vertices.z = 0; + for (int i = 0, n = skeleton->slotCount; i < n; i++) { + Slot* slot = skeleton->slots[i]; + if (!slot->attachment || slot->attachment->type != ATTACHMENT_REGION) continue; + RegionAttachment* attachment = (RegionAttachment*)slot->attachment; + CCTextureAtlas* regionTextureAtlas = (CCTextureAtlas*)attachment->region->page->texture; + if (regionTextureAtlas != textureAtlas) { + if (textureAtlas) { + textureAtlas->drawQuads(); + textureAtlas->removeAllQuads(); + } + } + textureAtlas = regionTextureAtlas; + if (textureAtlas->getCapacity() == textureAtlas->getTotalQuads() && + !textureAtlas->resizeCapacity(textureAtlas->getCapacity() * 2)) return; + RegionAttachment_updateQuad(attachment, slot, &quad); + textureAtlas->updateQuad(&quad, textureAtlas->getTotalQuads()); + } + if (textureAtlas) { + textureAtlas->drawQuads(); + textureAtlas->removeAllQuads(); + } + + if (debugSlots) { + // Slots. + ccDrawColor4B(0, 0, 255, 255); + glLineWidth(1); + CCPoint points[4]; + ccV3F_C4B_T2F_Quad quad; + for (int i = 0, n = skeleton->slotCount; i < n; i++) { + Slot* slot = skeleton->slots[i]; + if (!slot->attachment || slot->attachment->type != ATTACHMENT_REGION) continue; + RegionAttachment* attachment = (RegionAttachment*)slot->attachment; + RegionAttachment_updateQuad(attachment, slot, &quad); + points[0] = ccp(quad.bl.vertices.x, quad.bl.vertices.y); + points[1] = ccp(quad.br.vertices.x, quad.br.vertices.y); + points[2] = ccp(quad.tr.vertices.x, quad.tr.vertices.y); + points[3] = ccp(quad.tl.vertices.x, quad.tl.vertices.y); + ccDrawPoly(points, 4, true); + } + } + if (debugBones) { + // Bone lengths. + glLineWidth(2); + ccDrawColor4B(255, 0, 0, 255); + for (int i = 0, n = skeleton->boneCount; i < n; i++) { + Bone *bone = skeleton->bones[i]; + float x = bone->data->length * bone->m00 + bone->worldX; + float y = bone->data->length * bone->m10 + bone->worldY; + ccDrawLine(ccp(bone->worldX, bone->worldY), ccp(x, y)); + } + // Bone origins. + ccPointSize(4); + ccDrawColor4B(0, 0, 255, 255); // Root bone is blue. + for (int i = 0, n = skeleton->boneCount; i < n; i++) { + Bone *bone = skeleton->bones[i]; + ccDrawPoint(ccp(bone->worldX, bone->worldY)); + if (i == 0) ccDrawColor4B(0, 255, 0, 255); + } + } +} + +CCRect CCSkeleton::boundingBox () { + float minX = FLT_MAX, minY = FLT_MAX, maxX = FLT_MIN, maxY = FLT_MIN; + float scaleX = getScaleX(); + float scaleY = getScaleY(); + ccV3F_C4B_T2F_Quad quad; + for (int i = 0; i < skeleton->slotCount; ++i) { + Slot* slot = skeleton->slots[i]; + if (!slot->attachment || slot->attachment->type != ATTACHMENT_REGION) continue; + RegionAttachment* attachment = (RegionAttachment*)slot->attachment; + RegionAttachment_updateQuad(attachment, slot, &quad); + minX = min(minX, quad.bl.vertices.x * scaleX); + minY = min(minY, quad.bl.vertices.y * scaleY); + maxX = max(maxX, quad.bl.vertices.x * scaleX); + maxY = max(maxY, quad.bl.vertices.y * scaleY); + minX = min(minX, quad.br.vertices.x * scaleX); + minY = min(minY, quad.br.vertices.y * scaleY); + maxX = max(maxX, quad.br.vertices.x * scaleX); + maxY = max(maxY, quad.br.vertices.y * scaleY); + minX = min(minX, quad.tl.vertices.x * scaleX); + minY = min(minY, quad.tl.vertices.y * scaleY); + maxX = max(maxX, quad.tl.vertices.x * scaleX); + maxY = max(maxY, quad.tl.vertices.y * scaleY); + minX = min(minX, quad.tr.vertices.x * scaleX); + minY = min(minY, quad.tr.vertices.y * scaleY); + maxX = max(maxX, quad.tr.vertices.x * scaleX); + maxY = max(maxY, quad.tr.vertices.y * scaleY); + } + CCPoint position = getPosition(); + minX = position.x + minX; + minY = position.y + minY; + maxX = position.x + maxX; + maxY = position.y + maxY; + return CCRectMake(minX, minY, maxX - minX, maxY - minY); +} + +// CCBlendProtocol + +ccBlendFunc CCSkeleton::getBlendFunc () { + return blendFunc; +} + +void CCSkeleton::setBlendFunc (ccBlendFunc blendFunc) { + this->blendFunc = blendFunc; +} + +}} // namespace cocos2d { namespace extension { diff --git a/extensions/spine/spine-cocos2dx.h b/extensions/spine/spine-cocos2dx.h new file mode 100644 index 0000000000..0569d3dc4f --- /dev/null +++ b/extensions/spine/spine-cocos2dx.h @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_COCOS2DX_H_ +#define SPINE_COCOS2DX_H_ + +#include +#include "cocos2d.h" + +namespace cocos2d { namespace extension { + +class CCSkeleton: public cocos2d::CCNodeRGBA, public cocos2d::CCBlendProtocol { +private: + bool ownsSkeleton; + bool ownsStateData; + Atlas* atlas; + +public: + Skeleton* const skeleton; + AnimationState* const state; + float timeScale; + bool debugSlots; + bool debugBones; + + static CCSkeleton* createWithFile (const char* skeletonDataFile, Atlas* atlas, float scale = 1); + static CCSkeleton* createWithFile (const char* skeletonDataFile, const char* atlasFile, float scale = 1); + static CCSkeleton* createWithData (SkeletonData* skeletonData, AnimationStateData* stateData = 0); + + CCSkeleton (SkeletonData* skeletonData, AnimationStateData* stateData = 0); + virtual ~CCSkeleton (); + + virtual void update (float deltaTime); + virtual void draw (); + virtual cocos2d::CCRect boundingBox (); + + // CCBlendProtocol + CC_PROPERTY(cocos2d::ccBlendFunc, blendFunc, BlendFunc); +}; + +/**/ + +void RegionAttachment_updateQuad (RegionAttachment* self, Slot* slot, cocos2d::ccV3F_C4B_T2F_Quad* quad); + +}} // namespace cocos2d { namespace extension { + +#endif /* SPINE_COCOS2DX_H_ */ diff --git a/extensions/spine/spine.h b/extensions/spine/spine.h new file mode 100644 index 0000000000..d312d910ea --- /dev/null +++ b/extensions/spine/spine.h @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef SPINE_SPINE_H_ +#define SPINE_SPINE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* SPINE_SPINE_H_ */ diff --git a/samples/Cpp/TestCpp/Classes/SpineTest/SpineTest.cpp b/samples/Cpp/TestCpp/Classes/SpineTest/SpineTest.cpp new file mode 100644 index 0000000000..087211e3bf --- /dev/null +++ b/samples/Cpp/TestCpp/Classes/SpineTest/SpineTest.cpp @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "SpineTest.h" +#include +#include +#include + +using namespace cocos2d; +using namespace cocos2d::extension; +using namespace std; + +//------------------------------------------------------------------ +// +// SpineTestScene +// +//------------------------------------------------------------------ +void SpineTestScene::runThisTest() +{ + CCLayer* pLayer = SpineTestLayer::create(); + addChild(pLayer); + + CCDirector::sharedDirector()->replaceScene(this); +} + +bool SpineTestLayer::init () { + if (!CCLayer::init()) return false; + + skeletonNode = CCSkeleton::createWithFile("spine/spineboy.json", "spine/spineboy.atlas"); + AnimationStateData_setMixByName(skeletonNode->state->data, "walk", "jump", 0.4f); + AnimationStateData_setMixByName(skeletonNode->state->data, "jump", "walk", 0.4f); + AnimationState_setAnimationByName(skeletonNode->state, "walk", true); + skeletonNode->timeScale = 0.3f; + skeletonNode->debugBones = true; + + skeletonNode->runAction(CCRepeatForever::create(CCSequence::create(CCFadeOut::create(1), + CCFadeIn::create(1), + CCDelayTime::create(5), + NULL))); + + CCSize windowSize = CCDirector::sharedDirector()->getWinSize(); + skeletonNode->setPosition(ccp(windowSize.width / 2, 20)); + addChild(skeletonNode); + + scheduleUpdate(); + + return true; +} + +void SpineTestLayer::update (float deltaTime) { + if (skeletonNode->state->loop) { + if (skeletonNode->state->time > 2) AnimationState_setAnimationByName(skeletonNode->state, "jump", false); + } else { + if (skeletonNode->state->time > 1) AnimationState_setAnimationByName(skeletonNode->state, "walk", true); + } +} diff --git a/samples/Cpp/TestCpp/Classes/SpineTest/SpineTest.h b/samples/Cpp/TestCpp/Classes/SpineTest/SpineTest.h new file mode 100644 index 0000000000..10eadbc3b5 --- /dev/null +++ b/samples/Cpp/TestCpp/Classes/SpineTest/SpineTest.h @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef _SPINETEST_H_ +#define _SPINETEST_H_ + +#include "cocos2d.h" +#include "../testBasic.h" +#include + +class SpineTestScene : public TestScene +{ +public: + virtual void runThisTest(); +}; + +class SpineTestLayer: public cocos2d::CCLayer { +private: + cocos2d::extension::CCSkeleton* skeletonNode; + +public: + + virtual bool init (); + virtual void update (float deltaTime); + + CREATE_FUNC (SpineTestLayer); +}; + +#endif // _EXAMPLELAYER_H_ diff --git a/samples/Cpp/TestCpp/Classes/controller.cpp b/samples/Cpp/TestCpp/Classes/controller.cpp index 2b5a03a3e4..8fc54395a9 100644 --- a/samples/Cpp/TestCpp/Classes/controller.cpp +++ b/samples/Cpp/TestCpp/Classes/controller.cpp @@ -119,6 +119,9 @@ static TestScene* CreateTestScene(int nIdx) case TEST_FILEUTILS: pScene = new FileUtilsTestScene(); break; + case TEST_SPINE: + pScene = new SpineTestScene(); + break; default: break; } diff --git a/samples/Cpp/TestCpp/Classes/tests.h b/samples/Cpp/TestCpp/Classes/tests.h index 1148abd605..3fb7e97fda 100644 --- a/samples/Cpp/TestCpp/Classes/tests.h +++ b/samples/Cpp/TestCpp/Classes/tests.h @@ -53,6 +53,7 @@ #include "ChipmunkTest/ChipmunkTest.h" #endif #include "FileUtilsTest/FileUtilsTest.h" +#include "SpineTest/SpineTest.h" enum { @@ -109,6 +110,7 @@ enum TEST_CLIPPINGNODE, #endif TEST_FILEUTILS, + TEST_SPINE, TESTS_COUNT, }; @@ -165,7 +167,8 @@ const std::string g_aTestNames[TESTS_COUNT] = { #if (CC_TARGET_PLATFORM != CC_PLATFORM_MARMALADE) "ClippingNodeTest", #endif - "FileUtilsTest" + "FileUtilsTest", + "SpineTest" }; #endif diff --git a/samples/Cpp/TestCpp/Resources/hd/spine/spineboy.png.REMOVED.git-id b/samples/Cpp/TestCpp/Resources/hd/spine/spineboy.png.REMOVED.git-id new file mode 100644 index 0000000000..6d319f3003 --- /dev/null +++ b/samples/Cpp/TestCpp/Resources/hd/spine/spineboy.png.REMOVED.git-id @@ -0,0 +1 @@ +c22eca8f608170bab4ceb0086e48999fb2e3d32b \ No newline at end of file diff --git a/samples/Cpp/TestCpp/proj.ios/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id b/samples/Cpp/TestCpp/proj.ios/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id index a7db838120..d1b19d6df1 100644 --- a/samples/Cpp/TestCpp/proj.ios/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id +++ b/samples/Cpp/TestCpp/proj.ios/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id @@ -1 +1 @@ -7bd1abbd3c1270c95196be7965f911815e7ba4c4 \ No newline at end of file +655a6ce855dabb133120b19537ed3879b2fc8194 \ No newline at end of file