mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 09:46:02 +08:00
Merge remote-tracking branch 'origin/3.6-beta' into 3.6-beta
This commit is contained in:
commit
8d9d46ca56
@ -61,6 +61,8 @@ struct spTrackEntry {
|
|||||||
float alpha, mixTime, mixDuration, mixAlpha;
|
float alpha, mixTime, mixDuration, mixAlpha;
|
||||||
int* /*boolean*/ timelinesFirst;
|
int* /*boolean*/ timelinesFirst;
|
||||||
int timelinesFirstCount;
|
int timelinesFirstCount;
|
||||||
|
int* /*boolean*/ timelinesLast;
|
||||||
|
int timelinesLastCount;
|
||||||
float* timelinesRotation;
|
float* timelinesRotation;
|
||||||
int timelinesRotationCount;
|
int timelinesRotationCount;
|
||||||
void* rendererObject;
|
void* rendererObject;
|
||||||
@ -79,6 +81,8 @@ struct spTrackEntry {
|
|||||||
alpha(0), mixTime(0), mixDuration(0), mixAlpha(0),
|
alpha(0), mixTime(0), mixDuration(0), mixAlpha(0),
|
||||||
timelinesFirst(0),
|
timelinesFirst(0),
|
||||||
timelinesFirstCount(0),
|
timelinesFirstCount(0),
|
||||||
|
timelinesLast(0),
|
||||||
|
timelinesLastCount(0),
|
||||||
timelinesRotation(0),
|
timelinesRotation(0),
|
||||||
timelinesRotationCount(0) {
|
timelinesRotationCount(0) {
|
||||||
}
|
}
|
||||||
@ -95,6 +99,8 @@ struct spAnimationState {
|
|||||||
|
|
||||||
float timeScale;
|
float timeScale;
|
||||||
|
|
||||||
|
int /*boolean*/ multipleMixing;
|
||||||
|
|
||||||
void* rendererObject;
|
void* rendererObject;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -103,7 +109,9 @@ struct spAnimationState {
|
|||||||
tracksCount(0),
|
tracksCount(0),
|
||||||
tracks(0),
|
tracks(0),
|
||||||
listener(0),
|
listener(0),
|
||||||
timeScale(0) {
|
timeScale(0),
|
||||||
|
multipleMixing(0),
|
||||||
|
rendererObject(0) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@ -57,7 +57,7 @@ void _spAnimationState_ensureCapacityPropertyIDs(spAnimationState* self, int cap
|
|||||||
int _spAnimationState_addPropertyID(spAnimationState* self, int id);
|
int _spAnimationState_addPropertyID(spAnimationState* self, int id);
|
||||||
void _spAnimationState_setTimelinesFirst (spAnimationState* self, spTrackEntry* entry);
|
void _spAnimationState_setTimelinesFirst (spAnimationState* self, spTrackEntry* entry);
|
||||||
void _spAnimationState_checkTimelinesFirst (spAnimationState* self, spTrackEntry* entry);
|
void _spAnimationState_checkTimelinesFirst (spAnimationState* self, spTrackEntry* entry);
|
||||||
void _spAnimationState_checkTimelinesUsage (spAnimationState* self, spTrackEntry* entry);
|
void _spAnimationState_checkTimelinesUsage (spAnimationState* self, spTrackEntry* entry, int /*boolean*/ useTimelinesFirst);
|
||||||
|
|
||||||
_spEventQueue* _spEventQueue_create (_spAnimationState* state) {
|
_spEventQueue* _spEventQueue_create (_spAnimationState* state) {
|
||||||
_spEventQueue *self = CALLOC(_spEventQueue, 1);
|
_spEventQueue *self = CALLOC(_spEventQueue, 1);
|
||||||
@ -176,6 +176,7 @@ void _spEventQueue_drain (_spEventQueue* self) {
|
|||||||
|
|
||||||
void _spAnimationState_disposeTrackEntry (spTrackEntry* entry) {
|
void _spAnimationState_disposeTrackEntry (spTrackEntry* entry) {
|
||||||
FREE(entry->timelinesFirst);
|
FREE(entry->timelinesFirst);
|
||||||
|
FREE(entry->timelinesLast);
|
||||||
FREE(entry->timelinesRotation);
|
FREE(entry->timelinesRotation);
|
||||||
FREE(entry);
|
FREE(entry);
|
||||||
}
|
}
|
||||||
@ -370,6 +371,9 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* e
|
|||||||
int timelineCount;
|
int timelineCount;
|
||||||
spTimeline** timelines;
|
spTimeline** timelines;
|
||||||
int* timelinesFirst;
|
int* timelinesFirst;
|
||||||
|
int* timelinesLast;
|
||||||
|
float alphaBase;
|
||||||
|
float alphaMix;
|
||||||
float alpha;
|
float alpha;
|
||||||
int /*boolean*/ firstFrame;
|
int /*boolean*/ firstFrame;
|
||||||
float* timelinesRotation;
|
float* timelinesRotation;
|
||||||
@ -395,7 +399,9 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* e
|
|||||||
timelineCount = from->animation->timelinesCount;
|
timelineCount = from->animation->timelinesCount;
|
||||||
timelines = from->animation->timelines;
|
timelines = from->animation->timelines;
|
||||||
timelinesFirst = from->timelinesFirst;
|
timelinesFirst = from->timelinesFirst;
|
||||||
alpha = from->alpha * entry->mixAlpha * (1 - mix);
|
timelinesLast = self->multipleMixing ? 0 : from->timelinesLast;
|
||||||
|
alphaBase = from->alpha * entry->mixAlpha;
|
||||||
|
alphaMix = alphaBase * (1 - mix);
|
||||||
|
|
||||||
firstFrame = from->timelinesRotationCount == 0;
|
firstFrame = from->timelinesRotationCount == 0;
|
||||||
if (firstFrame) _spAnimationState_resizeTimelinesRotation(from, timelineCount << 1);
|
if (firstFrame) _spAnimationState_resizeTimelinesRotation(from, timelineCount << 1);
|
||||||
@ -404,6 +410,7 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* e
|
|||||||
for (i = 0; i < timelineCount; i++) {
|
for (i = 0; i < timelineCount; i++) {
|
||||||
timeline = timelines[i];
|
timeline = timelines[i];
|
||||||
setupPose = timelinesFirst[i];
|
setupPose = timelinesFirst[i];
|
||||||
|
alpha = timelinesLast != 0 && setupPose && !timelinesLast[i] ? alphaBase : alphaMix;
|
||||||
if (timeline->type == SP_TIMELINE_ROTATE)
|
if (timeline->type == SP_TIMELINE_ROTATE)
|
||||||
_spAnimationState_applyRotateTimeline(self, timeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i << 1, firstFrame);
|
_spAnimationState_applyRotateTimeline(self, timeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i << 1, firstFrame);
|
||||||
else {
|
else {
|
||||||
@ -574,6 +581,7 @@ void spAnimationState_clearTrack (spAnimationState* self, int trackIndex) {
|
|||||||
void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEntry* current, int /*boolean*/ interrupt) {
|
void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEntry* current, int /*boolean*/ interrupt) {
|
||||||
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
|
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
|
||||||
spTrackEntry* from = _spAnimationState_expandToIndex(self, index);
|
spTrackEntry* from = _spAnimationState_expandToIndex(self, index);
|
||||||
|
spTrackEntry* mixingFrom = 0;
|
||||||
self->tracks[index] = current;
|
self->tracks[index] = current;
|
||||||
|
|
||||||
if (from) {
|
if (from) {
|
||||||
@ -581,10 +589,26 @@ void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEnt
|
|||||||
current->mixingFrom = from;
|
current->mixingFrom = from;
|
||||||
current->mixTime = 0;
|
current->mixTime = 0;
|
||||||
|
|
||||||
from->timelinesRotationCount = 0;
|
mixingFrom = from->mixingFrom;
|
||||||
|
if (mixingFrom != 0 && from->mixDuration > 0) {
|
||||||
|
if (self->multipleMixing && from->mixTime / from->mixDuration < 0.5 && mixingFrom->animation != SP_EMPTY_ANIMATION) {
|
||||||
|
current->mixingFrom = mixingFrom;
|
||||||
|
mixingFrom->mixingFrom = from;
|
||||||
|
mixingFrom->mixTime = from->mixDuration - from->mixTime;
|
||||||
|
mixingFrom->mixDuration = from->mixDuration;
|
||||||
|
from->mixingFrom = 0;
|
||||||
|
from = mixingFrom;
|
||||||
|
}
|
||||||
|
|
||||||
/* If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero. */
|
current->mixAlpha *= MIN(from->mixTime / from->mixDuration, 1);
|
||||||
if (from->mixingFrom && from->mixDuration > 0) current->mixAlpha *= MIN(from->mixTime / from->mixDuration, 1);
|
if (!self->multipleMixing) {
|
||||||
|
from->mixAlpha = 0;
|
||||||
|
from->mixTime = 0;
|
||||||
|
from->mixDuration = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from->timelinesRotationCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_spEventQueue_start(internal->queue, current);
|
_spEventQueue_start(internal->queue, current);
|
||||||
@ -740,8 +764,9 @@ void _spAnimationState_disposeNext (spAnimationState* self, spTrackEntry* entry)
|
|||||||
|
|
||||||
void _spAnimationState_animationsChanged (spAnimationState* self) {
|
void _spAnimationState_animationsChanged (spAnimationState* self) {
|
||||||
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
|
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
|
||||||
int i, n;
|
int i, n, ii, nn, lowestMixingFrom;
|
||||||
spTrackEntry* entry;
|
spTrackEntry* entry;
|
||||||
|
spTimeline** timelines;
|
||||||
internal->animationsChanged = 0;
|
internal->animationsChanged = 0;
|
||||||
|
|
||||||
i = 0; n = self->tracksCount;
|
i = 0; n = self->tracksCount;
|
||||||
@ -758,6 +783,31 @@ void _spAnimationState_animationsChanged (spAnimationState* self) {
|
|||||||
entry = self->tracks[i];
|
entry = self->tracks[i];
|
||||||
if (entry) _spAnimationState_checkTimelinesFirst(self, entry);
|
if (entry) _spAnimationState_checkTimelinesFirst(self, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self->multipleMixing) return;
|
||||||
|
|
||||||
|
internal->propertyIDsCount = 0;
|
||||||
|
lowestMixingFrom = n;
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
entry = self->tracks[i];
|
||||||
|
if (entry == 0 || entry->mixingFrom == 0) continue;
|
||||||
|
lowestMixingFrom = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (i = n - 1; i >= lowestMixingFrom; i--) {
|
||||||
|
entry = self->tracks[i];
|
||||||
|
if (entry == 0) continue;
|
||||||
|
|
||||||
|
timelines = entry->animation->timelines;
|
||||||
|
for (ii = 0, nn = entry->animation->timelinesCount; ii < nn; ii++)
|
||||||
|
_spAnimationState_addPropertyID(self, spTimeline_getPropertyId(timelines[ii]));
|
||||||
|
|
||||||
|
entry = entry->mixingFrom;
|
||||||
|
while (entry != 0) {
|
||||||
|
_spAnimationState_checkTimelinesUsage(self, entry, 0);
|
||||||
|
entry = entry->mixingFrom;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float* _spAnimationState_resizeTimelinesRotation(spTrackEntry* entry, int newSize) {
|
float* _spAnimationState_resizeTimelinesRotation(spTrackEntry* entry, int newSize) {
|
||||||
@ -781,6 +831,17 @@ int* _spAnimationState_resizeTimelinesFirst(spTrackEntry* entry, int newSize) {
|
|||||||
return entry->timelinesFirst;
|
return entry->timelinesFirst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int* _spAnimationState_resizeTimelinesLast(spTrackEntry* entry, int newSize) {
|
||||||
|
if (entry->timelinesLastCount != newSize) {
|
||||||
|
int* newTimelinesLast = CALLOC(int, newSize);
|
||||||
|
FREE(entry->timelinesLast);
|
||||||
|
entry->timelinesLast = newTimelinesLast;
|
||||||
|
entry->timelinesLastCount = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry->timelinesLast;
|
||||||
|
}
|
||||||
|
|
||||||
void _spAnimationState_ensureCapacityPropertyIDs(spAnimationState* self, int capacity) {
|
void _spAnimationState_ensureCapacityPropertyIDs(spAnimationState* self, int capacity) {
|
||||||
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
|
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
|
||||||
if (internal->propertyIDsCapacity < capacity) {
|
if (internal->propertyIDsCapacity < capacity) {
|
||||||
@ -813,7 +874,7 @@ void _spAnimationState_setTimelinesFirst (spAnimationState* self, spTrackEntry*
|
|||||||
|
|
||||||
if (entry->mixingFrom) {
|
if (entry->mixingFrom) {
|
||||||
_spAnimationState_setTimelinesFirst(self, entry->mixingFrom);
|
_spAnimationState_setTimelinesFirst(self, entry->mixingFrom);
|
||||||
_spAnimationState_checkTimelinesUsage(self, entry);
|
_spAnimationState_checkTimelinesUsage(self, entry, -1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -828,16 +889,16 @@ void _spAnimationState_setTimelinesFirst (spAnimationState* self, spTrackEntry*
|
|||||||
|
|
||||||
void _spAnimationState_checkTimelinesFirst (spAnimationState* self, spTrackEntry* entry) {
|
void _spAnimationState_checkTimelinesFirst (spAnimationState* self, spTrackEntry* entry) {
|
||||||
if (entry->mixingFrom) _spAnimationState_checkTimelinesFirst(self, entry->mixingFrom);
|
if (entry->mixingFrom) _spAnimationState_checkTimelinesFirst(self, entry->mixingFrom);
|
||||||
_spAnimationState_checkTimelinesUsage(self, entry);
|
_spAnimationState_checkTimelinesUsage(self, entry, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _spAnimationState_checkTimelinesUsage (spAnimationState* self, spTrackEntry* entry) {
|
void _spAnimationState_checkTimelinesUsage (spAnimationState* self, spTrackEntry* entry, int /*boolean*/ useTimelinesFirst) {
|
||||||
int i, n;
|
int i, n;
|
||||||
int* usage;
|
int* usage;
|
||||||
spTimeline** timelines;
|
spTimeline** timelines;
|
||||||
n = entry->animation->timelinesCount;
|
n = entry->animation->timelinesCount;
|
||||||
timelines = entry->animation->timelines;
|
timelines = entry->animation->timelines;
|
||||||
usage = _spAnimationState_resizeTimelinesFirst(entry, n);
|
usage = useTimelinesFirst ? _spAnimationState_resizeTimelinesFirst(entry, n) : _spAnimationState_resizeTimelinesLast(entry, n);
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
usage[i] = _spAnimationState_addPropertyID(self, spTimeline_getPropertyId(timelines[i]));
|
usage[i] = _spAnimationState_addPropertyID(self, spTimeline_getPropertyId(timelines[i]));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,26 @@ namespace Spine {
|
|||||||
private readonly HashSet<int> propertyIDs = new HashSet<int>();
|
private readonly HashSet<int> propertyIDs = new HashSet<int>();
|
||||||
private readonly ExposedList<Event> events = new ExposedList<Event>();
|
private readonly ExposedList<Event> events = new ExposedList<Event>();
|
||||||
private readonly EventQueue queue;
|
private readonly EventQueue queue;
|
||||||
|
|
||||||
private bool animationsChanged;
|
private bool animationsChanged;
|
||||||
|
private bool multipleMixing;
|
||||||
|
/// <summary>
|
||||||
|
/// <para>When false, only two animations can be mixed at once. Interrupting a mix by setting a new animation will choose from the
|
||||||
|
/// two old animations the one that is closest to being fully mixed in and the other is discarded. Discarding an animation in
|
||||||
|
/// this way may cause keyed values to jump.</para>
|
||||||
|
/// <para>When true, any number of animations may be mixed at once without causing keyed values to jump. Mixing is done by mixing out
|
||||||
|
/// one or more animations while mixing in the newest one. When animations key the same value, this may cause "dipping", where
|
||||||
|
/// the value moves toward the setup pose as the old animation mixes out, then back to the keyed value as the new animation
|
||||||
|
/// mixes in.</para>
|
||||||
|
/// Defaults to false.</summary>
|
||||||
|
public bool MultipleMixing {
|
||||||
|
get { return multipleMixing; }
|
||||||
|
set {
|
||||||
|
multipleMixing = value;
|
||||||
|
animationsChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private float timeScale = 1;
|
private float timeScale = 1;
|
||||||
|
|
||||||
Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
|
Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
|
||||||
@ -214,7 +233,9 @@ namespace Spine {
|
|||||||
int timelineCount = timelines.Count;
|
int timelineCount = timelines.Count;
|
||||||
var timelinesFirst = from.timelinesFirst;
|
var timelinesFirst = from.timelinesFirst;
|
||||||
var timelinesFirstItems = timelinesFirst.Items;
|
var timelinesFirstItems = timelinesFirst.Items;
|
||||||
float alpha = from.alpha * entry.mixAlpha * (1 - mix);
|
var timelinesLastItems = multipleMixing ? null : from.timelinesLast.Items;
|
||||||
|
float alphaBase = from.alpha * entry.mixAlpha;
|
||||||
|
float alphaMix = alphaBase * (1 - mix);
|
||||||
|
|
||||||
bool firstFrame = entry.timelinesRotation.Count == 0;
|
bool firstFrame = entry.timelinesRotation.Count == 0;
|
||||||
if (firstFrame) entry.timelinesRotation.EnsureCapacity(timelines.Count << 1);
|
if (firstFrame) entry.timelinesRotation.EnsureCapacity(timelines.Count << 1);
|
||||||
@ -223,6 +244,7 @@ namespace Spine {
|
|||||||
for (int i = 0; i < timelineCount; i++) {
|
for (int i = 0; i < timelineCount; i++) {
|
||||||
Timeline timeline = timelinesItems[i];
|
Timeline timeline = timelinesItems[i];
|
||||||
bool setupPose = timelinesFirstItems[i];
|
bool setupPose = timelinesFirstItems[i];
|
||||||
|
float alpha = timelinesLastItems != null && setupPose && !timelinesLastItems[i] ? alphaBase : alphaMix;
|
||||||
var rotateTimeline = timeline as RotateTimeline;
|
var rotateTimeline = timeline as RotateTimeline;
|
||||||
if (rotateTimeline != null) {
|
if (rotateTimeline != null) {
|
||||||
ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i << 1, firstFrame);
|
ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i << 1, firstFrame);
|
||||||
@ -389,10 +411,31 @@ namespace Spine {
|
|||||||
current.mixingFrom = from;
|
current.mixingFrom = from;
|
||||||
current.mixTime = 0;
|
current.mixTime = 0;
|
||||||
|
|
||||||
from.timelinesRotation.Clear();
|
//from.timelinesRotation.Clear();
|
||||||
|
var mixingFrom = from.mixingFrom;
|
||||||
|
float mixProgress = from.mixTime / from.mixDuration;
|
||||||
|
if (mixingFrom != null && from.mixDuration > 0) {
|
||||||
|
// A mix was interrupted, mix from the closest animation.
|
||||||
|
if (!multipleMixing && mixProgress < 0.5f && mixingFrom.animation != AnimationState.EmptyAnimation) {
|
||||||
|
current.mixingFrom = mixingFrom;
|
||||||
|
mixingFrom.mixingFrom = from;
|
||||||
|
mixingFrom.mixTime = from.mixDuration - from.mixTime;
|
||||||
|
mixingFrom.mixDuration = from.mixDuration;
|
||||||
|
from.mixingFrom = null;
|
||||||
|
from = mixingFrom;
|
||||||
|
}
|
||||||
|
|
||||||
// If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
|
// The interrupted mix will mix out from its current percentage to zero.
|
||||||
if (from.mixingFrom != null && from.mixDuration > 0) current.mixAlpha *= Math.Min(from.mixTime / from.mixDuration, 1);
|
if (multipleMixing) current.mixAlpha *= Math.Min(mixProgress, 1);
|
||||||
|
|
||||||
|
if (!multipleMixing) {
|
||||||
|
from.mixAlpha = 0;
|
||||||
|
from.mixTime = 0;
|
||||||
|
from.mixDuration = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from.timelinesRotation.Clear(); // Reset rotation for mixing out, in case entry was mixed in.
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.Start(current);
|
queue.Start(current);
|
||||||
@ -586,13 +629,41 @@ namespace Spine {
|
|||||||
TrackEntry entry = tracks.Items[i];
|
TrackEntry entry = tracks.Items[i];
|
||||||
if (entry != null) CheckTimelinesFirst(entry);
|
if (entry != null) CheckTimelinesFirst(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (multipleMixing) return;
|
||||||
|
|
||||||
|
// Set timelinesLast for mixingFrom entries, from highest track to lowest that has mixingFrom.
|
||||||
|
propertyIDs.Clear();
|
||||||
|
int lowestMixingFrom = n;
|
||||||
|
for (i = 0; i < n; i++) { // Find lowest track with a mixingFrom entry.
|
||||||
|
TrackEntry entry = tracks.Items[i];
|
||||||
|
if (entry == null || entry.mixingFrom == null) continue;
|
||||||
|
lowestMixingFrom = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (i = n - 1; i >= lowestMixingFrom; i--) { // Find first non-null entry.
|
||||||
|
TrackEntry entry = tracks.Items[i];
|
||||||
|
if (entry == null) continue;
|
||||||
|
|
||||||
|
// Store properties for non-mixingFrom entry but don't set timelinesLast, which is only used for mixingFrom entries.
|
||||||
|
var timelines = entry.animation.timelines;
|
||||||
|
var timelinesItems = timelines.Items;
|
||||||
|
for (int ii = 0, nn = timelines.Count; ii < nn; ii++)
|
||||||
|
propertyIDs.Add(timelinesItems[ii].PropertyId);
|
||||||
|
|
||||||
|
entry = entry.mixingFrom;
|
||||||
|
while (entry != null) {
|
||||||
|
CheckTimelinesUsage(entry, entry.timelinesLast);
|
||||||
|
entry = entry.mixingFrom;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>From last to first mixingFrom entries, sets timelinesFirst to true on last, calls checkTimelineUsage on rest.</summary>
|
/// <summary>From last to first mixingFrom entries, sets timelinesFirst to true on last, calls checkTimelineUsage on rest.</summary>
|
||||||
private void SetTimelinesFirst (TrackEntry entry) {
|
private void SetTimelinesFirst (TrackEntry entry) {
|
||||||
if (entry.mixingFrom != null) {
|
if (entry.mixingFrom != null) {
|
||||||
SetTimelinesFirst(entry.mixingFrom);
|
SetTimelinesFirst(entry.mixingFrom);
|
||||||
CheckTimelinesUsage(entry);
|
CheckTimelinesUsage(entry, entry.timelinesFirst);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var propertyIDs = this.propertyIDs;
|
var propertyIDs = this.propertyIDs;
|
||||||
@ -610,14 +681,14 @@ namespace Spine {
|
|||||||
/// <summary>From last to first mixingFrom entries, calls checkTimelineUsage.</summary>
|
/// <summary>From last to first mixingFrom entries, calls checkTimelineUsage.</summary>
|
||||||
private void CheckTimelinesFirst (TrackEntry entry) {
|
private void CheckTimelinesFirst (TrackEntry entry) {
|
||||||
if (entry.mixingFrom != null) CheckTimelinesFirst(entry.mixingFrom);
|
if (entry.mixingFrom != null) CheckTimelinesFirst(entry.mixingFrom);
|
||||||
CheckTimelinesUsage(entry);
|
CheckTimelinesUsage(entry, entry.timelinesFirst);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckTimelinesUsage (TrackEntry entry) {
|
private void CheckTimelinesUsage (TrackEntry entry, ExposedList<bool> usageArray) {
|
||||||
var propertyIDs = this.propertyIDs;
|
var propertyIDs = this.propertyIDs;
|
||||||
var timelines = entry.animation.timelines;
|
var timelines = entry.animation.timelines;
|
||||||
int n = timelines.Count;
|
int n = timelines.Count;
|
||||||
var usageArray = entry.timelinesFirst;
|
//var usageArray = entry.timelinesFirst;
|
||||||
usageArray.EnsureCapacity(n);
|
usageArray.EnsureCapacity(n);
|
||||||
var usage = usageArray.Items;
|
var usage = usageArray.Items;
|
||||||
var timelinesItems = timelines.Items;
|
var timelinesItems = timelines.Items;
|
||||||
@ -662,6 +733,7 @@ namespace Spine {
|
|||||||
internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f;
|
internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f;
|
||||||
internal float alpha, mixTime, mixDuration, mixAlpha;
|
internal float alpha, mixTime, mixDuration, mixAlpha;
|
||||||
internal readonly ExposedList<bool> timelinesFirst = new ExposedList<bool>();
|
internal readonly ExposedList<bool> timelinesFirst = new ExposedList<bool>();
|
||||||
|
internal readonly ExposedList<bool> timelinesLast = new ExposedList<bool>();
|
||||||
internal readonly ExposedList<float> timelinesRotation = new ExposedList<float>();
|
internal readonly ExposedList<float> timelinesRotation = new ExposedList<float>();
|
||||||
|
|
||||||
// IPoolable.Reset()
|
// IPoolable.Reset()
|
||||||
@ -670,6 +742,7 @@ namespace Spine {
|
|||||||
mixingFrom = null;
|
mixingFrom = null;
|
||||||
animation = null;
|
animation = null;
|
||||||
timelinesFirst.Clear();
|
timelinesFirst.Clear();
|
||||||
|
timelinesLast.Clear();
|
||||||
timelinesRotation.Clear();
|
timelinesRotation.Clear();
|
||||||
|
|
||||||
Start = null;
|
Start = null;
|
||||||
|
|||||||
@ -53,6 +53,12 @@ function loadSkeleton (jsonFile, atlasFile, animation, skin, scale, x, y)
|
|||||||
local stateData = spine.AnimationStateData.new(skeletonData)
|
local stateData = spine.AnimationStateData.new(skeletonData)
|
||||||
local state = spine.AnimationState.new(stateData)
|
local state = spine.AnimationState.new(stateData)
|
||||||
state:setAnimationByName(0, animation, true)
|
state:setAnimationByName(0, animation, true)
|
||||||
|
if (jsonFile == "spineboy") then
|
||||||
|
stateData:setMix("walk", "jump", 0.5)
|
||||||
|
stateData:setMix("jump", "run", 0.5)
|
||||||
|
state:addAnimationByName(0, "jump", false, 3)
|
||||||
|
state:addAnimationByName(0, "run", true, 0)
|
||||||
|
end
|
||||||
|
|
||||||
-- set some event callbacks
|
-- set some event callbacks
|
||||||
state.onStart = function (entry)
|
state.onStart = function (entry)
|
||||||
@ -82,14 +88,13 @@ end
|
|||||||
|
|
||||||
function love.load(arg)
|
function love.load(arg)
|
||||||
if arg[#arg] == "-debug" then require("mobdebug").start() end
|
if arg[#arg] == "-debug" then require("mobdebug").start() end
|
||||||
table.insert(skeletons, loadSkeleton("test", "test", "animation", nil, 0.5, 400, 300))
|
-- table.insert(skeletons, loadSkeleton("test", "test", "animation", nil, 0.5, 400, 300))
|
||||||
table.insert(skeletons, loadSkeleton("TwoColorTest", "TwoColorTest", "animation", nil, 0.3, 400, 300))
|
|
||||||
table.insert(skeletons, loadSkeleton("spineboy", "spineboy", "walk", nil, 0.5, 400, 500))
|
table.insert(skeletons, loadSkeleton("spineboy", "spineboy", "walk", nil, 0.5, 400, 500))
|
||||||
table.insert(skeletons, loadSkeleton("raptor", "raptor", "walk", nil, 0.3, 400, 500))
|
--[[ table.insert(skeletons, loadSkeleton("raptor", "raptor", "walk", nil, 0.3, 400, 500))
|
||||||
table.insert(skeletons, loadSkeleton("goblins-mesh", "goblins", "walk", "goblin", 1, 400, 500))
|
table.insert(skeletons, loadSkeleton("goblins-mesh", "goblins", "walk", "goblin", 1, 400, 500))
|
||||||
table.insert(skeletons, loadSkeleton("tank", "tank", "drive", nil, 0.2, 600, 500))
|
table.insert(skeletons, loadSkeleton("tank", "tank", "drive", nil, 0.2, 600, 500))
|
||||||
table.insert(skeletons, loadSkeleton("vine", "vine", "animation", nil, 0.3, 400, 500))
|
table.insert(skeletons, loadSkeleton("vine", "vine", "animation", nil, 0.3, 400, 500))
|
||||||
table.insert(skeletons, loadSkeleton("stretchyman", "stretchyman", "sneak", nil, 0.3, 200, 500))
|
table.insert(skeletons, loadSkeleton("stretchyman", "stretchyman", "sneak", nil, 0.3, 200, 500))]]--
|
||||||
skeletonRenderer = spine.SkeletonRenderer.new(true)
|
skeletonRenderer = spine.SkeletonRenderer.new(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -169,6 +169,7 @@ function TrackEntry.new ()
|
|||||||
delay = 0, trackTime = 0, trackLast = 0, nextTrackLast = 0, trackEnd = 0, timeScale = 0,
|
delay = 0, trackTime = 0, trackLast = 0, nextTrackLast = 0, trackEnd = 0, timeScale = 0,
|
||||||
alpha = 0, mixTime = 0, mixDuration = 0, mixAlpha = 0,
|
alpha = 0, mixTime = 0, mixDuration = 0, mixAlpha = 0,
|
||||||
timelinesFirst = {},
|
timelinesFirst = {},
|
||||||
|
timelinesLast = {},
|
||||||
timelinesRotation = {}
|
timelinesRotation = {}
|
||||||
}
|
}
|
||||||
setmetatable(self, TrackEntry)
|
setmetatable(self, TrackEntry)
|
||||||
@ -202,7 +203,8 @@ function AnimationState.new (data)
|
|||||||
queue = nil,
|
queue = nil,
|
||||||
propertyIDs = {},
|
propertyIDs = {},
|
||||||
animationsChanged = false,
|
animationsChanged = false,
|
||||||
timeScale = 1
|
timeScale = 1,
|
||||||
|
mixingMultiple = false
|
||||||
}
|
}
|
||||||
self.queue = EventQueue.new(self)
|
self.queue = EventQueue.new(self)
|
||||||
setmetatable(self, AnimationState)
|
setmetatable(self, AnimationState)
|
||||||
@ -356,8 +358,11 @@ function AnimationState:applyMixingFrom (entry, skeleton)
|
|||||||
local animationLast = from.animationLast
|
local animationLast = from.animationLast
|
||||||
local animationTime = from:getAnimationTime()
|
local animationTime = from:getAnimationTime()
|
||||||
local timelines = from.animation.timelines
|
local timelines = from.animation.timelines
|
||||||
local timelinesFirst = from.timelinesFirst;
|
local timelinesFirst = from.timelinesFirst
|
||||||
local alpha = from.alpha * entry.mixAlpha * (1 - mix)
|
local timelinesLast = nil
|
||||||
|
if (self.multipleMixing == false) then timelinesLast = from.timelinesLast end
|
||||||
|
local alphaBase = from.alpha * entry.mixAlpha
|
||||||
|
local alphaMix = alphaBase * (1 - mix)
|
||||||
|
|
||||||
local firstFrame = #from.timelinesRotation == 0
|
local firstFrame = #from.timelinesRotation == 0
|
||||||
local timelinesRotation = from.timelinesRotation
|
local timelinesRotation = from.timelinesRotation
|
||||||
@ -365,6 +370,12 @@ function AnimationState:applyMixingFrom (entry, skeleton)
|
|||||||
local skip = false
|
local skip = false
|
||||||
for i,timeline in ipairs(timelines) do
|
for i,timeline in ipairs(timelines) do
|
||||||
local setupPose = timelinesFirst[i]
|
local setupPose = timelinesFirst[i]
|
||||||
|
local alpha = 1;
|
||||||
|
if (timelinesLast ~= nil and setupPose and not timlinesLast[i]) then
|
||||||
|
alpha = alphaBase
|
||||||
|
else
|
||||||
|
alpha = alphaMix
|
||||||
|
end
|
||||||
if timeline.type == Animation.TimelineType.rotate then
|
if timeline.type == Animation.TimelineType.rotate then
|
||||||
self:applyRotateTimeline(timeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i * 2, firstFrame) -- FIXME passing i * 2, correct indexing?
|
self:applyRotateTimeline(timeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i * 2, firstFrame) -- FIXME passing i * 2, correct indexing?
|
||||||
else
|
else
|
||||||
@ -541,10 +552,27 @@ function AnimationState:setCurrent (index, current, interrupt)
|
|||||||
current.mixingFrom = from
|
current.mixingFrom = from
|
||||||
current.mixTime = 0
|
current.mixTime = 0
|
||||||
|
|
||||||
from.timelinesRotation = {};
|
local mixingFrom = from.mixingFrom
|
||||||
|
if (mixingFrom ~= nil and from.mixDuration > 0) then
|
||||||
|
if (not self.multipleMixing and from.mixTime / from.mixDuration < 0.5 and mixingFrom.animation ~= EMPTY_ANIMATION) then
|
||||||
|
current.mixingFrom = mixingFrom
|
||||||
|
mixingFrom.mixingFrom = from
|
||||||
|
mixingFrom.mixTime = from.mixDuration - from.mixTime
|
||||||
|
mixingFrom.mixDuration = from.mixDuration
|
||||||
|
from.mixingFrom = nil
|
||||||
|
from = mixingFrom
|
||||||
|
end
|
||||||
|
|
||||||
-- If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
|
current.mixAlpha = current.mixAlpha * math_min(from.mixTime / from.mixDuration, 1)
|
||||||
if from.mixingFrom and from.mixDuration > 0 then current.mixAlpha = current.mixAlpha * math_min(from.mixTime / from.mixDuration, 1) end
|
|
||||||
|
if (not self.multipleMixing) then
|
||||||
|
from.mixAlpha = 0;
|
||||||
|
from.mixTime = 0;
|
||||||
|
from.mixDuration = 0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
from.timelinesRotation = {};
|
||||||
end
|
end
|
||||||
|
|
||||||
queue:start(current)
|
queue:start(current)
|
||||||
@ -724,6 +752,42 @@ function AnimationState:_animationsChanged ()
|
|||||||
if entry then self:checkTimelinesFirst(entry) end
|
if entry then self:checkTimelinesFirst(entry) end
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if (self.multipleMixing) then return end
|
||||||
|
|
||||||
|
self.propertyIDs = {}
|
||||||
|
local lowestMixingFrom = n
|
||||||
|
i = 0;
|
||||||
|
while i < n do
|
||||||
|
entry = self.tracks[i]
|
||||||
|
if not (entry == nil or entry.mixingFrom == nil) then
|
||||||
|
lowestMixingFrom = i
|
||||||
|
i = n + 1 -- break
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
i = n - 1
|
||||||
|
while i >= lowestMixingFrom do
|
||||||
|
local entry = self.tracks[i]
|
||||||
|
if (entry) then
|
||||||
|
local propertyIDs = self.propertyIDs
|
||||||
|
local timelines = entry.animation.timelines
|
||||||
|
local ii = 1
|
||||||
|
local nn = #entry.animation.timelines;
|
||||||
|
while ii <= nn do
|
||||||
|
local id = "" .. timelines[ii]:getPropertyId()
|
||||||
|
propertyIDs[id] = id
|
||||||
|
ii = ii + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
entry = entry.mixingFrom
|
||||||
|
while (entry) do
|
||||||
|
self:checkTimelinesUsage(entry, entry.timelinesLast)
|
||||||
|
entry = entry.mixingFrom;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
i = i - 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function AnimationState:setTimelinesFirst (entry)
|
function AnimationState:setTimelinesFirst (entry)
|
||||||
|
|||||||
@ -116,6 +116,7 @@ void spineboy (SkeletonData* skeletonData, Atlas* atlas) {
|
|||||||
|
|
||||||
SkeletonDrawable* drawable = new SkeletonDrawable(skeletonData, stateData);
|
SkeletonDrawable* drawable = new SkeletonDrawable(skeletonData, stateData);
|
||||||
drawable->timeScale = 1;
|
drawable->timeScale = 1;
|
||||||
|
drawable->state->multipleMixing = -1;
|
||||||
|
|
||||||
Skeleton* skeleton = drawable->skeleton;
|
Skeleton* skeleton = drawable->skeleton;
|
||||||
skeleton->flipX = false;
|
skeleton->flipX = false;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user