From 6bda0c236b0f27b19a271cf906c24ffe6f4be553 Mon Sep 17 00:00:00 2001 From: badlogic Date: Fri, 17 Apr 2020 12:06:36 +0200 Subject: [PATCH] [cpp] Port AnimationState deform mixing while attachment timelines mix out. See #1653. --- .../spine-cpp/include/spine/AnimationState.h | 9 +- .../include/spine/AttachmentTimeline.h | 9 +- spine-cpp/spine-cpp/include/spine/Slot.h | 5 + .../spine-cpp/src/spine/AnimationState.cpp | 113 ++++++++++-------- .../src/spine/AttachmentTimeline.cpp | 16 +-- .../spine-cpp/src/spine/DrawOrderTimeline.cpp | 12 +- spine-cpp/spine-cpp/src/spine/Slot.cpp | 9 ++ 7 files changed, 107 insertions(+), 66 deletions(-) diff --git a/spine-cpp/spine-cpp/include/spine/AnimationState.h b/spine-cpp/spine-cpp/include/spine/AnimationState.h index 818d3e366..8f165da52 100644 --- a/spine-cpp/spine-cpp/include/spine/AnimationState.h +++ b/spine-cpp/spine-cpp/include/spine/AnimationState.h @@ -36,6 +36,7 @@ #include #include #include +#include "Slot.h" #ifdef SPINE_USE_STD_FUNCTION #include @@ -59,6 +60,7 @@ namespace spine { class AnimationStateData; class Skeleton; class RotateTimeline; + class AttachmentTimeline; #ifdef SPINE_USE_STD_FUNCTION typedef std::function AnimationStateListener; @@ -399,11 +401,14 @@ namespace spine { AnimationStateListener _listener; AnimationStateListenerObject* _listenerObject; + int _unkeyedState; + float _timeScale; static Animation* getEmptyAnimation(); static void applyRotateTimeline(RotateTimeline* rotateTimeline, Skeleton& skeleton, float time, float alpha, MixBlend pose, Vector& timelinesRotation, size_t i, bool firstFrame); + void applyAttachmentTimeline(AttachmentTimeline* attachmentTimeline, Skeleton& skeleton, float animationTime, MixBlend pose, bool firstFrame); /// Returns true when all mixing from entries are complete. bool updateMixingFrom(TrackEntry* to, float delta); @@ -428,8 +433,8 @@ namespace spine { void computeHold(TrackEntry *entry); - void computeNotLast(TrackEntry *entry); - }; + void setAttachment(Skeleton &skeleton, spine::Slot &slot, const String &attachmentName, bool attachments); + }; } #endif /* Spine_AnimationState_h */ diff --git a/spine-cpp/spine-cpp/include/spine/AttachmentTimeline.h b/spine-cpp/spine-cpp/include/spine/AttachmentTimeline.h index c8a022a33..3aed1015c 100644 --- a/spine-cpp/spine-cpp/include/spine/AttachmentTimeline.h +++ b/spine-cpp/spine-cpp/include/spine/AttachmentTimeline.h @@ -40,6 +40,7 @@ namespace spine { class Skeleton; + class Slot; class Event; class SP_API AttachmentTimeline : public Timeline { @@ -60,14 +61,16 @@ namespace spine { size_t getSlotIndex(); void setSlotIndex(size_t inValue); - const Vector& getFrames(); - const Vector& getAttachmentNames(); + Vector& getFrames(); + Vector& getAttachmentNames(); size_t getFrameCount(); private: size_t _slotIndex; Vector _frames; Vector _attachmentNames; - }; + + void setAttachment(Skeleton& skeleton, Slot& slot, String* attachmentName); + }; } #endif /* Spine_AttachmentTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Slot.h b/spine-cpp/spine-cpp/include/spine/Slot.h index 0f2f5144f..f75fbbf0a 100644 --- a/spine-cpp/spine-cpp/include/spine/Slot.h +++ b/spine-cpp/spine-cpp/include/spine/Slot.h @@ -102,6 +102,10 @@ public: void setAttachment(Attachment *inValue); + int getAttachmentState(); + + void setAttachmentState(int state); + float getAttachmentTime(); void setAttachmentTime(float inValue); @@ -116,6 +120,7 @@ private: Color _darkColor; bool _hasDarkColor; Attachment *_attachment; + int _attachmentState; float _attachmentTime; Vector _deform; }; diff --git a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp index 26bb86471..657770396 100644 --- a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp +++ b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include @@ -290,7 +292,9 @@ const int Subsequent = 0; const int First = 1; const int Hold = 2; const int HoldMix = 3; -const int NotLast = 4; + +const int Setup = 1; +const int Current = 2; AnimationState::AnimationState(AnimationStateData *data) : _data(data), @@ -298,6 +302,7 @@ AnimationState::AnimationState(AnimationStateData *data) : _animationsChanged(false), _listener(dummyOnAnimationEventFunc), _listenerObject(NULL), + _unkeyedState(0), _timeScale(1) { } @@ -418,8 +423,13 @@ bool AnimationState::apply(Skeleton &skeleton) { 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) - timelines[ii]->apply(skeleton, animationLast, animationTime, &_events, mix, blend, MixDirection_In); + 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); + else + timeline->apply(skeleton, animationLast, animationTime, &_events, mix, blend, MixDirection_In); + } } else { Vector &timelineMode = current._timelineMode; @@ -431,13 +441,12 @@ bool AnimationState::apply(Skeleton &skeleton) { Timeline *timeline = timelines[ii]; assert(timeline); - MixBlend timelineBlend = (timelineMode[ii] & (NotLast - 1)) == Subsequent ? blend : MixBlend_Setup; + MixBlend timelineBlend = timelineMode[ii] == Subsequent ? blend : MixBlend_Setup; - RotateTimeline *rotateTimeline = NULL; - if (timeline->getRTTI().isExactly(RotateTimeline::rtti)) rotateTimeline = static_cast(timeline); - - if (rotateTimeline != NULL) - applyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame); + if (timeline->getRTTI().isExactly(RotateTimeline::rtti)) + applyRotateTimeline(static_cast(timeline), skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame); + else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) + applyAttachmentTimeline(static_cast(timeline), skeleton, animationTime, timelineBlend, true); else timeline->apply(skeleton, animationLast, animationTime, &_events, mix, timelineBlend, MixDirection_In); } @@ -449,6 +458,17 @@ bool AnimationState::apply(Skeleton &skeleton) { current._nextTrackLast = current._trackTime; } + int setupState = _unkeyedState + Setup; + Vector& slots = skeleton.getSlots(); + for (int i = 0, n = slots.size(); i < n; i++) { + Slot* slot = slots[i]; + if (slot->getAttachmentState() == setupState) { + const String& attachmentName = slot->getData().getAttachmentName(); + slot->setAttachment(attachmentName.isEmpty() ? NULL : skeleton.getAttachment(slot->getData().getIndex(), attachmentName)); + } + } + _unkeyedState += 2; + _queue->drain(); return applied; } @@ -636,6 +656,28 @@ Animation *AnimationState::getEmptyAnimation() { return &ret; } +void AnimationState::applyAttachmentTimeline(AttachmentTimeline* attachmentTimeline, Skeleton& skeleton, float time, MixBlend blend, bool attachments) { + Slot* slot = skeleton.getSlots()[attachmentTimeline->getSlotIndex()]; + if (!slot->getBone().isActive()) return; + + Vector& frames = attachmentTimeline->getFrames(); + if (time < frames[0]) { + 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); + } + + /* If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later.*/ + if (slot->getAttachmentState() <= _unkeyedState) slot->setAttachmentState(_unkeyedState + Setup); +} + + void AnimationState::applyRotateTimeline(RotateTimeline *rotateTimeline, Skeleton &skeleton, float time, float alpha, MixBlend blend, Vector &timelinesRotation, size_t i, bool firstFrame ) { @@ -784,14 +826,10 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle MixDirection direction = MixDirection_Out; MixBlend timelineBlend; float alpha; - switch (timelineMode[i] & (NotLast - 1)) { + switch (timelineMode[i]) { case Subsequent: - timelineBlend = blend; - if (!attachments && (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))) { - if ((timelineMode[i] & NotLast) == NotLast) continue; - timelineBlend = MixBlend_Setup; - } if (!drawOrder && (timeline->getRTTI().isExactly(DrawOrderTimeline::rtti))) continue; + timelineBlend = blend; alpha = alphaMix; break; case First: @@ -811,15 +849,12 @@ 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); - } else { - if (timelineBlend == MixBlend_Setup) { - if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) { - if (attachments || (timelineMode[i] & NotLast) == NotLast) direction = MixDirection_In; - } else if (timeline->getRTTI().isExactly(DrawOrderTimeline::rtti)) { - if (drawOrder) direction = MixDirection_In; - } - } - timeline->apply(skeleton, animationLast, animationTime, eventBuffer, alpha, timelineBlend, direction); + } else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) { + applyAttachmentTimeline(static_cast(timeline), skeleton, animationTime, 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); } } } @@ -835,6 +870,11 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle return mix; } +void AnimationState::setAttachment(Skeleton& skeleton, Slot& slot, const String& attachmentName, bool attachments) { + slot.setAttachment(attachmentName.isEmpty() ? NULL : skeleton.getAttachment(slot.getData().getIndex(), attachmentName)); + if (attachments) slot.setAttachmentState(_unkeyedState + Current); +} + void AnimationState::queueEvents(TrackEntry *entry, float animationTime) { float animationStart = entry->_animationStart, animationEnd = entry->_animationEnd; float duration = animationEnd - animationStart; @@ -953,15 +993,6 @@ void AnimationState::animationsChanged() { entry = entry->_mixingTo; } while (entry != NULL); } - - _propertyIDs.clear(); - for (int i = (int)_tracks.size() - 1; i >= 0; i--) { - TrackEntry *entry = _tracks[i]; - while (entry) { - computeNotLast(entry); - entry = entry->_mixingFrom; - } - } } void AnimationState::computeHold(TrackEntry *entry) { @@ -1013,19 +1044,3 @@ void AnimationState::computeHold(TrackEntry *entry) { } } } - -void AnimationState::computeNotLast(TrackEntry *entry) { - Vector &timelines = entry->_animation->_timelines; - size_t timelinesCount = timelines.size(); - Vector &timelineMode = entry->_timelineMode; - - for (size_t i = 0; i < timelinesCount; i++) { - if (timelines[i]->getRTTI().isExactly(AttachmentTimeline::rtti)) { - AttachmentTimeline *timeline = static_cast(timelines[i]); - if (!_propertyIDs.containsKey(timeline->getSlotIndex())) - _propertyIDs.put(timeline->getSlotIndex(), true); - else - timelineMode[i] |= NotLast; - } - } -} diff --git a/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp b/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp index e71e4e18f..06e83013b 100644 --- a/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp +++ b/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp @@ -57,6 +57,10 @@ AttachmentTimeline::AttachmentTimeline(int frameCount) : Timeline(), _slotIndex( } } +void AttachmentTimeline::setAttachment(Skeleton& skeleton, Slot& slot, String* attachmentName) { + slot.setAttachment(attachmentName == NULL || attachmentName->isEmpty() ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName)); +} + void AttachmentTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, MixBlend blend, MixDirection direction ) { @@ -71,17 +75,15 @@ void AttachmentTimeline::apply(Skeleton &skeleton, float lastTime, float time, V Slot &slot = *slotP; if (!slot._bone.isActive()) return; - if (direction == MixDirection_Out && blend == MixBlend_Setup) { - attachmentName = &slot._data._attachmentName; - slot.setAttachment(attachmentName->length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName)); + if (direction == MixDirection_Out) { + if (blend == MixBlend_Setup) setAttachment(skeleton, slot, &slot._data._attachmentName); return; } if (time < _frames[0]) { // Time is before first frame. if (blend == MixBlend_Setup || blend == MixBlend_First) { - attachmentName = &slot._data._attachmentName; - slot.setAttachment(attachmentName->length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName)); + setAttachment(skeleton, slot, &slot._data._attachmentName); } return; } @@ -115,11 +117,11 @@ void AttachmentTimeline::setSlotIndex(size_t inValue) { _slotIndex = inValue; } -const Vector &AttachmentTimeline::getFrames() { +Vector &AttachmentTimeline::getFrames() { return _frames; } -const Vector &AttachmentTimeline::getAttachmentNames() { +Vector &AttachmentTimeline::getAttachmentNames() { return _attachmentNames; } diff --git a/spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp b/spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp index 9f4028891..c0c2c8dbd 100644 --- a/spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp +++ b/spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp @@ -66,11 +66,13 @@ void DrawOrderTimeline::apply(Skeleton &skeleton, float lastTime, float time, Ve Vector &drawOrder = skeleton._drawOrder; Vector &slots = skeleton._slots; - if (direction == MixDirection_Out && blend == MixBlend_Setup) { - drawOrder.clear(); - drawOrder.ensureCapacity(slots.size()); - for (size_t i = 0, n = slots.size(); i < n; ++i) - drawOrder.add(slots[i]); + if (direction == MixDirection_Out) { + if (blend == MixBlend_Setup) { + drawOrder.clear(); + drawOrder.ensureCapacity(slots.size()); + for (size_t i = 0, n = slots.size(); i < n; ++i) + drawOrder.add(slots[i]); + } return; } diff --git a/spine-cpp/spine-cpp/src/spine/Slot.cpp b/spine-cpp/spine-cpp/src/spine/Slot.cpp index 0cc8f23e6..f45d88c46 100644 --- a/spine-cpp/spine-cpp/src/spine/Slot.cpp +++ b/spine-cpp/spine-cpp/src/spine/Slot.cpp @@ -48,6 +48,7 @@ Slot::Slot(SlotData &data, Bone &bone) : _darkColor(0, 0, 0, 0), _hasDarkColor(data.hasDarkColor()), _attachment(NULL), + _attachmentState(0), _attachmentTime(0) { setToSetupPose(); } @@ -102,6 +103,14 @@ void Slot::setAttachment(Attachment *inValue) { _deform.clear(); } +int Slot::getAttachmentState() { + return _attachmentState; +} + +void Slot::setAttachmentState(int state) { + _attachmentState = state; +} + float Slot::getAttachmentTime() { return _skeleton.getTime() - _attachmentTime; }