mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 01:36:02 +08:00
[cpp] More 4.0 porting, animation state.
This commit is contained in:
parent
7b154fb92f
commit
df6de224e3
@ -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;
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -83,4 +83,4 @@ void RotateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
|
||||
case MixBlend_Add:
|
||||
bone->_rotation += r * alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user