[cpp] Port AnimationState deform mixing while attachment timelines mix out. See #1653.

This commit is contained in:
badlogic 2020-04-17 12:06:36 +02:00
parent 7732838d6f
commit 6bda0c236b
7 changed files with 107 additions and 66 deletions

View File

@ -36,6 +36,7 @@
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/HasRendererObject.h>
#include "Slot.h"
#ifdef SPINE_USE_STD_FUNCTION
#include <functional>
@ -59,6 +60,7 @@ namespace spine {
class AnimationStateData;
class Skeleton;
class RotateTimeline;
class AttachmentTimeline;
#ifdef SPINE_USE_STD_FUNCTION
typedef std::function<void (AnimationState* state, EventType type, TrackEntry* entry, Event* event)> 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<float>& 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 */

View File

@ -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<float>& getFrames();
const Vector<String>& getAttachmentNames();
Vector<float>& getFrames();
Vector<String>& getAttachmentNames();
size_t getFrameCount();
private:
size_t _slotIndex;
Vector<float> _frames;
Vector<String> _attachmentNames;
};
void setAttachment(Skeleton& skeleton, Slot& slot, String* attachmentName);
};
}
#endif /* Spine_AttachmentTimeline_h */

View File

@ -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<float> _deform;
};

View File

@ -43,6 +43,8 @@
#include <spine/AttachmentTimeline.h>
#include <spine/DrawOrderTimeline.h>
#include <spine/EventTimeline.h>
#include <spine/Slot.h>
#include <spine/SlotData.h>
#include <float.h>
@ -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<Timeline *> &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<AttachmentTimeline *>(timeline), skeleton, animationTime, blend, true);
else
timeline->apply(skeleton, animationLast, animationTime, &_events, mix, blend, MixDirection_In);
}
} else {
Vector<int> &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<RotateTimeline *>(timeline);
if (rotateTimeline != NULL)
applyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
if (timeline->getRTTI().isExactly(RotateTimeline::rtti))
applyRotateTimeline(static_cast<RotateTimeline *>(timeline), skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(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<Slot*>& 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<float>& 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<float> &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<AttachmentTimeline*>(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<Timeline *> &timelines = entry->_animation->_timelines;
size_t timelinesCount = timelines.size();
Vector<int> &timelineMode = entry->_timelineMode;
for (size_t i = 0; i < timelinesCount; i++) {
if (timelines[i]->getRTTI().isExactly(AttachmentTimeline::rtti)) {
AttachmentTimeline *timeline = static_cast<AttachmentTimeline *>(timelines[i]);
if (!_propertyIDs.containsKey(timeline->getSlotIndex()))
_propertyIDs.put(timeline->getSlotIndex(), true);
else
timelineMode[i] |= NotLast;
}
}
}

View File

@ -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<Event *> *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<float> &AttachmentTimeline::getFrames() {
Vector<float> &AttachmentTimeline::getFrames() {
return _frames;
}
const Vector<String> &AttachmentTimeline::getAttachmentNames() {
Vector<String> &AttachmentTimeline::getAttachmentNames() {
return _attachmentNames;
}

View File

@ -66,11 +66,13 @@ void DrawOrderTimeline::apply(Skeleton &skeleton, float lastTime, float time, Ve
Vector<Slot *> &drawOrder = skeleton._drawOrder;
Vector<Slot *> &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;
}

View File

@ -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;
}