diff --git a/spine-cpp/spine-cpp/include/spine/Animation.h b/spine-cpp/spine-cpp/include/spine/Animation.h index 06a8ec4bf..061f4d27a 100644 --- a/spine-cpp/spine-cpp/include/spine/Animation.h +++ b/spine-cpp/spine-cpp/include/spine/Animation.h @@ -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; diff --git a/spine-cpp/spine-cpp/include/spine/AnimationState.h b/spine-cpp/spine-cpp/include/spine/AnimationState.h index ad2783094..4408dd4c6 100644 --- a/spine-cpp/spine-cpp/include/spine/AnimationState.h +++ b/spine-cpp/spine-cpp/include/spine/AnimationState.h @@ -32,10 +32,27 @@ #define Spine_AnimationState_h #include +#include 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 _timelineData; - Vector _timelineDipMix; + Vector _timelineDipMix; Vector _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 mixingToArray, HashSet 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& mixingToArray, Vector& 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 eventQueueEntries = new List(); -// internal bool drainDisabled; -// -// private readonly AnimationState state; -// private readonly Pool trackEntryPool; -// internal event Action animationsChanged; -// -// internal EventQueue(AnimationState state, Action HandleanimationsChanged, Pool 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 _eventQueueEntries; + bool _drainDisabled; + + AnimationState& _state; + Pool& _trackEntryPool; + + EventQueue(AnimationState& state, Pool& 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("", new Vector(), 0); -// internal const int Subsequent = 0, First = 1, Dip = 2, DipMix = 3; + // // private AnimationStateData data; // @@ -408,27 +304,23 @@ namespace Spine // // private readonly HashSet propertyIDs = new HashSet(); // private readonly Vector mixingTo = new Vector(); -// 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 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); } }; } diff --git a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp index 011f0b660..fedf61748 100644 --- a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp +++ b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp @@ -31,6 +31,9 @@ #include #include +#include + +#include #include @@ -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& mixingToArray, Vector& 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(mixingToArray.size()) - 1; + Vector& timelines = _animation->_timelines; + int timelinesCount = static_cast(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& timelines = _animation->_timelines; + for (int i = 0, n = static_cast(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& 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; + } }