mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 07:14:55 +08:00
[libgdx] Added IK softness.
This commit is contained in:
parent
4c9d80926c
commit
15b4c54888
@ -1326,15 +1326,16 @@ public class Animation {
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes an IK constraint's {@link IkConstraint#getMix()}, {@link IkConstraint#getBendDirection()},
|
||||
* {@link IkConstraint#getStretch()}, and {@link IkConstraint#getCompress()}. */
|
||||
/** Changes an IK constraint's {@link IkConstraint#getMix()},{@link IkConstraint#getSoftness()},
|
||||
* {@link IkConstraint#getBendDirection()}, {@link IkConstraint#getStretch()}, and {@link IkConstraint#getCompress()}. */
|
||||
static public class IkConstraintTimeline extends CurveTimeline {
|
||||
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;
|
||||
static public final int ENTRIES = 6;
|
||||
static private final int PREV_TIME = -6, PREV_MIX = -5, PREV_SOFTNESS = -4, PREV_BEND_DIRECTION = -3, PREV_COMPRESS = -2,
|
||||
PREV_STRETCH = -1;
|
||||
static private final int MIX = 1, SOFTNESS = 2, BEND_DIRECTION = 3, COMPRESS = 4, STRETCH = 5;
|
||||
|
||||
int ikConstraintIndex;
|
||||
private final float[] frames; // time, mix, bendDirection, compress, stretch, ...
|
||||
private final float[] frames; // time, mix, softness, bendDirection, compress, stretch, ...
|
||||
|
||||
public IkConstraintTimeline (int frameCount) {
|
||||
super(frameCount);
|
||||
@ -1355,16 +1356,18 @@ public class Animation {
|
||||
return ikConstraintIndex;
|
||||
}
|
||||
|
||||
/** The time in seconds, mix, bend direction, compress, and stretch for each key frame. */
|
||||
/** The time in seconds, mix, softness, bend direction, compress, and stretch for each key frame. */
|
||||
public float[] getFrames () {
|
||||
return frames;
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
/** Sets the time in seconds, mix, softness, bend direction, compress, and stretch for the specified key frame. */
|
||||
public void setFrame (int frameIndex, float time, float mix, float softness, int bendDirection, boolean compress,
|
||||
boolean stretch) {
|
||||
frameIndex *= ENTRIES;
|
||||
frames[frameIndex] = time;
|
||||
frames[frameIndex + MIX] = mix;
|
||||
frames[frameIndex + SOFTNESS] = softness;
|
||||
frames[frameIndex + BEND_DIRECTION] = bendDirection;
|
||||
frames[frameIndex + COMPRESS] = compress ? 1 : 0;
|
||||
frames[frameIndex + STRETCH] = stretch ? 1 : 0;
|
||||
@ -1380,12 +1383,14 @@ public class Animation {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
constraint.mix = constraint.data.mix;
|
||||
constraint.softness = constraint.data.softness;
|
||||
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.softness += (constraint.data.softness - constraint.softness) * alpha;
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.compress = constraint.data.compress;
|
||||
constraint.stretch = constraint.data.stretch;
|
||||
@ -1396,6 +1401,8 @@ public class Animation {
|
||||
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
|
||||
if (blend == setup) {
|
||||
constraint.mix = constraint.data.mix + (frames[frames.length + PREV_MIX] - constraint.data.mix) * alpha;
|
||||
constraint.softness = constraint.data.softness
|
||||
+ (frames[frames.length + PREV_SOFTNESS] - constraint.data.softness) * alpha;
|
||||
if (direction == out) {
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.compress = constraint.data.compress;
|
||||
@ -1407,6 +1414,7 @@ public class Animation {
|
||||
}
|
||||
} else {
|
||||
constraint.mix += (frames[frames.length + PREV_MIX] - constraint.mix) * alpha;
|
||||
constraint.softness += (frames[frames.length + PREV_SOFTNESS] - constraint.softness) * alpha;
|
||||
if (direction == in) {
|
||||
constraint.bendDirection = (int)frames[frames.length + PREV_BEND_DIRECTION];
|
||||
constraint.compress = frames[frames.length + PREV_COMPRESS] != 0;
|
||||
@ -1419,11 +1427,14 @@ public class Animation {
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frame = binarySearch(frames, time, ENTRIES);
|
||||
float mix = frames[frame + PREV_MIX];
|
||||
float softness = frames[frame + PREV_SOFTNESS];
|
||||
float frameTime = frames[frame];
|
||||
float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
if (blend == setup) {
|
||||
constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
|
||||
constraint.softness = constraint.data.softness
|
||||
+ (softness + (frames[frame + SOFTNESS] - softness) * percent - constraint.data.softness) * alpha;
|
||||
if (direction == out) {
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.compress = constraint.data.compress;
|
||||
@ -1435,6 +1446,7 @@ public class Animation {
|
||||
}
|
||||
} else {
|
||||
constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
|
||||
constraint.softness += (softness + (frames[frame + SOFTNESS] - softness) * percent - constraint.softness) * alpha;
|
||||
if (direction == in) {
|
||||
constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
|
||||
constraint.compress = frames[frame + PREV_COMPRESS] != 0;
|
||||
|
||||
@ -43,7 +43,7 @@ public class IkConstraint implements Updatable {
|
||||
Bone target;
|
||||
int bendDirection;
|
||||
boolean compress, stretch;
|
||||
float mix = 1;
|
||||
float mix = 1, softness;
|
||||
|
||||
boolean active;
|
||||
|
||||
@ -52,6 +52,7 @@ public class IkConstraint implements Updatable {
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
mix = data.mix;
|
||||
softness = data.softness;
|
||||
bendDirection = data.bendDirection;
|
||||
compress = data.compress;
|
||||
stretch = data.stretch;
|
||||
@ -72,6 +73,7 @@ public class IkConstraint implements Updatable {
|
||||
bones.add(skeleton.bones.get(bone.data.index));
|
||||
target = skeleton.bones.get(constraint.target.data.index);
|
||||
mix = constraint.mix;
|
||||
softness = constraint.softness;
|
||||
bendDirection = constraint.bendDirection;
|
||||
compress = constraint.compress;
|
||||
stretch = constraint.stretch;
|
||||
@ -90,7 +92,7 @@ public class IkConstraint implements Updatable {
|
||||
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);
|
||||
apply(bones.first(), bones.get(1), target.worldX, target.worldY, bendDirection, stretch, softness, mix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -119,6 +121,15 @@ public class IkConstraint implements Updatable {
|
||||
this.mix = mix;
|
||||
}
|
||||
|
||||
/** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */
|
||||
public float getSoftness () {
|
||||
return softness;
|
||||
}
|
||||
|
||||
public void setSoftness (float softness) {
|
||||
this.softness = softness;
|
||||
}
|
||||
|
||||
/** Controls the bend direction of the IK bones, either 1 or -1. */
|
||||
public int getBendDirection () {
|
||||
return bendDirection;
|
||||
@ -189,7 +200,8 @@ public class IkConstraint implements Updatable {
|
||||
|
||||
/** Applies 2 bone IK. The target is specified in the world coordinate system.
|
||||
* @param child A direct descendant of the parent bone. */
|
||||
static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, boolean stretch, float alpha) {
|
||||
static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, boolean stretch, float softness,
|
||||
float alpha) {
|
||||
if (parent == null) throw new IllegalArgumentException("parent cannot be null.");
|
||||
if (child == null) throw new IllegalArgumentException("child cannot be null.");
|
||||
if (alpha == 0) {
|
||||
@ -233,12 +245,23 @@ public class IkConstraint implements Updatable {
|
||||
b = pp.b;
|
||||
c = pp.c;
|
||||
d = pp.d;
|
||||
float id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||
float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py, dd = tx * tx + ty * ty;
|
||||
x = cwx - pp.worldX;
|
||||
y = cwy - pp.worldY;
|
||||
float id = 1 / (a * d - b * c), x = cwx - pp.worldX, y = cwy - pp.worldY;
|
||||
float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
|
||||
float l1 = (float)Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
|
||||
x = targetX - pp.worldX;
|
||||
y = targetY - pp.worldY;
|
||||
float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
|
||||
float dd = tx * tx + ty * ty;
|
||||
if (softness != 0) {
|
||||
float td = (float)Math.sqrt(dd), sd = td - l1 - l2 + softness;
|
||||
if (sd > 0) {
|
||||
float p = Math.min(1, sd / (softness * 2)) - 1;
|
||||
p = (sd - softness * (1 - p * p)) / td;
|
||||
tx -= p * tx;
|
||||
ty -= p * ty;
|
||||
dd = tx * tx + ty * ty;
|
||||
}
|
||||
}
|
||||
outer:
|
||||
if (u) {
|
||||
l2 *= psx;
|
||||
|
||||
@ -39,7 +39,7 @@ public class IkConstraintData extends ConstraintData {
|
||||
BoneData target;
|
||||
int bendDirection = 1;
|
||||
boolean compress, stretch, uniform;
|
||||
float mix = 1;
|
||||
float mix = 1, softness;
|
||||
|
||||
public IkConstraintData (String name) {
|
||||
super(name);
|
||||
@ -69,6 +69,15 @@ public class IkConstraintData extends ConstraintData {
|
||||
this.mix = mix;
|
||||
}
|
||||
|
||||
/** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */
|
||||
public float getSoftness () {
|
||||
return softness;
|
||||
}
|
||||
|
||||
public void setSoftness (float softness) {
|
||||
this.softness = softness;
|
||||
}
|
||||
|
||||
/** Controls the bend direction of the IK bones, either 1 or -1. */
|
||||
public int getBendDirection () {
|
||||
return bendDirection;
|
||||
|
||||
@ -419,6 +419,7 @@ public class Skeleton {
|
||||
for (int i = 0, n = ikConstraints.size; i < n; i++) {
|
||||
IkConstraint constraint = ikConstraints.get(i);
|
||||
constraint.mix = constraint.data.mix;
|
||||
constraint.softness = constraint.data.softness;
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.compress = constraint.data.compress;
|
||||
constraint.stretch = constraint.data.stretch;
|
||||
|
||||
@ -212,6 +212,7 @@ public class SkeletonBinary {
|
||||
bones[ii] = skeletonData.bones.get(input.readInt(true));
|
||||
data.target = skeletonData.bones.get(input.readInt(true));
|
||||
data.mix = input.readFloat();
|
||||
data.softness = input.readFloat();
|
||||
data.bendDirection = input.readByte();
|
||||
data.compress = input.readBoolean();
|
||||
data.stretch = input.readBoolean();
|
||||
@ -668,8 +669,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(),
|
||||
input.readBoolean());
|
||||
timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readByte(),
|
||||
input.readBoolean(), input.readBoolean());
|
||||
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
|
||||
}
|
||||
timelines.add(timeline);
|
||||
|
||||
@ -197,6 +197,7 @@ public class SkeletonJson {
|
||||
if (data.target == null) throw new SerializationException("IK target bone not found: " + targetName);
|
||||
|
||||
data.mix = constraintMap.getFloat("mix", 1);
|
||||
data.softness = constraintMap.getFloat("softness", 0);
|
||||
data.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1;
|
||||
data.compress = constraintMap.getBoolean("compress", false);
|
||||
data.stretch = constraintMap.getBoolean("stretch", false);
|
||||
@ -608,8 +609,8 @@ public class SkeletonJson {
|
||||
int frameIndex = 0;
|
||||
for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) {
|
||||
timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), valueMap.getFloat("mix", 1),
|
||||
valueMap.getBoolean("bendPositive", true) ? 1 : -1, valueMap.getBoolean("compress", false),
|
||||
valueMap.getBoolean("stretch", false));
|
||||
valueMap.getFloat("softness", 0), valueMap.getBoolean("bendPositive", true) ? 1 : -1,
|
||||
valueMap.getBoolean("compress", false), valueMap.getBoolean("stretch", false));
|
||||
readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user