diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index 07d29ff7c..46ac00f68 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -719,16 +719,37 @@ public class Animation { float frameTime = frames[frame]; float percent = getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)); - if (alpha < 1) { - for (int i = 0; i < vertexCount; i++) { - float prev = prevVertices[i]; - vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; - } - } else { + // BOZO - Test. + if (alpha == 1) { + // Absolute. for (int i = 0; i < vertexCount; i++) { float prev = prevVertices[i]; vertices[i] = prev + (nextVertices[i] - prev) * percent; } + } else { + if (setupPose) { + VertexAttachment vertexAttachment = (VertexAttachment)slotAttachment; + if (vertexAttachment.getBones() == null) { + // Vertex positions. + float[] setupVertices = vertexAttachment.getVertices(); + for (int i = 0; i < vertexCount; i++) { + float prev = prevVertices[i], setup = setupVertices[i]; + vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha; + } + } else { + // Deform offsets. + for (int i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha; + } + } + } else { + // Additive. + for (int i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; + } + } } } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java index 076e7b146..49ee0e2bf 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java @@ -41,7 +41,11 @@ import com.esotericsoftware.spine.Animation.AttachmentTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.Timeline; -/** Stores state for applying one or more animations over time and automatically mixes (crossfades) when animations change. */ +/** Stores state for applying one or more animations over time and mixing (crossfading) between animations. + *
+ * Animations on different tracks are applied sequentially each frame, from lowest to highest track index. This enables animations
+ * to be layered, where higher tracks either key only a subset of the skeleton pose or use alpha < 1 to mix with the pose on the
+ * lower track. */
public class AnimationState {
static private final Animation emptyAnimation = new Animation("
+ * It may be desired to use {@link AnimationState#setEmptyAnimations(float)} to mix the skeletons back to the setup pose,
+ * rather than leaving them in their last pose. */
public void clearTracks () {
queue.drainDisabled = true;
for (int i = 0, n = tracks.size; i < n; i++)
@@ -248,7 +251,10 @@ public class AnimationState {
queue.drain();
}
- /** Removes all animations from the track, leaving skeletons in their last pose. */
+ /** Removes all animations from the track, leaving skeletons in their last pose.
+ *
+ * It may be desired to use {@link AnimationState#setEmptyAnimation(int, float)} to mix the skeletons back to the setup pose,
+ * rather than leaving them in their last pose. */
public void clearTrack (int trackIndex) {
if (trackIndex >= tracks.size) return;
TrackEntry current = tracks.get(trackIndex);
@@ -269,23 +275,6 @@ public class AnimationState {
queue.drain();
}
- /** @param entry May be null. */
- private void disposeNext (TrackEntry entry) {
- TrackEntry next = entry.next;
- while (next != null) {
- queue.dispose(next);
- next = next.next;
- }
- entry.next = null;
- }
-
- private TrackEntry expandToIndex (int index) {
- if (index < tracks.size) return tracks.get(index);
- tracks.ensureCapacity(index - tracks.size + 1);
- tracks.size = index + 1;
- return null;
- }
-
private void setCurrent (int index, TrackEntry entry) {
TrackEntry current = expandToIndex(index);
tracks.set(index, entry);
@@ -311,49 +300,6 @@ public class AnimationState {
animationsChanged = true;
}
- private void animationsChanged () {
- animationsChanged = false;
- propertyIDs.clear();
- int i = 0, n = tracks.size;
- for (; i < n; i++) {
- TrackEntry entry = tracks.get(i);
- if (entry == null) continue;
- if (entry.mixingFrom != null) {
- setTimelinesFirst(entry.mixingFrom);
- checkTimelinesFirst(entry);
- } else
- setTimelinesFirst(entry);
- i++;
- break;
- }
- for (; i < n; i++) {
- TrackEntry entry = tracks.get(i);
- if (entry == null) continue;
- if (entry.mixingFrom != null) checkTimelinesFirst(entry.mixingFrom);
- checkTimelinesFirst(entry);
- }
- }
-
- private void setTimelinesFirst (TrackEntry entry) {
- IntSet propertyIDs = this.propertyIDs;
- Array
+ * It may be desired to use {@link AnimationState#addEmptyAnimation(int, float, float)} to mix the skeletons back to the
+ * setup pose, rather than leaving them in their last pose. */
public float getTrackEnd () {
return trackEnd;
}
@@ -626,8 +635,8 @@ public class AnimationState {
/** Seconds when this animation starts, both initially and after looping. Defaults to 0.
*
- * When changing the animation start time, it often makes sense to also change {@link #getAnimationLast()} to control which
- * timeline keys will trigger. */
+ * When changing the animation start time, it often makes sense to set {@link #getAnimationLast()} to the same value to
+ * prevent timeline keys before the start time from triggering. */
public float getAnimationStart () {
return animationStart;
}
@@ -636,7 +645,7 @@ public class AnimationState {
this.animationStart = animationStart;
}
- /** Seconds for the last frame of this animation. Non-looping animations won't play past this time. Looping animation will
+ /** Seconds for the last frame of this animation. Non-looping animations won't play past this time. Looping animations will
* loop back to {@link #getAnimationStart()} at this time. Defaults to the animation duration. */
public float getAnimationEnd () {
return animationEnd;
@@ -658,9 +667,8 @@ public class AnimationState {
nextAnimationLast = animationLast;
}
- /** Uses the {@link #getTrackTime() track time} to compute the animation time between the {@link #getAnimationStart()
- * animation start} and {@link #getAnimationEnd() animation end}. When the track time is 0, the animation time is equal to
- * the animation start time. */
+ /** Uses {@link #getTrackTime()} to compute the animation time between {@link #getAnimationStart()} and
+ * {@link #getAnimationEnd()}. When the track time is 0, the animation time is equal to the animation start time. */
public float getAnimationTime () {
if (loop) {
float duration = animationEnd - animationStart;
@@ -690,11 +698,11 @@ public class AnimationState {
this.listener = listener;
}
- /** Values < 1 mix this animation with the skeleton pose. Defaults to 1, which overwrites the skeleton pose with this
- * animation.
+ /** Values < 1 mix this animation with the last skeleton pose. Defaults to 1, which overwrites the last skeleton pose with
+ * this animation.
*
- * Typically track 0 is used to completely pose the skeleton, then alpha can be used on higher tracks. Generally it doesn't
- * make sense to use alpha on track 0, since the skeleton pose is probably from the last frame render. */
+ * Typically track 0 is used to completely pose the skeleton, then alpha can be used on higher tracks. It doesn't make sense
+ * to use alpha on track 0 if the skeleton pose is from the last frame render. */
public float getAlpha () {
return alpha;
}
@@ -758,7 +766,7 @@ public class AnimationState {
/** Seconds for mixing from the previous animation to this animation. Defaults to the value provided by
* {@link AnimationStateData} based on the animation before this animation (if any).
*
- * The mix duration must be set before the next time the animation state is updated. */
+ * The mix duration must be set before {@link AnimationState#update(float)} is next called. */
public float getMixDuration () {
return mixDuration;
}
@@ -879,18 +887,18 @@ public class AnimationState {
}
static public interface AnimationStateListener {
- /** Invoked just after this entry is set as the current entry. */
+ /** Invoked when this entry has been set as the current entry. */
public void start (TrackEntry entry);
- /** Invoked just after another entry is set to replace this entry as the current entry. This entry may continue being
- * applied for mixing. */
+ /** Invoked when another entry replaces this entry as the current entry. This entry may continue being applied for
+ * mixing. */
public void interrupt (TrackEntry entry);
- /** Invoked just before this entry will no longer be the current entry and will never be applied again. */
+ /** Invoked when this entry is no longer the current entry and will never be applied again. */
public void end (TrackEntry entry);
- /** Invoked just before this track entry will be disposed. References to the entry should not be kept after dispose is
- * called, as it may be destroyed or reused. */
+ /** Invoked when this entry will be disposed. References to the entry should not be kept after dispose is called, as it may
+ * be destroyed or reused. */
public void dispose (TrackEntry entry);
/** Invoked every time this entry's animation completes a loop. */