[libgdx] Added separate X and Y sliders for path constraint translate mix.

This commit is contained in:
Nathan Sweet 2020-11-15 17:10:14 -08:00
parent dbd90caa53
commit da1b280275
6 changed files with 140 additions and 44 deletions

View File

@ -2312,8 +2312,12 @@ public class Animation {
}
}
/** Changes a transform constraint's {@link PathConstraint#getMixRotate()} and {@link PathConstraint#getMixTranslate()}. */
static public class PathConstraintMixTimeline extends CurveTimeline2 {
/** Changes a transform constraint's {@link PathConstraint#getMixRotate()}, {@link PathConstraint#getMixX()}, and
* {@link PathConstraint#getMixY()}. */
static public class PathConstraintMixTimeline extends CurveTimeline {
static public final int ENTRIES = 4;
static private final int ROTATE = 1, X = 2, Y = 3;
final int pathConstraintIndex;
public PathConstraintMixTimeline (int frameCount, int bezierCount, int pathConstraintIndex) {
@ -2321,12 +2325,27 @@ public class Animation {
this.pathConstraintIndex = pathConstraintIndex;
}
public int getFrameEntries () {
return ENTRIES;
}
/** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed when this timeline
* is applied. */
public int getPathConstraintIndex () {
return pathConstraintIndex;
}
/** Sets the time and color for the specified frame.
* @param frame Between 0 and <code>frameCount</code>, inclusive.
* @param time The frame time in seconds. */
public void setFrame (int frame, float time, float mixRotate, float mixX, float mixY) {
frame <<= 2;
frames[frame] = time;
frames[frame + ROTATE] = mixRotate;
frames[frame + X] = mixX;
frames[frame + Y] = mixY;
}
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
MixDirection direction) {
@ -2338,41 +2357,50 @@ public class Animation {
switch (blend) {
case setup:
constraint.mixRotate = constraint.data.mixRotate;
constraint.mixTranslate = constraint.data.mixTranslate;
constraint.mixX = constraint.data.mixX;
constraint.mixY = constraint.data.mixY;
return;
case first:
constraint.mixRotate += (constraint.data.mixRotate - constraint.mixRotate) * alpha;
constraint.mixTranslate += (constraint.data.mixTranslate - constraint.mixTranslate) * alpha;
constraint.mixX += (constraint.data.mixX - constraint.mixX) * alpha;
constraint.mixY += (constraint.data.mixY - constraint.mixY) * alpha;
}
return;
}
float rotate, translate;
int i = search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
float rotate, x, y;
int i = search(frames, time, ENTRIES), curveType = (int)curves[i >> 2];
switch (curveType) {
case LINEAR:
float before = frames[i];
rotate = frames[i + VALUE1];
translate = frames[i + VALUE2];
rotate = frames[i + ROTATE];
x = frames[i + X];
y = frames[i + Y];
float t = (time - before) / (frames[i + ENTRIES] - before);
rotate += (frames[i + ENTRIES + VALUE1] - rotate) * t;
translate += (frames[i + ENTRIES + VALUE2] - translate) * t;
rotate += (frames[i + ENTRIES + ROTATE] - rotate) * t;
x += (frames[i + ENTRIES + X] - x) * t;
y += (frames[i + ENTRIES + Y] - y) * t;
break;
case STEPPED:
rotate = frames[i + VALUE1];
translate = frames[i + VALUE2];
rotate = frames[i + ROTATE];
x = frames[i + X];
y = frames[i + Y];
break;
default:
rotate = getBezierValue(time, i, VALUE1, curveType - BEZIER);
translate = getBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER);
rotate = getBezierValue(time, i, ROTATE, curveType - BEZIER);
x = getBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER);
y = getBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER);
}
if (blend == setup) {
constraint.mixRotate = constraint.data.mixRotate + (rotate - constraint.data.mixRotate) * alpha;
constraint.mixTranslate = constraint.data.mixTranslate + (translate - constraint.data.mixTranslate) * alpha;
PathConstraintData data = constraint.data;
constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
constraint.mixX = data.mixX + (x - data.mixX) * alpha;
constraint.mixY = data.mixY + (y - data.mixY) * alpha;
} else {
constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
constraint.mixTranslate += (translate - constraint.mixTranslate) * alpha;
constraint.mixX += (x - constraint.mixX) * alpha;
constraint.mixY += (y - constraint.mixY) * alpha;
}
}
}

View File

@ -50,7 +50,7 @@ public class PathConstraint implements Updatable {
final PathConstraintData data;
final Array<Bone> bones;
Slot target;
float position, spacing, mixRotate, mixTranslate;
float position, spacing, mixRotate, mixX, mixY;
boolean active;
@ -69,7 +69,8 @@ public class PathConstraint implements Updatable {
position = data.position;
spacing = data.spacing;
mixRotate = data.mixRotate;
mixTranslate = data.mixTranslate;
mixX = data.mixX;
mixY = data.mixY;
}
/** Copy constructor. */
@ -84,7 +85,8 @@ public class PathConstraint implements Updatable {
position = constraint.position;
spacing = constraint.spacing;
mixRotate = constraint.mixRotate;
mixTranslate = constraint.mixTranslate;
mixX = constraint.mixX;
mixY = constraint.mixY;
}
/** Applies the constraint to the constrained bones. */
@ -92,9 +94,8 @@ public class PathConstraint implements Updatable {
Attachment attachment = target.attachment;
if (!(attachment instanceof PathAttachment)) return;
float mixRotate = this.mixRotate, mixTranslate = this.mixTranslate;
boolean translate = mixTranslate > 0, rotate = mixRotate > 0;
if (!translate && !rotate) return;
float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY;
if (mixRotate == 0 && mixX == 0 && mixY == 0) return;
PathConstraintData data = this.data;
boolean percentSpacing = data.spacingMode == SpacingMode.percent;
@ -145,8 +146,8 @@ public class PathConstraint implements Updatable {
}
for (int i = 0, p = 3; i < boneCount; i++, p += 3) {
Bone bone = (Bone)bones[i];
bone.worldX += (boneX - bone.worldX) * mixTranslate;
bone.worldY += (boneY - bone.worldY) * mixTranslate;
bone.worldX += (boneX - bone.worldX) * mixX;
bone.worldY += (boneY - bone.worldY) * mixY;
float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
if (scale) {
float length = lengths[i];
@ -158,7 +159,7 @@ public class PathConstraint implements Updatable {
}
boneX = x;
boneY = y;
if (rotate) {
if (mixRotate > 0) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d, r, cos, sin;
if (tangents)
r = positions[p - 1];
@ -473,13 +474,22 @@ public class PathConstraint implements Updatable {
this.mixRotate = mixRotate;
}
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translation. */
public float getMixTranslate () {
return mixTranslate;
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */
public float getMixX () {
return mixX;
}
public void setMixTranslate (float mixTranslate) {
this.mixTranslate = mixTranslate;
public void setMixX (float mixX) {
this.mixX = mixX;
}
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */
public float getMixY () {
return mixY;
}
public void setMixY (float mixY) {
this.mixY = mixY;
}
/** The bones that will be modified by this path constraint. */

View File

@ -41,7 +41,7 @@ public class PathConstraintData extends ConstraintData {
SpacingMode spacingMode;
RotateMode rotateMode;
float offsetRotation;
float position, spacing, mixRotate, mixTranslate;
float position, spacing, mixRotate, mixX, mixY;
public PathConstraintData (String name) {
super(name);
@ -128,13 +128,22 @@ public class PathConstraintData extends ConstraintData {
this.mixRotate = mixRotate;
}
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translation. */
public float getMixTranslate () {
return mixTranslate;
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */
public float getMixX () {
return mixX;
}
public void setMixTranslate (float mixTranslate) {
this.mixTranslate = mixTranslate;
public void setMixX (float mixX) {
this.mixX = mixX;
}
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */
public float getMixY () {
return mixY;
}
public void setMixY (float mixY) {
this.mixY = mixY;
}
/** Controls how the first bone is positioned along the path.

View File

@ -420,7 +420,8 @@ public class Skeleton {
constraint.position = data.position;
constraint.spacing = data.spacing;
constraint.mixRotate = data.mixRotate;
constraint.mixTranslate = data.mixTranslate;
constraint.mixX = data.mixX;
constraint.mixY = data.mixY;
}
}

View File

@ -267,7 +267,8 @@ public class SkeletonBinary extends SkeletonLoader {
data.spacing = input.readFloat();
if (data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed) data.spacing *= scale;
data.mixRotate = input.readFloat();
data.mixTranslate = input.readFloat();
data.mixX = input.readFloat();
data.mixY = input.readFloat();
o[i] = data;
}
@ -859,8 +860,29 @@ public class SkeletonBinary extends SkeletonLoader {
data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1));
break;
case PATH_MIX:
timelines
.add(readTimeline(input, new PathConstraintMixTimeline(input.readInt(true), input.readInt(true), index), 1));
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(input.readInt(true), input.readInt(true),
index);
float time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat();
for (int frame = 0, bezier = 0, frameLast = nn - 1;; frame++) {
timeline.setFrame(frame, time, mixRotate, mixX, mixY);
if (frame == frameLast) break;
float time2 = input.readFloat(), mixRotate2 = input.readFloat(), mixX2 = input.readFloat(),
mixY2 = input.readFloat();
switch (input.readByte()) {
case CURVE_STEPPED:
timeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, timeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
setBezier(input, timeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
setBezier(input, timeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
}
time = time2;
mixRotate = mixRotate2;
mixX = mixX2;
mixY = mixY2;
}
timelines.add(timeline);
}
}
}

View File

@ -261,7 +261,8 @@ public class SkeletonJson extends SkeletonLoader {
data.spacing = constraintMap.getFloat("spacing", 0);
if (data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed) data.spacing *= scale;
data.mixRotate = constraintMap.getFloat("mixRotate", 1);
data.mixTranslate = constraintMap.getFloat("mixTranslate", 1);
data.mixX = constraintMap.getFloat("mixX", 1);
data.mixY = constraintMap.getFloat("mixY", 1);
skeletonData.pathConstraints.add(data);
}
@ -819,8 +820,33 @@ public class SkeletonJson extends SkeletonLoader {
timelines.add(readTimeline(keyMap, timeline, 0,
data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1));
} else if (timelineName.equals("mix")) {
CurveTimeline2 timeline = new PathConstraintMixTimeline(timelineMap.size, timelineMap.size << 1, index);
timelines.add(readTimeline(keyMap, timeline, "mixRotate", "mixTranslate", 1, 1));
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(timelineMap.size, timelineMap.size * 3, index);
float time = keyMap.getFloat("time", 0);
float mixRotate = keyMap.getFloat("mixRotate", 1);
float mixX = keyMap.getFloat("mixX", 1), mixY = keyMap.getFloat("mixY", mixX);
for (int frame = 0, bezier = 0;; frame++) {
timeline.setFrame(frame, time, mixRotate, mixX, mixY);
JsonValue nextMap = keyMap.next;
if (nextMap == null) {
timeline.shrink(bezier);
break;
}
float time2 = nextMap.getFloat("time", 0);
float mixRotate2 = nextMap.getFloat("mixRotate", 1);
float mixX2 = nextMap.getFloat("mixX", 1), mixY2 = nextMap.getFloat("mixY", mixX2);
JsonValue curve = keyMap.get("curve");
if (curve != null) {
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
}
time = time2;
mixRotate = mixRotate2;
mixX = mixX2;
mixY = mixY2;
keyMap = nextMap;
}
timelines.add(timeline);
}
}
}