From df6de224e34d2f237797b8af3b0db8c52bf0b4b2 Mon Sep 17 00:00:00 2001 From: badlogic Date: Thu, 4 Mar 2021 17:30:45 +0100 Subject: [PATCH] [cpp] More 4.0 porting, animation state. --- spine-cpp/spine-cpp/include/spine/Animation.h | 6 +- .../spine-cpp/include/spine/AnimationState.h | 17 ++- .../spine-cpp/include/spine/DeformTimeline.h | 4 +- spine-cpp/spine-cpp/include/spine/HashMap.h | 8 ++ .../spine-cpp/src/spine/AnimationState.cpp | 127 ++++++++---------- .../src/spine/AttachmentTimeline.cpp | 2 - .../spine-cpp/src/spine/RotateTimeline.cpp | 2 +- 7 files changed, 88 insertions(+), 78 deletions(-) diff --git a/spine-cpp/spine-cpp/include/spine/Animation.h b/spine-cpp/spine-cpp/include/spine/Animation.h index ab2c606b4..c8bee750f 100644 --- a/spine-cpp/spine-cpp/include/spine/Animation.h +++ b/spine-cpp/spine-cpp/include/spine/Animation.h @@ -45,6 +45,8 @@ class Skeleton; class Event; +class AnimationState; + class SP_API Animation : public SpineObject { friend class AnimationState; @@ -115,8 +117,8 @@ public: void setDuration(float inValue); private: - Vector _timelines; - HashMap _timelineIds; + Vector _timelines; + HashMap _timelineIds; float _duration; String _name; diff --git a/spine-cpp/spine-cpp/include/spine/AnimationState.h b/spine-cpp/spine-cpp/include/spine/AnimationState.h index 300edf0a3..ce8ee5964 100644 --- a/spine-cpp/spine-cpp/include/spine/AnimationState.h +++ b/spine-cpp/spine-cpp/include/spine/AnimationState.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -94,6 +95,8 @@ namespace spine { /// The animation to apply for this track entry. Animation* getAnimation(); + TrackEntry* getPrevious(); + /// 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); @@ -112,6 +115,9 @@ namespace spine { bool getHoldPrevious(); void setHoldPrevious(bool inValue); + bool getReverse(); + void setReverse(bool inValue); + /// Seconds to postpone playing the animation. When a track entry is the current track entry, delay postpones incrementing /// the track time. When a track entry is queued, delay is the time from the start of the previous animation to when the /// track entry will become the current track entry. @@ -228,19 +234,21 @@ namespace spine { /// TrackEntry chooses the short way the first time it is applied and remembers that direction. void resetRotationDirections(); + float getTrackComplete(); + void setListener(AnimationStateListener listener); void setListener(AnimationStateListenerObject* listener); private: Animation* _animation; - + TrackEntry* _previous; TrackEntry* _next; TrackEntry* _mixingFrom; TrackEntry* _mixingTo; int _trackIndex; - bool _loop, _holdPrevious; + bool _loop, _holdPrevious, _reverse; float _eventThreshold, _attachmentThreshold, _drawOrderThreshold; float _animationStart, _animationEnd, _animationLast, _nextAnimationLast; float _delay, _trackTime, _trackLast, _nextTrackLast, _trackEnd, _timeScale; @@ -403,7 +411,7 @@ namespace spine { Vector _events; EventQueue* _queue; - HashMap _propertyIDs; + HashMap _propertyIDs; bool _animationsChanged; AnimationStateListener _listener; @@ -428,6 +436,9 @@ namespace spine { /// Sets the active TrackEntry for a given track number. void setCurrent(size_t index, TrackEntry *current, bool interrupt); + /// Removes the next entry and all entries after it for the specified entry. */ + void clearNext(TrackEntry *entry); + TrackEntry* expandToIndex(size_t index); /// Object-pooling version of new TrackEntry. Obtain an unused TrackEntry from the pool and clear/initialize its values. diff --git a/spine-cpp/spine-cpp/include/spine/DeformTimeline.h b/spine-cpp/spine-cpp/include/spine/DeformTimeline.h index 499e975a5..899510ce3 100644 --- a/spine-cpp/spine-cpp/include/spine/DeformTimeline.h +++ b/spine-cpp/spine-cpp/include/spine/DeformTimeline.h @@ -52,7 +52,7 @@ namespace spine { /// Sets the time and value of the specified keyframe. void setFrame(int frameIndex, float time, Vector &vertices); - Vector > &getVertices(); + Vector > &getVertices(); VertexAttachment *getAttachment(); @@ -70,7 +70,7 @@ namespace spine { protected: int _slotIndex; - Vector > _vertices; + Vector > _vertices; VertexAttachment *_attachment; }; diff --git a/spine-cpp/spine-cpp/include/spine/HashMap.h b/spine-cpp/spine-cpp/include/spine/HashMap.h index 9a1a1ecdc..6e4ac7952 100755 --- a/spine-cpp/spine-cpp/include/spine/HashMap.h +++ b/spine-cpp/spine-cpp/include/spine/HashMap.h @@ -129,6 +129,14 @@ public: } } + bool addAll(Vector &keys, const V &value) { + size_t oldSize = _size; + for (size_t i = 0; i < keys.size(); i++) { + put(keys[i], value); + } + return _size != oldSize; + } + bool containsKey(const K &key) { return find(key) != NULL; } diff --git a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp index 6185b8855..fc6e3d8b7 100644 --- a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp +++ b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp @@ -57,7 +57,7 @@ void dummyOnAnimationEventFunc(AnimationState *state, spine::EventType type, Tra SP_UNUSED(event); } -TrackEntry::TrackEntry() : _animation(NULL), _next(NULL), _mixingFrom(NULL), _mixingTo(0), _trackIndex(0), _loop(false), _holdPrevious(false), +TrackEntry::TrackEntry() : _animation(NULL), _previous(NULL), _next(NULL), _mixingFrom(NULL), _mixingTo(0), _trackIndex(0), _loop(false), _holdPrevious(false), _reverse(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), @@ -71,6 +71,8 @@ int TrackEntry::getTrackIndex() { return _trackIndex; } Animation *TrackEntry::getAnimation() { return _animation; } +TrackEntry* TrackEntry::getPrevious() { return _previous; } + bool TrackEntry::getLoop() { return _loop; } void TrackEntry::setLoop(bool inValue) { _loop = inValue; } @@ -79,6 +81,10 @@ bool TrackEntry::getHoldPrevious() { return _holdPrevious; } void TrackEntry::setHoldPrevious(bool inValue) { _holdPrevious = inValue; } +bool TrackEntry::getReverse() { return _reverse; } + +void TrackEntry::setReverse(bool inValue) { _reverse = inValue; } + float TrackEntry::getDelay() { return _delay; } void TrackEntry::setDelay(float inValue) { _delay = inValue; } @@ -174,6 +180,7 @@ void TrackEntry::setListener(AnimationStateListenerObject* inValue) { void TrackEntry::reset() { _animation = NULL; + _previous = NULL; _next = NULL; _mixingFrom = NULL; _mixingTo = NULL; @@ -188,6 +195,15 @@ void TrackEntry::reset() { _listenerObject = NULL; } +float TrackEntry::getTrackComplete() { + float duration = _animationEnd - _animationStart; + if (duration != 0) { + if (_loop) return duration * (1 + (int)(_trackTime / duration)); // Completion of next loop. + if (_trackTime < duration) return duration; // Before duration. + } + return _trackTime; // Next update. +} + EventQueueEntry::EventQueueEntry(EventType eventType, TrackEntry *trackEntry, Event *event) : _type(eventType), _entry(trackEntry), @@ -412,15 +428,21 @@ bool AnimationState::apply(Skeleton &skeleton) { // apply current entry. float animationLast = current._animationLast, animationTime = current.getAnimationTime(); + float applyTime = animationTime; + Vector *applyEvents = &_events; + if (current._reverse) { + applyTime = current._animation->getDuration() - applyTime; + applyEvents = NULL; + } size_t timelineCount = current._animation->_timelines.size(); Vector &timelines = current._animation->_timelines; if ((i == 0 && mix == 1) || blend == MixBlend_Add) { for (size_t ii = 0; ii < timelineCount; ++ii) { Timeline *timeline = timelines[ii]; if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) - applyAttachmentTimeline(static_cast(timeline), skeleton, animationTime, blend, true); + applyAttachmentTimeline(static_cast(timeline), skeleton, applyTime, blend, true); else - timeline->apply(skeleton, animationLast, animationTime, &_events, mix, blend, MixDirection_In); + timeline->apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection_In); } } else { Vector &timelineMode = current._timelineMode; @@ -436,11 +458,11 @@ bool AnimationState::apply(Skeleton &skeleton) { MixBlend timelineBlend = timelineMode[ii] == Subsequent ? blend : MixBlend_Setup; if (timeline->getRTTI().isExactly(RotateTimeline::rtti)) - applyRotateTimeline(static_cast(timeline), skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame); + applyRotateTimeline(static_cast(timeline), skeleton, applyTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame); else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) - applyAttachmentTimeline(static_cast(timeline), skeleton, animationTime, timelineBlend, true); + applyAttachmentTimeline(static_cast(timeline), skeleton, applyTime, timelineBlend, true); else - timeline->apply(skeleton, animationLast, animationTime, &_events, mix, timelineBlend, MixDirection_In); + timeline->apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend, MixDirection_In); } } @@ -555,19 +577,8 @@ TrackEntry *AnimationState::addAnimation(size_t trackIndex, Animation *animation _queue->drain(); } else { last->_next = entry; - if (delay <= 0) { - float duration = last->_animationEnd - last->_animationStart; - if (duration != 0) { - if (last->_loop) { - delay += duration * (1 + (int) (last->_trackTime / duration)); - } else { - delay += MathUtil::max(duration, last->_trackTime); - } - delay -= _data->getMix(last->_animation, animation); - } else { - delay = last->_trackTime; - } - } + entry->_previous = last; + if (delay <= 0) delay += last->getTrackComplete() - entry->_mixDuration; } entry->_delay = delay; @@ -582,14 +593,11 @@ TrackEntry *AnimationState::setEmptyAnimation(size_t trackIndex, float mixDurati } TrackEntry *AnimationState::addEmptyAnimation(size_t trackIndex, float mixDuration, float delay) { - if (delay <= 0) { - delay -= mixDuration; - } - - TrackEntry *entry = addAnimation(trackIndex, AnimationState::getEmptyAnimation(), false, delay); + TrackEntry *entry = addAnimation(trackIndex, AnimationState::getEmptyAnimation(), false, delay <= 0 ? 1 : delay); entry->_mixDuration = mixDuration; entry->_trackEnd = mixDuration; - return entry; + if (delay <= 0 && entry->_previous != NULL) entry->_delay = entry->_previous->getTrackComplete() - entry->_mixDuration; + return entry; } void AnimationState::setEmptyAnimations(float mixDuration) { @@ -657,12 +665,7 @@ void AnimationState::applyAttachmentTimeline(AttachmentTimeline* attachmentTimel if (blend == MixBlend_Setup || blend == MixBlend_First) setAttachment(skeleton, *slot, slot->getData().getAttachmentName(), attachments); } else { - int frameIndex = 0; - if (time >= frames[attachmentTimeline->getFrames().size() - 1]) - frameIndex = attachmentTimeline->getFrames().size() - 1; - else - frameIndex = Animation::binarySearch(frames, time) - 1; - setAttachment(skeleton, *slot, attachmentTimeline->getAttachmentNames()[frameIndex], attachments); + setAttachment(skeleton, *slot, attachmentTimeline->getAttachmentNames()[Animation::search(frames, time)], attachments); } /* If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later.*/ @@ -696,21 +699,7 @@ void AnimationState::applyRotateTimeline(RotateTimeline *rotateTimeline, Skeleto } } else { r1 = blend == MixBlend_Setup ? bone->_data._rotation : bone->_rotation; - if (time >= frames[frames.size() - RotateTimeline::ENTRIES]) { - // Time is after last frame. - r2 = bone->_data._rotation + frames[frames.size() + RotateTimeline::PREV_ROTATION]; - } else { - // Interpolate between the previous frame and the current frame. - int frame = Animation::binarySearch(frames, time, RotateTimeline::ENTRIES); - float prevRotation = frames[frame + RotateTimeline::PREV_ROTATION]; - float frameTime = frames[frame]; - float percent = rotateTimeline->getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + - RotateTimeline::PREV_TIME] - frameTime)); - r2 = frames[frame + RotateTimeline::ROTATION] - prevRotation; - r2 -= (16384 - (int) (16384.499999999996 - r2 / 360)) * 360; - r2 = prevRotation + r2 * percent + bone->_data._rotation; - r2 -= (16384 - (int) (16384.499999999996 - r2 / 360)) * 360; - } + r2 = bone->_data._rotation + rotateTimeline->getCurveValue(time); } // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. @@ -743,8 +732,7 @@ void AnimationState::applyRotateTimeline(RotateTimeline *rotateTimeline, Skeleto timelinesRotation[i] = total; } timelinesRotation[i + 1] = diff; - r1 += total * alpha; - bone->_rotation = r1 - (16384 - (int) (16384.499999999996 - r1 / 360)) * 360; + bone->_rotation = r1 + total * alpha; } bool AnimationState::updateMixingFrom(TrackEntry *to, float delta) { @@ -793,16 +781,22 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle if (blend != MixBlend_First) blend = from->_mixBlend; } - Vector *eventBuffer = mix < from->_eventThreshold ? &_events : NULL; bool attachments = mix < from->_attachmentThreshold, drawOrder = mix < from->_drawOrderThreshold; - float animationLast = from->_animationLast, animationTime = from->getAnimationTime(); Vector &timelines = from->_animation->_timelines; size_t timelineCount = timelines.size(); float alphaHold = from->_alpha * to->_interruptAlpha, alphaMix = alphaHold * (1 - mix); + float animationLast = from->_animationLast, animationTime = from->getAnimationTime(); + float applyTime = animationTime; + Vector *events = NULL; + if (from->_reverse) { + applyTime = from->_animation->_duration - applyTime; + } else { + if (mix < from->_eventThreshold) events = &_events; + } if (blend == MixBlend_Add) { for (size_t i = 0; i < timelineCount; i++) - timelines[i]->apply(skeleton, animationLast, animationTime, eventBuffer, alphaMix, blend, MixDirection_Out); + timelines[i]->apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection_Out); } else { Vector &timelineMode = from->_timelineMode; Vector &timelineHoldMix = from->_timelineHoldMix; @@ -844,13 +838,13 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle } from->_totalAlpha += alpha; if ((timeline->getRTTI().isExactly(RotateTimeline::rtti))) { - applyRotateTimeline((RotateTimeline*)timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame); + applyRotateTimeline((RotateTimeline*)timeline, skeleton, applyTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame); } else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) { - applyAttachmentTimeline(static_cast(timeline), skeleton, animationTime, timelineBlend, attachments); + applyAttachmentTimeline(static_cast(timeline), skeleton, applyTime, timelineBlend, attachments); } else { if (drawOrder && timeline->getRTTI().isExactly(DrawOrderTimeline::rtti) && timelineBlend == MixBlend_Setup) direction = MixDirection_In; - timeline->apply(skeleton, animationLast, animationTime, eventBuffer, alpha, timelineBlend, direction); + timeline->apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction); } } } @@ -897,13 +891,18 @@ void AnimationState::queueEvents(TrackEntry *entry, float animationTime) { for (; i < n; ++i) { Event *e = _events[i]; if (e->_time < animationStart) continue; // Discard events outside animation start/end. - _queue->event(entry, _events[i]); + _queue->event(entry, e); } } +void AnimationState::clearNext(TrackEntry *entry) { + disposeNext(entry->_next); +} + void AnimationState::setCurrent(size_t index, TrackEntry *current, bool interrupt) { TrackEntry *from = expandToIndex(index); _tracks[index] = current; + current->_previous = NULL; if (from != NULL) { if (interrupt) _queue->interrupt(from); @@ -1002,13 +1001,7 @@ void AnimationState::computeHold(TrackEntry *entry) { if (to != NULL && to->_holdPrevious) { for (size_t i = 0; i < timelinesCount; i++) { - int id = timelines[i]->getPropertyId(); - if (!_propertyIDs.containsKey(id)) { - _propertyIDs.put(id, true); - timelineMode[i] = HoldFirst; - } else { - timelineMode[i] = HoldSubsequent; - } + timelineMode[i] = _propertyIDs.addAll(timelines[i]->getPropertyIds(), true) ? HoldFirst : HoldSubsequent; } return; } @@ -1018,19 +1011,17 @@ void AnimationState::computeHold(TrackEntry *entry) { continue_outer: for (; i < timelinesCount; ++i) { Timeline *timeline = timelines[i]; - int id = timeline->getPropertyId(); - if (_propertyIDs.containsKey(id)) { + Vector &ids = timeline->getPropertyIds(); + if (!_propertyIDs.addAll(ids, true)) { timelineMode[i] = Subsequent; } else { - _propertyIDs.put(id, true); - if (to == NULL || timeline->getRTTI().isExactly(AttachmentTimeline::rtti) || timeline->getRTTI().isExactly(DrawOrderTimeline::rtti) || - timeline->getRTTI().isExactly(EventTimeline::rtti) || !to->_animation->hasTimeline(id)) { + timeline->getRTTI().isExactly(EventTimeline::rtti) || !to->_animation->hasTimeline(ids)) { timelineMode[i] = First; } else { for (TrackEntry *next = to->_mixingTo; next != NULL; next = next->_mixingTo) { - if (next->_animation->hasTimeline(id)) continue; + if (next->_animation->hasTimeline(ids)) continue; if (next->_mixDuration > 0) { timelineMode[i] = HoldMix; timelineHoldMix[i] = next; diff --git a/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp b/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp index 556f99010..30f6b5dd7 100644 --- a/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp +++ b/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp @@ -67,7 +67,6 @@ void AttachmentTimeline::apply(Skeleton &skeleton, float lastTime, float time, V SP_UNUSED(pEvents); SP_UNUSED(alpha); - String *attachmentName; Slot *slot = skeleton._slots[_slotIndex]; if (!slot->_bone._active) return; @@ -84,7 +83,6 @@ void AttachmentTimeline::apply(Skeleton &skeleton, float lastTime, float time, V return; } - size_t frameIndex; if (time < _frames[0]) { if (blend == MixBlend_Setup || blend == MixBlend_First) setAttachment(skeleton, *slot, &slot->_data._attachmentName); return; diff --git a/spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp b/spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp index b6eda65f0..c8b191deb 100644 --- a/spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp +++ b/spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp @@ -83,4 +83,4 @@ void RotateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto case MixBlend_Add: bone->_rotation += r * alpha; } -} \ No newline at end of file +}