From 0ff14743f5eb46f80f801d0d0ed66bc89ea6d7cc Mon Sep 17 00:00:00 2001 From: badlogic Date: Tue, 11 Sep 2018 14:49:23 +0200 Subject: [PATCH 1/2] [c] Ported holdPrevious in AnimationState. See #1169. --- .../spine-c/include/spine/AnimationState.h | 16 +-- spine-c/spine-c/src/spine/AnimationState.c | 123 +++++++++--------- 2 files changed, 72 insertions(+), 67 deletions(-) diff --git a/spine-c/spine-c/include/spine/AnimationState.h b/spine-c/spine-c/include/spine/AnimationState.h index 4c93afb3b..b44726924 100644 --- a/spine-c/spine-c/include/spine/AnimationState.h +++ b/spine-c/spine-c/include/spine/AnimationState.h @@ -56,16 +56,18 @@ struct spTrackEntry { spAnimation* animation; spTrackEntry* next; spTrackEntry* mixingFrom; + spTrackEntry* mixingTo; spAnimationStateListener listener; int trackIndex; int /*boolean*/ loop; + int /*boolean*/ holdPrevious; float eventThreshold, attachmentThreshold, drawOrderThreshold; float animationStart, animationEnd, animationLast, nextAnimationLast; float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale; float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha; spMixBlend mixBlend; - spIntArray* timelineData; - spTrackEntryArray* timelineDipMix; + spIntArray* timelineMode; + spTrackEntryArray* timelineHoldMix; float* timelinesRotation; int timelinesRotationCount; void* rendererObject; @@ -74,17 +76,18 @@ struct spTrackEntry { #ifdef __cplusplus spTrackEntry() : animation(0), - next(0), mixingFrom(0), + next(0), mixingFrom(0), mixingTo(0), listener(0), trackIndex(0), loop(0), + holdPrevious(0), eventThreshold(0), attachmentThreshold(0), drawOrderThreshold(0), animationStart(0), animationEnd(0), animationLast(0), nextAnimationLast(0), delay(0), trackTime(0), trackLast(0), nextTrackLast(0), trackEnd(0), timeScale(0), alpha(0), mixTime(0), mixDuration(0), interruptAlpha(0), totalAlpha(0), mixBlend(SP_MIX_BLEND_REPLACE), - timelineData(0), - timelineDipMix(0), + timelineMode(0), + timelineHoldMix(0), timelinesRotation(0), timelinesRotationCount(0), rendererObject(0), userData(0) { @@ -102,8 +105,6 @@ struct spAnimationState { float timeScale; - spTrackEntryArray* mixingTo; - void* rendererObject; void* userData; @@ -114,7 +115,6 @@ struct spAnimationState { tracks(0), listener(0), timeScale(0), - mixingTo(0), rendererObject(0), userData(0) { } diff --git a/spine-c/spine-c/src/spine/AnimationState.c b/spine-c/spine-c/src/spine/AnimationState.c index d54a13225..daeb9a826 100644 --- a/spine-c/spine-c/src/spine/AnimationState.c +++ b/spine-c/spine-c/src/spine/AnimationState.c @@ -34,8 +34,8 @@ #define SUBSEQUENT 0 #define FIRST 1 -#define DIP 2 -#define DIP_MIX 3 +#define HOLD 2 +#define HOLD_MIX 3 _SP_ARRAY_IMPLEMENT_TYPE(spTrackEntryArray, spTrackEntry*) @@ -62,7 +62,7 @@ float* _spAnimationState_resizeTimelinesRotation(spTrackEntry* entry, int newSiz int* _spAnimationState_resizeTimelinesFirst(spTrackEntry* entry, int newSize); void _spAnimationState_ensureCapacityPropertyIDs(spAnimationState* self, int capacity); int _spAnimationState_addPropertyID(spAnimationState* self, int id); -spTrackEntry* _spTrackEntry_setTimelineData(spTrackEntry* self, spTrackEntry* to, spTrackEntryArray* mixingToArray, spAnimationState* state); +void _spTrackEntry_setTimelineData(spTrackEntry* self, spAnimationState* state); _spEventQueue* _spEventQueue_create (_spAnimationState* state) { @@ -192,8 +192,8 @@ void _spAnimationState_disableQueue(spAnimationState* self) { } void _spAnimationState_disposeTrackEntry (spTrackEntry* entry) { - spIntArray_dispose(entry->timelineData); - spTrackEntryArray_dispose(entry->timelineDipMix); + spIntArray_dispose(entry->timelineMode); + spTrackEntryArray_dispose(entry->timelineHoldMix); FREE(entry->timelinesRotation); FREE(entry); } @@ -237,8 +237,6 @@ spAnimationState* spAnimationState_create (spAnimationStateData* data) { internal->propertyIDs = CALLOC(int, 128); internal->propertyIDsCapacity = 128; - self->mixingTo = spTrackEntryArray_create(16); - return self; } @@ -251,7 +249,6 @@ void spAnimationState_dispose (spAnimationState* self) { _spEventQueue_free(internal->queue); FREE(internal->events); FREE(internal->propertyIDs); - spTrackEntryArray_dispose(self->mixingTo); FREE(internal); } @@ -305,6 +302,7 @@ void spAnimationState_update (spAnimationState* self, float delta) { /* End mixing from entries once all have completed. */ spTrackEntry* from = current->mixingFrom; current->mixingFrom = 0; + if (from != 0) from->mixingTo = 0; while (from != 0) { _spEventQueue_end(internal->queue, from); from = from->mixingFrom; @@ -333,6 +331,7 @@ int /*boolean*/ _spAnimationState_updateMixingFrom (spAnimationState* self, spTr /* Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame). */ if (from->totalAlpha == 0 || to->mixDuration == 0) { to->mixingFrom = from->mixingFrom; + if (from->mixingFrom != 0) from->mixingFrom->mixingTo = to; to->interruptAlpha = from->interruptAlpha; _spEventQueue_end(internal->queue, from); } @@ -378,11 +377,11 @@ int spAnimationState_apply (spAnimationState* self, spSkeleton* skeleton) { animationLast = current->animationLast; animationTime = spTrackEntry_getAnimationTime(current); timelineCount = current->animation->timelinesCount; timelines = current->animation->timelines; - if (mix == 1 || blend == SP_MIX_BLEND_ADD) { + if (i == 0 && (mix == 1 || blend == SP_MIX_BLEND_ADD)) { for (ii = 0; ii < timelineCount; ii++) spTimeline_apply(timelines[ii], skeleton, animationLast, animationTime, internal->events, &internal->eventsCount, mix, blend, SP_MIX_DIRECTION_IN); } else { - spIntArray* timelineData = current->timelineData; + spIntArray* timelineMode = current->timelineMode; firstFrame = current->timelinesRotationCount == 0; if (firstFrame) _spAnimationState_resizeTimelinesRotation(current, timelineCount << 1); @@ -390,7 +389,7 @@ int spAnimationState_apply (spAnimationState* self, spSkeleton* skeleton) { for (ii = 0; ii < timelineCount; ii++) { timeline = timelines[ii]; - timelineBlend = timelineData->items[ii] == SUBSEQUENT ? blend : SP_MIX_BLEND_SETUP; + timelineBlend = timelineMode->items[ii] == SUBSEQUENT ? blend : SP_MIX_BLEND_SETUP; if (timeline->type == SP_TIMELINE_ROTATE) _spAnimationState_applyRotateTimeline(self, timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame); else @@ -417,16 +416,16 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* t float animationTime; int timelineCount; spTimeline** timelines; - spIntArray* timelineData; - spTrackEntryArray* timelineDipMix; + spIntArray* timelineMode; + spTrackEntryArray* timelineHoldMix; spMixBlend timelineBlend; - float alphaDip; + float alphaHold; float alphaMix; float alpha; int /*boolean*/ firstFrame; float* timelinesRotation; int i; - spTrackEntry* dipMix; + spTrackEntry* holdMix; spTrackEntry* from = to->mixingFrom; if (from->mixingFrom) _spAnimationState_applyMixingFrom(self, from, skeleton, blend); @@ -447,15 +446,15 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* t animationTime = spTrackEntry_getAnimationTime(from); timelineCount = from->animation->timelinesCount; timelines = from->animation->timelines; - alphaDip = from->alpha * to->interruptAlpha; alphaMix = alphaDip * (1 - mix); + alphaHold = from->alpha * to->interruptAlpha; alphaMix = alphaHold * (1 - mix); if (blend == SP_MIX_BLEND_ADD) { for (i = 0; i < timelineCount; i++) { spTimeline *timeline = timelines[i]; spTimeline_apply(timeline, skeleton, animationLast, animationTime, events, &internal->eventsCount, alphaMix, blend, SP_MIX_DIRECTION_OUT); } } else { - timelineData = from->timelineData; - timelineDipMix = from->timelineDipMix; + timelineMode = from->timelineMode; + timelineHoldMix = from->timelineHoldMix; firstFrame = from->timelinesRotationCount == 0; if (firstFrame) _spAnimationState_resizeTimelinesRotation(from, timelineCount << 1); @@ -464,7 +463,7 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* t from->totalAlpha = 0; for (i = 0; i < timelineCount; i++) { spTimeline *timeline = timelines[i]; - switch (timelineData->items[i]) { + switch (timelineMode->items[i]) { case SUBSEQUENT: if (!attachments && timeline->type == SP_TIMELINE_ATTACHMENT) continue; if (!drawOrder && timeline->type == SP_TIMELINE_DRAWORDER) continue; @@ -475,14 +474,14 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* t timelineBlend = SP_MIX_BLEND_SETUP; alpha = alphaMix; break; - case DIP: + case HOLD: timelineBlend = SP_MIX_BLEND_SETUP; - alpha = alphaDip; + alpha = alphaHold; break; default: timelineBlend = SP_MIX_BLEND_SETUP; - dipMix = timelineDipMix->items[i]; - alpha = alphaDip * MAX(0, 1 - dipMix->mixTime / dipMix->mixDuration); + holdMix = timelineHoldMix->items[i]; + alpha = alphaHold * MAX(0, 1 - holdMix->mixTime / holdMix->mixDuration); break; } from->totalAlpha += alpha; @@ -648,6 +647,7 @@ void spAnimationState_clearTrack (spAnimationState* self, int trackIndex) { if (!from) break; _spEventQueue_end(internal->queue, from); entry->mixingFrom = 0; + entry->mixingTo = 0; entry = from; } @@ -663,6 +663,7 @@ void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEnt if (from) { if (interrupt) _spEventQueue_interrupt(internal->queue, from); current->mixingFrom = from; + from->mixingTo = current; current->mixTime = 0; /* Store the interrupted mix percentage. */ @@ -794,6 +795,7 @@ spTrackEntry* _spAnimationState_trackEntry (spAnimationState* self, int trackInd entry->trackIndex = trackIndex; entry->animation = animation; entry->loop = loop; + entry->holdPrevious = 0; entry->eventThreshold = 0; entry->attachmentThreshold = 0; @@ -817,8 +819,9 @@ spTrackEntry* _spAnimationState_trackEntry (spAnimationState* self, int trackInd entry->mixDuration = !last ? 0 : spAnimationStateData_getMix(self->data, last->animation, animation); entry->mixBlend = SP_MIX_BLEND_REPLACE; - entry->timelineData = spIntArray_create(16); - entry->timelineDipMix = spTrackEntryArray_create(16); + entry->timelineMode = spIntArray_create(16); + entry->timelineHoldMix = spTrackEntryArray_create(16); + return entry; } @@ -836,17 +839,19 @@ void _spAnimationState_animationsChanged (spAnimationState* self) { _spAnimationState* internal = SUB_CAST(_spAnimationState, self); int i, n; spTrackEntry* entry; - spTrackEntryArray* mixingTo; internal->animationsChanged = 0; internal->propertyIDsCount = 0; i = 0; n = self->tracksCount; - mixingTo = self->mixingTo; - for (;i < n; i++) { entry = self->tracks[i]; - if (entry != 0 && entry->mixBlend != SP_MIX_BLEND_ADD) _spTrackEntry_setTimelineData(entry, 0, mixingTo, self); + while (entry->mixingFrom != 0) + entry = entry->mixingFrom; + do { + if (entry->mixingTo == 0 || entry->mixBlend != SP_MIX_BLEND_ADD) _spTrackEntry_setTimelineData(entry, self); + entry = entry->mixingTo; + } while (entry != 0); } } @@ -912,51 +917,51 @@ int /*boolean*/ _spTrackEntry_hasTimeline(spTrackEntry* self, int id) { return 0; } -spTrackEntry* _spTrackEntry_setTimelineData(spTrackEntry* self, spTrackEntry* to, spTrackEntryArray* mixingToArray, spAnimationState* state) { - spTrackEntry* lastEntry; - spTrackEntry** mixingTo; - int mixingToLast; +void _spTrackEntry_setTimelineData(spTrackEntry* entry, spAnimationState* state) { + spTrackEntry* to; spTimeline** timelines; int timelinesCount; - int* timelineData; - spTrackEntry** timelineDipMix; - int i, ii; + int* timelineMode; + spTrackEntry** timelineHoldMix; + spTrackEntry* next; + int i; - if (to != 0) spTrackEntryArray_add(mixingToArray, to); - lastEntry = self->mixingFrom != 0 ? _spTrackEntry_setTimelineData(self->mixingFrom, self, mixingToArray, state) : self; - if (to != 0) spTrackEntryArray_pop(mixingToArray); + to = entry->mixingTo; + timelines = entry->animation->timelines; + timelinesCount = entry->animation->timelinesCount; + timelineMode = spIntArray_setSize(entry->timelineMode, timelinesCount)->items; + spTrackEntryArray_clear(entry->timelineHoldMix); + timelineHoldMix = spTrackEntryArray_setSize(entry->timelineHoldMix, timelinesCount)->items; - mixingTo = mixingToArray->items; - mixingToLast = mixingToArray->size - 1; - timelines = self->animation->timelines; - timelinesCount = self->animation->timelinesCount; - timelineData = spIntArray_setSize(self->timelineData, timelinesCount)->items; - spTrackEntryArray_clear(self->timelineDipMix); - timelineDipMix = spTrackEntryArray_setSize(self->timelineDipMix, timelinesCount)->items; + if (to != 0 && to->holdPrevious) { + for (i = 0; i < timelinesCount; i++) { + int id = spTimeline_getPropertyId(timelines[i]); + _spAnimationState_addPropertyID(state, id); + timelineMode[i] = HOLD; + } + return; + } i = 0; continue_outer: for (; i < timelinesCount; i++) { int id = spTimeline_getPropertyId(timelines[i]); if (!_spAnimationState_addPropertyID(state, id)) - timelineData[i] = SUBSEQUENT; + timelineMode[i] = SUBSEQUENT; else if (to == 0 || !_spTrackEntry_hasTimeline(to, id)) - timelineData[i] = FIRST; + timelineMode[i] = FIRST; else { - for (ii = mixingToLast; ii >= 0; ii--) { - spTrackEntry* entry = mixingTo[ii]; - if (!_spTrackEntry_hasTimeline(entry, id)) { - if (entry->mixDuration > 0) { - timelineData[i] = DIP_MIX; - timelineDipMix[i] = entry; - i++; - goto continue_outer; - } + for (next = to->mixingTo; next != 0; next = next->mixingTo) { + if (_spTrackEntry_hasTimeline(next, id)) continue; + if (next->mixDuration > 0) { + timelineMode[i] = HOLD_MIX; + timelineHoldMix[i] = next; + i++; + goto continue_outer; } break; } - timelineData[i] = DIP; + timelineMode[i] = HOLD; } } - return lastEntry; } From de6bbc356dc1222f7fe879a002bfe8af6c1488d4 Mon Sep 17 00:00:00 2001 From: badlogic Date: Tue, 11 Sep 2018 15:41:17 +0200 Subject: [PATCH 2/2] [cpp] Ported holdPrevious in AnimationState. See #1169. --- .../spine-cpp/include/spine/AnimationState.h | 42 +++-- .../spine-cpp/src/spine/AnimationState.cpp | 164 ++++++++++-------- 2 files changed, 122 insertions(+), 84 deletions(-) diff --git a/spine-cpp/spine-cpp/include/spine/AnimationState.h b/spine-cpp/spine-cpp/include/spine/AnimationState.h index 5eaa5e2fc..994b24944 100644 --- a/spine-cpp/spine-cpp/include/spine/AnimationState.h +++ b/spine-cpp/spine-cpp/include/spine/AnimationState.h @@ -76,6 +76,21 @@ namespace Spine { /// If true, the animation will repeat. If false, it will not, instead its last frame is applied if played beyond its duration. bool getLoop(); void setLoop(bool inValue); + + /// + /// If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead + /// of being mixed out. + /// + /// When mixing between animations that key the same property, if a lower track also keys that property then the value will + /// briefly dip toward the lower track value during the mix. This happens because the first animation mixes from 100% to 0% + /// while the second animation mixes from 0% to 100%. Setting holdPrevious to true applies the first animation + /// at 100% during the mix so the lower track value is overwritten. Such dipping does not occur on the lowest track which + /// keys the property, only when a higher track also keys the property. + /// + /// Snapping will occur if holdPrevious is true and this animation does not key all the same properties as the + /// previous animation. + bool getHoldPrevious(); + void setHoldPrevious(bool inValue); /// /// Seconds to postpone playing the animation. When a track entry is the current track entry, delay postpones incrementing @@ -203,8 +218,13 @@ namespace Spine { /// /// The track entry for the previous animation when mixing from the previous animation to this animation, or NULL if no - /// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a linked list. + /// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a double linked list with MixingTo. TrackEntry* getMixingFrom(); + + /// + /// The track entry for the next animation when mixing from this animation, or NULL if no mixing is currently occuring. + /// When mixing from multiple animations, MixingTo makes up a double linked list with MixingFrom. + TrackEntry* getMixingTo(); /// /// Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the @@ -224,25 +244,20 @@ namespace Spine { TrackEntry* _next; TrackEntry* _mixingFrom; + TrackEntry* _mixingTo; int _trackIndex; - bool _loop; + bool _loop, _holdPrevious; float _eventThreshold, _attachmentThreshold, _drawOrderThreshold; float _animationStart, _animationEnd, _animationLast, _nextAnimationLast; float _delay, _trackTime, _trackLast, _nextTrackLast, _trackEnd, _timeScale; float _alpha, _mixTime, _mixDuration, _interruptAlpha, _totalAlpha; MixBlend _mixBlend; - Vector _timelineData; - Vector _timelineDipMix; + Vector _timelineMode; + Vector _timelineHoldMix; Vector _timelinesRotation; OnAnimationEventFunc _onAnimationEventFunc; - /// Sets the timeline data. - /// @param to May be NULL. - TrackEntry* setTimelineData(TrackEntry* to, Vector& mixingToArray, Vector& propertyIDs); - - bool hasTimeline(int inId); - void reset(); }; @@ -384,7 +399,7 @@ namespace Spine { void* getRendererObject(); private: - static const int Subsequent, First, Dip, DipMix; + static const int Subsequent, First, Hold, HoldMix; AnimationStateData* _data; @@ -394,7 +409,6 @@ namespace Spine { EventQueue* _queue; Vector _propertyIDs; - Vector _mixingTo; bool _animationsChanged; void* _rendererObject; @@ -427,6 +441,10 @@ namespace Spine { void disposeNext(TrackEntry* entry); void animationsChanged(); + + void setTimelineModes(TrackEntry* entry); + + bool hasTimeline(TrackEntry* entry, int inId); }; } diff --git a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp index 9af748fbd..268f93d83 100644 --- a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp +++ b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp @@ -51,7 +51,7 @@ void dummyOnAnimationEventFunc(AnimationState *state, Spine::EventType type, Tra SP_UNUSED(event); } -TrackEntry::TrackEntry() : _animation(NULL), _next(NULL), _mixingFrom(NULL), _trackIndex(0), _loop(false), +TrackEntry::TrackEntry() : _animation(NULL), _next(NULL), _mixingFrom(NULL), _mixingTo(0), _trackIndex(0), _loop(false), _holdPrevious(false), _eventThreshold(0), _attachmentThreshold(0), _drawOrderThreshold(0), _animationStart(0), _animationEnd(0), _animationLast(0), _nextAnimationLast(0), _delay(0), _trackTime(0), _trackLast(0), _nextTrackLast(0), _trackEnd(0), _timeScale(1.0f), _alpha(0), _mixTime(0), @@ -67,6 +67,10 @@ bool TrackEntry::getLoop() { return _loop; } void TrackEntry::setLoop(bool inValue) { _loop = inValue; } +bool TrackEntry::getHoldPrevious() { return _holdPrevious; } + +void TrackEntry::setHoldPrevious(bool inValue) { _holdPrevious = inValue; } + float TrackEntry::getDelay() { return _delay; } void TrackEntry::setDelay(float inValue) { _delay = inValue; } @@ -143,6 +147,8 @@ void TrackEntry::setMixDuration(float inValue) { _mixDuration = inValue; } TrackEntry *TrackEntry::getMixingFrom() { return _mixingFrom; } +TrackEntry *TrackEntry::getMixingTo() { return _mixingTo; } + void TrackEntry::setMixBlend(MixBlend blend) { _mixBlend = blend; } MixBlend TrackEntry::getMixBlend() { return _mixBlend; } @@ -155,67 +161,14 @@ void TrackEntry::setOnAnimationEventFunc(OnAnimationEventFunc inValue) { _onAnimationEventFunc = inValue; } -TrackEntry *TrackEntry::setTimelineData(TrackEntry *to, Vector &mixingToArray, Vector &propertyIDs) { - if (to != NULL) mixingToArray.add(to); - TrackEntry *lastEntry = _mixingFrom != NULL ? _mixingFrom->setTimelineData(this, mixingToArray, propertyIDs) : this; - if (to != NULL) mixingToArray.removeAt(mixingToArray.size() - 1); - - size_t mixingToLast = mixingToArray.size() - 1; - Vector &timelines = _animation->_timelines; - size_t timelinesCount = timelines.size(); - _timelineData.setSize(timelinesCount, 0); - _timelineDipMix.setSize(timelinesCount, 0); - - // outer: - size_t i = 0; - continue_outer: - for (; i < timelinesCount; ++i) { - int id = timelines[i]->getPropertyId(); - if (propertyIDs.contains(id)) { - _timelineData[i] = AnimationState::Subsequent; - } else { - propertyIDs.add(id); - - if (to == NULL || !to->hasTimeline(id)) { - _timelineData[i] = AnimationState::First; - } else { - for (int ii = mixingToLast; ii >= 0; --ii) { - TrackEntry *entry = mixingToArray[ii]; - if (!entry->hasTimeline(id)) { - if (entry->_mixDuration > 0) { - _timelineData[i] = AnimationState::DipMix; - _timelineDipMix[i] = entry; - i++; - goto continue_outer; // continue outer; - } - break; - } - } - _timelineData[i] = AnimationState::Dip; - } - } - } - - return lastEntry; -} - -bool TrackEntry::hasTimeline(int inId) { - Vector &timelines = _animation->_timelines; - for (size_t i = 0, n = timelines.size(); i < n; ++i) { - if (timelines[i]->getPropertyId() == inId) { - return true; - } - } - return false; -} - void TrackEntry::reset() { _animation = NULL; _next = NULL; _mixingFrom = NULL; + _mixingTo = NULL; - _timelineData.clear(); - _timelineDipMix.clear(); + _timelineMode.clear(); + _timelineHoldMix.clear(); _timelinesRotation.clear(); _onAnimationEventFunc = dummyOnAnimationEventFunc; @@ -314,8 +267,8 @@ void EventQueue::drain() { const int AnimationState::Subsequent = 0; const int AnimationState::First = 1; -const int AnimationState::Dip = 2; -const int AnimationState::DipMix = 3; +const int AnimationState::Hold = 2; +const int AnimationState::HoldMix = 3; AnimationState::AnimationState(AnimationStateData *data) : _data(data), @@ -400,6 +353,7 @@ void AnimationState::update(float delta) { // End mixing from entries once all have completed. TrackEntry *from = current._mixingFrom; current._mixingFrom = NULL; + if (from != NULL) from->_mixingTo = NULL; while (from != NULL) { _queue->end(from); from = from->_mixingFrom; @@ -441,13 +395,13 @@ bool AnimationState::apply(Skeleton &skeleton) { float animationLast = current._animationLast, animationTime = current.getAnimationTime(); size_t timelineCount = current._animation->_timelines.size(); Vector &timelines = current._animation->_timelines; - if (mix == 1 || blend == MixBlend_Add) { + if (i == 0 && (mix == 1 || blend == MixBlend_Add)) { for (size_t ii = 0; ii < timelineCount; ++ii) { timelines[ii]->apply(skeleton, animationLast, animationTime, &_events, mix, blend, MixDirection_In); } } else { - Vector &timelineData = current._timelineData; + Vector &timelineMode = current._timelineMode; bool firstFrame = current._timelinesRotation.size() == 0; if (firstFrame) { @@ -459,7 +413,7 @@ bool AnimationState::apply(Skeleton &skeleton) { Timeline *timeline = timelines[ii]; assert(timeline); - MixBlend timelineBlend = timelineData[ii] == AnimationState::Subsequent ? blend : MixBlend_Setup; + MixBlend timelineBlend = timelineMode[ii] == AnimationState::Subsequent ? blend : MixBlend_Setup; RotateTimeline *rotateTimeline = NULL; if (timeline->getRTTI().isExactly(RotateTimeline::rtti)) { @@ -519,6 +473,7 @@ void AnimationState::clearTrack(size_t trackIndex) { _queue->end(from); entry->_mixingFrom = NULL; + entry->_mixingTo = NULL; entry = from; } @@ -764,6 +719,7 @@ bool AnimationState::updateMixingFrom(TrackEntry *to, float delta) { // Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame). if (from->_totalAlpha == 0 || to->_mixDuration == 0) { to->_mixingFrom = from->_mixingFrom; + if (from->_mixingFrom != NULL) from->_mixingFrom->_mixingTo = to; to->_interruptAlpha = from->_interruptAlpha; _queue->end(from); } @@ -800,14 +756,14 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle float animationLast = from->_animationLast, animationTime = from->getAnimationTime(); Vector &timelines = from->_animation->_timelines; size_t timelineCount = timelines.size(); - float alphaDip = from->_alpha * to->_interruptAlpha, alphaMix = alphaDip * (1 - mix); + float alphaHold = from->_alpha * to->_interruptAlpha, alphaMix = alphaHold * (1 - mix); if (blend == MixBlend_Add) { for (size_t i = 0; i < timelineCount; i++) timelines[i]->apply(skeleton, animationLast, animationTime, eventBuffer, alphaMix, blend, MixDirection_Out); } else { - Vector &timelineData = from->_timelineData; - Vector &timelineDipMix = from->_timelineDipMix; + Vector &timelineMode = from->_timelineMode; + Vector &timelineHoldMix = from->_timelineHoldMix; bool firstFrame = from->_timelinesRotation.size() == 0; if (firstFrame) { @@ -821,7 +777,7 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle Timeline *timeline = timelines[i]; MixBlend timelineBlend; float alpha; - switch (timelineData[i]) { + switch (timelineMode[i]) { case AnimationState::Subsequent: if (!attachments && (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))) continue; if (!drawOrder && (timeline->getRTTI().isExactly(DrawOrderTimeline::rtti))) continue; @@ -832,14 +788,14 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle timelineBlend = MixBlend_Setup; alpha = alphaMix; break; - case AnimationState::Dip: + case AnimationState::Hold: timelineBlend = MixBlend_Setup; - alpha = alphaDip; + alpha = alphaHold; break; default: timelineBlend = MixBlend_Setup; - TrackEntry *dipMix = timelineDipMix[i]; - alpha = alphaDip * MathUtil::max(0.0f, 1.0f - dipMix->_mixTime / dipMix->_mixDuration); + TrackEntry *holdMix = timelineHoldMix[i]; + alpha = alphaHold * MathUtil::max(0.0f, 1.0f - holdMix->_mixTime / holdMix->_mixDuration); break; } from->_totalAlpha += alpha; @@ -912,6 +868,7 @@ void AnimationState::setCurrent(size_t index, TrackEntry *current, bool interrup } current->_mixingFrom = from; + from->_mixingTo = current; current->_mixTime = 0; // Store interrupted mix percentage. @@ -944,6 +901,7 @@ TrackEntry *AnimationState::newTrackEntry(size_t trackIndex, Animation *animatio entry._trackIndex = trackIndex; entry._animation = animation; entry._loop = loop; + entry._holdPrevious = 0; entry._eventThreshold = 0; entry._attachmentThreshold = 0; @@ -985,8 +943,70 @@ void AnimationState::animationsChanged() { for (size_t i = 0, n = _tracks.size(); i < n; ++i) { TrackEntry *entry = _tracks[i]; - if (entry != NULL && (i == 0 ||entry->_mixBlend != MixBlend_Add)) { - entry->setTimelineData(NULL, _mixingTo, _propertyIDs); + + while (entry->_mixingFrom != NULL) + entry = entry->_mixingFrom; + + do { + if (entry->_mixingTo == NULL || entry->_mixBlend != MixBlend_Add) setTimelineModes(entry); + entry = entry->_mixingTo; + } while (entry != NULL); + } +} + +void AnimationState::setTimelineModes(TrackEntry *entry) { + TrackEntry* to = entry->_mixingTo; + Vector &timelines = entry->_animation->_timelines; + size_t timelinesCount = timelines.size(); + Vector &timelineMode = entry->_timelineMode; + timelineMode.setSize(timelinesCount, 0); + Vector &timelineHoldMix = entry->_timelineHoldMix; + timelineHoldMix.setSize(timelinesCount, 0); + + if (to != NULL && to->_holdPrevious) { + for (size_t i = 0; i < timelinesCount; i++) { + int id = timelines[i]->getPropertyId(); + if (!_propertyIDs.contains(id)) _propertyIDs.add(id); + timelineMode[i] = AnimationState::Hold; + } + return; + } + + // outer: + size_t i = 0; + continue_outer: + for (; i < timelinesCount; ++i) { + int id = timelines[i]->getPropertyId(); + if (_propertyIDs.contains(id)) { + timelineMode[i] = AnimationState::Subsequent; + } else { + _propertyIDs.add(id); + + if (to == NULL || !hasTimeline(to, id)) { + timelineMode[i] = AnimationState::First; + } else { + for (TrackEntry *next = to->_mixingTo; next != NULL; next = next->_mixingTo) { + if (hasTimeline(next, id)) continue; + if (entry->_mixDuration > 0) { + timelineMode[i] = AnimationState::HoldMix; + timelineHoldMix[i] = entry; + i++; + goto continue_outer; // continue outer; + } + break; + } + timelineMode[i] = AnimationState::Hold; + } } } } + +bool AnimationState::hasTimeline(TrackEntry* entry, int inId) { + Vector &timelines = entry->_animation->_timelines; + for (size_t i = 0, n = timelines.size(); i < n; ++i) { + if (timelines[i]->getPropertyId() == inId) { + return true; + } + } + return false; +}