mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-15 03:21:35 +08:00
[cpp] Port AnimationState deform mixing while attachment timelines mix out. See #1653.
This commit is contained in:
parent
7732838d6f
commit
6bda0c236b
@ -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 */
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user