This commit is contained in:
Stephen Gowen 2017-11-28 19:30:27 -05:00
parent 1449af5864
commit d5c47a709a
3 changed files with 351 additions and 267 deletions

View File

@ -45,8 +45,8 @@ namespace Spine
class Animation
{
friend class RotateTimeline;
friend class TranslateTimeline;
friend class AnimationState;
friend class TrackEntry;
friend class AnimationStateData;
friend class AttachmentTimeline;
@ -58,6 +58,7 @@ namespace Spine
friend class PathConstraintMixTimeline;
friend class PathConstraintPositionTimeline;
friend class PathConstraintSpacingTimeline;
friend class RotateTimeline;
friend class ScaleTimeline;
friend class ShearTimeline;
friend class TransformConstraintTimeline;

View File

@ -32,10 +32,27 @@
#define Spine_AnimationState_h
#include <spine/Vector.h>
#include <spine/Pool.h>
namespace Spine
{
enum EventType
{
EventType_Start,
EventType_Interrupt,
EventType_End,
EventType_Complete,
EventType_Dispose,
EventType_Event
};
class AnimationState;
class TrackEntry;
class Animation;
class Event;
typedef void (*OnAnimationEventFunc) (AnimationState* state, EventType type, TrackEntry* entry, Event* event);
/// State for the playback of an animation
class TrackEntry
@ -146,7 +163,7 @@ namespace Spine
void setDrawOrderThreshold(float inValue);
///
/// The animation queued to start after this animation, or null.
/// The animation queued to start after this animation, or NULL.
TrackEntry* getNext();
///
@ -175,7 +192,7 @@ namespace Spine
void setMixDuration(float inValue);
///
/// The track entry for the previous animation when mixing from the previous animation to this animation, or null if no
/// The track entry for the previous animation when mixing from the previous animation to this animation, or NULL if no
/// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a linked list.
TrackEntry* getMixingFrom();
@ -189,215 +206,94 @@ namespace Spine
/// TrackEntry chooses the short way the first time it is applied and remembers that direction.
void resetRotationDirections();
void setOnAnimationEventFunc(OnAnimationEventFunc inValue);
private:
Animation* _animation;
TrackEntry* _next;
TrackEntry* _mixingFrom;
int _trackIndex;
//
bool _loop;
float _eventThreshold, _attachmentThreshold, _drawOrderThreshold;
float _animationStart, _animationEnd, _animationLast, _nextAnimationLast;
float _delay, _trackTime, _trackLast, _nextTrackLast, _trackEnd, _timeScale = 1.0f;
float _alpha, _mixTime, _mixDuration, _interruptAlpha, _totalAlpha;
Vector<int> _timelineData;
Vector<TrackEntry> _timelineDipMix;
Vector<TrackEntry*> _timelineDipMix;
Vector<float> _timelinesRotation;
// event AnimationState.TrackEntryDelegate Start, Interrupt, End, Dispose, Complete;
// event AnimationState.TrackEntryEventDelegate Event;
OnAnimationEventFunc _onAnimationEventFunc;
/// Sets the timeline data.
/// @param to May be null.
// TrackEntry setTimelineData(TrackEntry* to, Vector<TrackEntry> mixingToArray, HashSet<int> propertyIDs)
// {
// if (to != null) mixingToArray.Add(to);
// var lastEntry = mixingFrom != null ? mixingFrom.setTimelineData(this, mixingToArray, propertyIDs) : this;
// if (to != null) mixingToArray.Pop();
//
// var mixingTo = mixingToArray.Items;
// int mixingToLast = mixingToArray.Count - 1;
// var timelines = animation.timelines.Items;
// int timelinesCount = animation.timelines.Count;
// var timelineDataItems = timelineData.Resize(timelinesCount).Items; // timelineData.setSize(timelinesCount);
// timelineDipMix.clear();
// var timelineDipMixItems = timelineDipMix.Resize(timelinesCount).Items; //timelineDipMix.setSize(timelinesCount);
//
// // outer:
// for (int i = 0; i < timelinesCount; i++) {
// int id = timelines[i].PropertyId;
// if (!propertyIDs.Add(id)) {
// timelineDataItems[i] = AnimationState.Subsequent;
// } else if (to == null || !to.hasTimeline(id)) {
// timelineDataItems[i] = AnimationState.First;
// } else {
// for (int ii = mixingToLast; ii >= 0; ii--) {
// var entry = mixingTo[ii];
// if (!entry.hasTimeline(id)) {
// if (entry.mixDuration > 0) {
// timelineDataItems[i] = AnimationState.DipMix;
// timelineDipMixItems[i] = entry;
// goto continue_outer; // continue outer;
// }
// break;
// }
// }
// timelineDataItems[i] = AnimationState.Dip;
// }
// continue_outer: {}
// }
// return lastEntry;
// }
/// @param to May be NULL.
TrackEntry* setTimelineData(TrackEntry* to, Vector<TrackEntry*>& mixingToArray, Vector<int>& propertyIDs);
bool hasTimeline(int inId)
bool hasTimeline(int inId);
void reset();
};
class EventQueueEntry
{
friend class EventQueue;
public:
EventType _type;
TrackEntry* _entry;
Event* _event;
EventQueueEntry(EventType eventType, TrackEntry* trackEntry, Event* event = NULL)
{
// var timelines = animation.timelines.Items;
// for (int i = 0, n = animation.timelines.Count; i < n; ++i)
// {
// if (timelines[i].PropertyId == inId)
// {
// return true;
// }
// }
return false;
_type = eventType;
_entry = trackEntry;
_event = event;
}
// void onStart() { if (Start != null) Start(this); }
// void onInterrupt() { if (Interrupt != null) Interrupt(this); }
// void onEnd() { if (End != null) End(this); }
// void onDispose() { if (Dispose != null) Dispose(this); }
// void onComplete() { if (Complete != null) Complete(this); }
// void onEvent(Event& e) { if (Event != null) Event(this, e); }
//
// void reset()
// {
// next = null;
// mixingFrom = null;
// animation = null;
// timelineData.clear();
// timelineDipMix.clear();
// timelinesRotation.clear();
//
// Start = null;
// Interrupt = null;
// End = null;
// Dispose = null;
// Complete = null;
// Event = null;
// }
};
class EventQueue
{
// private readonly List<EventQueueEntry> eventQueueEntries = new List<EventQueueEntry>();
// internal bool drainDisabled;
//
// private readonly AnimationState state;
// private readonly Pool<TrackEntry> trackEntryPool;
// internal event Action animationsChanged;
//
// internal EventQueue(AnimationState state, Action HandleanimationsChanged, Pool<TrackEntry> trackEntryPool) {
// this.state = state;
// this.animationsChanged += HandleanimationsChanged;
// this.trackEntryPool = trackEntryPool;
// }
//
// struct EventQueueEntry {
// public EventType type;
// public TrackEntry entry;
// public Event e;
//
// public EventQueueEntry(EventType eventType, TrackEntry trackEntry, Event e = null) {
// this.type = eventType;
// this.entry = trackEntry;
// this.e = e;
// }
// }
//
// enum EventType {
// Start, Interrupt, End, Dispose, Complete, Event
// }
//
// internal void Start(TrackEntry entry) {
// eventQueueEntries.Add(new EventQueueEntry(EventType.Start, entry));
// if (animationsChanged != null) animationsChanged();
// }
//
// internal void Interrupt(TrackEntry entry) {
// eventQueueEntries.Add(new EventQueueEntry(EventType.Interrupt, entry));
// }
//
// internal void End(TrackEntry entry) {
// eventQueueEntries.Add(new EventQueueEntry(EventType.End, entry));
// if (animationsChanged != null) animationsChanged();
// }
//
// internal void Dispose(TrackEntry entry) {
// eventQueueEntries.Add(new EventQueueEntry(EventType.Dispose, entry));
// }
//
// internal void Complete(TrackEntry entry) {
// eventQueueEntries.Add(new EventQueueEntry(EventType.Complete, entry));
// }
//
// internal void Event(TrackEntry entry, Event e) {
// eventQueueEntries.Add(new EventQueueEntry(EventType.Event, entry, e));
// }
//
// /// Raises all events in the queue and drains the queue.
// internal void drain() {
// if (drainDisabled) return;
// drainDisabled = true;
//
// var entries = this.eventQueueEntries;
// AnimationState state = this.state;
//
// // Don't cache entries.Count so callbacks can queue their own events (eg, call setAnimation in AnimationState_Complete).
// for (int i = 0; i < entries.Count; i++) {
// var queueEntry = entries[i];
// TrackEntry trackEntry = queueEntry.entry;
//
// switch (queueEntry.type) {
// case EventType.Start:
// trackEntry.onStart();
// state.onStart(trackEntry);
// break;
// case EventType.Interrupt:
// trackEntry.onInterrupt();
// state.onInterrupt(trackEntry);
// break;
// case EventType.End:
// trackEntry.onEnd();
// state.onEnd(trackEntry);
// case EventType.Dispose:
// trackEntry.onDispose();
// state.onDispose(trackEntry);
// trackEntryPool.Free(trackEntry); // Pooling
// break;
// case EventType.Complete:
// trackEntry.onComplete();
// state.onComplete(trackEntry);
// break;
// case EventType.Event:
// trackEntry.onEvent(queueEntry.e);
// state.onEvent(trackEntry, queueEntry.e);
// break;
// }
// }
// eventQueueEntries.clear();
//
// drainDisabled = false;
// }
//
// internal void clear() {
// eventQueueEntries.clear();
// }
friend class AnimationState;
private:
Vector<EventQueueEntry*> _eventQueueEntries;
bool _drainDisabled;
AnimationState& _state;
Pool<TrackEntry>& _trackEntryPool;
EventQueue(AnimationState& state, Pool<TrackEntry>& trackEntryPool);
void start(TrackEntry* entry);
void interrupt(TrackEntry* entry);
void end(TrackEntry* entry);
void dispose(TrackEntry* entry);
void complete(TrackEntry* entry);
void event(TrackEntry* entry, Event* e);
/// Raises all events in the queue and drains the queue.
void drain();
void clear();
};
class AnimationState
{
friend class TrackEntry;
friend class EventQueue;
public:
void setOnAnimationEventFunc(OnAnimationEventFunc inValue);
private:
static const int Subsequent, First, Dip, DipMix;
// static readonly Animation EmptyAnimation = new Animation("<empty>", new Vector<Timeline>(), 0);
// internal const int Subsequent = 0, First = 1, Dip = 2, DipMix = 3;
//
// private AnimationStateData data;
//
@ -408,27 +304,23 @@ namespace Spine
//
// private readonly HashSet<int> propertyIDs = new HashSet<int>();
// private readonly Vector<TrackEntry> mixingTo = new Vector<TrackEntry>();
// private bool animationsChanged;
bool _animationsChanged;
//
// private float timeScale = 1;
//
// public AnimationStateData Data { get { return data; } }
// /// A list of tracks that have animations, which may contain nulls.
// /// A list of tracks that have animations, which may contain NULLs.
// public Vector<TrackEntry> Tracks { get { return tracks; } }
// public float TimeScale { get { return timeScale; } set { timeScale = value; } }
//
// public delegate void TrackEntryDelegate(TrackEntry trackEntry);
// public event TrackEntryDelegate Start, Interrupt, End, Dispose, Complete;
//
// public delegate void TrackEntryEventDelegate(TrackEntry trackEntry, Event e);
// public event TrackEntryEventDelegate Event;
OnAnimationEventFunc _onAnimationEventFunc;
//
// public AnimationState(AnimationStateData data) {
// if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
// this.data = data;
// this.queue = new EventQueue(
// if (data == NULL) throw new ArgumentNULLException("data", "data cannot be NULL.");
// _data = data;
// _queue = new EventQueue(
// this,
// delegate { this.animationsChanged = true; },
// delegate { _animationsChanged = true; },
// trackEntryPool
// );
// }
@ -441,7 +333,7 @@ namespace Spine
// var tracksItems = tracks.Items;
// for (int i = 0, n = tracks.Count; i < n; i++) {
// TrackEntry current = tracksItems[i];
// if (current == null) continue;
// if (current == NULL) continue;
//
// current.animationLast = current.nextAnimationLast;
// current.trackLast = current.nextTrackLast;
@ -456,7 +348,7 @@ namespace Spine
// }
//
// TrackEntry next = current.next;
// if (next != null) {
// if (next != NULL) {
// // When the next entry's delay is passed, change to the next entry, preserving leftover time.
// float nextTime = current.trackLast - next.delay;
// if (nextTime >= 0) {
@ -464,26 +356,26 @@ namespace Spine
// next.trackTime = nextTime + (delta * next.timeScale);
// current.trackTime += currentDelta;
// setCurrent(i, next, true);
// while (next.mixingFrom != null) {
// while (next.mixingFrom != NULL) {
// next.mixTime += currentDelta;
// next = next.mixingFrom;
// }
// continue;
// }
// } else if (current.trackLast >= current.trackEnd && current.mixingFrom == null) {
// } else if (current.trackLast >= current.trackEnd && current.mixingFrom == NULL) {
// // clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom.
// tracksItems[i] = null;
// tracksItems[i] = NULL;
//
// queue.End(current);
// queue.end(current);
// disposeNext(current);
// continue;
// }
// if (current.mixingFrom != null && updateMixingFrom(current, delta)) {
// if (current.mixingFrom != NULL && updateMixingFrom(current, delta)) {
// // End mixing from entries once all have completed.
// var from = current.mixingFrom;
// current.mixingFrom = null;
// while (from != null) {
// queue.End(from);
// current.mixingFrom = NULL;
// while (from != NULL) {
// queue.end(from);
// from = from.mixingFrom;
// }
// }
@ -497,7 +389,7 @@ namespace Spine
// /// Returns true when all mixing from entries are complete.
// private bool updateMixingFrom(TrackEntry to, float delta) {
// TrackEntry from = to.mixingFrom;
// if (from == null) return true;
// if (from == NULL) return true;
//
// bool finished = updateMixingFrom(from, delta);
//
@ -507,7 +399,7 @@ namespace Spine
// if (from.totalAlpha == 0 || to.mixDuration == 0) {
// to.mixingFrom = from.mixingFrom;
// to.interruptAlpha = from.interruptAlpha;
// queue.End(from);
// queue.end(from);
// }
// return finished;
// }
@ -523,24 +415,24 @@ namespace Spine
// /// Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the
// /// animation state can be applied to multiple skeletons to pose them identically.
// public bool apply(Skeleton skeleton) {
// if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
// if (skeleton == NULL) throw new ArgumentNULLException("skeleton", "skeleton cannot be NULL.");
// if (animationsChanged) animationsChanged();
//
// var events = this.events;
// var events = _events;
//
// bool applied = false;
// var tracksItems = tracks.Items;
// for (int i = 0, m = tracks.Count; i < m; i++) {
// TrackEntry current = tracksItems[i];
// if (current == null || current.delay > 0) continue;
// if (current == NULL || current.delay > 0) continue;
// applied = true;
// MixPose currentPose = i == 0 ? MixPose.Current : MixPose.CurrentLayered;
//
// // apply mixing from entries first.
// float mix = current.alpha;
// if (current.mixingFrom != null)
// if (current.mixingFrom != NULL)
// mix *= applyMixingFrom(current, skeleton, currentPose);
// else if (current.trackTime >= current.trackEnd && current.next == null) //
// else if (current.trackTime >= current.trackEnd && current.next == NULL) //
// mix = 0; // Set to setup pose the last time the entry will be applied.
//
// // apply current entry.
@ -562,7 +454,7 @@ namespace Spine
// Timeline timeline = timelinesItems[ii];
// MixPose pose = timelineData[ii] >= AnimationState.First ? MixPose.Setup : currentPose;
// var rotateTimeline = timeline as RotateTimeline;
// if (rotateTimeline != null)
// if (rotateTimeline != NULL)
// applyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, pose, timelinesRotation, ii << 1, firstFrame);
// else
// timeline.apply(skeleton, animationLast, animationTime, events, mix, pose, MixDirection.In);
@ -580,7 +472,7 @@ namespace Spine
//
// private float applyMixingFrom(TrackEntry to, Skeleton skeleton, MixPose currentPose) {
// TrackEntry from = to.mixingFrom;
// if (from.mixingFrom != null) applyMixingFrom(from, skeleton, currentPose);
// if (from.mixingFrom != NULL) applyMixingFrom(from, skeleton, currentPose);
//
// float mix;
// if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes.
@ -591,7 +483,7 @@ namespace Spine
// if (mix > 1) mix = 1;
// }
//
// var eventBuffer = mix < from.eventThreshold ? this.events : null;
// var eventBuffer = mix < from.eventThreshold ? _events : NULL;
// bool attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
// float animationLast = from.animationLast, animationTime = from.AnimationTime;
// var timelines = from.animation.timelines;
@ -632,7 +524,7 @@ namespace Spine
// }
// from.totalAlpha += alpha;
// var rotateTimeline = timeline as RotateTimeline;
// if (rotateTimeline != null) {
// if (rotateTimeline != NULL) {
// applyRotateTimeline(rotateTimeline, skeleton, animationTime, alpha, pose, timelinesRotation, i << 1, firstFrame);
// } else {
// timeline.apply(skeleton, animationLast, animationTime, eventBuffer, alpha, pose, MixDirection.Out);
@ -640,7 +532,7 @@ namespace Spine
// }
//
// if (to.mixDuration > 0) queueEvents(from, animationTime);
// this.events.clear(false);
// _events.clear(false);
// from.nextAnimationLast = animationTime;
// from.nextTrackLast = from.trackTime;
//
@ -653,7 +545,7 @@ namespace Spine
// if (firstFrame) timelinesRotation[i] = 0;
//
// if (alpha == 1) {
// rotateTimeline.apply(skeleton, 0, time, null, 1, pose, MixDirection.In);
// rotateTimeline.apply(skeleton, 0, time, NULL, 1, pose, MixDirection.In);
// return;
// }
//
@ -718,27 +610,27 @@ namespace Spine
// float trackLastWrapped = entry.trackLast % duration;
//
// // Queue events before complete.
// var events = this.events;
// var events = _events;
// var eventsItems = events.Items;
// int i = 0, n = events.Count;
// for (; i < n; i++) {
// var e = eventsItems[i];
// if (e.time < trackLastWrapped) break;
// if (e.time > animationEnd) continue; // Discard events outside animation start/end.
// queue.Event(entry, e);
// queue.event(entry, e);
// }
//
// // Queue complete if completed a loop iteration or the animation.
// if (entry.loop ? (trackLastWrapped > entry.trackTime % duration)
// : (animationTime >= animationEnd && entry.animationLast < animationEnd)) {
// queue.Complete(entry);
// queue.complete(entry);
// }
//
// // Queue events after complete.
// for (; i < n; i++) {
// Event e = eventsItems[i];
// if (e.time < animationStart) continue; // Discard events outside animation start/end.
// queue.Event(entry, eventsItems[i]);
// queue.event(entry, eventsItems[i]);
// }
// }
//
@ -764,22 +656,22 @@ namespace Spine
// public void clearTrack(int trackIndex) {
// if (trackIndex >= tracks.Count) return;
// TrackEntry current = tracks.Items[trackIndex];
// if (current == null) return;
// if (current == NULL) return;
//
// queue.End(current);
// queue.end(current);
//
// disposeNext(current);
//
// TrackEntry entry = current;
// while (true) {
// TrackEntry from = entry.mixingFrom;
// if (from == null) break;
// queue.End(from);
// entry.mixingFrom = null;
// if (from == NULL) break;
// queue.end(from);
// entry.mixingFrom = NULL;
// entry = from;
// }
//
// tracks.Items[current.trackIndex] = null;
// tracks.Items[current.trackIndex] = NULL;
//
// queue.drain();
// }
@ -789,26 +681,26 @@ namespace Spine
// TrackEntry from = expandToIndex(index);
// tracks.Items[index] = current;
//
// if (from != null) {
// if (interrupt) queue.Interrupt(from);
// if (from != NULL) {
// if (interrupt) queue.interrupt(from);
// current.mixingFrom = from;
// current.mixTime = 0;
//
// // Store interrupted mix percentage.
// if (from.mixingFrom != null && from.mixDuration > 0)
// if (from.mixingFrom != NULL && from.mixDuration > 0)
// current.interruptAlpha *= Math.Min(1, from.mixTime / from.mixDuration);
//
// from.timelinesRotation.clear(); // Reset rotation for mixing out, in case entry was mixed in.
// }
//
// queue.Start(current); // triggers animationsChanged
// queue.start(current); // triggers animationsChanged
// }
//
//
// /// Sets an animation by name. setAnimation(int, Animation, bool)
// public TrackEntry setAnimation(int trackIndex, string animationName, bool loop) {
// Animation animation = data.skeletonData.FindAnimation(animationName);
// if (animation == null) throw new ArgumentException("Animation not found: " + animationName, "animationName");
// if (animation == NULL) throw new ArgumentException("Animation not found: " + animationName, "animationName");
// return setAnimation(trackIndex, animation, loop);
// }
//
@ -820,15 +712,15 @@ namespace Spine
// /// A track entry to allow further customization of animation playback. References to the track entry must not be kept
// /// after AnimationState.Dispose.
// public TrackEntry setAnimation(int trackIndex, Animation animation, bool loop) {
// if (animation == null) throw new ArgumentNullException("animation", "animation cannot be null.");
// if (animation == NULL) throw new ArgumentNULLException("animation", "animation cannot be NULL.");
// bool interrupt = true;
// TrackEntry current = expandToIndex(trackIndex);
// if (current != null) {
// if (current != NULL) {
// if (current.nextTrackLast == -1) {
// // Don't mix from an entry that was never applied.
// tracks.Items[trackIndex] = current.mixingFrom;
// queue.Interrupt(current);
// queue.End(current);
// queue.interrupt(current);
// queue.end(current);
// disposeNext(current);
// current = current.mixingFrom;
// interrupt = false;
@ -846,7 +738,7 @@ namespace Spine
// /// addAnimation(int, Animation, bool, float)
// public TrackEntry addAnimation(int trackIndex, string animationName, bool loop, float delay) {
// Animation animation = data.skeletonData.FindAnimation(animationName);
// if (animation == null) throw new ArgumentException("Animation not found: " + animationName, "animationName");
// if (animation == NULL) throw new ArgumentException("Animation not found: " + animationName, "animationName");
// return addAnimation(trackIndex, animation, loop, delay);
// }
//
@ -859,17 +751,17 @@ namespace Spine
// /// @return A track entry to allow further customization of animation playback. References to the track entry must not be kept
// /// after AnimationState.Dispose
// public TrackEntry addAnimation(int trackIndex, Animation animation, bool loop, float delay) {
// if (animation == null) throw new ArgumentNullException("animation", "animation cannot be null.");
// if (animation == NULL) throw new ArgumentNULLException("animation", "animation cannot be NULL.");
//
// TrackEntry last = expandToIndex(trackIndex);
// if (last != null) {
// while (last.next != null)
// if (last != NULL) {
// while (last.next != NULL)
// last = last.next;
// }
//
// TrackEntry entry = newTrackEntry(trackIndex, animation, loop, last);
//
// if (last == null) {
// if (last == NULL) {
// setCurrent(trackIndex, entry, true);
// queue.drain();
// } else {
@ -921,7 +813,7 @@ namespace Spine
// queue.drainDisabled = true;
// for (int i = 0, n = tracks.Count; i < n; i++) {
// TrackEntry current = tracks.Items[i];
// if (current != null) setEmptyAnimation(i, mixDuration);
// if (current != NULL) setEmptyAnimation(i, mixDuration);
// }
// queue.drainDisabled = olddrainDisabled;
// queue.drain();
@ -930,12 +822,12 @@ namespace Spine
// private TrackEntry expandToIndex(int index) {
// if (index < tracks.Count) return tracks.Items[index];
// while (index >= tracks.Count)
// tracks.Add(null);
// return null;
// tracks.Add(NULL);
// return NULL;
// }
//
// /// Object-pooling version of new TrackEntry. Obtain an unused TrackEntry from the pool and clear/initialize its values.
// /// @param last May be null.
// /// @param last May be NULL.
// private TrackEntry newTrackEntry(int trackIndex, Animation animation, bool loop, TrackEntry last) {
// TrackEntry entry = trackEntryPool.Obtain(); // Pooling
// entry.trackIndex = trackIndex;
@ -961,45 +853,45 @@ namespace Spine
// entry.alpha = 1;
// entry.interruptAlpha = 1;
// entry.mixTime = 0;
// entry.mixDuration = (last == null) ? 0 : data.GetMix(last.animation, animation);
// entry.mixDuration = (last == NULL) ? 0 : data.GetMix(last.animation, animation);
// return entry;
// }
//
// /// Dispose all track entries queued after the given TrackEntry.
// private void disposeNext(TrackEntry entry) {
// TrackEntry next = entry.next;
// while (next != null) {
// queue.Dispose(next);
// while (next != NULL) {
// queue.dispose(next);
// next = next.next;
// }
// entry.next = null;
// entry.next = NULL;
// }
//
// private void animationsChanged() {
// animationsChanged = false;
//
// var propertyIDs = this.propertyIDs;
// var propertyIDs = _propertyIDs;
// propertyIDs.clear();
// var mixingTo = this.mixingTo;
// var mixingTo = _mixingTo;
//
// var tracksItems = tracks.Items;
// for (int i = 0, n = tracks.Count; i < n; i++) {
// var entry = tracksItems[i];
// if (entry != null) entry.setTimelineData(null, mixingTo, propertyIDs);
// if (entry != NULL) entry.setTimelineData(NULL, mixingTo, propertyIDs);
// }
// }
//
// /// @return The track entry for the animation currently playing on the track, or null if no animation is currently playing.
// /// @return The track entry for the animation currently playing on the track, or NULL if no animation is currently playing.
// public TrackEntry getCurrent(int trackIndex) {
// return (trackIndex >= tracks.Count) ? null : tracks.Items[trackIndex];
// return (trackIndex >= tracks.Count) ? NULL : tracks.Items[trackIndex];
// }
//
// internal void onStart(TrackEntry entry) { if (Start != null) Start(entry); }
// internal void onInterrupt(TrackEntry entry) { if (Interrupt != null) Interrupt(entry); }
// internal void onEnd(TrackEntry entry) { if (End != null) End(entry); }
// internal void onDispose(TrackEntry entry) { if (Dispose != null) Dispose(entry); }
// internal void onComplete(TrackEntry entry) { if (Complete != null) Complete(entry); }
// internal void onEvent(TrackEntry entry, Event e) { if (Event != null) Event(entry, e); }
// internal void onStart(TrackEntry entry) { if (Start != NULL) Start(entry); }
// internal void onInterrupt(TrackEntry entry) { if (Interrupt != NULL) Interrupt(entry); }
// internal void onEnd(TrackEntry entry) { if (End != NULL) End(entry); }
// internal void onDispose(TrackEntry entry) { if (Dispose != NULL) Dispose(entry); }
// internal void onComplete(TrackEntry entry) { if (Complete != NULL) Complete(entry); }
// internal void onEvent(TrackEntry entry, Event e) { if (Event != NULL) Event(entry, e); }
};
}

View File

@ -31,6 +31,9 @@
#include <spine/AnimationState.h>
#include <spine/Animation.h>
#include <spine/Event.h>
#include <spine/Timeline.h>
#include <spine/MathUtil.h>
@ -38,7 +41,7 @@ namespace Spine
{
TrackEntry::TrackEntry()
{
// Empty
}
int TrackEntry::getTrackIndex() { return _trackIndex; }
@ -116,11 +119,199 @@ namespace Spine
TrackEntry* TrackEntry::getMixingFrom() { return _mixingFrom; }
// event AnimationState.TrackEntryDelegate Start, Interrupt, End, Dispose, Complete;
// event AnimationState.TrackEntryEventDelegate Event;
void TrackEntry::resetRotationDirections()
{
_timelinesRotation.clear();
}
void TrackEntry::setOnAnimationEventFunc(OnAnimationEventFunc inValue)
{
_onAnimationEventFunc = inValue;
}
TrackEntry* TrackEntry::setTimelineData(TrackEntry* to, Vector<TrackEntry*>& mixingToArray, Vector<int>& propertyIDs)
{
if (to != NULL)
{
mixingToArray.push_back(to);
}
TrackEntry* lastEntry = _mixingFrom != NULL ? _mixingFrom->setTimelineData(this, mixingToArray, propertyIDs) : this;
if (to != NULL)
{
mixingToArray.erase(mixingToArray.size() - 1);
}
int mixingToLast = static_cast<int>(mixingToArray.size()) - 1;
Vector<Timeline*>& timelines = _animation->_timelines;
int timelinesCount = static_cast<int>(timelines.size());
_timelineData.reserve(timelinesCount);
_timelineDipMix.clear();
_timelineDipMix.reserve(timelinesCount);
// outer:
for (int i = 0; i < timelinesCount; ++i)
{
int id = timelines[i]->getPropertyId();
if (propertyIDs.contains(id))
{
_timelineData[i] = AnimationState::Subsequent;
}
else if (to == NULL || !to->hasTimeline(id))
{
_timelineData[i] = AnimationState::First;
}
else
{
for (int ii = mixingToLast; ii >= 0; --ii)
{
TrackEntry* entry = mixingToArray[ii];
if (!entry->hasTimeline(id))
{
if (entry->_mixDuration > 0)
{
_timelineData[i] = AnimationState::DipMix;
_timelineDipMix[i] = entry;
goto continue_outer; // continue outer;
}
break;
}
}
_timelineData[i] = AnimationState::Dip;
}
continue_outer: {}
}
return lastEntry;
}
bool TrackEntry::hasTimeline(int inId)
{
Vector<Timeline*>& timelines = _animation->_timelines;
for (int i = 0, n = static_cast<int>(timelines.size()); i < n; ++i)
{
if (timelines[i]->getPropertyId() == inId)
{
return true;
}
}
return false;
}
void TrackEntry::reset()
{
_animation = NULL;
_next = NULL;
_mixingFrom = NULL;
_timelineData.clear();
_timelineDipMix.clear();
_timelinesRotation.clear();
_onAnimationEventFunc = NULL;
}
EventQueue::EventQueue(AnimationState& state, Pool<TrackEntry>& trackEntryPool) : _state(state), _trackEntryPool(trackEntryPool)
{
// Empty
}
void EventQueue::start(TrackEntry* entry)
{
_eventQueueEntries.push_back(new EventQueueEntry(EventType_Start, entry));
_state._animationsChanged = true;
}
void EventQueue::interrupt(TrackEntry* entry)
{
_eventQueueEntries.push_back(new EventQueueEntry(EventType_Interrupt, entry));
}
void EventQueue::end(TrackEntry* entry)
{
_eventQueueEntries.push_back(new EventQueueEntry(EventType_End, entry));
_state._animationsChanged = true;
}
void EventQueue::dispose(TrackEntry* entry)
{
_eventQueueEntries.push_back(new EventQueueEntry(EventType_Dispose, entry));
}
void EventQueue::complete(TrackEntry* entry)
{
_eventQueueEntries.push_back(new EventQueueEntry(EventType_Complete, entry));
}
void EventQueue::event(TrackEntry* entry, Event* e)
{
_eventQueueEntries.push_back(new EventQueueEntry(EventType_Event, entry, e));
}
/// Raises all events in the queue and drains the queue.
void EventQueue::drain()
{
if (_drainDisabled)
{
return;
}
_drainDisabled = true;
AnimationState& state = _state;
// Don't cache entries.size() so callbacks can queue their own events (eg, call setAnimation in AnimationState_Complete).
for (int i = 0; i < _eventQueueEntries.size(); ++i)
{
EventQueueEntry* queueEntry = _eventQueueEntries[i];
TrackEntry* trackEntry = queueEntry->_entry;
switch (queueEntry->_type)
{
// case EventType_Start:
// trackEntry.onStart();
// state.onStart(trackEntry);
// break;
// case EventType_Interrupt:
// trackEntry.onInterrupt();
// state.onInterrupt(trackEntry);
// break;
// case EventType_End:
// trackEntry.onEnd();
// state.onEnd(trackEntry);
// case EventType_Dispose:
// trackEntry.onDispose();
// state.onDispose(trackEntry);
// trackEntryPool.Free(trackEntry); // Pooling
// break;
// case EventType_Complete:
// trackEntry.onComplete();
// state.onComplete(trackEntry);
// break;
// case EventType_Event:
// trackEntry.onEvent(queueEntry.e);
// state.onEvent(trackEntry, queueEntry.e);
// break;
}
}
_eventQueueEntries.clear();
_drainDisabled = false;
}
void EventQueue::clear()
{
_eventQueueEntries.clear();
}
const int AnimationState::Subsequent = 0;
const int AnimationState::First = 1;
const int AnimationState::Dip = 2;
const int AnimationState::DipMix = 3;
void AnimationState::setOnAnimationEventFunc(OnAnimationEventFunc inValue)
{
_onAnimationEventFunc = inValue;
}
}