mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
[libgdx] Animation/AnimationState additive improvements.
* Replaced MixBlend and MixDirection with booleans. * MixBlend.first functionality is no longer needed, cases simplified. * Timelines know if they support additive or use instant transitions. * When applying an animation with additive, timelines that don't support additive use the hold system to prevent dipping. * Additive uses setup pose to prevent accumulation across frames. No longer need to reset additive properties. * Added TrackEntry#setAdditive(boolean). * Simplified AnimationState code.
This commit is contained in:
parent
063f163d56
commit
4259e86e19
@ -31,8 +31,6 @@ package com.esotericsoftware.spine;
|
|||||||
|
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.Animation.MixDirection;
|
|
||||||
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
||||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||||
@ -81,7 +79,7 @@ public class BonePlotting {
|
|||||||
for (Animation animation : skeletonData.getAnimations()) {
|
for (Animation animation : skeletonData.getAnimations()) {
|
||||||
float time = 0;
|
float time = 0;
|
||||||
while (time < animation.getDuration()) {
|
while (time < animation.getDuration()) {
|
||||||
animation.apply(skeleton, time, time, false, null, 1, MixBlend.first, MixDirection.in, false);
|
animation.apply(skeleton, time, time, false, null, 1, true, false, false, false);
|
||||||
skeleton.update(fps);
|
skeleton.update(fps);
|
||||||
skeleton.updateWorldTransform(Physics.update);
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,6 @@ import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
|
|||||||
import com.badlogic.gdx.graphics.OrthographicCamera;
|
import com.badlogic.gdx.graphics.OrthographicCamera;
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
|
|
||||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||||
import com.badlogic.gdx.math.MathUtils;
|
import com.badlogic.gdx.math.MathUtils;
|
||||||
import com.badlogic.gdx.math.Matrix4;
|
import com.badlogic.gdx.math.Matrix4;
|
||||||
@ -51,8 +50,6 @@ import com.badlogic.gdx.physics.box2d.World;
|
|||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.badlogic.gdx.utils.ScreenUtils;
|
import com.badlogic.gdx.utils.ScreenUtils;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.Animation.MixDirection;
|
|
||||||
import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader;
|
import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader;
|
||||||
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.Sequence;
|
import com.esotericsoftware.spine.attachments.Sequence;
|
||||||
@ -144,7 +141,7 @@ public class Box2DExample extends ApplicationAdapter {
|
|||||||
batch.setTransformMatrix(camera.view);
|
batch.setTransformMatrix(camera.view);
|
||||||
batch.begin();
|
batch.begin();
|
||||||
|
|
||||||
animation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in, false);
|
animation.apply(skeleton, time, time, true, events, 1, true, false, false, false);
|
||||||
skeleton.x += 8 * delta;
|
skeleton.x += 8 * delta;
|
||||||
skeleton.update(delta);
|
skeleton.update(delta);
|
||||||
skeleton.updateWorldTransform(Physics.update);
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
|
|||||||
@ -34,8 +34,6 @@ import java.util.Arrays;
|
|||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.Animation.EventTimeline;
|
import com.esotericsoftware.spine.Animation.EventTimeline;
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.Animation.MixDirection;
|
|
||||||
|
|
||||||
/** Unit tests to ensure {@link EventTimeline} is working as expected. */
|
/** Unit tests to ensure {@link EventTimeline} is working as expected. */
|
||||||
public class EventTimelineTests {
|
public class EventTimelineTests {
|
||||||
@ -176,7 +174,7 @@ public class EventTimelineTests {
|
|||||||
|
|
||||||
int beforeCount = firedEvents.size;
|
int beforeCount = firedEvents.size;
|
||||||
Array<Event> original = new Array(firedEvents);
|
Array<Event> original = new Array(firedEvents);
|
||||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1, MixBlend.first, MixDirection.in, false);
|
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1, true, false, false, false);
|
||||||
|
|
||||||
while (beforeCount < firedEvents.size) {
|
while (beforeCount < firedEvents.size) {
|
||||||
char fired = firedEvents.get(beforeCount).getData().getName().charAt(0);
|
char fired = firedEvents.get(beforeCount).getData().getName().charAt(0);
|
||||||
@ -185,7 +183,7 @@ public class EventTimelineTests {
|
|||||||
} else {
|
} else {
|
||||||
if (firedEvents.size > eventsCount) {
|
if (firedEvents.size > eventsCount) {
|
||||||
if (print) System.out.println(lastTimeLooped + "->" + timeLooped + ": " + fired + " == ?");
|
if (print) System.out.println(lastTimeLooped + "->" + timeLooped + ": " + fired + " == ?");
|
||||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1, MixBlend.first, MixDirection.in, false);
|
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1, true, false, false, false);
|
||||||
fail("Too many events fired.");
|
fail("Too many events fired.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,7 +191,7 @@ public class EventTimelineTests {
|
|||||||
System.out.println(lastTimeLooped + "->" + timeLooped + ": " + fired + " == " + events[eventIndex]);
|
System.out.println(lastTimeLooped + "->" + timeLooped + ": " + fired + " == " + events[eventIndex]);
|
||||||
}
|
}
|
||||||
if (fired != events[eventIndex]) {
|
if (fired != events[eventIndex]) {
|
||||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1, MixBlend.first, MixDirection.in, false);
|
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1, true, false, false, false);
|
||||||
fail("Wrong event fired.");
|
fail("Wrong event fired.");
|
||||||
}
|
}
|
||||||
eventIndex++;
|
eventIndex++;
|
||||||
@ -205,7 +203,7 @@ public class EventTimelineTests {
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if (firedEvents.size < eventsCount) {
|
if (firedEvents.size < eventsCount) {
|
||||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1, MixBlend.first, MixDirection.in, false);
|
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1, true, false, false, false);
|
||||||
if (print) System.out.println(firedEvents);
|
if (print) System.out.println(firedEvents);
|
||||||
fail("Event not fired: " + events[eventIndex] + ", " + frames[eventIndex]);
|
fail("Event not fired: " + events[eventIndex] + ", " + frames[eventIndex]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,8 +42,6 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
|||||||
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
|
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
|
||||||
import com.badlogic.gdx.utils.ScreenUtils;
|
import com.badlogic.gdx.utils.ScreenUtils;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.Animation.MixDirection;
|
|
||||||
import com.esotericsoftware.spine.utils.TwoColorPolygonBatch;
|
import com.esotericsoftware.spine.utils.TwoColorPolygonBatch;
|
||||||
|
|
||||||
/** Demonstrates rendering an animation to a frame buffer (FBO) and then rendering the FBO to the screen. */
|
/** Demonstrates rendering an animation to a frame buffer (FBO) and then rendering the FBO to the screen. */
|
||||||
@ -80,7 +78,7 @@ public class FboTest extends ApplicationAdapter {
|
|||||||
|
|
||||||
// Apply the pose for the first frame of the run animation.
|
// Apply the pose for the first frame of the run animation.
|
||||||
Animation animation = skeleton.getData().findAnimation("run");
|
Animation animation = skeleton.getData().findAnimation("run");
|
||||||
animation.apply(skeleton, -1, 0, true, null, 1, MixBlend.first, MixDirection.in, false);
|
animation.apply(skeleton, -1, 0, true, null, 1, true, false, false, false);
|
||||||
|
|
||||||
// Compute the world transform for the pose.
|
// Compute the world transform for the pose.
|
||||||
skeleton.updateWorldTransform(Physics.update);
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
|
|||||||
@ -36,6 +36,7 @@ import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration;
|
|||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.graphics.Texture;
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.utils.SkeletonSerializer;
|
import com.esotericsoftware.spine.utils.SkeletonSerializer;
|
||||||
|
|
||||||
public class HeadlessTest implements ApplicationListener {
|
public class HeadlessTest implements ApplicationListener {
|
||||||
|
|||||||
@ -56,9 +56,6 @@ import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
|
|||||||
import com.badlogic.gdx.utils.Align;
|
import com.badlogic.gdx.utils.Align;
|
||||||
import com.badlogic.gdx.utils.ScreenUtils;
|
import com.badlogic.gdx.utils.ScreenUtils;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.Animation.MixDirection;
|
|
||||||
|
|
||||||
/** Demonstrates simplistic usage of lighting with normal maps.
|
/** Demonstrates simplistic usage of lighting with normal maps.
|
||||||
* <p>
|
* <p>
|
||||||
* Note the normals are not rotated when bones are rotated, making lighting incorrect. */
|
* Note the normals are not rotated when bones are rotated, making lighting incorrect. */
|
||||||
@ -136,7 +133,7 @@ public class NormalMapTest extends ApplicationAdapter {
|
|||||||
float lastTime = time;
|
float lastTime = time;
|
||||||
float delta = Gdx.graphics.getDeltaTime();
|
float delta = Gdx.graphics.getDeltaTime();
|
||||||
time += delta;
|
time += delta;
|
||||||
if (animation != null) animation.apply(skeleton, lastTime, time, true, null, 1, MixBlend.first, MixDirection.in, false);
|
if (animation != null) animation.apply(skeleton, lastTime, time, true, null, 1, true, false, false, false);
|
||||||
skeleton.update(delta);
|
skeleton.update(delta);
|
||||||
skeleton.updateWorldTransform(Physics.update);
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
|
|
||||||
|
|||||||
@ -46,8 +46,6 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
|||||||
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
|
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
|
||||||
import com.badlogic.gdx.utils.ScreenUtils;
|
import com.badlogic.gdx.utils.ScreenUtils;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.Animation.MixDirection;
|
|
||||||
import com.esotericsoftware.spine.utils.TwoColorPolygonBatch;
|
import com.esotericsoftware.spine.utils.TwoColorPolygonBatch;
|
||||||
|
|
||||||
/** Demonstrates rendering an animation to a frame buffer (FBO) and then writing each frame as a PNG. */
|
/** Demonstrates rendering an animation to a frame buffer (FBO) and then writing each frame as a PNG. */
|
||||||
@ -103,7 +101,7 @@ public class PngExportTest extends ApplicationAdapter {
|
|||||||
float fps = 1 / 15f, time = 0;
|
float fps = 1 / 15f, time = 0;
|
||||||
int frame = 1;
|
int frame = 1;
|
||||||
while (time < animation.getDuration()) {
|
while (time < animation.getDuration()) {
|
||||||
animation.apply(skeleton, time, time, false, null, 1, MixBlend.first, MixDirection.in, false);
|
animation.apply(skeleton, time, time, false, null, 1, true, false, false, false);
|
||||||
skeleton.update(fps);
|
skeleton.update(fps);
|
||||||
skeleton.updateWorldTransform(Physics.update);
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
|
|
||||||
|
|||||||
@ -37,9 +37,6 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
|||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.badlogic.gdx.utils.ScreenUtils;
|
import com.badlogic.gdx.utils.ScreenUtils;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.Animation.MixDirection;
|
|
||||||
|
|
||||||
/** Demonstrates using the timeline API. See {@link SimpleTest1} for a higher level API using {@link AnimationState}.
|
/** Demonstrates using the timeline API. See {@link SimpleTest1} for a higher level API using {@link AnimationState}.
|
||||||
* <p>
|
* <p>
|
||||||
* See: https://esotericsoftware.com/spine-applying-animations */
|
* See: https://esotericsoftware.com/spine-applying-animations */
|
||||||
@ -108,24 +105,23 @@ public class TimelineApiTest extends ApplicationAdapter {
|
|||||||
skeleton.setX(-50);
|
skeleton.setX(-50);
|
||||||
} else if (time > beforeJump + jump) {
|
} else if (time > beforeJump + jump) {
|
||||||
// just walk after jump
|
// just walk after jump
|
||||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in, false);
|
walkAnimation.apply(skeleton, time, time, true, events, 1, true, false, false, false);
|
||||||
} else if (time > blendOutStart) {
|
} else if (time > blendOutStart) {
|
||||||
// blend out jump
|
// blend out jump
|
||||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in, false);
|
walkAnimation.apply(skeleton, time, time, true, events, 1, true, false, false, false);
|
||||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, 1 - (time - blendOutStart) / blendOut,
|
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, 1 - (time - blendOutStart) / blendOut,
|
||||||
MixBlend.first, MixDirection.in, false);
|
true, false, false, false);
|
||||||
} else if (time > beforeJump + blendIn) {
|
} else if (time > beforeJump + blendIn) {
|
||||||
// just jump
|
// just jump
|
||||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, 1, MixBlend.first, MixDirection.in,
|
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, 1, true, false, false, false);
|
||||||
false);
|
|
||||||
} else if (time > beforeJump) {
|
} else if (time > beforeJump) {
|
||||||
// blend in jump
|
// blend in jump
|
||||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in, false);
|
walkAnimation.apply(skeleton, time, time, true, events, 1, true, false, false, false);
|
||||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, (time - beforeJump) / blendIn,
|
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, (time - beforeJump) / blendIn, true,
|
||||||
MixBlend.first, MixDirection.in, false);
|
false, false, false);
|
||||||
} else {
|
} else {
|
||||||
// just walk before jump
|
// just walk before jump
|
||||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in, false);
|
walkAnimation.apply(skeleton, time, time, true, events, 1, true, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
skeleton.update(delta);
|
skeleton.update(delta);
|
||||||
|
|||||||
@ -1900,8 +1900,8 @@ public class SkeletonSerializer {
|
|||||||
json.writeName("mixDuration");
|
json.writeName("mixDuration");
|
||||||
json.writeValue(obj.getMixDuration());
|
json.writeValue(obj.getMixDuration());
|
||||||
|
|
||||||
json.writeName("mixBlend");
|
json.writeName("additive");
|
||||||
json.writeValue(obj.getMixBlend().name());
|
json.writeValue(obj.getAdditive());
|
||||||
|
|
||||||
json.writeName("mixingFrom");
|
json.writeName("mixingFrom");
|
||||||
if (obj.getMixingFrom() == null) {
|
if (obj.getMixingFrom() == null) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -41,9 +41,6 @@ import com.badlogic.gdx.utils.SnapshotArray;
|
|||||||
import com.esotericsoftware.spine.Animation.AttachmentTimeline;
|
import com.esotericsoftware.spine.Animation.AttachmentTimeline;
|
||||||
import com.esotericsoftware.spine.Animation.DrawOrderFolderTimeline;
|
import com.esotericsoftware.spine.Animation.DrawOrderFolderTimeline;
|
||||||
import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
|
import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
|
||||||
import com.esotericsoftware.spine.Animation.EventTimeline;
|
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.Animation.MixDirection;
|
|
||||||
import com.esotericsoftware.spine.Animation.RotateTimeline;
|
import com.esotericsoftware.spine.Animation.RotateTimeline;
|
||||||
import com.esotericsoftware.spine.Animation.Timeline;
|
import com.esotericsoftware.spine.Animation.Timeline;
|
||||||
|
|
||||||
@ -213,16 +210,12 @@ public class AnimationState {
|
|||||||
if (current == null || current.delay > 0) continue;
|
if (current == null || current.delay > 0) continue;
|
||||||
applied = true;
|
applied = true;
|
||||||
|
|
||||||
// Track 0 animations aren't for layering, so never use current values before the first key.
|
|
||||||
MixBlend blend = i == 0 ? MixBlend.first : current.mixBlend;
|
|
||||||
|
|
||||||
// Apply mixing from entries first.
|
// Apply mixing from entries first.
|
||||||
float alpha = current.alpha;
|
float alpha = current.alpha;
|
||||||
if (current.mixingFrom != null)
|
if (current.mixingFrom != null)
|
||||||
alpha *= applyMixingFrom(current, skeleton);
|
alpha *= applyMixingFrom(current, skeleton);
|
||||||
else if (current.trackTime >= current.trackEnd && current.next == null) //
|
else if (current.trackTime >= current.trackEnd && current.next == null) //
|
||||||
alpha = 0; // Set to setup pose the last time the entry will be applied.
|
alpha = 0; // Set to setup pose the last time the entry will be applied.
|
||||||
boolean attachments = alpha >= current.alphaAttachmentThreshold;
|
|
||||||
|
|
||||||
// Apply current entry.
|
// Apply current entry.
|
||||||
float animationLast = current.animationLast, animationTime = current.getAnimationTime(), applyTime = animationTime;
|
float animationLast = current.animationLast, animationTime = current.getAnimationTime(), applyTime = animationTime;
|
||||||
@ -233,33 +226,31 @@ public class AnimationState {
|
|||||||
}
|
}
|
||||||
int timelineCount = current.animation.timelines.size;
|
int timelineCount = current.animation.timelines.size;
|
||||||
Timeline[] timelines = current.animation.timelines.items;
|
Timeline[] timelines = current.animation.timelines.items;
|
||||||
if ((i == 0 && alpha == 1) || blend == MixBlend.add) {
|
if (i == 0 && alpha == 1) {
|
||||||
if (i == 0) attachments = true;
|
|
||||||
for (int ii = 0; ii < timelineCount; ii++) {
|
for (int ii = 0; ii < timelineCount; ii++) {
|
||||||
Timeline timeline = timelines[ii];
|
Timeline timeline = timelines[ii];
|
||||||
if (timeline instanceof AttachmentTimeline attachmentTimeline)
|
if (timeline instanceof AttachmentTimeline attachmentTimeline)
|
||||||
applyAttachmentTimeline(attachmentTimeline, skeleton, applyTime, blend, false, attachments);
|
applyAttachmentTimeline(attachmentTimeline, skeleton, applyTime, true, false, true);
|
||||||
else
|
else
|
||||||
timeline.apply(skeleton, animationLast, applyTime, applyEvents, alpha, blend, MixDirection.in, false);
|
timeline.apply(skeleton, animationLast, applyTime, applyEvents, alpha, true, false, false, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int[] timelineMode = current.timelineMode.items;
|
int[] timelineMode = current.timelineMode.items;
|
||||||
|
boolean attachments = alpha >= current.alphaAttachmentThreshold;
|
||||||
boolean shortestRotation = current.shortestRotation;
|
boolean add = current.additive, shortestRotation = add || current.shortestRotation;
|
||||||
boolean firstFrame = !shortestRotation && current.timelinesRotation.size != timelineCount << 1;
|
boolean firstFrame = !shortestRotation && current.timelinesRotation.size != timelineCount << 1;
|
||||||
if (firstFrame) current.timelinesRotation.setSize(timelineCount << 1);
|
float[] timelinesRotation = firstFrame ? current.timelinesRotation.setSize(timelineCount << 1)
|
||||||
float[] timelinesRotation = current.timelinesRotation.items;
|
: current.timelinesRotation.items;
|
||||||
|
|
||||||
for (int ii = 0; ii < timelineCount; ii++) {
|
for (int ii = 0; ii < timelineCount; ii++) {
|
||||||
Timeline timeline = timelines[ii];
|
Timeline timeline = timelines[ii];
|
||||||
MixBlend timelineBlend = timelineMode[ii] == SUBSEQUENT ? current.mixBlend : MixBlend.setup;
|
boolean fromSetup = timelineMode[ii] == FIRST;
|
||||||
if (!shortestRotation && timeline instanceof RotateTimeline rotateTimeline) {
|
if (!shortestRotation && timeline instanceof RotateTimeline rotateTimeline) {
|
||||||
applyRotateTimeline(rotateTimeline, skeleton, applyTime, alpha, timelineBlend, timelinesRotation, ii << 1,
|
applyRotateTimeline(rotateTimeline, skeleton, applyTime, alpha, fromSetup, timelinesRotation, ii << 1,
|
||||||
firstFrame);
|
firstFrame);
|
||||||
} else if (timeline instanceof AttachmentTimeline attachmentTimeline)
|
} else if (timeline instanceof AttachmentTimeline attachmentTimeline)
|
||||||
applyAttachmentTimeline(attachmentTimeline, skeleton, applyTime, blend, false, attachments);
|
applyAttachmentTimeline(attachmentTimeline, skeleton, applyTime, fromSetup, false, attachments);
|
||||||
else
|
else
|
||||||
timeline.apply(skeleton, animationLast, applyTime, applyEvents, alpha, timelineBlend, MixDirection.in, false);
|
timeline.apply(skeleton, animationLast, applyTime, applyEvents, alpha, fromSetup, add, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queueEvents(current, animationTime);
|
queueEvents(current, animationTime);
|
||||||
@ -309,62 +300,49 @@ public class AnimationState {
|
|||||||
else {
|
else {
|
||||||
if (mix < from.eventThreshold) events = this.events;
|
if (mix < from.eventThreshold) events = this.events;
|
||||||
}
|
}
|
||||||
|
int[] timelineMode = from.timelineMode.items;
|
||||||
MixBlend blend = from.mixBlend;
|
TrackEntry[] timelineHoldMix = from.timelineHoldMix.items;
|
||||||
if (blend == MixBlend.add) {
|
boolean add = from.additive, shortestRotation = add || from.shortestRotation;
|
||||||
for (int i = 0; i < timelineCount; i++)
|
boolean firstFrame = !shortestRotation && from.timelinesRotation.size != timelineCount << 1;
|
||||||
timelines[i].apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.out, false);
|
float[] timelinesRotation = firstFrame ? from.timelinesRotation.setSize(timelineCount << 1) : from.timelinesRotation.items;
|
||||||
} else {
|
from.totalAlpha = 0;
|
||||||
int[] timelineMode = from.timelineMode.items;
|
for (int i = 0; i < timelineCount; i++) {
|
||||||
TrackEntry[] timelineHoldMix = from.timelineHoldMix.items;
|
Timeline timeline = timelines[i];
|
||||||
|
boolean fromSetup;
|
||||||
boolean shortestRotation = from.shortestRotation;
|
float alpha;
|
||||||
boolean firstFrame = !shortestRotation && from.timelinesRotation.size != timelineCount << 1;
|
switch (timelineMode[i]) {
|
||||||
if (firstFrame) from.timelinesRotation.setSize(timelineCount << 1);
|
case SUBSEQUENT -> {
|
||||||
float[] timelinesRotation = from.timelinesRotation.items;
|
if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
|
||||||
|
fromSetup = false;
|
||||||
from.totalAlpha = 0;
|
alpha = alphaMix;
|
||||||
for (int i = 0; i < timelineCount; i++) {
|
}
|
||||||
Timeline timeline = timelines[i];
|
case FIRST -> {
|
||||||
MixBlend timelineBlend;
|
fromSetup = true;
|
||||||
float alpha;
|
alpha = alphaMix;
|
||||||
switch (timelineMode[i]) {
|
}
|
||||||
case SUBSEQUENT -> {
|
case HOLD_SUBSEQUENT -> {
|
||||||
if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
|
fromSetup = false;
|
||||||
timelineBlend = blend;
|
alpha = alphaHold;
|
||||||
alpha = alphaMix;
|
}
|
||||||
}
|
case HOLD_FIRST -> {
|
||||||
case FIRST -> {
|
fromSetup = true;
|
||||||
timelineBlend = MixBlend.setup;
|
alpha = alphaHold;
|
||||||
alpha = alphaMix;
|
}
|
||||||
}
|
default -> { // HOLD_MIX
|
||||||
case HOLD_SUBSEQUENT -> {
|
fromSetup = true;
|
||||||
timelineBlend = blend;
|
TrackEntry holdMix = timelineHoldMix[i];
|
||||||
alpha = alphaHold;
|
alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
|
||||||
}
|
}
|
||||||
case HOLD_FIRST -> {
|
}
|
||||||
timelineBlend = MixBlend.setup;
|
from.totalAlpha += alpha;
|
||||||
alpha = alphaHold;
|
if (!shortestRotation && timeline instanceof RotateTimeline rotateTimeline) {
|
||||||
}
|
applyRotateTimeline(rotateTimeline, skeleton, applyTime, alpha, fromSetup, timelinesRotation, i << 1, firstFrame);
|
||||||
default -> { // HOLD_MIX
|
} else if (timeline instanceof AttachmentTimeline attachmentTimeline)
|
||||||
timelineBlend = MixBlend.setup;
|
applyAttachmentTimeline(attachmentTimeline, skeleton, applyTime, fromSetup, true,
|
||||||
TrackEntry holdMix = timelineHoldMix[i];
|
attachments && alpha >= from.alphaAttachmentThreshold);
|
||||||
alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
|
else {
|
||||||
}
|
boolean out = !drawOrder || !(timeline instanceof DrawOrderTimeline) || !fromSetup;
|
||||||
}
|
timeline.apply(skeleton, animationLast, applyTime, events, alpha, fromSetup, add, out, false);
|
||||||
from.totalAlpha += alpha;
|
|
||||||
if (!shortestRotation && timeline instanceof RotateTimeline rotateTimeline) {
|
|
||||||
applyRotateTimeline(rotateTimeline, skeleton, applyTime, alpha, timelineBlend, timelinesRotation, i << 1,
|
|
||||||
firstFrame);
|
|
||||||
} else if (timeline instanceof AttachmentTimeline attachmentTimeline)
|
|
||||||
applyAttachmentTimeline(attachmentTimeline, skeleton, applyTime, timelineBlend, true,
|
|
||||||
attachments && alpha >= from.alphaAttachmentThreshold);
|
|
||||||
else {
|
|
||||||
MixDirection direction = MixDirection.out;
|
|
||||||
if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup)
|
|
||||||
direction = MixDirection.in;
|
|
||||||
timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,17 +358,14 @@ public class AnimationState {
|
|||||||
* @param attachments False when: 1) the attachment timeline is mixing out, 2) mix < attachmentThreshold, and 3) the timeline
|
* @param attachments False when: 1) the attachment timeline is mixing out, 2) mix < attachmentThreshold, and 3) the timeline
|
||||||
* is not the last timeline to set the slot's attachment. In that case the timeline is applied only so subsequent
|
* is not the last timeline to set the slot's attachment. In that case the timeline is applied only so subsequent
|
||||||
* timelines see any deform. */
|
* timelines see any deform. */
|
||||||
private void applyAttachmentTimeline (AttachmentTimeline timeline, Skeleton skeleton, float time, MixBlend blend, boolean out,
|
private void applyAttachmentTimeline (AttachmentTimeline timeline, Skeleton skeleton, float time, boolean fromSetup,
|
||||||
boolean attachments) {
|
boolean out, boolean attachments) {
|
||||||
|
|
||||||
Slot slot = skeleton.slots.items[timeline.slotIndex];
|
Slot slot = skeleton.slots.items[timeline.slotIndex];
|
||||||
if (!slot.bone.active) return;
|
if (!slot.bone.active) return;
|
||||||
|
|
||||||
if (out) {
|
if (out || time < timeline.frames[0]) {
|
||||||
if (blend == MixBlend.setup) setAttachment(skeleton, slot, slot.data.attachmentName, attachments);
|
if (fromSetup) setAttachment(skeleton, slot, slot.data.attachmentName, attachments);
|
||||||
} else if (time < timeline.frames[0]) { // Time is before first frame.
|
|
||||||
if (blend == MixBlend.setup || blend == MixBlend.first)
|
|
||||||
setAttachment(skeleton, slot, slot.data.attachmentName, attachments);
|
|
||||||
} else
|
} else
|
||||||
setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search(timeline.frames, time)], attachments);
|
setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search(timeline.frames, time)], attachments);
|
||||||
|
|
||||||
@ -405,13 +380,13 @@ public class AnimationState {
|
|||||||
|
|
||||||
/** Applies the rotate timeline, mixing with the current pose while keeping the same rotation direction chosen as the shortest
|
/** Applies the rotate timeline, mixing with the current pose while keeping the same rotation direction chosen as the shortest
|
||||||
* the first time the mixing was applied. */
|
* the first time the mixing was applied. */
|
||||||
private void applyRotateTimeline (RotateTimeline timeline, Skeleton skeleton, float time, float alpha, MixBlend blend,
|
private void applyRotateTimeline (RotateTimeline timeline, Skeleton skeleton, float time, float alpha, boolean fromSetup,
|
||||||
float[] timelinesRotation, int i, boolean firstFrame) {
|
float[] timelinesRotation, int i, boolean firstFrame) {
|
||||||
|
|
||||||
if (firstFrame) timelinesRotation[i] = 0;
|
if (firstFrame) timelinesRotation[i] = 0;
|
||||||
|
|
||||||
if (alpha == 1) {
|
if (alpha == 1) {
|
||||||
timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.in, false);
|
timeline.apply(skeleton, 0, time, null, 1, fromSetup, false, false, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,22 +394,12 @@ public class AnimationState {
|
|||||||
if (!bone.active) return;
|
if (!bone.active) return;
|
||||||
BoneLocal pose = bone.pose, setup = bone.data.setup;
|
BoneLocal pose = bone.pose, setup = bone.data.setup;
|
||||||
float[] frames = timeline.frames;
|
float[] frames = timeline.frames;
|
||||||
float r1, r2;
|
|
||||||
if (time < frames[0]) { // Time is before first frame.
|
if (time < frames[0]) { // Time is before first frame.
|
||||||
switch (blend) {
|
if (fromSetup) pose.rotation = setup.rotation;
|
||||||
case setup:
|
return;
|
||||||
pose.rotation = setup.rotation;
|
|
||||||
// Fall through.
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
case first:
|
|
||||||
r1 = pose.rotation;
|
|
||||||
r2 = setup.rotation;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
r1 = blend == MixBlend.setup ? setup.rotation : pose.rotation;
|
|
||||||
r2 = setup.rotation + timeline.getCurveValue(time);
|
|
||||||
}
|
}
|
||||||
|
float r1 = fromSetup ? setup.rotation : pose.rotation;
|
||||||
|
float r2 = setup.rotation + timeline.getCurveValue(time);
|
||||||
|
|
||||||
// Mix between rotations using the direction of the shortest route on the first frame.
|
// Mix between rotations using the direction of the shortest route on the first frame.
|
||||||
float total, diff = r2 - r1;
|
float total, diff = r2 - r1;
|
||||||
@ -727,6 +692,7 @@ public class AnimationState {
|
|||||||
entry.loop = loop;
|
entry.loop = loop;
|
||||||
entry.holdPrevious = false;
|
entry.holdPrevious = false;
|
||||||
|
|
||||||
|
entry.additive = false;
|
||||||
entry.reverse = false;
|
entry.reverse = false;
|
||||||
entry.shortestRotation = false;
|
entry.shortestRotation = false;
|
||||||
|
|
||||||
@ -752,7 +718,6 @@ public class AnimationState {
|
|||||||
entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation);
|
entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation);
|
||||||
entry.interruptAlpha = 1;
|
entry.interruptAlpha = 1;
|
||||||
entry.totalAlpha = 0;
|
entry.totalAlpha = 0;
|
||||||
entry.mixBlend = MixBlend.replace;
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,7 +735,6 @@ public class AnimationState {
|
|||||||
animationsChanged = false;
|
animationsChanged = false;
|
||||||
|
|
||||||
// Process in the order that animations are applied.
|
// Process in the order that animations are applied.
|
||||||
propertyIds.clear(2048);
|
|
||||||
int n = tracks.size;
|
int n = tracks.size;
|
||||||
TrackEntry[] tracks = this.tracks.items;
|
TrackEntry[] tracks = this.tracks.items;
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
@ -779,44 +743,43 @@ public class AnimationState {
|
|||||||
while (entry.mixingFrom != null) // Move to last entry, then iterate in reverse.
|
while (entry.mixingFrom != null) // Move to last entry, then iterate in reverse.
|
||||||
entry = entry.mixingFrom;
|
entry = entry.mixingFrom;
|
||||||
do {
|
do {
|
||||||
if (entry.mixingTo == null || entry.mixBlend != MixBlend.add) computeHold(entry);
|
computeHold(entry);
|
||||||
entry = entry.mixingTo;
|
entry = entry.mixingTo;
|
||||||
} while (entry != null);
|
} while (entry != null);
|
||||||
}
|
}
|
||||||
|
propertyIds.clear(2048);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void computeHold (TrackEntry entry) {
|
private void computeHold (TrackEntry entry) {
|
||||||
TrackEntry to = entry.mixingTo;
|
|
||||||
Timeline[] timelines = entry.animation.timelines.items;
|
Timeline[] timelines = entry.animation.timelines.items;
|
||||||
int timelinesCount = entry.animation.timelines.size;
|
int timelinesCount = entry.animation.timelines.size;
|
||||||
int[] timelineMode = entry.timelineMode.setSize(timelinesCount);
|
int[] timelineMode = entry.timelineMode.setSize(timelinesCount);
|
||||||
entry.timelineHoldMix.clear();
|
entry.timelineHoldMix.clear();
|
||||||
TrackEntry[] timelineHoldMix = entry.timelineHoldMix.setSize(timelinesCount);
|
TrackEntry[] timelineHoldMix = entry.timelineHoldMix.setSize(timelinesCount);
|
||||||
ObjectSet<String> propertyIds = this.propertyIds;
|
ObjectSet<String> propertyIds = this.propertyIds;
|
||||||
|
boolean holdPrevious = false, add = entry.additive;
|
||||||
if (to != null && to.holdPrevious) {
|
TrackEntry to = entry.mixingTo;
|
||||||
for (int i = 0; i < timelinesCount; i++) {
|
if (to != null) {
|
||||||
boolean first = propertyIds.addAll(timelines[i].getPropertyIds());
|
if (to.additive)
|
||||||
if (first && timelines[i] instanceof DrawOrderFolderTimeline && propertyIds.contains(DrawOrderTimeline.propertyID))
|
to = null;
|
||||||
first = false; // DrawOrderTimeline changed.
|
else
|
||||||
timelineMode[i] = first ? HOLD_FIRST : HOLD_SUBSEQUENT;
|
holdPrevious = to.holdPrevious;
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
for (int i = 0; i < timelinesCount; i++) {
|
for (int i = 0; i < timelinesCount; i++) {
|
||||||
Timeline timeline = timelines[i];
|
Timeline timeline = timelines[i];
|
||||||
String[] ids = timeline.getPropertyIds();
|
String[] ids = timeline.propertyIds;
|
||||||
if (!propertyIds.addAll(ids))
|
boolean first = propertyIds.addAll(ids)
|
||||||
timelineMode[i] = SUBSEQUENT;
|
&& !(timeline instanceof DrawOrderFolderTimeline && propertyIds.contains(DrawOrderTimeline.propertyID));
|
||||||
else if (timeline instanceof DrawOrderFolderTimeline && propertyIds.contains(DrawOrderTimeline.propertyID))
|
if (add && timeline.additive)
|
||||||
timelineMode[i] = SUBSEQUENT; // DrawOrderTimeline changed.
|
timelineMode[i] = first ? FIRST : SUBSEQUENT;
|
||||||
else if (to == null || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline
|
else if (!first)
|
||||||
|| timeline instanceof DrawOrderFolderTimeline || timeline instanceof EventTimeline
|
timelineMode[i] = holdPrevious ? HOLD_SUBSEQUENT : SUBSEQUENT;
|
||||||
|| !to.animation.hasTimeline(ids)) {
|
else if (holdPrevious)
|
||||||
|
timelineMode[i] = HOLD_FIRST;
|
||||||
|
else if (to == null || timeline.instant || !to.animation.hasTimeline(ids))
|
||||||
timelineMode[i] = FIRST;
|
timelineMode[i] = FIRST;
|
||||||
} else {
|
else {
|
||||||
for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {
|
for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {
|
||||||
if (next.animation.hasTimeline(ids)) continue;
|
if (next.animation.hasTimeline(ids)) continue;
|
||||||
if (next.mixDuration > 0) {
|
if (next.mixDuration > 0) {
|
||||||
@ -909,12 +872,11 @@ public class AnimationState {
|
|||||||
@Null TrackEntry previous, next, mixingFrom, mixingTo;
|
@Null TrackEntry previous, next, mixingFrom, mixingTo;
|
||||||
@Null AnimationStateListener listener;
|
@Null AnimationStateListener listener;
|
||||||
int trackIndex;
|
int trackIndex;
|
||||||
boolean loop, holdPrevious, reverse, shortestRotation;
|
boolean loop, holdPrevious, additive, reverse, shortestRotation;
|
||||||
float eventThreshold, mixAttachmentThreshold, alphaAttachmentThreshold, mixDrawOrderThreshold;
|
float eventThreshold, mixAttachmentThreshold, alphaAttachmentThreshold, mixDrawOrderThreshold;
|
||||||
float animationStart, animationEnd, animationLast, nextAnimationLast;
|
float animationStart, animationEnd, animationLast, nextAnimationLast;
|
||||||
float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale;
|
float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale;
|
||||||
float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha;
|
float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha;
|
||||||
MixBlend mixBlend = MixBlend.replace;
|
|
||||||
|
|
||||||
final IntArray timelineMode = new IntArray();
|
final IntArray timelineMode = new IntArray();
|
||||||
final Array<TrackEntry> timelineHoldMix = new Array(true, 8, TrackEntry[]::new);
|
final Array<TrackEntry> timelineHoldMix = new Array(true, 8, TrackEntry[]::new);
|
||||||
@ -1233,26 +1195,18 @@ public class AnimationState {
|
|||||||
* entry is looping, its next loop completion is used instead of its duration. */
|
* entry is looping, its next loop completion is used instead of its duration. */
|
||||||
public void setMixDuration (float mixDuration, float delay) {
|
public void setMixDuration (float mixDuration, float delay) {
|
||||||
this.mixDuration = mixDuration;
|
this.mixDuration = mixDuration;
|
||||||
if (delay <= 0) {
|
if (delay <= 0) delay = previous == null ? 0 : Math.max(delay + previous.getTrackComplete() - mixDuration, 0);
|
||||||
if (previous != null)
|
|
||||||
delay = Math.max(delay + previous.getTrackComplete() - mixDuration, 0);
|
|
||||||
else
|
|
||||||
delay = 0;
|
|
||||||
}
|
|
||||||
this.delay = delay;
|
this.delay = delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Controls how properties keyed in the animation are mixed with lower tracks. Defaults to {@link MixBlend#replace}.
|
/** When true, timelines in this animation that support additive are added to the setup or current pose. Additive can be set
|
||||||
* <p>
|
* for a new track entry only before {@link AnimationState#apply(Skeleton)} is next called. */
|
||||||
* The <code>mixBlend</code> can be set for a new track entry only before {@link AnimationState#apply(Skeleton)} is next
|
public boolean getAdditive () {
|
||||||
* called. */
|
return additive;
|
||||||
public MixBlend getMixBlend () {
|
|
||||||
return mixBlend;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMixBlend (MixBlend mixBlend) {
|
public void setAdditive (boolean additive) {
|
||||||
if (mixBlend == null) throw new IllegalArgumentException("mixBlend cannot be null.");
|
this.additive = additive;
|
||||||
this.mixBlend = mixBlend;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The track entry for the previous animation when mixing from the previous animation to this animation, or null if no
|
/** The track entry for the previous animation when mixing from the previous animation to this animation, or null if no
|
||||||
@ -1302,10 +1256,10 @@ public class AnimationState {
|
|||||||
/** Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the
|
/** Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the
|
||||||
* long way around when using {@link #getAlpha()} and starting animations on other tracks.
|
* long way around when using {@link #getAlpha()} and starting animations on other tracks.
|
||||||
* <p>
|
* <p>
|
||||||
* Mixing with {@link MixBlend#replace} involves finding a rotation between two others, which has two possible solutions:
|
* Mixing involves finding a rotation between two others, which has two possible solutions: the short way or the long way
|
||||||
* the short way or the long way around. The two rotations likely change over time, so which direction is the short or long
|
* around. The two rotations likely change over time, so which direction is the short or long way also changes. If the short
|
||||||
* way also changes. If the short way was always chosen, bones would flip to the other side when that direction became the
|
* way was always chosen, bones would flip to the other side when that direction became the long way. TrackEntry chooses the
|
||||||
* long way. TrackEntry chooses the short way the first time it is applied and remembers that direction. */
|
* short way the first time it is applied and remembers that direction. */
|
||||||
public void resetRotationDirections () {
|
public void resetRotationDirections () {
|
||||||
timelinesRotation.clear();
|
timelinesRotation.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,7 @@ import com.esotericsoftware.spine.AnimationState.AnimationStateListener;
|
|||||||
/** Stores the current pose values for an {@link Event}.
|
/** Stores the current pose values for an {@link Event}.
|
||||||
* <p>
|
* <p>
|
||||||
* See Timeline
|
* See Timeline
|
||||||
* {@link Timeline#apply(Skeleton, float, float, com.badlogic.gdx.utils.Array, float, com.esotericsoftware.spine.Animation.MixBlend, com.esotericsoftware.spine.Animation.MixDirection, boolean)},
|
* {@link Timeline#apply(Skeleton, float, float, com.badlogic.gdx.utils.Array, float, boolean, boolean, boolean, boolean)},
|
||||||
* AnimationStateListener {@link AnimationStateListener#event(com.esotericsoftware.spine.AnimationState.TrackEntry, Event)}, and
|
* AnimationStateListener {@link AnimationStateListener#event(com.esotericsoftware.spine.AnimationState.TrackEntry, Event)}, and
|
||||||
* <a href="https://esotericsoftware.com/spine-events">Events</a> in the Spine User Guide. */
|
* <a href="https://esotericsoftware.com/spine-events">Events</a> in the Spine User Guide. */
|
||||||
public class Event {
|
public class Event {
|
||||||
|
|||||||
@ -30,8 +30,6 @@
|
|||||||
package com.esotericsoftware.spine;
|
package com.esotericsoftware.spine;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.Animation.ConstraintTimeline;
|
import com.esotericsoftware.spine.Animation.ConstraintTimeline;
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.Animation.MixDirection;
|
|
||||||
import com.esotericsoftware.spine.Animation.PhysicsConstraintTimeline;
|
import com.esotericsoftware.spine.Animation.PhysicsConstraintTimeline;
|
||||||
import com.esotericsoftware.spine.Animation.SlotTimeline;
|
import com.esotericsoftware.spine.Animation.SlotTimeline;
|
||||||
import com.esotericsoftware.spine.Animation.Timeline;
|
import com.esotericsoftware.spine.Animation.Timeline;
|
||||||
@ -78,8 +76,7 @@ public class Slider extends Constraint<Slider, SliderData, SliderPose> {
|
|||||||
for (int i = 0, n = animation.bones.size; i < n; i++)
|
for (int i = 0, n = animation.bones.size; i < n; i++)
|
||||||
bones[indices[i]].applied.modifyLocal(skeleton);
|
bones[indices[i]].applied.modifyLocal(skeleton);
|
||||||
|
|
||||||
animation.apply(skeleton, p.time, p.time, data.loop, null, p.mix, data.additive ? MixBlend.add : MixBlend.replace,
|
animation.apply(skeleton, p.time, p.time, data.loop, null, p.mix, false, data.additive, false, true);
|
||||||
MixDirection.in, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sort (Skeleton skeleton) {
|
void sort (Skeleton skeleton) {
|
||||||
|
|||||||
@ -50,7 +50,6 @@ import com.badlogic.gdx.utils.Array;
|
|||||||
import com.badlogic.gdx.utils.Null;
|
import com.badlogic.gdx.utils.Null;
|
||||||
import com.badlogic.gdx.utils.viewport.ScreenViewport;
|
import com.badlogic.gdx.utils.viewport.ScreenViewport;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.AnimationState.AnimationStateAdapter;
|
import com.esotericsoftware.spine.AnimationState.AnimationStateAdapter;
|
||||||
import com.esotericsoftware.spine.AnimationState.TrackEntry;
|
import com.esotericsoftware.spine.AnimationState.TrackEntry;
|
||||||
import com.esotericsoftware.spine.utils.TwoColorPolygonBatch;
|
import com.esotericsoftware.spine.utils.TwoColorPolygonBatch;
|
||||||
@ -216,7 +215,7 @@ public class SkeletonViewer extends ApplicationAdapter {
|
|||||||
entry = state.setAnimation(track, ui.animationList.getSelected(), ui.loopCheckbox.isChecked());
|
entry = state.setAnimation(track, ui.animationList.getSelected(), ui.loopCheckbox.isChecked());
|
||||||
entry.setHoldPrevious(track > 0 && ui.holdPrevCheckbox.isChecked());
|
entry.setHoldPrevious(track > 0 && ui.holdPrevCheckbox.isChecked());
|
||||||
}
|
}
|
||||||
entry.setMixBlend(track > 0 && ui.addCheckbox.isChecked() ? MixBlend.add : MixBlend.replace);
|
entry.setAdditive(track > 0 && ui.addCheckbox.isChecked());
|
||||||
entry.setReverse(ui.reverseCheckbox.isChecked());
|
entry.setReverse(ui.reverseCheckbox.isChecked());
|
||||||
entry.setAlpha(ui.alphaSlider.getValue());
|
entry.setAlpha(ui.alphaSlider.getValue());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,7 +67,6 @@ import com.badlogic.gdx.utils.Align;
|
|||||||
import com.badlogic.gdx.utils.Null;
|
import com.badlogic.gdx.utils.Null;
|
||||||
import com.badlogic.gdx.utils.viewport.ScreenViewport;
|
import com.badlogic.gdx.utils.viewport.ScreenViewport;
|
||||||
|
|
||||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
|
||||||
import com.esotericsoftware.spine.AnimationState.TrackEntry;
|
import com.esotericsoftware.spine.AnimationState.TrackEntry;
|
||||||
|
|
||||||
import java.awt.FileDialog;
|
import java.awt.FileDialog;
|
||||||
@ -572,7 +571,7 @@ class SkeletonViewerUI {
|
|||||||
loopCheckbox.setChecked(current.getLoop());
|
loopCheckbox.setChecked(current.getLoop());
|
||||||
reverseCheckbox.setChecked(current.getReverse());
|
reverseCheckbox.setChecked(current.getReverse());
|
||||||
if (track > 0) {
|
if (track > 0) {
|
||||||
addCheckbox.setChecked(current.getMixBlend() == MixBlend.add);
|
addCheckbox.setChecked(current.getAdditive());
|
||||||
holdPrevCheckbox.setChecked(current.getHoldPrevious());
|
holdPrevCheckbox.setChecked(current.getHoldPrevious());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user