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 465cfb21b..8bc95c640 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -136,8 +136,8 @@ public class Animation { static public interface Timeline { /** Sets the value(s) for the specified time. * @param events May be null to not collect fired events. - * @param setupPose If true, the timeline is mixed with the setup pose, else it is mixed with the current pose. Passing true - * when alpha is 1 is slightly more efficient. + * @param setupPose True when the timeline is mixed with the setup pose, false when it is mixed with the current pose. + * Passing true when alpha is 1 is slightly more efficient. * @param mixingOut True when mixing over time toward the setup or current pose, false when mixing toward the keyed pose. * Irrelevant when alpha is 1. */ public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha, boolean setupPose, @@ -916,7 +916,7 @@ public class Animation { if (time < frames[0]) return; // Time is before first frame. // BOZO - Finish timelines handling setupPose and mixingOut from here down. - + IkConstraint constraint = skeleton.ikConstraints.get(ikConstraintIndex); if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame. 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 f3806240c..12cec0d18 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java @@ -52,7 +52,7 @@ public class AnimationState { static private final Animation emptyAnimation = new Animation("", new Array(0), 0); private AnimationStateData data; - private final Array tracks = new Array(); + final Array tracks = new Array(); private final Array events = new Array(); final Array listeners = new Array(); private final EventQueue queue = new EventQueue(); @@ -60,9 +60,6 @@ public class AnimationState { boolean animationsChanged; private float timeScale = 1; - StringBuilder last = new StringBuilder(); - StringBuilder log = new StringBuilder(); - final Pool trackEntryPool = new Pool() { protected Object newObject () { return new TrackEntry(); @@ -112,9 +109,9 @@ public class AnimationState { } continue; } - updateMixingFrom(current, delta); + updateMixingFrom(current, delta, true); } else { - updateMixingFrom(current, delta); + updateMixingFrom(current, delta, true); // Clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom. if (current.trackLast >= current.trackEnd && current.mixingFrom == null) { tracks.set(i, null); @@ -130,11 +127,11 @@ public class AnimationState { queue.drain(); } - private void updateMixingFrom (TrackEntry entry, float delta) { + private void updateMixingFrom (TrackEntry entry, float delta, boolean canEnd) { TrackEntry from = entry.mixingFrom; if (from == null) return; - if (entry.mixTime >= entry.mixDuration && entry.mixTime > 0) { + if (canEnd && entry.mixTime >= entry.mixDuration && entry.mixTime > 0) { queue.end(from); TrackEntry newFrom = from.mixingFrom; entry.mixingFrom = newFrom; @@ -150,7 +147,7 @@ public class AnimationState { from.trackTime += mixingFromDelta; entry.mixTime += mixingFromDelta; - updateMixingFrom(from, delta); + updateMixingFrom(from, delta, canEnd && from.alpha == 1); } /** Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the @@ -161,7 +158,7 @@ public class AnimationState { Array events = this.events; - for (int i = 0; i < tracks.size; i++) { + for (int i = 0, n = tracks.size; i < n; i++) { TrackEntry current = tracks.get(i); if (current == null || current.delay > 0) continue; @@ -171,24 +168,24 @@ public class AnimationState { // Apply current entry. float animationLast = current.animationLast, animationTime = current.getAnimationTime(); - Array timelines = current.animation.timelines; - log("apply current: " + current + ", mix: " + mix + " * " + current.alpha); + int timelineCount = current.animation.timelines.size; + Object[] timelines = current.animation.timelines.items; if (mix == 1) { - for (int ii = 0, n = timelines.size; ii < n; ii++) - timelines.get(ii).apply(skeleton, animationLast, animationTime, events, 1, false, false); + for (int ii = 0; ii < timelineCount; ii++) + ((Timeline)timelines[ii]).apply(skeleton, animationLast, animationTime, events, 1, true, false); } else { boolean firstFrame = current.timelinesRotation.size == 0; - if (firstFrame) current.timelinesRotation.setSize(timelines.size << 1); + if (firstFrame) current.timelinesRotation.setSize(timelineCount << 1); float[] timelinesRotation = current.timelinesRotation.items; + boolean[] timelinesFirst = current.timelinesFirst.items; - for (int ii = 0, n = timelines.size; ii < n; ii++) { - Timeline timeline = timelines.get(ii); + for (int ii = 0; ii < timelineCount; ii++) { + Timeline timeline = (Timeline)timelines[ii]; if (timeline instanceof RotateTimeline) { - applyRotateTimeline((RotateTimeline)timeline, skeleton, animationLast, animationTime, events, mix, - timelinesFirst[ii], false, timelinesRotation, ii << 1, firstFrame); - } else { + applyRotateTimeline(timeline, skeleton, animationTime, mix, timelinesFirst[ii], timelinesRotation, ii << 1, + firstFrame); + } else timeline.apply(skeleton, animationLast, animationTime, events, mix, timelinesFirst[ii], false); - } } } queueEvents(current, animationTime); @@ -197,62 +194,44 @@ public class AnimationState { } queue.drain(); - - if (!log.toString().equals(last.toString())) { - System.out.println(log); - last.setLength(0); - last.append(log); - } - log.setLength(0); - } - - void log (String m) { - log.append(m); - log.append('\n'); } private float applyMixingFrom (TrackEntry entry, Skeleton skeleton, float alpha) { + TrackEntry from = entry.mixingFrom; + if (from.mixingFrom != null) applyMixingFrom(from, skeleton, alpha); + float mix; if (entry.mixDuration == 0) // Single frame mix to undo mixingFrom changes. mix = 1; else { - mix = alpha * entry.mixTime / entry.mixDuration; + mix = entry.mixTime / entry.mixDuration; if (mix > 1) mix = 1; + mix *= alpha; } - TrackEntry from = entry.mixingFrom; - if (from.mixingFrom != null) applyMixingFrom(from, skeleton, alpha); - Array events = mix < from.eventThreshold ? this.events : null; boolean attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold; - float animationLast = from.animationLast, animationTime = from.getAnimationTime(); - Array timelines = from.animation.timelines; - int timelineCount = timelines.size; - boolean[] timelinesFirst = from.timelinesFirst.items, timelinesLast = from.timelinesLast.items; - float alphaFull = from.alpha, alphaMix = alphaFull * (1 - mix); + alpha = from.alpha * (1 - mix); + int timelineCount = from.animation.timelines.size; + Object[] timelines = from.animation.timelines.items; + boolean[] timelinesFirst = from.timelinesFirst.items; boolean firstFrame = from.timelinesRotation.size == 0; if (firstFrame) from.timelinesRotation.setSize(timelineCount << 1); float[] timelinesRotation = from.timelinesRotation.items; - log("applyMixingFrom: " + entry.mixingFrom + " -> " + entry + ", mix: " + entry.mixTime / entry.mixDuration); - if (timelineCount == 0) log("apply from: " + from + " " + alphaFull + " * " + entry.alpha); - for (int i = 0; i < timelineCount; i++) { - Timeline timeline = timelines.get(i); + Timeline timeline = (Timeline)timelines[i]; boolean setupPose = timelinesFirst[i]; - float a = timelinesLast[i] ? alphaMix : alphaFull; - log("apply from: " + from + " " + a + " * " + entry.alpha); - if (timeline instanceof RotateTimeline) { - applyRotateTimeline((RotateTimeline)timeline, skeleton, animationLast, animationTime, events, a, setupPose, setupPose, - timelinesRotation, i << 1, firstFrame); - } else { + if (timeline instanceof RotateTimeline) + applyRotateTimeline(timeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i << 1, firstFrame); + else { if (!setupPose) { if (!attachments && timeline instanceof AttachmentTimeline) continue; if (!drawOrder && timeline instanceof DrawOrderTimeline) continue; } - timeline.apply(skeleton, animationLast, animationTime, events, a, setupPose, setupPose); + timeline.apply(skeleton, animationLast, animationTime, events, alpha, setupPose, true); } } @@ -263,18 +242,18 @@ public class AnimationState { return mix; } - /** @param events May be null. */ - private void applyRotateTimeline (RotateTimeline timeline, Skeleton skeleton, float lastTime, float time, Array events, - float alpha, boolean setupPose, boolean mixingOut, float[] timelinesRotation, int i, boolean firstFrame) { + private void applyRotateTimeline (Timeline timeline, Skeleton skeleton, float time, float alpha, boolean setupPose, + float[] timelinesRotation, int i, boolean firstFrame) { if (alpha == 1) { - timeline.apply(skeleton, lastTime, time, events, 1, setupPose, setupPose); + timeline.apply(skeleton, 0, time, null, 1, setupPose, false); return; } - float[] frames = timeline.frames; + RotateTimeline rotateTimeline = (RotateTimeline)timeline; + float[] frames = rotateTimeline.frames; if (time < frames[0]) return; // Time is before first frame. - Bone bone = skeleton.bones.get(timeline.boneIndex); + Bone bone = skeleton.bones.get(rotateTimeline.boneIndex); float r2; if (time >= frames[frames.length - ENTRIES]) // Time is after last frame. @@ -284,7 +263,7 @@ public class AnimationState { int frame = Animation.binarySearch(frames, time, ENTRIES); float prevRotation = frames[frame + PREV_ROTATION]; float frameTime = frames[frame]; - float percent = timeline.getCurvePercent((frame >> 1) - 1, + float percent = rotateTimeline.getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); r2 = frames[frame + ROTATION] - prevRotation; @@ -293,7 +272,7 @@ public class AnimationState { r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360; } - // Mix between two rotations using the direction of the shortest route on the first frame while detecting crosses. + // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. float r1 = setupPose ? bone.data.rotation : bone.rotation; float total, diff = r2 - r1; if (diff == 0) { @@ -319,7 +298,7 @@ public class AnimationState { if (Math.abs(lastTotal) > 180) lastTotal += 360 * Math.signum(lastTotal); dir = current; } - total = diff + lastTotal - lastTotal % 360; // Keep loops part of lastTotal. + total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal. if (dir != current) total += 360 * Math.signum(lastTotal); timelinesRotation[i] = total; } @@ -405,19 +384,12 @@ public class AnimationState { if (from != null) { queue.interrupt(from); current.mixingFrom = from; - // entry.mixTime = Math.max(0, entry.mixDuration - current.trackTime); - // log("setCurrent mixTime: " + entry.mixDuration + " - " + current.trackTime + " = " + entry.mixTime); current.mixTime = 0; - from.timelinesRotation.clear(); // BOZO - Needed? Recursive? + from.timelinesRotation.clear(); -// float alpha = 1; - float duration = from.animationEnd - from.animationStart; - if (duration > 0) from.alpha *= (from.getAnimationTime() - from.animationStart) / duration; -// do { -// from.alpha *= alpha; -// from = from.mixingFrom; -// } while (from != null); + // If not completely mixed in, set alpha so mixing out happens from current mix to zero. + if (from.mixingFrom != null) from.alpha *= Math.min(from.mixTime / from.mixDuration, 1); } queue.start(current); @@ -592,32 +564,6 @@ public class AnimationState { TrackEntry entry = tracks.get(i); if (entry != null) checkTimelinesFirst(entry); } - - // Compute timelinesLast from highest to lowest track entries that have mixingFrom. - propertyIDs.clear(); - int lowestMixingFrom = n; - for (i = 0; i < n; i++) { // Find lowest with a mixingFrom entry. - TrackEntry entry = tracks.get(i); - if (entry == null) continue; - if (entry.mixingFrom != null) { - lowestMixingFrom = i; - break; - } - } - for (i = n - 1; i >= lowestMixingFrom; i--) { - TrackEntry entry = tracks.get(i); - if (entry == null) continue; - - Array timelines = entry.animation.timelines; - for (int ii = 0, nn = timelines.size; ii < nn; ii++) - propertyIDs.add(timelines.get(ii).getPropertyId()); - - entry = entry.mixingFrom; - while (entry != null) { - checkTimelinesUsage(entry, entry.timelinesLast); - entry = entry.mixingFrom; - } - } } /** From last to first mixingFrom entries, sets timelinesFirst to true on last, calls checkTimelineUsage on rest. */ @@ -727,7 +673,7 @@ public class AnimationState { float animationStart, animationEnd, animationLast, nextAnimationLast; float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale; float alpha, mixTime, mixDuration, mixAlpha; - final BooleanArray timelinesFirst = new BooleanArray(), timelinesLast = new BooleanArray(); + final BooleanArray timelinesFirst = new BooleanArray(); final FloatArray timelinesRotation = new FloatArray(); public void reset () { @@ -736,7 +682,6 @@ public class AnimationState { animation = null; listener = null; timelinesFirst.clear(); - timelinesLast.clear(); timelinesRotation.clear(); }