Bladeren bron

[csharp] Port IkConstraint unform and compress.

pharan 7 jaren geleden
bovenliggende
commit
cbd6b2617e

+ 26 - 8
spine-csharp/src/Animation.cs

@@ -1210,15 +1210,15 @@ namespace Spine {
 	}
 
 	public class IkConstraintTimeline : CurveTimeline {
-		public const int ENTRIES = 4;
-		private const int PREV_TIME = -4, PREV_MIX = -3, PREV_BEND_DIRECTION = -2, PREV_STRETCH = -1;
-		private const int MIX = 1, BEND_DIRECTION = 2, STRETCH = 3;
+		public const int ENTRIES = 5;
+		private const int PREV_TIME = -5, PREV_MIX = -4, PREV_BEND_DIRECTION = -3, PREV_COMPRESS = -2, PREV_STRETCH = -1;
+		private const int MIX = 1, BEND_DIRECTION = 2, COMPRESS = 3, STRETCH = 4;
 
 		internal int ikConstraintIndex;
 		internal float[] frames;
 
 		public int IkConstraintIndex { get { return ikConstraintIndex; } set { ikConstraintIndex = value; } }
-		public float[] Frames { get { return frames; } set { frames = value; } } // time, mix, bendDirection, ...
+		public float[] Frames { get { return frames; } set { frames = value; } } // time, mix, bendDirection, compress, stretch ...
 
 		override public int PropertyId {
 			get { return ((int)TimelineType.IkConstraint << 24) + ikConstraintIndex; }
@@ -1229,12 +1229,13 @@ namespace Spine {
 			frames = new float[frameCount * ENTRIES];
 		}
 			
-		/// <summary>Sets the time, mix and bend direction of the specified keyframe.</summary>
-		public void SetFrame (int frameIndex, float time, float mix, int bendDirection, bool stretch) {
+		/// <summary>Sets the time, mix, bend direction, compress and stretch of the specified keyframe.</summary>
+		public void SetFrame (int frameIndex, float time, float mix, int bendDirection, bool compress, bool 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;
 		}
 
@@ -1246,11 +1247,13 @@ namespace Spine {
 				case MixBlend.Setup:
 					constraint.mix = constraint.data.mix;
 					constraint.bendDirection = constraint.data.bendDirection;
+					constraint.compress = constraint.data.compress;
 					constraint.stretch = constraint.data.stretch;
 					return;
 				case MixBlend.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;
 				}
@@ -1262,15 +1265,18 @@ namespace Spine {
 					constraint.mix = constraint.data.mix + (frames[frames.Length + PREV_MIX] - constraint.data.mix) * alpha;
 					if (direction == MixDirection.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 == MixDirection.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;
 					}
 				}
@@ -1285,10 +1291,22 @@ namespace Spine {
 
 			if (blend == MixBlend.Setup) {
 				constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
-				constraint.bendDirection = direction == MixDirection.Out ? constraint.data.bendDirection : (int)frames[frame + PREV_BEND_DIRECTION];
+				if (direction == MixDirection.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 == MixDirection.In) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
+				if (direction == MixDirection.In) {
+					constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
+					constraint.compress = frames[frame + PREV_COMPRESS] != 0;
+					constraint.stretch = frames[frame + PREV_STRETCH] != 0;
+				}
 			}
 		}
 	}

+ 23 - 11
spine-csharp/src/IkConstraint.cs

@@ -36,7 +36,7 @@ namespace Spine {
 		internal ExposedList<Bone> bones = new ExposedList<Bone>();
 		internal Bone target;
 		internal int bendDirection;
-		internal bool stretch;
+		internal bool compress, stretch;
 		internal float mix;
 
 		public IkConstraintData Data { get { return data; } }
@@ -59,9 +59,17 @@ namespace Spine {
 			set { bendDirection = value; }
 		}
 
+		/// <summary>
+		/// When true and only a single bone is being constrained, 
+		/// if the target is too close, the bone is scaled to reach it.</summary>
+		public bool Compress {
+			get { return compress; }
+			set { compress = value; }
+		}
+
 		/// <summary>
 		/// 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 nonuniform scale, stretching is not applied.</summary>
+		/// If the parent bone has nonuniform scale, stretching is not applied.</summary>
 		public bool Stretch {
 			get { return stretch; }
 			set { stretch = value; }
@@ -79,6 +87,7 @@ namespace Spine {
 			this.data = data;
 			mix = data.mix;
 			bendDirection = data.bendDirection;
+			compress = data.compress;
 			stretch = data.stretch;
 
 			bones = new ExposedList<Bone>(data.bones.Count);
@@ -97,7 +106,7 @@ namespace Spine {
 			ExposedList<Bone> bones = this.bones;
 			switch (bones.Count) {
 			case 1:
-				Apply(bones.Items[0], target.worldX, target.worldY, stretch, mix);
+				Apply(bones.Items[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix);
 				break;
 			case 2:
 				Apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, stretch, mix);
@@ -109,9 +118,8 @@ namespace Spine {
 			return data.name;
 		}
 
-		/// <summary>Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified
-		/// in the world coordinate system.</summary>
-		static public void Apply (Bone bone, float targetX, float targetY, bool stretch, float alpha) {
+		/// <summary>Applies 1 bone IK. The target is specified in the world coordinate system.</summary>
+		static public void Apply (Bone bone, float targetX, float targetY, bool compress, bool stretch, bool uniform, float alpha) {
 			if (!bone.appliedValid) bone.UpdateAppliedTransform();
 			Bone p = bone.parent;
 			float id = 1 / (p.a * p.d - p.b * p.c);
@@ -121,14 +129,18 @@ namespace Spine {
 			if (bone.ascaleX < 0) rotationIK += 180;
 			if (rotationIK > 180)
 				rotationIK -= 360;
-			else if (rotationIK < -180)
+			else if (rotationIK < -180) //
 				rotationIK += 360;
-			float sx = bone.ascaleX;
-			if (stretch) {
+			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.UpdateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX,
 				bone.ashearY);
 		}
 

+ 23 - 2
spine-csharp/src/IkConstraintData.cs

@@ -39,7 +39,7 @@ namespace Spine {
 		internal List<BoneData> bones = new List<BoneData>();
 		internal BoneData target;
 		internal int bendDirection = 1;
-		internal bool stretch;
+		internal bool compress, stretch, uniform;
 		internal float mix = 1;
 
 		/// <summary>The IK constraint's name, which is unique within the skeleton.</summary>
@@ -63,12 +63,27 @@ namespace Spine {
 			set { target = value; }
 		}
 
+		/// <summary>
+		/// A percentage (0-1) that controls the mix between the constraint and unconstrained rotations.</summary>
+		public float Mix {
+			get { return mix; }
+			set { mix = value; }
+		}
+
 		/// <summary>Controls the bend direction of the IK bones, either 1 or -1.</summary>
 		public int BendDirection {
 			get { return bendDirection; }
 			set { bendDirection = value; }
 		}
 
+		/// <summary>
+		/// When true, and only a single bone is being constrained, 
+		/// if the target is too close, the bone is scaled to reach it. </summary>
+		public bool Compress {
+			get { return compress; }
+			set { compress = value; }
+		}
+
 		/// <summary>
 		/// When true, if the target is out of range, the parent bone is scaled on the X axis to reach it. 
 		/// If the bone has local nonuniform scale, stretching is not applied.</summary>
@@ -77,7 +92,13 @@ namespace Spine {
 			set { stretch = value; }
 		}
 
-		public float Mix { get { return mix; } set { mix = value; } }
+		/// <summary>
+		/// When true, only a single bone is being constrained and Compress or Stretch is used, 
+		/// the bone is scaled both on the X and Y axes.</summary>
+		public bool Uniform {
+			get { return uniform; }
+			set { uniform = value; }
+		}
 
 		public IkConstraintData (string name) {
 			if (name == null) throw new ArgumentNullException("name", "name cannot be null.");

+ 2 - 1
spine-csharp/src/Skeleton.cs

@@ -308,9 +308,10 @@ namespace Spine {
 			var ikConstraintsItems = this.ikConstraints.Items;
 			for (int i = 0, n = ikConstraints.Count; i < n; i++) {
 				IkConstraint constraint = ikConstraintsItems[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;
 			}
 
 			var transformConstraintsItems = this.transformConstraints.Items;

+ 6 - 3
spine-csharp/src/SkeletonBinary.cs

@@ -210,7 +210,9 @@ namespace Spine {
 				data.target = skeletonData.bones.Items[ReadVarint(input, true)];
 				data.mix = ReadFloat(input);
 				data.bendDirection = ReadSByte(input);
+				data.compress = ReadBoolean(input);
 				data.stretch = ReadBoolean(input);
+				data.uniform = ReadBoolean(input);
 				skeletonData.ikConstraints.Add(data);
 			}
 
@@ -646,10 +648,11 @@ namespace Spine {
 			for (int i = 0, n = ReadVarint(input, true); i < n; i++) {				
 				int index = ReadVarint(input, true);
 				int frameCount = ReadVarint(input, true);
-				IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
-				timeline.ikConstraintIndex = index;
+				IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount) {
+					ikConstraintIndex = index
+				};
 				for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
-					timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input), ReadBoolean(input));
+					timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input), ReadBoolean(input), ReadBoolean(input));
 					if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
 				}
 				timelines.Add(timeline);

+ 11 - 7
spine-csharp/src/SkeletonJson.cs

@@ -180,10 +180,11 @@ namespace Spine {
 					string targetName = (string)constraintMap["target"];
 					data.target = skeletonData.FindBone(targetName);
 					if (data.target == null) throw new Exception("Target bone not found: " + targetName);
-
+					data.mix = GetFloat(constraintMap, "mix", 1);
 					data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1;
+					data.compress = GetBoolean(constraintMap, "compress", false);
 					data.stretch = GetBoolean(constraintMap, "stretch", false);
-					data.mix = GetFloat(constraintMap, "mix", 1);
+					data.uniform = GetBoolean(constraintMap, "uniform", false);
 
 					skeletonData.ikConstraints.Add(data);
 				}
@@ -595,11 +596,14 @@ namespace Spine {
 					timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint);
 					int frameIndex = 0;
 					foreach (Dictionary<string, Object> valueMap in values) {
-						float time = (float)valueMap["time"];
-						float mix = GetFloat(valueMap, "mix", 1);
-						bool bendPositive = GetBoolean(valueMap, "bendPositive", true);
-						bool stretch = GetBoolean(valueMap, "stretch", false);
-						timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1, stretch);
+						timeline.SetFrame(
+							frameIndex,
+							(float)valueMap["time"],
+							GetFloat(valueMap, "mix", 1),
+							GetBoolean(valueMap, "bendPositive", true) ? 1 : -1,
+							GetBoolean(valueMap, "compress", true),
+							GetBoolean(valueMap, "stretch", false)
+						);
 						ReadCurve(valueMap, timeline, frameIndex);
 						frameIndex++;
 					}