Added IK constraint compress and uniform settings.

This commit is contained in:
NathanSweet 2018-08-23 12:15:32 +02:00
parent 671c921089
commit 7df713b13f
6 changed files with 98 additions and 47 deletions

View File

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

View File

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

View File

@ -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,25 +78,6 @@ public class IkConstraintData {
this.target = target;
}
/** Controls the bend direction of the IK bones, either 1 or -1. */
public int getBendDirection () {
return bendDirection;
}
public void setBendDirection (int bendDirection) {
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. */
public boolean getStretch () {
return stretch;
}
public void setStretch (boolean stretch) {
this.stretch = stretch;
}
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
public float getMix () {
return mix;
@ -106,6 +87,44 @@ public class IkConstraintData {
this.mix = mix;
}
/** Controls the bend direction of the IK bones, either 1 or -1. */
public int getBendDirection () {
return bendDirection;
}
public void setBendDirection (int bendDirection) {
this.bendDirection = bendDirection;
}
/** 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;
}
public void setStretch (boolean stretch) {
this.stretch = stretch;
}
/** 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 setUniform (boolean uniform) {
this.uniform = uniform;
}
public String toString () {
return name;
}

View File

@ -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.bendDirection = constraint.data.bendDirection;
constraint.stretch = constraint.data.stretch;
constraint.mix = constraint.data.mix;
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
}
Array<TransformConstraint> transformConstraints = this.transformConstraints;

View File

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

View File

@ -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.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1;
data.stretch = constraintMap.getBoolean("stretch", false);
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.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++;
}