[cpp] More 4.0 porting, animation state.

This commit is contained in:
badlogic 2021-03-04 17:30:45 +01:00
parent 7b154fb92f
commit df6de224e3
7 changed files with 88 additions and 78 deletions

View File

@ -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<Timeline *> _timelines;
HashMap<PropertyId, bool> _timelineIds;
Vector<Timeline *> _timelines;
HashMap<PropertyId, bool> _timelineIds;
float _duration;
String _name;

View File

@ -32,6 +32,7 @@
#include <spine/Vector.h>
#include <spine/Pool.h>
#include <spine/Property.h>
#include <spine/MixBlend.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
@ -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<Event*> _events;
EventQueue* _queue;
HashMap<int, bool> _propertyIDs;
HashMap<PropertyId, bool> _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.

View File

@ -52,7 +52,7 @@ namespace spine {
/// Sets the time and value of the specified keyframe.
void setFrame(int frameIndex, float time, Vector<float> &vertices);
Vector <Vector<float>> &getVertices();
Vector <Vector<float> > &getVertices();
VertexAttachment *getAttachment();
@ -70,7 +70,7 @@ namespace spine {
protected:
int _slotIndex;
Vector <Vector<float>> _vertices;
Vector <Vector<float> > _vertices;
VertexAttachment *_attachment;
};

View File

@ -129,6 +129,14 @@ public:
}
}
bool addAll(Vector<K> &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;
}

View File

@ -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<Event *> *applyEvents = &_events;
if (current._reverse) {
applyTime = current._animation->getDuration() - applyTime;
applyEvents = NULL;
}
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) {
Timeline *timeline = timelines[ii];
if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, animationTime, blend, true);
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(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<int> &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<RotateTimeline *>(timeline), skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
applyRotateTimeline(static_cast<RotateTimeline *>(timeline), skeleton, applyTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, animationTime, timelineBlend, true);
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(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<Event *> *eventBuffer = mix < from->_eventThreshold ? &_events : NULL;
bool attachments = mix < from->_attachmentThreshold, drawOrder = mix < from->_drawOrderThreshold;
float animationLast = from->_animationLast, animationTime = from->getAnimationTime();
Vector<Timeline *> &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<Event *> *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<int> &timelineMode = from->_timelineMode;
Vector<TrackEntry *> &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<AttachmentTimeline*>(timeline), skeleton, animationTime, timelineBlend, attachments);
applyAttachmentTimeline(static_cast<AttachmentTimeline*>(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<PropertyId> &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;

View File

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

View File

@ -83,4 +83,4 @@ void RotateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
case MixBlend_Add:
bone->_rotation += r * alpha;
}
}
}