Browse Source

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

Nathan Sweet 4 years ago
parent
commit
da1b280275

+ 45 - 17
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java

@@ -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;
 			}
 		}
 	}

+ 24 - 14
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java

@@ -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. */

+ 15 - 6
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java

@@ -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.

+ 2 - 1
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java

@@ -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;
 		}
 	}
 

+ 25 - 3
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java

@@ -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);
 				}
 			}
 		}

+ 29 - 3
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java

@@ -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);
 				}
 			}
 		}