From 782bde21a0218cc13c51d4a91c7eccfbab617921 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Thu, 26 Sep 2013 09:37:11 +0200 Subject: [PATCH] Event timeline for spine-c, AnimationState refactoring. closes #113 --- spine-c/include/spine/Animation.h | 25 +- spine-c/include/spine/AnimationState.h | 58 ++-- spine-c/include/spine/Event.h | 58 ++++ spine-c/include/spine/EventData.h | 56 ++++ spine-c/include/spine/SkeletonData.h | 6 + spine-c/include/spine/extension.h | 15 +- spine-c/spine-c.vcxproj | 4 + spine-c/spine-c.vcxproj.filters | 12 + spine-c/src/spine/Animation.c | 136 +++++++-- spine-c/src/spine/AnimationState.c | 324 ++++++++++++++-------- spine-c/src/spine/Bone.c | 1 - spine-c/src/spine/BoundingBoxAttachment.c | 1 - spine-c/src/spine/Event.c | 46 +++ spine-c/src/spine/EventData.c | 47 ++++ spine-c/src/spine/Json.c | 1 - spine-c/src/spine/RegionAttachment.c | 1 - spine-c/src/spine/SkeletonBounds.c | 31 +-- spine-c/src/spine/SkeletonData.c | 7 + spine-c/src/spine/SkeletonJson.c | 80 ++++-- spine-c/src/spine/Skin.c | 16 +- spine-c/src/spine/Slot.c | 10 +- spine-c/src/spine/extension.c | 4 +- spine-sfml/example/main.cpp | 33 ++- 23 files changed, 738 insertions(+), 234 deletions(-) create mode 100644 spine-c/include/spine/Event.h create mode 100644 spine-c/include/spine/EventData.h create mode 100644 spine-c/src/spine/Event.c create mode 100644 spine-c/src/spine/EventData.c diff --git a/spine-c/include/spine/Animation.h b/spine-c/include/spine/Animation.h index 1f2a5e510..6093dc092 100644 --- a/spine-c/include/spine/Animation.h +++ b/spine-c/include/spine/Animation.h @@ -34,6 +34,8 @@ #ifndef SPINE_ANIMATION_H_ #define SPINE_ANIMATION_H_ +#include + #ifdef __cplusplus extern "C" { #endif @@ -52,8 +54,10 @@ typedef struct { 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); +void Animation_apply (const Animation* self, struct Skeleton* skeleton, float lastTime, float time, int loop, + Event**/*out*/events, int*/*out*/eventCount); +void Animation_mix (const Animation* self, struct Skeleton* skeleton, float lastTime, float time, int loop, Event**/*out*/events, + int*/*out*/eventCount, float alpha); /**/ @@ -62,7 +66,8 @@ struct Timeline { }; void Timeline_dispose (Timeline* self); -void Timeline_apply (const Timeline* self, struct Skeleton* skeleton, float time, float alpha); +void Timeline_apply (const Timeline* self, struct Skeleton* skeleton, float lastTime, float time, Event** firedEvents, + int* eventCount, float alpha); /**/ @@ -143,7 +148,19 @@ typedef struct { Timeline super; int const framesLength; float* const frames; /* time, ... */ - int slotIndex; + Event** const events; +} EventTimeline; + +EventTimeline* EventTimeline_create (int frameCount); + +void EventTimeline_setFrame (EventTimeline* self, int frameIndex, float time, Event* event); + +/**/ + +typedef struct { + Timeline super; + int const framesLength; + float* const frames; /* time, ... */ const int** const drawOrders; int const slotCount; } DrawOrderTimeline; diff --git a/spine-c/include/spine/AnimationState.h b/spine-c/include/spine/AnimationState.h index b11fc59e2..8d5b33a3e 100644 --- a/spine-c/include/spine/AnimationState.h +++ b/spine-c/include/spine/AnimationState.h @@ -35,41 +35,63 @@ #define SPINE_ANIMATIONSTATE_H_ #include +#include #ifdef __cplusplus extern "C" { #endif -typedef struct { - AnimationStateData* const data; - Animation* const animation; - float time; +typedef enum { + ANIMATION_START, ANIMATION_END, ANIMATION_COMPLETE, ANIMATION_EVENT +} EventType; + +typedef struct AnimationState AnimationState; + +typedef void (*AnimationStateListener) (AnimationState* state, int trackIndex, EventType type, Event* event, int loopCount); + +typedef struct TrackEntry TrackEntry; +struct TrackEntry { + TrackEntry* next; + TrackEntry* previous; + Animation* animation; int/*bool*/loop; -} AnimationState; + float delay, time, lastTime, endTime; + AnimationStateListener listener; + float mixTime, mixDuration; +}; + +struct AnimationState { + AnimationStateData* const data; + + int trackCount; + TrackEntry** tracks; + + AnimationStateListener listener; +}; /* @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/*bool*/loop); -/* @param animation May be 0. */ -void AnimationState_setAnimation (AnimationState* self, Animation* animation, int/*bool*/loop); +void AnimationState_clear (AnimationState* self); +void AnimationState_clearTrack (AnimationState* self, int trackIndex); -/** @param animationName May be 0. - * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ -void AnimationState_addAnimationByName (AnimationState* self, const char* animationName, int/*bool*/loop, float delay); -/** @param animation May be 0. - * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ -void AnimationState_addAnimation (AnimationState* self, Animation* animation, int/*bool*/loop, float delay); +/** Set the current animation. Any queued animations are cleared. */ +TrackEntry* AnimationState_setAnimationByName (AnimationState* self, int trackIndex, const char* animationName, int/*bool*/loop); +TrackEntry* AnimationState_setAnimation (AnimationState* self, int trackIndex, Animation* animation, int/*bool*/loop); -void AnimationState_clearAnimation (AnimationState* self); +/** Adds an animation to be played delay seconds after the current or last queued animation, taking into account any mix + * duration. + * @param animation May be 0 to queue clearing the AnimationState. */ +TrackEntry* AnimationState_addAnimationByName (AnimationState* self, int trackIndex, const char* animationName, int/*bool*/loop, + float delay); +TrackEntry* AnimationState_addAnimation (AnimationState* self, int trackIndex, Animation* animation, int/*bool*/loop, + float delay); -int/*bool*/AnimationState_isComplete (AnimationState* self); +TrackEntry* AnimationState_getCurrent (AnimationState* self, int trackIndex); #ifdef __cplusplus } diff --git a/spine-c/include/spine/Event.h b/spine-c/include/spine/Event.h new file mode 100644 index 000000000..b6b5bcc2c --- /dev/null +++ b/spine-c/include/spine/Event.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.0 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Single User License or Spine Professional License must be + * purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration 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_EVENT_H_ +#define SPINE_EVENT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Event Event; +struct Event { + EventData* const data; + int intValue; + float floatValue; + const char* stringValue; +}; + +Event* Event_create (EventData* data); +void Event_dispose (Event* self); + +#ifdef __cplusplus +} +#endif + +#endif /* SPINE_EVENT_H_ */ diff --git a/spine-c/include/spine/EventData.h b/spine-c/include/spine/EventData.h new file mode 100644 index 000000000..da8583006 --- /dev/null +++ b/spine-c/include/spine/EventData.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.0 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Single User License or Spine Professional License must be + * purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration 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_EVENTDATA_H_ +#define SPINE_EVENTDATA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct EventData EventData; +struct EventData { + const char* const name; + int intValue; + float floatValue; + const char* stringValue; +}; + +EventData* EventData_create (const char* name); +void EventData_dispose (EventData* self); + +#ifdef __cplusplus +} +#endif + +#endif /* SPINE_EVENTDATA_H_ */ diff --git a/spine-c/include/spine/SkeletonData.h b/spine-c/include/spine/SkeletonData.h index fa2261cb2..8b8747a3d 100644 --- a/spine-c/include/spine/SkeletonData.h +++ b/spine-c/include/spine/SkeletonData.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #ifdef __cplusplus @@ -54,6 +55,9 @@ typedef struct { Skin** skins; Skin* defaultSkin; + int eventCount; + EventData** events; + int animationCount; Animation** animations; } SkeletonData; @@ -69,6 +73,8 @@ int SkeletonData_findSlotIndex (const SkeletonData* self, const char* slotName); Skin* SkeletonData_findSkin (const SkeletonData* self, const char* skinName); +EventData* SkeletonData_findEvent (const SkeletonData* self, const char* eventName); + Animation* SkeletonData_findAnimation (const SkeletonData* self, const char* animationName); #ifdef __cplusplus diff --git a/spine-c/include/spine/extension.h b/spine-c/include/spine/extension.h index 73a21aa31..3f2373cef 100644 --- a/spine-c/include/spine/extension.h +++ b/spine-c/include/spine/extension.h @@ -64,7 +64,7 @@ /* All allocation uses these. */ #define MALLOC(TYPE,COUNT) ((TYPE*)_malloc(sizeof(TYPE) * COUNT)) -#define CALLOC(TYPE,COUNT) ((TYPE*)_calloc(1, sizeof(TYPE) * COUNT)) +#define CALLOC(TYPE,COUNT) ((TYPE*)_calloc(COUNT, sizeof(TYPE))) #define NEW(TYPE) CALLOC(TYPE,1) /* Gets the direct super class. Type safe. */ @@ -88,8 +88,15 @@ /* 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) +#ifdef __STDC_VERSION__ + #define FMOD(A,B) fmodf(A, B) +#else + #define FMOD(A,B) (float)fmod(A, B) +#endif + #include #include +#include #include #include #include @@ -141,14 +148,16 @@ 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 (*apply) (const Timeline* self, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, int* eventCount, + 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 (*apply) (const Timeline* self, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, int* eventCount, + float alpha)); void _CurveTimeline_deinit (CurveTimeline* self); #ifdef __cplusplus diff --git a/spine-c/spine-c.vcxproj b/spine-c/spine-c.vcxproj index a65aa687d..b3f4c1bae 100644 --- a/spine-c/spine-c.vcxproj +++ b/spine-c/spine-c.vcxproj @@ -81,6 +81,8 @@ + + @@ -104,6 +106,8 @@ + + diff --git a/spine-c/spine-c.vcxproj.filters b/spine-c/spine-c.vcxproj.filters index 1ee8b0a6f..b8134c757 100644 --- a/spine-c/spine-c.vcxproj.filters +++ b/spine-c/spine-c.vcxproj.filters @@ -78,6 +78,12 @@ Header Files + + Header Files + + + Header Files + @@ -140,5 +146,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/spine-c/src/spine/Animation.c b/spine-c/src/spine/Animation.c index da304f5e7..b6ccd207b 100644 --- a/spine-c/src/spine/Animation.c +++ b/spine-c/src/spine/Animation.c @@ -32,7 +32,7 @@ *****************************************************************************/ #include -#include +#include #include Animation* Animation_create (const char* name, int timelineCount) { @@ -52,42 +52,44 @@ void Animation_dispose (Animation* self) { FREE(self); } -void Animation_apply (const Animation* self, Skeleton* skeleton, float time, int/*bool*/loop) { +void Animation_apply (const Animation* self, Skeleton* skeleton, float lastTime, float time, int loop, Event** events, + int* eventCount) { int i, n = self->timelineCount; -#ifdef __STDC_VERSION__ - if (loop && self->duration) time = fmodf(time, self->duration); -#else - if (loop && self->duration) time = (float)fmod(time, self->duration); -#endif + if (loop && self->duration) { + time = FMOD(time, self->duration); + lastTime = FMOD(lastTime, self->duration); + } for (i = 0; i < n; ++i) - Timeline_apply(self->timelines[i], skeleton, time, 1); + Timeline_apply(self->timelines[i], skeleton, lastTime, time, events, eventCount, 1); } -void Animation_mix (const Animation* self, Skeleton* skeleton, float time, int/*bool*/loop, float alpha) { +void Animation_mix (const Animation* self, Skeleton* skeleton, float lastTime, float time, int loop, Event** events, + int* eventCount, float alpha) { int i, n = self->timelineCount; -#ifdef __STDC_VERSION__ - if (loop && self->duration) time = fmodf(time, self->duration); -#else - if (loop && self->duration) time = (float)fmod(time, self->duration); -#endif + if (loop && self->duration) { + time = FMOD(time, self->duration); + lastTime = FMOD(lastTime, self->duration); + } for (i = 0; i < n; ++i) - Timeline_apply(self->timelines[i], skeleton, time, alpha); + Timeline_apply(self->timelines[i], skeleton, lastTime, time, events, eventCount, alpha); } /**/ typedef struct _TimelineVtable { - void (*apply) (const Timeline* self, Skeleton* skeleton, float time, float alpha); + void (*apply) (const Timeline* self, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, int* eventCount, + 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)) { + void (*apply) (const Timeline* self, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, int* eventCount, + float alpha)) { CONST_CAST(_TimelineVtable*, self->vtable) = NEW(_TimelineVtable); VTABLE(Timeline, self)->dispose = dispose; VTABLE(Timeline, self)->apply = apply; @@ -101,8 +103,9 @@ 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); +void Timeline_apply (const Timeline* self, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, int* eventCount, + float alpha) { + VTABLE(Timeline, self)->apply(self, skeleton, lastTime, time, firedEvents, eventCount, alpha); } /**/ @@ -113,7 +116,8 @@ 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)) { + void (*apply) (const Timeline* self, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, int* eventCount, + float alpha)) { _Timeline_init(SUPER(self), dispose, apply); self->curves = CALLOC(float, (frameCount - 1) * 6); } @@ -226,8 +230,8 @@ void _BaseTimeline_dispose (Timeline* timeline) { /* 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)) { - + void (*apply) (const Timeline* self, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, int* eventCount, + float alpha)) { struct BaseTimeline* self = NEW(struct BaseTimeline); _CurveTimeline_init(SUPER(self), frameCount, _BaseTimeline_dispose, apply); @@ -242,7 +246,8 @@ void (*apply) (const Timeline* self, Skeleton* skeleton, float time, float alpha 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) { +void _RotateTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, + int* eventCount, float alpha) { Bone *bone; int frameIndex; float lastFrameValue, frameTime, percent, amount; @@ -299,7 +304,8 @@ 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) { +void _TranslateTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, + int* eventCount, float alpha) { Bone *bone; int frameIndex; float lastFrameX, lastFrameY, frameTime, percent; @@ -343,7 +349,8 @@ void TranslateTimeline_setFrame (TranslateTimeline* self, int frameIndex, float /**/ -void _ScaleTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float time, float alpha) { +void _ScaleTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, + int* eventCount, float alpha) { Bone *bone; int frameIndex; float lastFrameX, lastFrameY, frameTime, percent; @@ -389,7 +396,8 @@ 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) { +void _ColorTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, + int* eventCount, float alpha) { Slot *slot; int frameIndex; float lastFrameR, lastFrameG, lastFrameB, lastFrameA, percent, frameTime; @@ -451,7 +459,8 @@ void ColorTimeline_setFrame (ColorTimeline* self, int frameIndex, float time, fl /**/ -void _AttachmentTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float time, float alpha) { +void _AttachmentTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, + int* eventCount, float alpha) { int frameIndex; const char* attachmentName; AttachmentTimeline* self = (AttachmentTimeline*)timeline; @@ -485,9 +494,9 @@ 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); + CONST_CAST(char**, self->attachmentNames) = CALLOC(char*, frameCount); return self; } @@ -504,7 +513,74 @@ void AttachmentTimeline_setFrame (AttachmentTimeline* self, int frameIndex, floa /**/ -void _DrawOrderTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float time, float alpha) { +void _EventTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, + int* eventCount, float alpha) { + int frameIndex; + EventTimeline* self = (EventTimeline*)timeline; + + if (time < self->frames[0]) return; /* Time is before first frame. */ + + if (lastTime >= self->frames[self->framesLength - 1]) return; /* Last time is after last frame. */ + + if (lastTime > time) { + /* Fire events after last time for looped animations. */ + _EventTimeline_apply(timeline, skeleton, lastTime, INT_MAX, firedEvents, eventCount, alpha); + lastTime = 0; + } + + if (self->framesLength == 1) + frameIndex = 0; + else { + frameIndex = binarySearch(self->frames, self->framesLength, lastTime, 1); + float frame = self->frames[frameIndex]; + while (frameIndex > 0) { + float lastFrame = self->frames[frameIndex - 1]; + /* Fire multiple events with the same frame and events that occurred at lastTime. */ + if (lastFrame != frame && lastFrame != lastTime) break; + frameIndex--; + } + } + for (; frameIndex < self->framesLength && time > self->frames[frameIndex]; frameIndex++) { + firedEvents[*eventCount] = self->events[frameIndex]; + (*eventCount)++; + } +} + +void _EventTimeline_dispose (Timeline* timeline) { + EventTimeline* self = SUB_CAST(EventTimeline, timeline); + int i; + + _Timeline_deinit(timeline); + + for (i = 0; i < self->framesLength; ++i) + FREE(self->events[i]); + FREE(self->events); + FREE(self->frames); + FREE(self); +} + +EventTimeline* EventTimeline_create (int frameCount) { + EventTimeline* self = NEW(EventTimeline); + _Timeline_init(SUPER(self), _EventTimeline_dispose, _EventTimeline_apply); + + CONST_CAST(int, self->framesLength) = frameCount; + CONST_CAST(float*, self->frames) = CALLOC(float, frameCount); + CONST_CAST(Event**, self->events) = CALLOC(Event*, frameCount); + + return self; +} + +void EventTimeline_setFrame (EventTimeline* self, int frameIndex, float time, Event* event) { + self->frames[frameIndex] = time; + + FREE(self->events[frameIndex]); + self->events[frameIndex] = event; +} + +/**/ + +void _DrawOrderTimeline_apply (const Timeline* timeline, Skeleton* skeleton, float lastTime, float time, Event** firedEvents, + int* eventCount, float alpha) { int i; int frameIndex; const int* drawOrderToSetupIndex; @@ -543,9 +619,9 @@ DrawOrderTimeline* DrawOrderTimeline_create (int frameCount, int slotCount) { DrawOrderTimeline* self = NEW(DrawOrderTimeline); _Timeline_init(SUPER(self), _DrawOrderTimeline_dispose, _DrawOrderTimeline_apply); - CONST_CAST(int**, self->drawOrders) = CALLOC(int*, frameCount); CONST_CAST(int, self->framesLength) = frameCount; CONST_CAST(float*, self->frames) = CALLOC(float, frameCount); + CONST_CAST(int**, self->drawOrders) = CALLOC(int*, frameCount); CONST_CAST(int, self->slotCount) = slotCount; return self; diff --git a/spine-c/src/spine/AnimationState.c b/spine-c/src/spine/AnimationState.c index 6690def5f..b4313917d 100644 --- a/spine-c/src/spine/AnimationState.c +++ b/spine-c/src/spine/AnimationState.c @@ -31,153 +31,233 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +#include #include +#include +#include #include +#include +#include +#include +#include -typedef struct _Entry _Entry; -struct _Entry { - Animation* animation; - int/*bool*/loop; - float delay; - _Entry* next; -}; +TrackEntry* _TrackEntry_create () { + TrackEntry* entry = NEW(TrackEntry); + return entry; +} + +void _TrackEntry_dispose (TrackEntry* entry) { + FREE(entry); +} + +void _TrackEntry_disposeAll (TrackEntry* entry) { + while (entry) { + TrackEntry* next = entry->next; + _TrackEntry_dispose(entry); + entry = next; + } +} + +/**/ typedef struct { AnimationState super; - Animation *previous; - float previousTime; - int/*bool*/previousLoop; - float mixTime; - float mixDuration; - _Entry* queue; -} _Internal; + Event** events; +} _AnimationState; + +void _AnimationState_setCurrent (AnimationState* self, int index, TrackEntry* entry); AnimationState* AnimationState_create (AnimationStateData* data) { - AnimationState* self = SUPER(NEW(_Internal)); + _AnimationState* internal = NEW(_AnimationState); + internal->events = MALLOC(Event*, 64); + + AnimationState* self = SUPER(internal); CONST_CAST(AnimationStateData*, self->data) = data; return self; } -void _AnimationState_clearQueue (AnimationState* self) { - _Internal* internal = SUB_CAST(_Internal, self); - _Entry* entry = internal->queue; - while (entry) { - _Entry* nextEntry = entry->next; - FREE(entry); - entry = nextEntry; - } - internal->queue = 0; -} - void AnimationState_dispose (AnimationState* self) { - _AnimationState_clearQueue(self); + int i; + for (i = 0; i < self->trackCount; i++) + _TrackEntry_disposeAll(self->tracks[i]); FREE(self); } -#include +void AnimationState_update (AnimationState* self, float delta) { + int i; + for (i = 0; i < self->trackCount; i++) { + TrackEntry* current = self->tracks[i]; + if (!current) continue; -void AnimationState_addAnimation (AnimationState* self, Animation* animation, int/*bool*/loop, float delay) { - _Entry* existingEntry; - Animation* previousAnimation; + float time = current->time + delta; + float endTime = current->endTime; - _Internal* internal = SUB_CAST(_Internal, self); - _Entry* entry = NEW(_Entry); - entry->animation = animation; - entry->loop = loop; + current->time = time; + if (current->previous) { + current->previous->time += delta; + current->mixTime += delta; + } - existingEntry = internal->queue; - if (existingEntry) { - while (existingEntry->next) - existingEntry = existingEntry->next; - existingEntry->next = entry; - previousAnimation = existingEntry->animation; - } else { - internal->queue = entry; - previousAnimation = self->animation; + /* Check if completed the animation or a loop iteration. */ + if (current->loop ? + (FMOD(current->lastTime, endTime) > FMOD(time, endTime)) : (current->lastTime < endTime && time >= endTime)) { + int count = (int)(time / endTime); + if (current->listener) current->listener(self, i, ANIMATION_COMPLETE, 0, count); + if (self->listener) self->listener(self, i, ANIMATION_COMPLETE, 0, count); + } + + TrackEntry* next = current->next; + if (next && time >= next->delay) { + if (next->animation) + _AnimationState_setCurrent(self, i, next); + else + AnimationState_clearTrack(self, i); + } + } +} +void AnimationState_apply (AnimationState* self, Skeleton* skeleton) { + _AnimationState* internal = SUB_CAST(_AnimationState, self); + + int i, ii; + int eventCount; + for (i = 0; i < self->trackCount; i++) { + TrackEntry* current = self->tracks[i]; + if (!current) continue; + + eventCount = 0; + + TrackEntry* previous = current->previous; + if (!previous) { + Animation_apply(current->animation, skeleton, current->lastTime, current->time, current->loop, internal->events, + &eventCount); + } else { + Animation_apply(previous->animation, skeleton, INT_MAX, previous->time, previous->loop, internal->events, &eventCount); + float alpha = current->mixTime / current->mixDuration; + if (alpha >= 1) { + alpha = 1; + _TrackEntry_dispose(current->previous); + current->previous = 0; + } + Animation_apply(current->animation, skeleton, current->lastTime, current->time, current->loop, internal->events, + &eventCount); + } + + for (ii = 0; ii < eventCount; ii++) { + Event* event = internal->events[ii]; + if (current->listener) current->listener(self, i, ANIMATION_EVENT, event, 0); + if (self->listener) self->listener(self, i, ANIMATION_EVENT, event, 0); + } + + current->lastTime = current->time; + } +} + +void AnimationState_clear (AnimationState* self) { + int i; + for (i = 0; i < self->trackCount; i++) + AnimationState_clearTrack(self, i); + self->trackCount = 0; +} + +void AnimationState_clearTrack (AnimationState* self, int trackIndex) { + if (trackIndex >= self->trackCount) return; + TrackEntry* current = self->tracks[trackIndex]; + if (!current) return; + + if (current->listener) current->listener(self, trackIndex, ANIMATION_END, 0, 0); + if (self->listener) self->listener(self, trackIndex, ANIMATION_END, 0, 0); + + self->tracks[trackIndex] = 0; + _TrackEntry_disposeAll(current); + if (current->previous) _TrackEntry_dispose(current->previous); +} + +TrackEntry* _AnimationState_expandToIndex (AnimationState* self, int index) { + if (index < self->trackCount) return self->tracks[index]; + TrackEntry** newTracks = CALLOC(TrackEntry*, index + 1); + memcpy(newTracks, self->tracks, self->trackCount * sizeof(TrackEntry*)); + self->tracks = newTracks; + self->trackCount = index + 1; + return 0; +} + +void _AnimationState_setCurrent (AnimationState* self, int index, TrackEntry* entry) { + TrackEntry* current = _AnimationState_expandToIndex(self, index); + if (current) { + if (current->previous) { + _TrackEntry_dispose(current->previous); + current->previous = 0; + } + + if (current->listener) current->listener(self, index, ANIMATION_END, 0, 0); + if (self->listener) self->listener(self, index, ANIMATION_END, 0, 0); + + entry->mixDuration = AnimationStateData_getMix(self->data, current->animation, entry->animation); + if (entry->mixDuration > 0) { + entry->mixTime = 0; + entry->previous = current; + } else + _TrackEntry_dispose(current); } + self->tracks[index] = entry; + + if (entry->listener) current->listener(self, index, ANIMATION_START, 0, 0); + if (self->listener) self->listener(self, index, ANIMATION_START, 0, 0); +} + +TrackEntry* AnimationState_setAnimationByName (AnimationState* self, int trackIndex, const char* animationName, int/*bool*/loop) { + Animation* animation = animationName ? SkeletonData_findAnimation(self->data->skeletonData, animationName) : 0; + return AnimationState_setAnimation(self, trackIndex, animation, loop); +} + +TrackEntry* AnimationState_setAnimation (AnimationState* self, int trackIndex, Animation* animation, int/*bool*/loop) { + TrackEntry* current = _AnimationState_expandToIndex(self, trackIndex); + if (current) _TrackEntry_disposeAll(current->next); + + TrackEntry* entry = _TrackEntry_create(); + entry->animation = animation; + entry->loop = loop; + entry->time = 0; + entry->endTime = animation->duration; + _AnimationState_setCurrent(self, trackIndex, entry); + return entry; +} + +TrackEntry* AnimationState_addAnimationByName (AnimationState* self, int trackIndex, const char* animationName, int/*bool*/loop, + float delay) { + Animation* animation = animationName ? SkeletonData_findAnimation(self->data->skeletonData, animationName) : 0; + return AnimationState_addAnimation(self, trackIndex, animation, loop, delay); +} + +TrackEntry* AnimationState_addAnimation (AnimationState* self, int trackIndex, Animation* animation, int/*bool*/loop, float delay) { + TrackEntry* entry = _TrackEntry_create(); + entry->animation = animation; + entry->loop = loop; + entry->time = 0; + entry->endTime = animation ? animation->duration : 0; + + TrackEntry* last = _AnimationState_expandToIndex(self, trackIndex); + if (last) { + while (last->next) + last = last->next; + last->next = entry; + } else + self->tracks[trackIndex] = entry; + if (delay <= 0) { - if (previousAnimation) - delay = previousAnimation->duration - AnimationStateData_getMix(self->data, previousAnimation, animation) + delay; - else + if (last) { + delay += last->endTime; + if (animation) delay -= AnimationStateData_getMix(self->data, last->animation, animation); + } else delay = 0; } entry->delay = delay; + + return entry; } -void AnimationState_addAnimationByName (AnimationState* self, const char* animationName, int/*bool*/loop, float delay) { - Animation* animation = animationName ? SkeletonData_findAnimation(self->data->skeletonData, animationName) : 0; - AnimationState_addAnimation(self, animation, loop, delay); -} - -void _AnimationState_setAnimation (AnimationState* self, Animation* newAnimation, int/*bool*/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_setAnimation (AnimationState* self, Animation* newAnimation, int/*bool*/loop) { - _AnimationState_clearQueue(self); - _AnimationState_setAnimation(self, newAnimation, loop); -} - -void AnimationState_setAnimationByName (AnimationState* self, const char* animationName, int/*bool*/loop) { - Animation* animation = animationName ? SkeletonData_findAnimation(self->data->skeletonData, animationName) : 0; - AnimationState_setAnimation(self, animation, loop); -} - -void AnimationState_clearAnimation (AnimationState* self) { - _Internal* internal = SUB_CAST(_Internal, self); - internal->previous = 0; - CONST_CAST(Animation*, self->animation) = 0; - _AnimationState_clearQueue(self); -} - -void AnimationState_update (AnimationState* self, float delta) { - _Entry* next; - _Internal* internal = SUB_CAST(_Internal, self); - - self->time += delta; - internal->previousTime += delta; - internal->mixTime += delta; - - if (internal->queue && self->time >= internal->queue->delay) { - _AnimationState_setAnimation(self, internal->queue->animation, internal->queue->loop); - next = internal->queue->next; - FREE(internal->queue); - internal->queue = next; - } -} - -void AnimationState_apply (AnimationState* self, Skeleton* skeleton) { - _Internal* internal; - float alpha; - - if (!self->animation) return; - internal = SUB_CAST(_Internal, self); - if (internal->previous) { - Animation_apply(internal->previous, skeleton, internal->previousTime, internal->previousLoop); - 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); -} - -int/*bool*/AnimationState_isComplete (AnimationState* self) { - return !self->animation || self->time >= self->animation->duration; +TrackEntry* AnimationState_getCurrent (AnimationState* self, int trackIndex) { + if (trackIndex >= self->trackCount) return 0; + return self->tracks[trackIndex]; } diff --git a/spine-c/src/spine/Bone.c b/spine-c/src/spine/Bone.c index 15c15ad0a..3e0f10a3c 100644 --- a/spine-c/src/spine/Bone.c +++ b/spine-c/src/spine/Bone.c @@ -32,7 +32,6 @@ *****************************************************************************/ #include -#include #include static int yDown; diff --git a/spine-c/src/spine/BoundingBoxAttachment.c b/spine-c/src/spine/BoundingBoxAttachment.c index 07cc1aeaf..6e01f3290 100644 --- a/spine-c/src/spine/BoundingBoxAttachment.c +++ b/spine-c/src/spine/BoundingBoxAttachment.c @@ -32,7 +32,6 @@ *****************************************************************************/ #include -#include #include BoundingBoxAttachment* BoundingBoxAttachment_create (const char* name) { diff --git a/spine-c/src/spine/Event.c b/spine-c/src/spine/Event.c new file mode 100644 index 000000000..88735ab06 --- /dev/null +++ b/spine-c/src/spine/Event.c @@ -0,0 +1,46 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.0 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Single User License or Spine Professional License must be + * purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration 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 + +Event* Event_create (EventData* data) { + Event* self = NEW(Event); + CONST_CAST(EventData*, self->data) = data; + return self; +} + +void Event_dispose (Event* self) { + FREE(self->stringValue); + FREE(self); +} diff --git a/spine-c/src/spine/EventData.c b/spine-c/src/spine/EventData.c new file mode 100644 index 000000000..9414565dc --- /dev/null +++ b/spine-c/src/spine/EventData.c @@ -0,0 +1,47 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.0 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Single User License or Spine Professional License must be + * purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration 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 + +EventData* EventData_create (const char* name) { + EventData* self = NEW(EventData); + MALLOC_STR(self->name, name); + return self; +} + +void EventData_dispose (EventData* self) { + FREE(self->stringValue); + FREE(self->name); + FREE(self); +} diff --git a/spine-c/src/spine/Json.c b/spine-c/src/spine/Json.c index ef19a6915..941d807d6 100644 --- a/spine-c/src/spine/Json.c +++ b/spine-c/src/spine/Json.c @@ -24,7 +24,6 @@ /* JSON parser in C. */ #include "Json.h" -#include #include #include #include diff --git a/spine-c/src/spine/RegionAttachment.c b/spine-c/src/spine/RegionAttachment.c index 1d75827d0..547d38c01 100644 --- a/spine-c/src/spine/RegionAttachment.c +++ b/spine-c/src/spine/RegionAttachment.c @@ -32,7 +32,6 @@ *****************************************************************************/ #include -#include #include RegionAttachment* RegionAttachment_create (const char* name) { diff --git a/spine-c/src/spine/SkeletonBounds.c b/spine-c/src/spine/SkeletonBounds.c index 2f4a18cfa..b9f9d4e1a 100644 --- a/spine-c/src/spine/SkeletonBounds.c +++ b/spine-c/src/spine/SkeletonBounds.c @@ -32,7 +32,6 @@ *****************************************************************************/ #include -#include #include #include @@ -51,8 +50,8 @@ void BoundingPolygon_dispose (BoundingPolygon* self) { int/*bool*/BoundingPolygon_containsPoint (BoundingPolygon* self, float x, float y) { int prevIndex = self->count - 2; int inside = 0; - int i = 0; - for (; i < self->count; i += 2) { + int i; + for (i = 0; i < self->count; i += 2) { float vertexY = self->vertices[i + 1]; float prevY = self->vertices[prevIndex + 1]; if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { @@ -68,8 +67,8 @@ int/*bool*/BoundingPolygon_intersectsSegment (BoundingPolygon* self, float x1, f float width12 = x1 - x2, height12 = y1 - y2; float det1 = x1 * y2 - y1 * x2; float x3 = self->vertices[self->count - 2], y3 = self->vertices[self->count - 1]; - int i = 0; - for (; i < self->count; i += 2) { + int i; + for (i = 0; i < self->count; i += 2) { float x4 = self->vertices[i], y4 = self->vertices[i + 1]; float det2 = x3 * y4 - y3 * x4; float width34 = x3 - x4, height34 = y3 - y4; @@ -90,15 +89,15 @@ int/*bool*/BoundingPolygon_intersectsSegment (BoundingPolygon* self, float x1, f typedef struct { SkeletonBounds super; int capacity; -} _Internal; +} _SkeletonBounds; SkeletonBounds* SkeletonBounds_create () { - return SUPER(NEW(_Internal)); + return SUPER(NEW(_SkeletonBounds)); } void SkeletonBounds_dispose (SkeletonBounds* self) { - int i = 0; - for (; i < SUB_CAST(_Internal, self)->capacity; ++i) + int i; + for (i = 0; i < SUB_CAST(_SkeletonBounds, self)->capacity; ++i) if (self->polygons[i]) BoundingPolygon_dispose(self->polygons[i]); FREE(self->polygons); FREE(self->boundingBoxes); @@ -108,7 +107,7 @@ void SkeletonBounds_dispose (SkeletonBounds* self) { void SkeletonBounds_update (SkeletonBounds* self, Skeleton* skeleton, int/*bool*/updateAabb) { int i; - _Internal* internal = SUB_CAST(_Internal, self); + _SkeletonBounds* internal = SUB_CAST(_SkeletonBounds, self); if (internal->capacity < skeleton->slotCount) { BoundingPolygon** newPolygons; @@ -188,22 +187,22 @@ int/*bool*/SkeletonBounds_aabbIntersectsSkeleton (SkeletonBounds* self, Skeleton } BoundingBoxAttachment* SkeletonBounds_containsPoint (SkeletonBounds* self, float x, float y) { - int i = 0; - for (; i < self->count; ++i) + int i; + for (i = 0; i < self->count; ++i) if (BoundingPolygon_containsPoint(self->polygons[i], x, y)) return self->boundingBoxes[i]; return 0; } BoundingBoxAttachment* SkeletonBounds_intersectsSegment (SkeletonBounds* self, float x1, float y1, float x2, float y2) { - int i = 0; - for (; i < self->count; ++i) + int i; + for (i = 0; i < self->count; ++i) if (BoundingPolygon_intersectsSegment(self->polygons[i], x1, y1, x2, y2)) return self->boundingBoxes[i]; return 0; } BoundingPolygon* SkeletonBounds_getPolygon (SkeletonBounds* self, BoundingBoxAttachment* boundingBox) { - int i = 0; - for (; i < self->count; ++i) + int i; + for (i = 0; i < self->count; ++i) if (self->boundingBoxes[i] == boundingBox) return self->polygons[i]; return 0; } diff --git a/spine-c/src/spine/SkeletonData.c b/spine-c/src/spine/SkeletonData.c index 65f60feb0..2b3e60c47 100644 --- a/spine-c/src/spine/SkeletonData.c +++ b/spine-c/src/spine/SkeletonData.c @@ -95,6 +95,13 @@ Skin* SkeletonData_findSkin (const SkeletonData* self, const char* skinName) { return 0; } +EventData* SkeletonData_findEvent (const SkeletonData* self, const char* eventName) { + int i; + for (i = 0; i < self->eventCount; ++i) + if (strcmp(self->events[i]->name, eventName) == 0) return self->events[i]; + return 0; +} + Animation* SkeletonData_findAnimation (const SkeletonData* self, const char* animationName) { int i; for (i = 0; i < self->animationCount; ++i) diff --git a/spine-c/src/spine/SkeletonJson.c b/spine-c/src/spine/SkeletonJson.c index 1f800ac30..0f1db79db 100644 --- a/spine-c/src/spine/SkeletonJson.c +++ b/spine-c/src/spine/SkeletonJson.c @@ -32,9 +32,7 @@ *****************************************************************************/ #include -#include #include -#include #include "Json.h" #include #include @@ -43,10 +41,10 @@ typedef struct { SkeletonJson super; int ownsLoader; -} _Internal; +} _SkeletonJson; SkeletonJson* SkeletonJson_createWithLoader (AttachmentLoader* attachmentLoader) { - SkeletonJson* self = SUPER(NEW(_Internal)); + SkeletonJson* self = SUPER(NEW(_SkeletonJson)); self->scale = 1; self->attachmentLoader = attachmentLoader; return self; @@ -55,12 +53,12 @@ SkeletonJson* SkeletonJson_createWithLoader (AttachmentLoader* attachmentLoader) SkeletonJson* SkeletonJson_create (Atlas* atlas) { AtlasAttachmentLoader* attachmentLoader = AtlasAttachmentLoader_create(atlas); SkeletonJson* self = SkeletonJson_createWithLoader(SUPER(attachmentLoader)); - SUB_CAST(_Internal, self)->ownsLoader = 1; + SUB_CAST(_SkeletonJson, self)->ownsLoader = 1; return self; } void SkeletonJson_dispose (SkeletonJson* self) { - if (SUB_CAST(_Internal, self)->ownsLoader) AttachmentLoader_dispose(self->attachmentLoader); + if (SUB_CAST(_SkeletonJson, self)->ownsLoader) AttachmentLoader_dispose(self->attachmentLoader); FREE(self->error); FREE(self); } @@ -114,13 +112,15 @@ static Animation* _SkeletonJson_readAnimation (SkeletonJson* self, Json* root, S Json* bones = Json_getItem(root, "bones"); Json* slots = Json_getItem(root, "slots"); Json* drawOrder = Json_getItem(root, "draworder"); - Json *boneMap, *slotMap, *timelineArray; + Json* events = Json_getItem(root, "events"); + Json *boneMap, *slotMap; int timelineCount = 0; for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next) timelineCount += boneMap->size; for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next) timelineCount += slotMap->size; + if (events) ++timelineCount; if (drawOrder) ++timelineCount; animation = Animation_create(root->name, timelineCount); @@ -129,6 +129,8 @@ static Animation* _SkeletonJson_readAnimation (SkeletonJson* self, Json* root, S ++skeletonData->animationCount; for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next) { + Json *timelineArray; + int boneIndex = SkeletonData_findBoneIndex(skeletonData, boneMap->name); if (boneIndex == -1) { Animation_dispose(animation); @@ -176,6 +178,8 @@ static Animation* _SkeletonJson_readAnimation (SkeletonJson* self, Json* root, S } for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next) { + Json *timelineArray; + int slotIndex = SkeletonData_findSlotIndex(skeletonData, slotMap->name); if (slotIndex == -1) { Animation_dispose(animation); @@ -220,6 +224,30 @@ static Animation* _SkeletonJson_readAnimation (SkeletonJson* self, Json* root, S } } + if (events) { + Json* frame; + float duration; + + EventTimeline* timeline = EventTimeline_create(events->size); + for (frame = events->child, i = 0; frame; frame = frame->next, ++i) { + EventData* eventData = SkeletonData_findEvent(skeletonData, Json_getString(frame, "name", 0)); + if (!eventData) { + Animation_dispose(animation); + _SkeletonJson_setError(self, 0, "Event not found: ", Json_getString(frame, "name", 0)); + return 0; + } + Event* event = Event_create(eventData); + event->intValue = Json_getInt(frame, "int", eventData->intValue); + event->floatValue = Json_getFloat(frame, "float", eventData->floatValue); + const char* stringValue = Json_getString(frame, "string", eventData->stringValue); + if (stringValue) MALLOC_STR(event->stringValue, stringValue); + EventTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), event); + } + animation->timelines[animation->timelineCount++] = (Timeline*)timeline; + duration = timeline->frames[events->size - 1]; + if (duration > animation->duration) animation->duration = duration; + } + if (drawOrder) { Json* frame; float duration; @@ -287,10 +315,7 @@ SkeletonData* SkeletonJson_readSkeletonDataFile (SkeletonJson* self, const char* SkeletonData* SkeletonJson_readSkeletonData (SkeletonJson* self, const char* json) { int i; SkeletonData* skeletonData; - Json *root, *bones, *boneMap, *slotMap, *attachmentsMap, *attachmentMap, *animationMap; - Json* slots; - Json* skinsMap; - Json* animations; + Json *root, *bones, *boneMap, *slots, *skins, *animations, *events; FREE(self->error); CONST_CAST(char*, self->error) = 0; @@ -335,6 +360,7 @@ SkeletonData* SkeletonJson_readSkeletonData (SkeletonJson* self, const char* jso slots = Json_getItem(root, "slots"); if (slots) { + Json *slotMap; skeletonData->slots = MALLOC(SlotData*, slots->size); for (slotMap = slots->child, i = 0; slotMap; slotMap = slotMap->next, ++i) { SlotData* slotData; @@ -369,10 +395,12 @@ SkeletonData* SkeletonJson_readSkeletonData (SkeletonJson* self, const char* jso } } - skinsMap = Json_getItem(root, "skins"); - if (skinsMap) { - skeletonData->skins = MALLOC(Skin*, skinsMap->size); - for (slotMap = skinsMap->child, i = 0; slotMap; slotMap = slotMap->next, ++i) { + skins = Json_getItem(root, "skins"); + if (skins) { + Json *slotMap; + skeletonData->skins = MALLOC(Skin*, skins->size); + for (slotMap = skins->child, i = 0; slotMap; slotMap = slotMap->next, ++i) { + Json *attachmentsMap; Skin *skin = Skin_create(slotMap->name); skeletonData->skins[i] = skin; @@ -381,6 +409,7 @@ SkeletonData* SkeletonJson_readSkeletonData (SkeletonJson* self, const char* jso for (attachmentsMap = slotMap->child; attachmentsMap; attachmentsMap = attachmentsMap->next) { int slotIndex = SkeletonData_findSlotIndex(skeletonData, attachmentsMap->name); + Json *attachmentMap; for (attachmentMap = attachmentsMap->child; attachmentMap; attachmentMap = attachmentMap->next) { Attachment* attachment; @@ -429,10 +458,10 @@ SkeletonData* SkeletonJson_readSkeletonData (SkeletonJson* self, const char* jso BoundingBoxAttachment* box = (BoundingBoxAttachment*)attachment; Json* verticesArray = Json_getItem(attachmentMap, "vertices"); Json* vertex; - int i = 0; + int i; box->verticesCount = verticesArray->size; box->vertices = MALLOC(float, verticesArray->size); - for (vertex = verticesArray->child; vertex; vertex = vertex->next, ++i) + for (vertex = verticesArray->child, i = 0; vertex; vertex = vertex->next, ++i) box->vertices[i] = vertex->valueFloat; break; } @@ -444,8 +473,25 @@ SkeletonData* SkeletonJson_readSkeletonData (SkeletonJson* self, const char* jso } } + /* Events. */ + events = Json_getItem(root, "events"); + if (events) { + Json *eventMap; + skeletonData->events = MALLOC(EventData*, events->size); + for (eventMap = events->child; eventMap; eventMap = eventMap->next) { + EventData* eventData = EventData_create(eventMap->name); + eventData->intValue = Json_getInt(eventMap, "int", 0); + eventData->floatValue = Json_getFloat(eventMap, "float", 0); + const char* stringValue = Json_getString(eventMap, "string", 0); + if (stringValue) MALLOC_STR(eventData->stringValue, stringValue); + skeletonData->events[skeletonData->eventCount++] = eventData; + } + } + + /* Animations. */ animations = Json_getItem(root, "animations"); if (animations) { + Json *animationMap; skeletonData->animations = MALLOC(Animation*, animations->size); for (animationMap = animations->child; animationMap; animationMap = animationMap->next) _SkeletonJson_readAnimation(self, animationMap, skeletonData); diff --git a/spine-c/src/spine/Skin.c b/spine-c/src/spine/Skin.c index 398261663..c90e3ef6d 100644 --- a/spine-c/src/spine/Skin.c +++ b/spine-c/src/spine/Skin.c @@ -61,16 +61,16 @@ void _Entry_dispose (_Entry* self) { typedef struct { Skin super; _Entry* entries; -} _Internal; +} _Skin; Skin* Skin_create (const char* name) { - Skin* self = SUPER(NEW(_Internal)); + Skin* self = SUPER(NEW(_Skin)); MALLOC_STR(self->name, name); return self; } void Skin_dispose (Skin* self) { - _Entry* entry = SUB_CAST(_Internal, self) ->entries; + _Entry* entry = SUB_CAST(_Skin, self)->entries; while (entry) { _Entry* nextEntry = entry->next; _Entry_dispose(entry); @@ -83,12 +83,12 @@ void Skin_dispose (Skin* self) { 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; + newEntry->next = SUB_CAST(_Skin, self)->entries; + SUB_CAST(_Skin, self)->entries = newEntry; } Attachment* Skin_getAttachment (const Skin* self, int slotIndex, const char* name) { - const _Entry* entry = SUB_CAST(_Internal, self) ->entries; + const _Entry* entry = SUB_CAST(_Skin, self)->entries; while (entry) { if (entry->slotIndex == slotIndex && strcmp(entry->name, name) == 0) return entry->attachment; entry = entry->next; @@ -97,7 +97,7 @@ Attachment* Skin_getAttachment (const Skin* self, int slotIndex, const char* nam } const char* Skin_getAttachmentName (const Skin* self, int slotIndex, int attachmentIndex) { - const _Entry* entry = SUB_CAST(_Internal, self) ->entries; + const _Entry* entry = SUB_CAST(_Skin, self)->entries; int i = 0; while (entry) { if (entry->slotIndex == slotIndex) { @@ -110,7 +110,7 @@ const char* Skin_getAttachmentName (const Skin* self, int slotIndex, int attachm } void Skin_attachAll (const Skin* self, Skeleton* skeleton, const Skin* oldSkin) { - const _Entry *entry = SUB_CAST(_Internal, oldSkin) ->entries; + const _Entry *entry = SUB_CAST(_Skin, oldSkin)->entries; while (entry) { Slot *slot = skeleton->slots[entry->slotIndex]; if (slot->attachment == entry->attachment) { diff --git a/spine-c/src/spine/Slot.c b/spine-c/src/spine/Slot.c index eec04df7f..6191b702f 100644 --- a/spine-c/src/spine/Slot.c +++ b/spine-c/src/spine/Slot.c @@ -38,10 +38,10 @@ typedef struct { Slot super; float attachmentTime; -} _Internal; +} _Slot; Slot* Slot_create (SlotData* data, Skeleton* skeleton, Bone* bone) { - Slot* self = SUPER(NEW(_Internal)); + Slot* self = SUPER(NEW(_Slot)); CONST_CAST(SlotData*, self->data) = data; CONST_CAST(Skeleton*, self->skeleton) = skeleton; CONST_CAST(Bone*, self->bone) = bone; @@ -55,15 +55,15 @@ void Slot_dispose (Slot* self) { void Slot_setAttachment (Slot* self, Attachment* attachment) { CONST_CAST(Attachment*, self->attachment) = attachment; - SUB_CAST(_Internal, self) ->attachmentTime = self->skeleton->time; + SUB_CAST(_Slot, self) ->attachmentTime = self->skeleton->time; } void Slot_setAttachmentTime (Slot* self, float time) { - SUB_CAST(_Internal, self) ->attachmentTime = self->skeleton->time - time; + SUB_CAST(_Slot, self) ->attachmentTime = self->skeleton->time - time; } float Slot_getAttachmentTime (const Slot* self) { - return self->skeleton->time - SUB_CAST(_Internal, self) ->attachmentTime; + return self->skeleton->time - SUB_CAST(_Slot, self) ->attachmentTime; } void Slot_setToSetupPose (Slot* self) { diff --git a/spine-c/src/spine/extension.c b/spine-c/src/spine/extension.c index dc733dcfc..1a21062a0 100644 --- a/spine-c/src/spine/extension.c +++ b/spine-c/src/spine/extension.c @@ -41,8 +41,8 @@ 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); + void* ptr = mallocFunc(num * size); + if (ptr) memset(ptr, 0, num * size); return ptr; } void _free (void* ptr) { diff --git a/spine-sfml/example/main.cpp b/spine-sfml/example/main.cpp index e913b1caf..b36ed1867 100644 --- a/spine-sfml/example/main.cpp +++ b/spine-sfml/example/main.cpp @@ -41,6 +41,28 @@ using namespace std; using namespace spine; #include +void callback (AnimationState* state, int trackIndex, EventType type, Event* event, int loopCount) { + TrackEntry* entry = AnimationState_getCurrent(state, trackIndex); + const char* animationName = (entry && entry->animation) ? entry->animation->name : 0; + + switch (type) { + case ANIMATION_START: + printf("%d start: %s\n", trackIndex, animationName); + break; + case ANIMATION_END: + printf("%d end: %s\n", trackIndex, animationName); + break; + case ANIMATION_COMPLETE: + printf("%d complete: %s, %d\n", trackIndex, animationName, loopCount); + break; + case ANIMATION_EVENT: + printf("%d event: %s, %s: %d, %f, %s\n", trackIndex, animationName, event->data->name, event->intValue, event->floatValue, + event->stringValue); + break; + } + fflush(stdout); +} + void spineboy () { // Load atlas, skeleton, and animations. Atlas* atlas = Atlas_readAtlasFile("../data/spineboy.atlas"); @@ -72,12 +94,13 @@ void spineboy () { Slot* headSlot = Skeleton_findSlot(skeleton, "head"); + drawable->state->listener = callback; if (true) { - AnimationState_setAnimationByName(drawable->state, "drawOrder", true); + AnimationState_setAnimationByName(drawable->state, 0, "drawOrder", true); } else { - AnimationState_setAnimationByName(drawable->state, "walk", true); - AnimationState_addAnimationByName(drawable->state, "jump", false, 0); - AnimationState_addAnimationByName(drawable->state, "walk", true, 0); + AnimationState_setAnimationByName(drawable->state, 0, "walk", true); + AnimationState_addAnimationByName(drawable->state, 0, "jump", false, 0); + AnimationState_addAnimationByName(drawable->state, 0, "walk", true, 0); } sf::RenderWindow window(sf::VideoMode(640, 480), "Spine SFML"); @@ -136,7 +159,7 @@ void goblins () { skeleton->root->y = 590; Skeleton_updateWorldTransform(skeleton); - AnimationState_setAnimation(drawable->state, walkAnimation, true); + AnimationState_setAnimation(drawable->state, 0, walkAnimation, true); sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML"); window.setFramerateLimit(60);