mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
wip
This commit is contained in:
parent
9bed800466
commit
0ca6803030
@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
#include <spine/Vector.h>
|
#include <spine/Vector.h>
|
||||||
#include <spine/Pool.h>
|
#include <spine/Pool.h>
|
||||||
|
#include <spine/MixPose.h>
|
||||||
|
|
||||||
namespace Spine
|
namespace Spine
|
||||||
{
|
{
|
||||||
@ -51,6 +52,9 @@ namespace Spine
|
|||||||
|
|
||||||
class Animation;
|
class Animation;
|
||||||
class Event;
|
class Event;
|
||||||
|
class AnimationStateData;
|
||||||
|
class Skeleton;
|
||||||
|
class RotateTimeline;
|
||||||
|
|
||||||
typedef void (*OnAnimationEventFunc) (AnimationState& state, EventType type, TrackEntry* entry, Event* event);
|
typedef void (*OnAnimationEventFunc) (AnimationState& state, EventType type, TrackEntry* entry, Event* event);
|
||||||
|
|
||||||
@ -58,6 +62,7 @@ namespace Spine
|
|||||||
class TrackEntry
|
class TrackEntry
|
||||||
{
|
{
|
||||||
friend class EventQueue;
|
friend class EventQueue;
|
||||||
|
friend class AnimationState;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TrackEntry();
|
TrackEntry();
|
||||||
@ -245,12 +250,7 @@ namespace Spine
|
|||||||
TrackEntry* _entry;
|
TrackEntry* _entry;
|
||||||
Event* _event;
|
Event* _event;
|
||||||
|
|
||||||
EventQueueEntry(EventType eventType, TrackEntry* trackEntry, Event* event = NULL)
|
EventQueueEntry(EventType eventType, TrackEntry* trackEntry, Event* event = NULL);
|
||||||
{
|
|
||||||
_type = eventType;
|
|
||||||
_entry = trackEntry;
|
|
||||||
_event = event;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class EventQueue
|
class EventQueue
|
||||||
@ -259,13 +259,18 @@ namespace Spine
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Vector<EventQueueEntry*> _eventQueueEntries;
|
Vector<EventQueueEntry*> _eventQueueEntries;
|
||||||
bool _drainDisabled;
|
|
||||||
|
|
||||||
AnimationState& _state;
|
AnimationState& _state;
|
||||||
Pool<TrackEntry>& _trackEntryPool;
|
Pool<TrackEntry>& _trackEntryPool;
|
||||||
|
bool _drainDisabled;
|
||||||
|
|
||||||
|
static EventQueue* newEventQueue(AnimationState& state, Pool<TrackEntry>& trackEntryPool);
|
||||||
|
|
||||||
|
static EventQueueEntry* newEventQueueEntry(EventType eventType, TrackEntry* entry, Event* event = NULL);
|
||||||
|
|
||||||
EventQueue(AnimationState& state, Pool<TrackEntry>& trackEntryPool);
|
EventQueue(AnimationState& state, Pool<TrackEntry>& trackEntryPool);
|
||||||
|
|
||||||
|
~EventQueue();
|
||||||
|
|
||||||
void start(TrackEntry* entry);
|
void start(TrackEntry* entry);
|
||||||
|
|
||||||
void interrupt(TrackEntry* entry);
|
void interrupt(TrackEntry* entry);
|
||||||
@ -276,12 +281,10 @@ namespace Spine
|
|||||||
|
|
||||||
void complete(TrackEntry* entry);
|
void complete(TrackEntry* entry);
|
||||||
|
|
||||||
void event(TrackEntry* entry, Event* e);
|
void event(TrackEntry* entry, Event* event);
|
||||||
|
|
||||||
/// Raises all events in the queue and drains the queue.
|
/// Raises all events in the queue and drains the queue.
|
||||||
void drain();
|
void drain();
|
||||||
|
|
||||||
void clear();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AnimationState
|
class AnimationState
|
||||||
@ -290,49 +293,17 @@ namespace Spine
|
|||||||
friend class EventQueue;
|
friend class EventQueue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AnimationState();
|
AnimationState(AnimationStateData& data);
|
||||||
|
|
||||||
|
~AnimationState();
|
||||||
|
|
||||||
void setOnAnimationEventFunc(OnAnimationEventFunc inValue);
|
void setOnAnimationEventFunc(OnAnimationEventFunc inValue);
|
||||||
|
|
||||||
private:
|
///
|
||||||
static const int Subsequent, First, Dip, DipMix;
|
/// Increments the track entry times, setting queued animations as current if needed
|
||||||
// static readonly Animation EmptyAnimation = new Animation("<empty>", new Vector<Timeline>(), 0);
|
/// @param delta delta time
|
||||||
|
void update(float delta)
|
||||||
//
|
{
|
||||||
// private AnimationStateData data;
|
|
||||||
//
|
|
||||||
// Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
|
|
||||||
// private readonly Vector<TrackEntry> tracks = new Vector<TrackEntry>();
|
|
||||||
// private readonly Vector<Event> events = new Vector<Event>();
|
|
||||||
// private readonly EventQueue queue; // Initialized by constructor.
|
|
||||||
//
|
|
||||||
// private readonly HashSet<int> propertyIDs = new HashSet<int>();
|
|
||||||
// private readonly Vector<TrackEntry> mixingTo = new Vector<TrackEntry>();
|
|
||||||
bool _animationsChanged;
|
|
||||||
//
|
|
||||||
// private float timeScale = 1;
|
|
||||||
//
|
|
||||||
// public AnimationStateData Data { get { return data; } }
|
|
||||||
// /// 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; } }
|
|
||||||
//
|
|
||||||
OnAnimationEventFunc _onAnimationEventFunc;
|
|
||||||
//
|
|
||||||
// public AnimationState(AnimationStateData data) {
|
|
||||||
// if (data == NULL) throw new ArgumentNULLException("data", "data cannot be NULL.");
|
|
||||||
// _data = data;
|
|
||||||
// _queue = new EventQueue(
|
|
||||||
// this,
|
|
||||||
// delegate { _animationsChanged = true; },
|
|
||||||
// trackEntryPool
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ///
|
|
||||||
// /// Increments the track entry times, setting queued animations as current if needed
|
|
||||||
// /// @param delta delta time
|
|
||||||
// public void update(float delta) {
|
|
||||||
// delta *= timeScale;
|
// delta *= timeScale;
|
||||||
// var tracksItems = tracks.Items;
|
// var tracksItems = tracks.Items;
|
||||||
// for (int i = 0, n = tracks.Count; i < n; i++) {
|
// for (int i = 0, n = tracks.Count; i < n; i++) {
|
||||||
@ -388,43 +359,18 @@ namespace Spine
|
|||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// queue.drain();
|
// queue.drain();
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// /// Returns true when all mixing from entries are complete.
|
///
|
||||||
// private bool updateMixingFrom(TrackEntry to, float delta) {
|
/// Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the
|
||||||
// TrackEntry from = to.mixingFrom;
|
/// animation state can be applied to multiple skeletons to pose them identically.
|
||||||
// if (from == NULL) return true;
|
bool apply(Skeleton& skeleton)
|
||||||
//
|
{
|
||||||
// bool finished = updateMixingFrom(from, delta);
|
|
||||||
//
|
|
||||||
// // Require mixTime > 0 to ensure the mixing from entry was applied at least once.
|
|
||||||
// if (to.mixTime > 0 && (to.mixTime >= to.mixDuration || to.timeScale == 0)) {
|
|
||||||
// // Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame).
|
|
||||||
// if (from.totalAlpha == 0 || to.mixDuration == 0) {
|
|
||||||
// to.mixingFrom = from.mixingFrom;
|
|
||||||
// to.interruptAlpha = from.interruptAlpha;
|
|
||||||
// queue.end(from);
|
|
||||||
// }
|
|
||||||
// return finished;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// from.animationLast = from.nextAnimationLast;
|
|
||||||
// from.trackLast = from.nextTrackLast;
|
|
||||||
// from.trackTime += delta * from.timeScale;
|
|
||||||
// to.mixTime += delta * to.timeScale;
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ///
|
|
||||||
// /// 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 (animationsChanged) animationsChanged();
|
// if (animationsChanged) animationsChanged();
|
||||||
//
|
//
|
||||||
// var events = _events;
|
// var events = _events;
|
||||||
//
|
//
|
||||||
// bool applied = false;
|
bool applied = false;
|
||||||
// var tracksItems = tracks.Items;
|
// var tracksItems = tracks.Items;
|
||||||
// for (int i = 0, m = tracks.Count; i < m; i++) {
|
// for (int i = 0, m = tracks.Count; i < m; i++) {
|
||||||
// TrackEntry current = tracksItems[i];
|
// TrackEntry current = tracksItems[i];
|
||||||
@ -471,10 +417,312 @@ namespace Spine
|
|||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// queue.drain();
|
// queue.drain();
|
||||||
// return applied;
|
return applied;
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Removes all animations from all tracks, leaving skeletons in their previous pose.
|
||||||
|
/// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose,
|
||||||
|
/// rather than leaving them in their previous pose.
|
||||||
|
void clearTracks()
|
||||||
|
{
|
||||||
|
// bool olddrainDisabled = queue.drainDisabled;
|
||||||
|
// queue.drainDisabled = true;
|
||||||
|
// for (int i = 0, n = tracks.Count; i < n; i++) {
|
||||||
|
// clearTrack(i);
|
||||||
|
// }
|
||||||
|
// tracks.clear();
|
||||||
|
// queue.drainDisabled = olddrainDisabled;
|
||||||
|
// queue.drain();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Removes all animations from the tracks, leaving skeletons in their previous pose.
|
||||||
|
/// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose,
|
||||||
|
/// rather than leaving them in their previous pose.
|
||||||
|
void clearTrack(int trackIndex)
|
||||||
|
{
|
||||||
|
// if (trackIndex >= tracks.Count) return;
|
||||||
|
// TrackEntry current = tracks.Items[trackIndex];
|
||||||
|
// if (current == NULL) return;
|
||||||
//
|
//
|
||||||
// private float applyMixingFrom(TrackEntry to, Skeleton skeleton, MixPose currentPose) {
|
// queue.end(current);
|
||||||
|
//
|
||||||
|
// disposeNext(current);
|
||||||
|
//
|
||||||
|
// TrackEntry entry = current;
|
||||||
|
// while (true) {
|
||||||
|
// TrackEntry from = entry.mixingFrom;
|
||||||
|
// if (from == NULL) break;
|
||||||
|
// queue.end(from);
|
||||||
|
// entry.mixingFrom = NULL;
|
||||||
|
// entry = from;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// tracks.Items[current.trackIndex] = NULL;
|
||||||
|
//
|
||||||
|
// queue.drain();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets an animation by name. setAnimation(int, Animation, bool)
|
||||||
|
TrackEntry* setAnimation(int trackIndex, std::string animationName, bool loop)
|
||||||
|
{
|
||||||
|
// Animation animation = data.skeletonData.FindAnimation(animationName);
|
||||||
|
// if (animation == NULL) throw new ArgumentException("Animation not found: " + animationName, "animationName");
|
||||||
|
// return setAnimation(trackIndex, animation, loop);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the current animation for a track, discarding any queued animations.
|
||||||
|
/// @param loop If true, the animation will repeat.
|
||||||
|
/// If false, it will not, instead its last frame is applied if played beyond its duration.
|
||||||
|
/// In either case TrackEntry.TrackEnd determines when the track is cleared.
|
||||||
|
/// @return
|
||||||
|
/// A track entry to allow further customization of animation playback. References to the track entry must not be kept
|
||||||
|
/// after AnimationState.Dispose.
|
||||||
|
TrackEntry* setAnimation(int trackIndex, Animation& animation, bool loop)
|
||||||
|
{
|
||||||
|
// bool interrupt = true;
|
||||||
|
// TrackEntry current = expandToIndex(trackIndex);
|
||||||
|
// 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);
|
||||||
|
// disposeNext(current);
|
||||||
|
// current = current.mixingFrom;
|
||||||
|
// interrupt = false;
|
||||||
|
// } else {
|
||||||
|
// disposeNext(current);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// TrackEntry entry = newTrackEntry(trackIndex, animation, loop, current);
|
||||||
|
// setCurrent(trackIndex, entry, interrupt);
|
||||||
|
// queue.drain();
|
||||||
|
// return entry;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Queues an animation by name.
|
||||||
|
/// addAnimation(int, Animation, bool, float)
|
||||||
|
TrackEntry* addAnimation(int trackIndex, std::string animationName, bool loop, float delay)
|
||||||
|
{
|
||||||
|
// Animation animation = data.skeletonData.FindAnimation(animationName);
|
||||||
|
// if (animation == NULL) throw new ArgumentException("Animation not found: " + animationName, "animationName");
|
||||||
|
// return addAnimation(trackIndex, animation, loop, delay);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an animation to be played delay seconds after the current or last queued animation
|
||||||
|
/// for a track. If the track is empty, it is equivalent to calling setAnimation.
|
||||||
|
/// @param delay
|
||||||
|
/// Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation
|
||||||
|
/// duration of the previous track minus any mix duration plus the negative delay.
|
||||||
|
///
|
||||||
|
/// @return A track entry to allow further customization of animation playback. References to the track entry must not be kept
|
||||||
|
/// after AnimationState.Dispose
|
||||||
|
TrackEntry* addAnimation(int trackIndex, Animation& animation, bool loop, float delay)
|
||||||
|
{
|
||||||
|
// TrackEntry last = expandToIndex(trackIndex);
|
||||||
|
// if (last != NULL) {
|
||||||
|
// while (last.next != NULL)
|
||||||
|
// last = last.next;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// TrackEntry entry = newTrackEntry(trackIndex, animation, loop, last);
|
||||||
|
//
|
||||||
|
// if (last == NULL) {
|
||||||
|
// setCurrent(trackIndex, entry, true);
|
||||||
|
// queue.drain();
|
||||||
|
// } else {
|
||||||
|
// last.next = entry;
|
||||||
|
// if (delay <= 0) {
|
||||||
|
// float duration = last.animationEnd - last.animationStart;
|
||||||
|
// if (duration != 0)
|
||||||
|
// delay += duration * (1 + (int)(last.trackTime / duration)) - data.GetMix(last.animation, animation);
|
||||||
|
// else
|
||||||
|
// delay = 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// entry.delay = delay;
|
||||||
|
// return entry;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Sets an empty animation for a track, discarding any queued animations, and mixes to it over the specified mix duration.
|
||||||
|
TrackEntry* setEmptyAnimation(int trackIndex, float mixDuration)
|
||||||
|
{
|
||||||
|
TrackEntry* entry = setAnimation(trackIndex, AnimationState::getEmptyAnimation(), false);
|
||||||
|
entry->_mixDuration = mixDuration;
|
||||||
|
entry->_trackEnd = mixDuration;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Adds an empty animation to be played after the current or last queued animation for a track, and mixes to it over the
|
||||||
|
/// specified mix duration.
|
||||||
|
/// @return
|
||||||
|
/// A track entry to allow further customization of animation playback. References to the track entry must not be kept after AnimationState.Dispose.
|
||||||
|
///
|
||||||
|
/// @param trackIndex Track number.
|
||||||
|
/// @param mixDuration Mix duration.
|
||||||
|
/// @param delay Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation
|
||||||
|
/// duration of the previous track minus any mix duration plus the negative delay.
|
||||||
|
TrackEntry* addEmptyAnimation(int trackIndex, float mixDuration, float delay)
|
||||||
|
{
|
||||||
|
if (delay <= 0)
|
||||||
|
{
|
||||||
|
delay -= mixDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackEntry* entry = addAnimation(trackIndex, AnimationState::getEmptyAnimation(), false, delay);
|
||||||
|
entry->_mixDuration = mixDuration;
|
||||||
|
entry->_trackEnd = mixDuration;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix duration.
|
||||||
|
void setEmptyAnimations(float mixDuration)
|
||||||
|
{
|
||||||
|
// bool olddrainDisabled = queue.drainDisabled;
|
||||||
|
// queue.drainDisabled = true;
|
||||||
|
// for (int i = 0, n = tracks.Count; i < n; i++)
|
||||||
|
// {
|
||||||
|
// TrackEntry current = tracks.Items[i];
|
||||||
|
// if (current != NULL) setEmptyAnimation(i, mixDuration);
|
||||||
|
// }
|
||||||
|
// queue.drainDisabled = olddrainDisabled;
|
||||||
|
// queue.drain();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return The track entry for the animation currently playing on the track, or NULL if no animation is currently playing.
|
||||||
|
TrackEntry* getCurrent(int trackIndex)
|
||||||
|
{
|
||||||
|
return trackIndex >= _tracks.size() ? NULL : _tracks[trackIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnimationStateData Data { get { return data; } }
|
||||||
|
// /// A list of tracks that have animations, which may contain NULLs.
|
||||||
|
// Vector<TrackEntry> Tracks { get { return tracks; } }
|
||||||
|
// float TimeScale { get { return timeScale; } set { timeScale = value; } }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int Subsequent, First, Dip, DipMix;
|
||||||
|
|
||||||
|
AnimationStateData& _data;
|
||||||
|
|
||||||
|
Pool<TrackEntry> _trackEntryPool;
|
||||||
|
Vector<TrackEntry*> _tracks;
|
||||||
|
Vector<Event> _events;
|
||||||
|
EventQueue* _queue;
|
||||||
|
|
||||||
|
Vector<int> _propertyIDs;
|
||||||
|
Vector<TrackEntry> _mixingTo;
|
||||||
|
bool _animationsChanged;
|
||||||
|
|
||||||
|
OnAnimationEventFunc _onAnimationEventFunc;
|
||||||
|
|
||||||
|
float _timeScale;
|
||||||
|
|
||||||
|
static Animation& getEmptyAnimation();
|
||||||
|
|
||||||
|
static void applyRotateTimeline(RotateTimeline* rotateTimeline, Skeleton& skeleton, float time, float alpha, MixPose pose,
|
||||||
|
Vector<float>& timelinesRotation, int i, bool firstFrame)
|
||||||
|
{
|
||||||
|
// if (firstFrame) timelinesRotation[i] = 0;
|
||||||
|
//
|
||||||
|
// if (alpha == 1) {
|
||||||
|
// rotateTimeline.apply(skeleton, 0, time, NULL, 1, pose, MixDirection.In);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Bone bone = skeleton.bones.Items[rotateTimeline.boneIndex];
|
||||||
|
// float[] frames = rotateTimeline.frames;
|
||||||
|
// if (time < frames[0]) {
|
||||||
|
// if (pose == MixPose.Setup) bone.rotation = bone.data.rotation;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// float r2;
|
||||||
|
// if (time >= frames[frames.Length - RotateTimeline.ENTRIES]) // Time is after last frame.
|
||||||
|
// r2 = bone.data.rotation + frames[frames.Length + 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;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
|
||||||
|
// float r1 = pose == MixPose.Setup ? bone.data.rotation : bone.rotation;
|
||||||
|
// float total, diff = r2 - r1;
|
||||||
|
// if (diff == 0) {
|
||||||
|
// total = timelinesRotation[i];
|
||||||
|
// } else {
|
||||||
|
// diff -= (16384 - (int)(16384.499999999996 - diff / 360)) * 360;
|
||||||
|
// float lastTotal, lastDiff;
|
||||||
|
// if (firstFrame) {
|
||||||
|
// lastTotal = 0;
|
||||||
|
// lastDiff = diff;
|
||||||
|
// } else {
|
||||||
|
// lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops.
|
||||||
|
// lastDiff = timelinesRotation[i + 1]; // Difference between bones.
|
||||||
|
// }
|
||||||
|
// bool current = diff > 0, dir = lastTotal >= 0;
|
||||||
|
// // Detect cross at 0 (not 180).
|
||||||
|
// if (Math.Sign(lastDiff) != Math.Sign(diff) && Math.Abs(lastDiff) <= 90) {
|
||||||
|
// // A cross after a 360 rotation is a loop.
|
||||||
|
// if (Math.Abs(lastTotal) > 180) lastTotal += 360 * Math.Sign(lastTotal);
|
||||||
|
// dir = current;
|
||||||
|
// }
|
||||||
|
// total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal.
|
||||||
|
// if (dir != current) total += 360 * Math.Sign(lastTotal);
|
||||||
|
// timelinesRotation[i] = total;
|
||||||
|
// }
|
||||||
|
// timelinesRotation[i + 1] = diff;
|
||||||
|
// r1 += total * alpha;
|
||||||
|
// bone.rotation = r1 - (16384 - (int)(16384.499999999996 - r1 / 360)) * 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true when all mixing from entries are complete.
|
||||||
|
bool updateMixingFrom(TrackEntry to, float delta)
|
||||||
|
{
|
||||||
|
// TrackEntry from = to.mixingFrom;
|
||||||
|
// if (from == NULL) return true;
|
||||||
|
//
|
||||||
|
// bool finished = updateMixingFrom(from, delta);
|
||||||
|
//
|
||||||
|
// // Require mixTime > 0 to ensure the mixing from entry was applied at least once.
|
||||||
|
// if (to.mixTime > 0 && (to.mixTime >= to.mixDuration || to.timeScale == 0)) {
|
||||||
|
// // Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame).
|
||||||
|
// if (from.totalAlpha == 0 || to.mixDuration == 0) {
|
||||||
|
// to.mixingFrom = from.mixingFrom;
|
||||||
|
// to.interruptAlpha = from.interruptAlpha;
|
||||||
|
// queue.end(from);
|
||||||
|
// }
|
||||||
|
// return finished;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// from.animationLast = from.nextAnimationLast;
|
||||||
|
// from.trackLast = from.nextTrackLast;
|
||||||
|
// from.trackTime += delta * from.timeScale;
|
||||||
|
// to.mixTime += delta * to.timeScale;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float applyMixingFrom(TrackEntry* to, Skeleton& skeleton, MixPose currentPose)
|
||||||
|
{
|
||||||
// TrackEntry from = to.mixingFrom;
|
// TrackEntry from = to.mixingFrom;
|
||||||
// if (from.mixingFrom != NULL) applyMixingFrom(from, skeleton, currentPose);
|
// if (from.mixingFrom != NULL) applyMixingFrom(from, skeleton, currentPose);
|
||||||
//
|
//
|
||||||
@ -541,74 +789,11 @@ namespace Spine
|
|||||||
// from.nextTrackLast = from.trackTime;
|
// from.nextTrackLast = from.trackTime;
|
||||||
//
|
//
|
||||||
// return mix;
|
// return mix;
|
||||||
// }
|
return 0;
|
||||||
//
|
}
|
||||||
// static private void applyRotateTimeline(RotateTimeline rotateTimeline, Skeleton skeleton, float time, float alpha, MixPose pose,
|
|
||||||
// float[] timelinesRotation, int i, bool firstFrame) {
|
void queueEvents(TrackEntry* entry, float animationTime)
|
||||||
//
|
{
|
||||||
// if (firstFrame) timelinesRotation[i] = 0;
|
|
||||||
//
|
|
||||||
// if (alpha == 1) {
|
|
||||||
// rotateTimeline.apply(skeleton, 0, time, NULL, 1, pose, MixDirection.In);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Bone bone = skeleton.bones.Items[rotateTimeline.boneIndex];
|
|
||||||
// float[] frames = rotateTimeline.frames;
|
|
||||||
// if (time < frames[0]) {
|
|
||||||
// if (pose == MixPose.Setup) bone.rotation = bone.data.rotation;
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// float r2;
|
|
||||||
// if (time >= frames[frames.Length - RotateTimeline.ENTRIES]) // Time is after last frame.
|
|
||||||
// r2 = bone.data.rotation + frames[frames.Length + 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;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
|
|
||||||
// float r1 = pose == MixPose.Setup ? bone.data.rotation : bone.rotation;
|
|
||||||
// float total, diff = r2 - r1;
|
|
||||||
// if (diff == 0) {
|
|
||||||
// total = timelinesRotation[i];
|
|
||||||
// } else {
|
|
||||||
// diff -= (16384 - (int)(16384.499999999996 - diff / 360)) * 360;
|
|
||||||
// float lastTotal, lastDiff;
|
|
||||||
// if (firstFrame) {
|
|
||||||
// lastTotal = 0;
|
|
||||||
// lastDiff = diff;
|
|
||||||
// } else {
|
|
||||||
// lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops.
|
|
||||||
// lastDiff = timelinesRotation[i + 1]; // Difference between bones.
|
|
||||||
// }
|
|
||||||
// bool current = diff > 0, dir = lastTotal >= 0;
|
|
||||||
// // Detect cross at 0 (not 180).
|
|
||||||
// if (Math.Sign(lastDiff) != Math.Sign(diff) && Math.Abs(lastDiff) <= 90) {
|
|
||||||
// // A cross after a 360 rotation is a loop.
|
|
||||||
// if (Math.Abs(lastTotal) > 180) lastTotal += 360 * Math.Sign(lastTotal);
|
|
||||||
// dir = current;
|
|
||||||
// }
|
|
||||||
// total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal.
|
|
||||||
// if (dir != current) total += 360 * Math.Sign(lastTotal);
|
|
||||||
// timelinesRotation[i] = total;
|
|
||||||
// }
|
|
||||||
// timelinesRotation[i + 1] = diff;
|
|
||||||
// r1 += total * alpha;
|
|
||||||
// bone.rotation = r1 - (16384 - (int)(16384.499999999996 - r1 / 360)) * 360;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private void queueEvents(TrackEntry entry, float animationTime) {
|
|
||||||
// float animationStart = entry.animationStart, animationEnd = entry.animationEnd;
|
// float animationStart = entry.animationStart, animationEnd = entry.animationEnd;
|
||||||
// float duration = animationEnd - animationStart;
|
// float duration = animationEnd - animationStart;
|
||||||
// float trackLastWrapped = entry.trackLast % duration;
|
// float trackLastWrapped = entry.trackLast % duration;
|
||||||
@ -636,52 +821,11 @@ namespace Spine
|
|||||||
// if (e.time < animationStart) continue; // Discard events outside animation start/end.
|
// if (e.time < animationStart) continue; // Discard events outside animation start/end.
|
||||||
// queue.event(entry, eventsItems[i]);
|
// queue.event(entry, eventsItems[i]);
|
||||||
// }
|
// }
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// ///
|
/// Sets the active TrackEntry for a given track number.
|
||||||
// /// Removes all animations from all tracks, leaving skeletons in their previous pose.
|
void setCurrent(int index, TrackEntry* current, bool interrupt)
|
||||||
// /// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose,
|
{
|
||||||
// /// rather than leaving them in their previous pose.
|
|
||||||
// public void clearTracks() {
|
|
||||||
// bool olddrainDisabled = queue.drainDisabled;
|
|
||||||
// queue.drainDisabled = true;
|
|
||||||
// for (int i = 0, n = tracks.Count; i < n; i++) {
|
|
||||||
// clearTrack(i);
|
|
||||||
// }
|
|
||||||
// tracks.clear();
|
|
||||||
// queue.drainDisabled = olddrainDisabled;
|
|
||||||
// queue.drain();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ///
|
|
||||||
// /// Removes all animations from the tracks, leaving skeletons in their previous pose.
|
|
||||||
// /// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose,
|
|
||||||
// /// rather than leaving them in their previous pose.
|
|
||||||
// public void clearTrack(int trackIndex) {
|
|
||||||
// if (trackIndex >= tracks.Count) return;
|
|
||||||
// TrackEntry current = tracks.Items[trackIndex];
|
|
||||||
// if (current == NULL) return;
|
|
||||||
//
|
|
||||||
// queue.end(current);
|
|
||||||
//
|
|
||||||
// disposeNext(current);
|
|
||||||
//
|
|
||||||
// TrackEntry entry = current;
|
|
||||||
// while (true) {
|
|
||||||
// TrackEntry from = entry.mixingFrom;
|
|
||||||
// if (from == NULL) break;
|
|
||||||
// queue.end(from);
|
|
||||||
// entry.mixingFrom = NULL;
|
|
||||||
// entry = from;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// tracks.Items[current.trackIndex] = NULL;
|
|
||||||
//
|
|
||||||
// queue.drain();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// Sets the active TrackEntry for a given track number.
|
|
||||||
// private void setCurrent(int index, TrackEntry current, bool interrupt) {
|
|
||||||
// TrackEntry from = expandToIndex(index);
|
// TrackEntry from = expandToIndex(index);
|
||||||
// tracks.Items[index] = current;
|
// tracks.Items[index] = current;
|
||||||
//
|
//
|
||||||
@ -698,141 +842,22 @@ namespace Spine
|
|||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// queue.start(current); // triggers animationsChanged
|
// queue.start(current); // triggers animationsChanged
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
//
|
TrackEntry* expandToIndex(int index)
|
||||||
// /// 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");
|
|
||||||
// return setAnimation(trackIndex, animation, loop);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// Sets the current animation for a track, discarding any queued animations.
|
|
||||||
// /// @param loop If true, the animation will repeat.
|
|
||||||
// /// If false, it will not, instead its last frame is applied if played beyond its duration.
|
|
||||||
// /// In either case TrackEntry.TrackEnd determines when the track is cleared.
|
|
||||||
// /// @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 setAnimation(int trackIndex, Animation animation, bool loop) {
|
|
||||||
// if (animation == NULL) throw new ArgumentNULLException("animation", "animation cannot be NULL.");
|
|
||||||
// bool interrupt = true;
|
|
||||||
// TrackEntry current = expandToIndex(trackIndex);
|
|
||||||
// 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);
|
|
||||||
// disposeNext(current);
|
|
||||||
// current = current.mixingFrom;
|
|
||||||
// interrupt = false;
|
|
||||||
// } else {
|
|
||||||
// disposeNext(current);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// TrackEntry entry = newTrackEntry(trackIndex, animation, loop, current);
|
|
||||||
// setCurrent(trackIndex, entry, interrupt);
|
|
||||||
// queue.drain();
|
|
||||||
// return entry;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// Queues an animation by name.
|
|
||||||
// /// 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");
|
|
||||||
// return addAnimation(trackIndex, animation, loop, delay);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// Adds an animation to be played delay seconds after the current or last queued animation
|
|
||||||
// /// for a track. If the track is empty, it is equivalent to calling setAnimation.
|
|
||||||
// /// @param delay
|
|
||||||
// /// Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation
|
|
||||||
// /// duration of the previous track minus any mix duration plus the negative delay.
|
|
||||||
// ///
|
|
||||||
// /// @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.");
|
|
||||||
//
|
|
||||||
// TrackEntry last = expandToIndex(trackIndex);
|
|
||||||
// if (last != NULL) {
|
|
||||||
// while (last.next != NULL)
|
|
||||||
// last = last.next;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// TrackEntry entry = newTrackEntry(trackIndex, animation, loop, last);
|
|
||||||
//
|
|
||||||
// if (last == NULL) {
|
|
||||||
// setCurrent(trackIndex, entry, true);
|
|
||||||
// queue.drain();
|
|
||||||
// } else {
|
|
||||||
// last.next = entry;
|
|
||||||
// if (delay <= 0) {
|
|
||||||
// float duration = last.animationEnd - last.animationStart;
|
|
||||||
// if (duration != 0)
|
|
||||||
// delay += duration * (1 + (int)(last.trackTime / duration)) - data.GetMix(last.animation, animation);
|
|
||||||
// else
|
|
||||||
// delay = 0;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// entry.delay = delay;
|
|
||||||
// return entry;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ///
|
|
||||||
// /// Sets an empty animation for a track, discarding any queued animations, and mixes to it over the specified mix duration.
|
|
||||||
// public TrackEntry setEmptyAnimation(int trackIndex, float mixDuration) {
|
|
||||||
// TrackEntry entry = setAnimation(trackIndex, AnimationState.EmptyAnimation, false);
|
|
||||||
// entry.mixDuration = mixDuration;
|
|
||||||
// entry.trackEnd = mixDuration;
|
|
||||||
// return entry;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ///
|
|
||||||
// /// Adds an empty animation to be played after the current or last queued animation for a track, and mixes to it over the
|
|
||||||
// /// specified mix duration.
|
|
||||||
// /// @return
|
|
||||||
// /// A track entry to allow further customization of animation playback. References to the track entry must not be kept after AnimationState.Dispose.
|
|
||||||
// ///
|
|
||||||
// /// @param trackIndex Track number.
|
|
||||||
// /// @param mixDuration Mix duration.
|
|
||||||
// /// @param delay Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation
|
|
||||||
// /// duration of the previous track minus any mix duration plus the negative delay.
|
|
||||||
// public TrackEntry addEmptyAnimation(int trackIndex, float mixDuration, float delay) {
|
|
||||||
// if (delay <= 0) delay -= mixDuration;
|
|
||||||
// TrackEntry entry = addAnimation(trackIndex, AnimationState.EmptyAnimation, false, delay);
|
|
||||||
// entry.mixDuration = mixDuration;
|
|
||||||
// entry.trackEnd = mixDuration;
|
|
||||||
// return entry;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ///
|
|
||||||
// /// Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix duration.
|
|
||||||
// public void setEmptyAnimations(float mixDuration) {
|
|
||||||
// bool olddrainDisabled = queue.drainDisabled;
|
|
||||||
// queue.drainDisabled = true;
|
|
||||||
// for (int i = 0, n = tracks.Count; i < n; i++) {
|
|
||||||
// TrackEntry current = tracks.Items[i];
|
|
||||||
// if (current != NULL) setEmptyAnimation(i, mixDuration);
|
|
||||||
// }
|
|
||||||
// queue.drainDisabled = olddrainDisabled;
|
|
||||||
// queue.drain();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private TrackEntry expandToIndex(int index) {
|
|
||||||
// if (index < tracks.Count) return tracks.Items[index];
|
// if (index < tracks.Count) return tracks.Items[index];
|
||||||
// while (index >= tracks.Count)
|
// while (index >= tracks.Count)
|
||||||
|
// {
|
||||||
// tracks.Add(NULL);
|
// tracks.Add(NULL);
|
||||||
// return 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.
|
/// Object-pooling version of new TrackEntry. Obtain an unused TrackEntry from the pool and clear/initialize its values.
|
||||||
// private TrackEntry newTrackEntry(int trackIndex, Animation animation, bool loop, TrackEntry last) {
|
/// @param last May be NULL.
|
||||||
|
TrackEntry* newTrackEntry(int trackIndex, Animation* animation, bool loop, TrackEntry* last)
|
||||||
|
{
|
||||||
// TrackEntry entry = trackEntryPool.Obtain(); // Pooling
|
// TrackEntry entry = trackEntryPool.Obtain(); // Pooling
|
||||||
// entry.trackIndex = trackIndex;
|
// entry.trackIndex = trackIndex;
|
||||||
// entry.animation = animation;
|
// entry.animation = animation;
|
||||||
@ -859,19 +884,23 @@ namespace Spine
|
|||||||
// entry.mixTime = 0;
|
// 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;
|
// return entry;
|
||||||
// }
|
return NULL;
|
||||||
//
|
}
|
||||||
// /// Dispose all track entries queued after the given TrackEntry.
|
|
||||||
// private void disposeNext(TrackEntry entry) {
|
/// Dispose all track entries queued after the given TrackEntry.
|
||||||
|
void disposeNext(TrackEntry* entry)
|
||||||
|
{
|
||||||
// TrackEntry next = entry.next;
|
// TrackEntry next = entry.next;
|
||||||
// while (next != NULL) {
|
// while (next != NULL)
|
||||||
|
// {
|
||||||
// queue.dispose(next);
|
// queue.dispose(next);
|
||||||
// next = next.next;
|
// next = next.next;
|
||||||
// }
|
// }
|
||||||
// entry.next = NULL;
|
// entry.next = NULL;
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// private void animationsChanged() {
|
void animationsChanged()
|
||||||
|
{
|
||||||
// animationsChanged = false;
|
// animationsChanged = false;
|
||||||
//
|
//
|
||||||
// var propertyIDs = _propertyIDs;
|
// var propertyIDs = _propertyIDs;
|
||||||
@ -879,23 +908,15 @@ namespace Spine
|
|||||||
// var mixingTo = _mixingTo;
|
// var mixingTo = _mixingTo;
|
||||||
//
|
//
|
||||||
// var tracksItems = tracks.Items;
|
// var tracksItems = tracks.Items;
|
||||||
// for (int i = 0, n = tracks.Count; i < n; i++) {
|
// for (int i = 0, n = tracks.Count; i < n; i++)
|
||||||
|
// {
|
||||||
// var entry = tracksItems[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.
|
|
||||||
// public TrackEntry getCurrent(int 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); }
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
/* All allocation uses these. */
|
/* All allocation uses these. */
|
||||||
#define MALLOC(TYPE,COUNT) ((TYPE*)spineAlloc(sizeof(TYPE) * (COUNT), __FILE__, __LINE__))
|
#define MALLOC(TYPE,COUNT) ((TYPE*)spineAlloc(sizeof(TYPE) * (COUNT), __FILE__, __LINE__))
|
||||||
|
#define NEW(TYPE) ((TYPE*)spineAlloc(sizeof(TYPE), __FILE__, __LINE__))
|
||||||
#define REALLOC(PTR,TYPE,COUNT) ((TYPE*)spineRealloc(PTR, sizeof(TYPE) * (COUNT), __FILE__, __LINE__))
|
#define REALLOC(PTR,TYPE,COUNT) ((TYPE*)spineRealloc(PTR, sizeof(TYPE) * (COUNT), __FILE__, __LINE__))
|
||||||
|
|
||||||
/* Frees memory. Can be used on const types. */
|
/* Frees memory. Can be used on const types. */
|
||||||
|
|||||||
@ -149,7 +149,7 @@ namespace Spine
|
|||||||
|
|
||||||
size_t index = hash(key);
|
size_t index = hash(key);
|
||||||
|
|
||||||
Entry* entry = MALLOC(Entry, 1);
|
Entry* entry = NEW(Entry);
|
||||||
new (entry) Entry();
|
new (entry) Entry();
|
||||||
entry->_key = key;
|
entry->_key = key;
|
||||||
entry->_value = value;
|
entry->_value = value;
|
||||||
|
|||||||
@ -62,7 +62,7 @@ namespace Spine
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
T* ret = MALLOC(T, 1);
|
T* ret = NEW(T);
|
||||||
new (ret) T();
|
new (ret) T();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@ -32,10 +32,14 @@
|
|||||||
|
|
||||||
#include <spine/Animation.h>
|
#include <spine/Animation.h>
|
||||||
#include <spine/Event.h>
|
#include <spine/Event.h>
|
||||||
|
#include <spine/AnimationStateData.h>
|
||||||
|
#include <spine/Skeleton.h>
|
||||||
|
#include <spine/RotateTimeline.h>
|
||||||
|
|
||||||
#include <spine/Timeline.h>
|
#include <spine/Timeline.h>
|
||||||
|
|
||||||
#include <spine/MathUtil.h>
|
#include <spine/MathUtil.h>
|
||||||
|
#include <spine/ContainerUtil.h>
|
||||||
|
|
||||||
namespace Spine
|
namespace Spine
|
||||||
{
|
{
|
||||||
@ -163,27 +167,32 @@ namespace Spine
|
|||||||
{
|
{
|
||||||
_timelineData[i] = AnimationState::Subsequent;
|
_timelineData[i] = AnimationState::Subsequent;
|
||||||
}
|
}
|
||||||
else if (to == NULL || !to->hasTimeline(id))
|
|
||||||
{
|
|
||||||
_timelineData[i] = AnimationState::First;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int ii = mixingToLast; ii >= 0; --ii)
|
propertyIDs.push_back(id);
|
||||||
|
|
||||||
|
if (to == NULL || !to->hasTimeline(id))
|
||||||
{
|
{
|
||||||
TrackEntry* entry = mixingToArray[ii];
|
_timelineData[i] = AnimationState::First;
|
||||||
if (!entry->hasTimeline(id))
|
}
|
||||||
{
|
else
|
||||||
if (entry->_mixDuration > 0)
|
{
|
||||||
{
|
for (int ii = mixingToLast; ii >= 0; --ii)
|
||||||
_timelineData[i] = AnimationState::DipMix;
|
{
|
||||||
_timelineDipMix[i] = entry;
|
TrackEntry* entry = mixingToArray[ii];
|
||||||
goto continue_outer; // continue outer;
|
if (!entry->hasTimeline(id))
|
||||||
}
|
{
|
||||||
break;
|
if (entry->_mixDuration > 0)
|
||||||
}
|
{
|
||||||
|
_timelineData[i] = AnimationState::DipMix;
|
||||||
|
_timelineDipMix[i] = entry;
|
||||||
|
goto continue_outer; // continue outer;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_timelineData[i] = AnimationState::Dip;
|
||||||
}
|
}
|
||||||
_timelineData[i] = AnimationState::Dip;
|
|
||||||
}
|
}
|
||||||
continue_outer: {}
|
continue_outer: {}
|
||||||
}
|
}
|
||||||
@ -217,41 +226,70 @@ namespace Spine
|
|||||||
_onAnimationEventFunc = NULL;
|
_onAnimationEventFunc = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
EventQueue::EventQueue(AnimationState& state, Pool<TrackEntry>& trackEntryPool) : _state(state), _trackEntryPool(trackEntryPool)
|
EventQueueEntry::EventQueueEntry(EventType eventType, TrackEntry* trackEntry, Event* event) :
|
||||||
|
_type(eventType),
|
||||||
|
_entry(trackEntry),
|
||||||
|
_event(event)
|
||||||
{
|
{
|
||||||
// Empty
|
// Empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EventQueue* EventQueue::newEventQueue(AnimationState& state, Pool<TrackEntry>& trackEntryPool)
|
||||||
|
{
|
||||||
|
EventQueue* ret = NEW(EventQueue);
|
||||||
|
new (ret) EventQueue(state, trackEntryPool);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventQueueEntry* EventQueue::newEventQueueEntry(EventType eventType, TrackEntry* entry, Event* event)
|
||||||
|
{
|
||||||
|
EventQueueEntry* ret = NEW(EventQueueEntry);
|
||||||
|
new (ret) EventQueueEntry(eventType, entry, event);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventQueue::EventQueue(AnimationState& state, Pool<TrackEntry>& trackEntryPool) : _state(state), _trackEntryPool(trackEntryPool), _drainDisabled(false)
|
||||||
|
{
|
||||||
|
// Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
EventQueue::~EventQueue()
|
||||||
|
{
|
||||||
|
ContainerUtil::cleanUpVectorOfPointers(_eventQueueEntries);
|
||||||
|
}
|
||||||
|
|
||||||
void EventQueue::start(TrackEntry* entry)
|
void EventQueue::start(TrackEntry* entry)
|
||||||
{
|
{
|
||||||
_eventQueueEntries.push_back(new EventQueueEntry(EventType_Start, entry));
|
_eventQueueEntries.push_back(newEventQueueEntry(EventType_Start, entry));
|
||||||
_state._animationsChanged = true;
|
_state._animationsChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventQueue::interrupt(TrackEntry* entry)
|
void EventQueue::interrupt(TrackEntry* entry)
|
||||||
{
|
{
|
||||||
_eventQueueEntries.push_back(new EventQueueEntry(EventType_Interrupt, entry));
|
_eventQueueEntries.push_back(newEventQueueEntry(EventType_Interrupt, entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventQueue::end(TrackEntry* entry)
|
void EventQueue::end(TrackEntry* entry)
|
||||||
{
|
{
|
||||||
_eventQueueEntries.push_back(new EventQueueEntry(EventType_End, entry));
|
_eventQueueEntries.push_back(newEventQueueEntry(EventType_End, entry));
|
||||||
_state._animationsChanged = true;
|
_state._animationsChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventQueue::dispose(TrackEntry* entry)
|
void EventQueue::dispose(TrackEntry* entry)
|
||||||
{
|
{
|
||||||
_eventQueueEntries.push_back(new EventQueueEntry(EventType_Dispose, entry));
|
_eventQueueEntries.push_back(newEventQueueEntry(EventType_Dispose, entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventQueue::complete(TrackEntry* entry)
|
void EventQueue::complete(TrackEntry* entry)
|
||||||
{
|
{
|
||||||
_eventQueueEntries.push_back(new EventQueueEntry(EventType_Complete, entry));
|
_eventQueueEntries.push_back(newEventQueueEntry(EventType_Complete, entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventQueue::event(TrackEntry* entry, Event* e)
|
void EventQueue::event(TrackEntry* entry, Event* event)
|
||||||
{
|
{
|
||||||
_eventQueueEntries.push_back(new EventQueueEntry(EventType_Event, entry, e));
|
_eventQueueEntries.push_back(newEventQueueEntry(EventType_Event, entry, event));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raises all events in the queue and drains the queue.
|
/// Raises all events in the queue and drains the queue.
|
||||||
@ -266,7 +304,7 @@ namespace Spine
|
|||||||
|
|
||||||
AnimationState& state = _state;
|
AnimationState& state = _state;
|
||||||
|
|
||||||
// Don't cache entries.size() so callbacks can queue their own events (eg, call setAnimation in AnimationState_Complete).
|
// Don't cache _eventQueueEntries.size() so callbacks can queue their own events (eg, call setAnimation in AnimationState_Complete).
|
||||||
for (int i = 0; i < _eventQueueEntries.size(); ++i)
|
for (int i = 0; i < _eventQueueEntries.size(); ++i)
|
||||||
{
|
{
|
||||||
EventQueueEntry* queueEntry = _eventQueueEntries[i];
|
EventQueueEntry* queueEntry = _eventQueueEntries[i];
|
||||||
@ -300,23 +338,34 @@ namespace Spine
|
|||||||
_drainDisabled = false;
|
_drainDisabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventQueue::clear()
|
|
||||||
{
|
|
||||||
_eventQueueEntries.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
const int AnimationState::Subsequent = 0;
|
const int AnimationState::Subsequent = 0;
|
||||||
const int AnimationState::First = 1;
|
const int AnimationState::First = 1;
|
||||||
const int AnimationState::Dip = 2;
|
const int AnimationState::Dip = 2;
|
||||||
const int AnimationState::DipMix = 3;
|
const int AnimationState::DipMix = 3;
|
||||||
|
|
||||||
AnimationState::AnimationState() : _onAnimationEventFunc(dummyOnAnimationEventFunc)
|
AnimationState::AnimationState(AnimationStateData& data) :
|
||||||
|
_data(data),
|
||||||
|
_queue(EventQueue::newEventQueue(*this, _trackEntryPool)),
|
||||||
|
_onAnimationEventFunc(dummyOnAnimationEventFunc),
|
||||||
|
_timeScale(1)
|
||||||
{
|
{
|
||||||
// Empty
|
// Empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AnimationState::~AnimationState()
|
||||||
|
{
|
||||||
|
DESTROY(EventQueue, _queue);
|
||||||
|
}
|
||||||
|
|
||||||
void AnimationState::setOnAnimationEventFunc(OnAnimationEventFunc inValue)
|
void AnimationState::setOnAnimationEventFunc(OnAnimationEventFunc inValue)
|
||||||
{
|
{
|
||||||
_onAnimationEventFunc = inValue;
|
_onAnimationEventFunc = inValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Animation& AnimationState::getEmptyAnimation()
|
||||||
|
{
|
||||||
|
static Vector<Timeline*> timelines;
|
||||||
|
static Animation ret(std::string("<empty>"), timelines, 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -138,7 +138,7 @@ namespace Spine
|
|||||||
}
|
}
|
||||||
strcpy(path + dirLength + needsSlash, name);
|
strcpy(path + dirLength + needsSlash, name);
|
||||||
|
|
||||||
AtlasPage* page = MALLOC(AtlasPage, 1);
|
AtlasPage* page = NEW(AtlasPage);
|
||||||
new (page) AtlasPage(std::string(name));
|
new (page) AtlasPage(std::string(name));
|
||||||
|
|
||||||
FREE(name);
|
FREE(name);
|
||||||
@ -188,7 +188,7 @@ namespace Spine
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AtlasRegion* region = MALLOC(AtlasRegion, 1);
|
AtlasRegion* region = NEW(AtlasRegion);
|
||||||
new (region) AtlasRegion();
|
new (region) AtlasRegion();
|
||||||
|
|
||||||
region->page = page;
|
region->page = page;
|
||||||
|
|||||||
@ -56,7 +56,7 @@ namespace Spine
|
|||||||
|
|
||||||
AtlasRegion& region = *regionP;
|
AtlasRegion& region = *regionP;
|
||||||
|
|
||||||
RegionAttachment* attachmentP = MALLOC(RegionAttachment, 1);
|
RegionAttachment* attachmentP = NEW(RegionAttachment);
|
||||||
new (attachmentP) RegionAttachment(name);
|
new (attachmentP) RegionAttachment(name);
|
||||||
|
|
||||||
RegionAttachment& attachment = *attachmentP;
|
RegionAttachment& attachment = *attachmentP;
|
||||||
@ -79,7 +79,7 @@ namespace Spine
|
|||||||
|
|
||||||
AtlasRegion& region = *regionP;
|
AtlasRegion& region = *regionP;
|
||||||
|
|
||||||
MeshAttachment* attachmentP = MALLOC(MeshAttachment, 1);
|
MeshAttachment* attachmentP = NEW(MeshAttachment);
|
||||||
new (attachmentP) MeshAttachment(name);
|
new (attachmentP) MeshAttachment(name);
|
||||||
|
|
||||||
MeshAttachment& attachment = *attachmentP;
|
MeshAttachment& attachment = *attachmentP;
|
||||||
@ -101,7 +101,7 @@ namespace Spine
|
|||||||
|
|
||||||
BoundingBoxAttachment* AtlasAttachmentLoader::newBoundingBoxAttachment(Skin& skin, std::string name)
|
BoundingBoxAttachment* AtlasAttachmentLoader::newBoundingBoxAttachment(Skin& skin, std::string name)
|
||||||
{
|
{
|
||||||
BoundingBoxAttachment* attachmentP = MALLOC(BoundingBoxAttachment, 1);
|
BoundingBoxAttachment* attachmentP = NEW(BoundingBoxAttachment);
|
||||||
new (attachmentP) BoundingBoxAttachment(name);
|
new (attachmentP) BoundingBoxAttachment(name);
|
||||||
|
|
||||||
return attachmentP;
|
return attachmentP;
|
||||||
@ -109,7 +109,7 @@ namespace Spine
|
|||||||
|
|
||||||
PathAttachment* AtlasAttachmentLoader::newPathAttachment(Skin& skin, std::string name)
|
PathAttachment* AtlasAttachmentLoader::newPathAttachment(Skin& skin, std::string name)
|
||||||
{
|
{
|
||||||
PathAttachment* attachmentP = MALLOC(PathAttachment, 1);
|
PathAttachment* attachmentP = NEW(PathAttachment);
|
||||||
new (attachmentP) PathAttachment(name);
|
new (attachmentP) PathAttachment(name);
|
||||||
|
|
||||||
return attachmentP;
|
return attachmentP;
|
||||||
@ -117,7 +117,7 @@ namespace Spine
|
|||||||
|
|
||||||
PointAttachment* AtlasAttachmentLoader::newPointAttachment(Skin& skin, std::string name)
|
PointAttachment* AtlasAttachmentLoader::newPointAttachment(Skin& skin, std::string name)
|
||||||
{
|
{
|
||||||
PointAttachment* attachmentP = MALLOC(PointAttachment, 1);
|
PointAttachment* attachmentP = NEW(PointAttachment);
|
||||||
new (attachmentP) PointAttachment(name);
|
new (attachmentP) PointAttachment(name);
|
||||||
|
|
||||||
return attachmentP;
|
return attachmentP;
|
||||||
@ -125,7 +125,7 @@ namespace Spine
|
|||||||
|
|
||||||
ClippingAttachment* AtlasAttachmentLoader::newClippingAttachment(Skin& skin, std::string name)
|
ClippingAttachment* AtlasAttachmentLoader::newClippingAttachment(Skin& skin, std::string name)
|
||||||
{
|
{
|
||||||
ClippingAttachment* attachmentP = MALLOC(ClippingAttachment, 1);
|
ClippingAttachment* attachmentP = NEW(ClippingAttachment);
|
||||||
new (attachmentP) ClippingAttachment(name);
|
new (attachmentP) ClippingAttachment(name);
|
||||||
|
|
||||||
return attachmentP;
|
return attachmentP;
|
||||||
|
|||||||
@ -75,13 +75,13 @@ namespace Spine
|
|||||||
Bone* bone;
|
Bone* bone;
|
||||||
if (data->getParent() == NULL)
|
if (data->getParent() == NULL)
|
||||||
{
|
{
|
||||||
bone = MALLOC(Bone, 1);
|
bone = NEW(Bone);
|
||||||
new (bone) Bone(*data, *this, NULL);
|
new (bone) Bone(*data, *this, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Bone* parent = _bones[data->getParent()->getIndex()];
|
Bone* parent = _bones[data->getParent()->getIndex()];
|
||||||
bone = MALLOC(Bone, 1);
|
bone = NEW(Bone);
|
||||||
new (bone) Bone(*data, *this, parent);
|
new (bone) Bone(*data, *this, parent);
|
||||||
parent->getChildren().push_back(bone);
|
parent->getChildren().push_back(bone);
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ namespace Spine
|
|||||||
SlotData* data = (*i);
|
SlotData* data = (*i);
|
||||||
|
|
||||||
Bone* bone = _bones[data->getBoneData().getIndex()];
|
Bone* bone = _bones[data->getBoneData().getIndex()];
|
||||||
Slot* slot = MALLOC(Slot, 1);
|
Slot* slot = NEW(Slot);
|
||||||
new (slot) Slot(*data, *bone);
|
new (slot) Slot(*data, *bone);
|
||||||
|
|
||||||
_slots.push_back(slot);
|
_slots.push_back(slot);
|
||||||
@ -108,7 +108,7 @@ namespace Spine
|
|||||||
{
|
{
|
||||||
IkConstraintData* data = (*i);
|
IkConstraintData* data = (*i);
|
||||||
|
|
||||||
IkConstraint* constraint = MALLOC(IkConstraint, 1);
|
IkConstraint* constraint = NEW(IkConstraint);
|
||||||
new (constraint) IkConstraint(*data, *this);
|
new (constraint) IkConstraint(*data, *this);
|
||||||
|
|
||||||
_ikConstraints.push_back(constraint);
|
_ikConstraints.push_back(constraint);
|
||||||
@ -119,7 +119,7 @@ namespace Spine
|
|||||||
{
|
{
|
||||||
TransformConstraintData* data = (*i);
|
TransformConstraintData* data = (*i);
|
||||||
|
|
||||||
TransformConstraint* constraint = MALLOC(TransformConstraint, 1);
|
TransformConstraint* constraint = NEW(TransformConstraint);
|
||||||
new (constraint) TransformConstraint(*data, *this);
|
new (constraint) TransformConstraint(*data, *this);
|
||||||
|
|
||||||
_transformConstraints.push_back(constraint);
|
_transformConstraints.push_back(constraint);
|
||||||
@ -130,7 +130,7 @@ namespace Spine
|
|||||||
{
|
{
|
||||||
PathConstraintData* data = (*i);
|
PathConstraintData* data = (*i);
|
||||||
|
|
||||||
PathConstraint* constraint = MALLOC(PathConstraint, 1);
|
PathConstraint* constraint = NEW(PathConstraint);
|
||||||
new (constraint) PathConstraint(*data, *this);
|
new (constraint) PathConstraint(*data, *this);
|
||||||
|
|
||||||
_pathConstraints.push_back(constraint);
|
_pathConstraints.push_back(constraint);
|
||||||
|
|||||||
@ -76,7 +76,7 @@ namespace Spine
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Polygon* polygonP = MALLOC(Polygon, 1);
|
Polygon* polygonP = NEW(Polygon);
|
||||||
new (polygonP) Polygon();
|
new (polygonP) Polygon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user