Przeglądaj źródła

Added IK constraint compress and uniform settings.

NathanSweet 7 lat temu
rodzic
commit
7df713b13f

+ 18 - 9
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java

@@ -1295,15 +1295,15 @@ public class Animation {
 		}
 	}
 
-	/** Changes an IK constraint's {@link IkConstraint#getMix()}, {@link IkConstraint#getBendDirection()}, and
-	 * {@link IkConstraint#getStretch()}. */
+	/** Changes an IK constraint's {@link IkConstraint#getMix()}, {@link IkConstraint#getBendDirection()},
+	 * {@link IkConstraint#getStretch()}, and {@link IkConstraint#getCompress()}. */
 	static public class IkConstraintTimeline extends CurveTimeline {
-		static public final int ENTRIES = 4;
-		static private final int PREV_TIME = -4, PREV_MIX = -3, PREV_BEND_DIRECTION = -2, PREV_STRETCH = -1;
-		static private final int MIX = 1, BEND_DIRECTION = 2, STRETCH = 3;
+		static public final int ENTRIES = 5;
+		static private final int PREV_TIME = -5, PREV_MIX = -4, PREV_BEND_DIRECTION = -3, PREV_COMPRESS = -2, PREV_STRETCH = -1;
+		static private final int MIX = 1, BEND_DIRECTION = 2, COMPRESS = 3, STRETCH = 4;
 
 		int ikConstraintIndex;
-		private final float[] frames; // time, mix, bendDirection, ...
+		private final float[] frames; // time, mix, bendDirection, compress, stretch, ...
 
 		public IkConstraintTimeline (int frameCount) {
 			super(frameCount);
@@ -1324,17 +1324,18 @@ public class Animation {
 			return ikConstraintIndex;
 		}
 
-		/** The time in seconds, mix, and bend direction for each key frame. */
+		/** The time in seconds, mix, bend direction, compress, and stretch for each key frame. */
 		public float[] getFrames () {
 			return frames;
 		}
 
-		/** Sets the time in seconds, mix, and bend direction for the specified key frame. */
-		public void setFrame (int frameIndex, float time, float mix, int bendDirection, boolean stretch) {
+		/** Sets the time in seconds, mix, bend direction, compress, and stretch for the specified key frame. */
+		public void setFrame (int frameIndex, float time, float mix, int bendDirection, boolean compress, boolean stretch) {
 			frameIndex *= ENTRIES;
 			frames[frameIndex] = time;
 			frames[frameIndex + MIX] = mix;
 			frames[frameIndex + BEND_DIRECTION] = bendDirection;
+			frames[frameIndex + COMPRESS] = compress ? 1 : 0;
 			frames[frameIndex + STRETCH] = stretch ? 1 : 0;
 		}
 
@@ -1348,11 +1349,13 @@ public class Animation {
 				case setup:
 					constraint.mix = constraint.data.mix;
 					constraint.bendDirection = constraint.data.bendDirection;
+					constraint.compress = constraint.data.compress;
 					constraint.stretch = constraint.data.stretch;
 					return;
 				case first:
 					constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
 					constraint.bendDirection = constraint.data.bendDirection;
+					constraint.compress = constraint.data.compress;
 					constraint.stretch = constraint.data.stretch;
 				}
 				return;
@@ -1363,15 +1366,18 @@ public class Animation {
 					constraint.mix = constraint.data.mix + (frames[frames.length + PREV_MIX] - constraint.data.mix) * alpha;
 					if (direction == out) {
 						constraint.bendDirection = constraint.data.bendDirection;
+						constraint.compress = constraint.data.compress;
 						constraint.stretch = constraint.data.stretch;
 					} else {
 						constraint.bendDirection = (int)frames[frames.length + PREV_BEND_DIRECTION];
+						constraint.compress = frames[frames.length + PREV_COMPRESS] != 0;
 						constraint.stretch = frames[frames.length + PREV_STRETCH] != 0;
 					}
 				} else {
 					constraint.mix += (frames[frames.length + PREV_MIX] - constraint.mix) * alpha;
 					if (direction == in) {
 						constraint.bendDirection = (int)frames[frames.length + PREV_BEND_DIRECTION];
+						constraint.compress = frames[frames.length + PREV_COMPRESS] != 0;
 						constraint.stretch = frames[frames.length + PREV_STRETCH] != 0;
 					}
 				}
@@ -1388,15 +1394,18 @@ public class Animation {
 				constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
 				if (direction == out) {
 					constraint.bendDirection = constraint.data.bendDirection;
+					constraint.compress = constraint.data.compress;
 					constraint.stretch = constraint.data.stretch;
 				} else {
 					constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
+					constraint.compress = frames[frame + PREV_COMPRESS] != 0;
 					constraint.stretch = frames[frame + PREV_STRETCH] != 0;
 				}
 			} else {
 				constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
 				if (direction == in) {
 					constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
+					constraint.compress = frames[frame + PREV_COMPRESS] != 0;
 					constraint.stretch = frames[frame + PREV_STRETCH] != 0;
 				}
 			}

+ 27 - 11
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java

@@ -43,7 +43,7 @@ public class IkConstraint implements Constraint {
 	final Array<Bone> bones;
 	Bone target;
 	int bendDirection;
-	boolean stretch;
+	boolean compress, stretch;
 	float mix = 1;
 
 	public IkConstraint (IkConstraintData data, Skeleton skeleton) {
@@ -52,6 +52,7 @@ public class IkConstraint implements Constraint {
 		this.data = data;
 		mix = data.mix;
 		bendDirection = data.bendDirection;
+		compress = data.compress;
 		stretch = data.stretch;
 
 		bones = new Array(data.bones.size);
@@ -71,6 +72,7 @@ public class IkConstraint implements Constraint {
 		target = skeleton.bones.get(constraint.target.data.index);
 		mix = constraint.mix;
 		bendDirection = constraint.bendDirection;
+		compress = constraint.compress;
 		stretch = constraint.stretch;
 	}
 
@@ -84,7 +86,7 @@ public class IkConstraint implements Constraint {
 		Array<Bone> bones = this.bones;
 		switch (bones.size) {
 		case 1:
-			apply(bones.first(), target.worldX, target.worldY, stretch, mix);
+			apply(bones.first(), target.worldX, target.worldY, compress, stretch, data.uniform, mix);
 			break;
 		case 2:
 			apply(bones.first(), bones.get(1), target.worldX, target.worldY, bendDirection, stretch, mix);
@@ -128,8 +130,17 @@ public class IkConstraint implements Constraint {
 		this.bendDirection = bendDirection;
 	}
 
-	/** When true, if the target is out of range, the parent bone is scaled on the X axis to reach it. If the parent bone has local
-	 * nonuniform scale, stretching is not applied. */
+	/** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */
+	public boolean getCompress () {
+		return compress;
+	}
+
+	public void setCompress (boolean compress) {
+		this.compress = compress;
+	}
+
+	/** When true, if the target is out of range, the parent bone is scaled to reach it. If more than one bone is being constrained
+	 * and the parent bone has local nonuniform scale, stretch is not applied. */
 	public boolean getStretch () {
 		return stretch;
 	}
@@ -148,7 +159,8 @@ public class IkConstraint implements Constraint {
 	}
 
 	/** Applies 1 bone IK. The target is specified in the world coordinate system. */
-	static public void apply (Bone bone, float targetX, float targetY, boolean stretch, float alpha) {
+	static public void apply (Bone bone, float targetX, float targetY, boolean compress, boolean stretch, boolean uniform,
+		float alpha) {
 		if (!bone.appliedValid) bone.updateAppliedTransform();
 		Bone p = bone.parent;
 		float id = 1 / (p.a * p.d - p.b * p.c);
@@ -158,14 +170,18 @@ public class IkConstraint implements Constraint {
 		if (bone.ascaleX < 0) rotationIK += 180;
 		if (rotationIK > 180)
 			rotationIK -= 360;
-		else if (rotationIK < -180) rotationIK += 360;
-		float sx = bone.ascaleX;
-		if (stretch) {
+		else if (rotationIK < -180) //
+			rotationIK += 360;
+		float sx = bone.ascaleX, sy = bone.ascaleY;
+		if (compress || stretch) {
 			float b = bone.data.length * sx, dd = (float)Math.sqrt(tx * tx + ty * ty);
-			if (dd > b && b > 0.0001f) sx *= (dd / b - 1) * alpha + 1;
+			if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001f) {
+				float s = (dd / b - 1) * alpha + 1;
+				sx *= s;
+				if (uniform) sy *= s;
+			}
 		}
-		bone.updateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, bone.ascaleY, bone.ashearX,
-			bone.ashearY);
+		bone.updateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY);
 	}
 
 	/** Applies 2 bone IK. The target is specified in the world coordinate system.

+ 27 - 8
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java

@@ -41,7 +41,7 @@ public class IkConstraintData {
 	final Array<BoneData> bones = new Array();
 	BoneData target;
 	int bendDirection = 1;
-	boolean stretch;
+	boolean compress, stretch, uniform;
 	float mix = 1;
 
 	public IkConstraintData (String name) {
@@ -78,6 +78,15 @@ public class IkConstraintData {
 		this.target = target;
 	}
 
+	/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
+	public float getMix () {
+		return mix;
+	}
+
+	public void setMix (float mix) {
+		this.mix = mix;
+	}
+
 	/** Controls the bend direction of the IK bones, either 1 or -1. */
 	public int getBendDirection () {
 		return bendDirection;
@@ -87,8 +96,17 @@ public class IkConstraintData {
 		this.bendDirection = bendDirection;
 	}
 
-	/** When true, if the target is out of range, the parent bone is scaled on the X axis to reach it. If the parent bone has local
-	 * nonuniform scale, stretching is not applied. */
+	/** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */
+	public boolean getCompress () {
+		return compress;
+	}
+
+	public void setCompress (boolean compress) {
+		this.compress = compress;
+	}
+
+	/** When true, if the target is out of range, the parent bone is scaled to reach it. If more than one bone is being constrained
+	 * and the parent bone has local nonuniform scale, stretch is not applied. */
 	public boolean getStretch () {
 		return stretch;
 	}
@@ -97,13 +115,14 @@ public class IkConstraintData {
 		this.stretch = stretch;
 	}
 
-	/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
-	public float getMix () {
-		return mix;
+	/** When true, only a single bone is being constrained, and {@link #getCompress()} or {@link #getStretch()} is used, the bone
+	 * is scaled on both the X and Y axes. */
+	public boolean getUniform () {
+		return uniform;
 	}
 
-	public void setMix (float mix) {
-		this.mix = mix;
+	public void setUniform (boolean uniform) {
+		this.uniform = uniform;
 	}
 
 	public String toString () {

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

@@ -394,9 +394,10 @@ public class Skeleton {
 		Array<IkConstraint> ikConstraints = this.ikConstraints;
 		for (int i = 0, n = ikConstraints.size; i < n; i++) {
 			IkConstraint constraint = ikConstraints.get(i);
+			constraint.mix = constraint.data.mix;
 			constraint.bendDirection = constraint.data.bendDirection;
+			constraint.compress = constraint.data.compress;
 			constraint.stretch = constraint.data.stretch;
-			constraint.mix = constraint.data.mix;
 		}
 
 		Array<TransformConstraint> transformConstraints = this.transformConstraints;

+ 5 - 2
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java

@@ -232,7 +232,9 @@ public class SkeletonBinary {
 				data.target = skeletonData.bones.get(input.readInt(true));
 				data.mix = input.readFloat();
 				data.bendDirection = input.readByte();
+				data.compress = input.readBoolean();
 				data.stretch = input.readBoolean();
+				data.uniform = input.readBoolean();
 				skeletonData.ikConstraints.add(data);
 			}
 
@@ -307,7 +309,7 @@ public class SkeletonBinary {
 				data.intValue = input.readInt(false);
 				data.floatValue = input.readFloat();
 				data.stringValue = input.readString();
-				data.audioPath = input.readString(); 
+				data.audioPath = input.readString();
 				skeletonData.events.add(data);
 			}
 
@@ -661,7 +663,8 @@ public class SkeletonBinary {
 				IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
 				timeline.ikConstraintIndex = index;
 				for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
-					timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readByte(), input.readBoolean());
+					timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readByte(), input.readBoolean(),
+						input.readBoolean());
 					if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
 				}
 				timelines.add(timeline);

+ 5 - 2
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java

@@ -185,9 +185,11 @@ public class SkeletonJson {
 			data.target = skeletonData.findBone(targetName);
 			if (data.target == null) throw new SerializationException("IK target bone not found: " + targetName);
 
+			data.mix = constraintMap.getFloat("mix", 1);
 			data.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1;
+			data.compress = constraintMap.getBoolean("compress", false);
 			data.stretch = constraintMap.getBoolean("stretch", false);
-			data.mix = constraintMap.getFloat("mix", 1);
+			data.uniform = constraintMap.getBoolean("uniform", false);
 
 			skeletonData.ikConstraints.add(data);
 		}
@@ -569,7 +571,8 @@ public class SkeletonJson {
 			int frameIndex = 0;
 			for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) {
 				timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("mix", 1),
-					valueMap.getBoolean("bendPositive", true) ? 1 : -1, valueMap.getBoolean("stretch", false));
+					valueMap.getBoolean("bendPositive", true) ? 1 : -1, valueMap.getBoolean("compress", false),
+					valueMap.getBoolean("stretch", false));
 				readCurve(valueMap, timeline, frameIndex);
 				frameIndex++;
 			}