mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 17:26:01 +08:00
[unity] Added soft IK support. See #1383.
This commit is contained in:
parent
64695a2c80
commit
505719c1f1
@ -1352,15 +1352,16 @@ namespace Spine {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Changes an IK constraint's <see cref="IkConstraint.Mix"/>, <see cref="IkConstraint.BendDirection"/>,
|
||||
/// <see cref="IkConstraint.Stretch"/>, and <see cref="IkConstraint.Compress"/>.</summary>
|
||||
/// <summary>Changes an IK constraint's <see cref="IkConstraint.Mix"/>, <see cref="IkConstraint.Softness"/>,
|
||||
/// <see cref="IkConstraint.BendDirection"/>, <see cref="IkConstraint.Stretch"/>, and <see cref="IkConstraint.Compress"/>.</summary>
|
||||
public class IkConstraintTimeline : CurveTimeline {
|
||||
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;
|
||||
public const int ENTRIES = 6;
|
||||
private const int PREV_TIME = -6, PREV_MIX = -5, PREV_SOFTNESS = -4, PREV_BEND_DIRECTION = -3, PREV_COMPRESS = -2,
|
||||
PREV_STRETCH = -1;
|
||||
private const int MIX = 1, SOFTNESS = 2, BEND_DIRECTION = 3, COMPRESS = 4, STRETCH = 5;
|
||||
|
||||
internal int ikConstraintIndex;
|
||||
internal float[] frames; // time, mix, bendDirection, compress, stretch, ...
|
||||
internal float[] frames; // time, mix, softness, bendDirection, compress, stretch, ...
|
||||
|
||||
public IkConstraintTimeline (int frameCount)
|
||||
: base(frameCount) {
|
||||
@ -1382,14 +1383,16 @@ namespace Spine {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The time in seconds, mix, bend direction, compress, and stretch for each key frame.</summary>
|
||||
/// <summary>The time in seconds, mix, softness, bend direction, compress, and stretch for each key frame.</summary>
|
||||
public float[] Frames { get { return frames; } set { frames = value; } }
|
||||
|
||||
/// <summary>Sets the time in seconds, mix, bend direction, compress, and stretch for the specified key frame.</summary>
|
||||
public void SetFrame (int frameIndex, float time, float mix, int bendDirection, bool compress, bool stretch) {
|
||||
/// <summary>Sets the time in seconds, mix, softness, bend direction, compress, and stretch for the specified key frame.</summary>
|
||||
public void SetFrame (int frameIndex, float time, float mix, float softness, int bendDirection, bool compress,
|
||||
bool 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;
|
||||
@ -1404,12 +1407,14 @@ namespace Spine {
|
||||
switch (blend) {
|
||||
case MixBlend.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 MixBlend.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;
|
||||
@ -1421,6 +1426,8 @@ namespace Spine {
|
||||
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||
if (blend == MixBlend.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 == MixDirection.Out) {
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.compress = constraint.data.compress;
|
||||
@ -1432,6 +1439,7 @@ namespace Spine {
|
||||
}
|
||||
} else {
|
||||
constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
|
||||
constraint.softness += (frames[frames.Length + PREV_SOFTNESS] - constraint.softness) * alpha;
|
||||
if (direction == MixDirection.In) {
|
||||
constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
|
||||
constraint.compress = frames[frames.Length + PREV_COMPRESS] != 0;
|
||||
@ -1444,11 +1452,14 @@ namespace Spine {
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frame = Animation.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 == MixBlend.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 == MixDirection.Out) {
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.compress = constraint.data.compress;
|
||||
@ -1460,6 +1471,7 @@ namespace Spine {
|
||||
}
|
||||
} 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 == MixDirection.In) {
|
||||
constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
|
||||
constraint.compress = frames[frame + PREV_COMPRESS] != 0;
|
||||
|
||||
@ -43,7 +43,7 @@ namespace Spine {
|
||||
internal Bone target;
|
||||
internal int bendDirection;
|
||||
internal bool compress, stretch;
|
||||
internal float mix = 1;
|
||||
internal float mix = 1, softness;
|
||||
|
||||
internal bool active;
|
||||
|
||||
@ -52,6 +52,7 @@ namespace Spine {
|
||||
if (skeleton == null) throw new ArgumentNullException("skeleton", "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 @@ namespace Spine {
|
||||
bones.Add(skeleton.Bones.Items[bone.data.index]);
|
||||
target = skeleton.Bones.Items[constraint.target.data.index];
|
||||
mix = constraint.mix;
|
||||
softness = constraint.softness;
|
||||
bendDirection = constraint.bendDirection;
|
||||
compress = constraint.compress;
|
||||
stretch = constraint.stretch;
|
||||
@ -90,7 +92,7 @@ namespace Spine {
|
||||
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);
|
||||
Apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, stretch, softness, mix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -112,6 +114,12 @@ namespace Spine {
|
||||
set { mix = value; }
|
||||
}
|
||||
|
||||
///<summary>For two bone IK, the distance from the maximum reach of the bones that rotation will slow.</summary>
|
||||
public float Softness {
|
||||
get { return softness; }
|
||||
set { softness = value; }
|
||||
}
|
||||
|
||||
/// <summary>Controls the bend direction of the IK bones, either 1 or -1.</summary>
|
||||
public int BendDirection {
|
||||
get { return bendDirection; }
|
||||
@ -174,7 +182,8 @@ namespace Spine {
|
||||
|
||||
/// <summary>Applies 2 bone IK. The target is specified in the world coordinate system.</summary>
|
||||
/// <param name="child">A direct descendant of the parent bone.</param>
|
||||
static public void Apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, bool stretch, float alpha) {
|
||||
static public void Apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, bool stretch, float softness,
|
||||
float alpha) {
|
||||
if (alpha == 0) {
|
||||
child.UpdateWorldTransform();
|
||||
return;
|
||||
@ -216,12 +225,29 @@ namespace Spine {
|
||||
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;
|
||||
if (l1 < 0.0001f) {
|
||||
Apply(parent, targetX, targetY, false, stretch, false, alpha);
|
||||
child.UpdateWorldTransform(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
softness *= psx * (csx + 1) / 2;
|
||||
float td = (float)Math.Sqrt(dd), sd = td - l1 - l2 * psx + 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;
|
||||
}
|
||||
}
|
||||
if (u) {
|
||||
l2 *= psx;
|
||||
float cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2);
|
||||
@ -229,7 +255,7 @@ namespace Spine {
|
||||
cos = -1;
|
||||
else if (cos > 1) {
|
||||
cos = 1;
|
||||
if (stretch && l1 + l2 > 0.0001f) sx *= ((float)Math.Sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
|
||||
if (stretch) sx *= ((float)Math.Sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
|
||||
}
|
||||
a2 = (float)Math.Acos(cos) * bendDir;
|
||||
a = l1 + l2 * cos;
|
||||
|
||||
@ -37,7 +37,7 @@ namespace Spine {
|
||||
internal BoneData target;
|
||||
internal int bendDirection = 1;
|
||||
internal bool compress, stretch, uniform;
|
||||
internal float mix = 1;
|
||||
internal float mix = 1, softness;
|
||||
|
||||
public IkConstraintData (string name) : base(name) {
|
||||
}
|
||||
@ -60,6 +60,12 @@ namespace Spine {
|
||||
set { mix = value; }
|
||||
}
|
||||
|
||||
///<summary>For two bone IK, the distance from the maximum reach of the bones that rotation will slow.</summary>
|
||||
public float Softness {
|
||||
get { return softness; }
|
||||
set { softness = value; }
|
||||
}
|
||||
|
||||
/// <summary>Controls the bend direction of the IK bones, either 1 or -1.</summary>
|
||||
public int BendDirection {
|
||||
get { return bendDirection; }
|
||||
|
||||
@ -384,6 +384,7 @@ namespace Spine {
|
||||
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
||||
IkConstraint constraint = ikConstraintsItems[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;
|
||||
|
||||
@ -210,6 +210,7 @@ namespace Spine {
|
||||
bones[ii] = skeletonData.bones.Items[input.ReadInt(true)];
|
||||
data.target = skeletonData.bones.Items[input.ReadInt(true)];
|
||||
data.mix = input.ReadFloat();
|
||||
data.softness = input.ReadFloat() * scale;
|
||||
data.bendDirection = input.ReadSByte();
|
||||
data.compress = input.ReadBoolean();
|
||||
data.stretch = input.ReadBoolean();
|
||||
@ -681,7 +682,7 @@ namespace Spine {
|
||||
ikConstraintIndex = index
|
||||
};
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat(), input.ReadSByte(), input.ReadBoolean(),
|
||||
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat(), input.ReadFloat() * scale, input.ReadSByte(), input.ReadBoolean(),
|
||||
input.ReadBoolean());
|
||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||
}
|
||||
|
||||
@ -188,6 +188,7 @@ namespace Spine {
|
||||
data.target = skeletonData.FindBone(targetName);
|
||||
if (data.target == null) throw new Exception("IK target bone not found: " + targetName);
|
||||
data.mix = GetFloat(constraintMap, "mix", 1);
|
||||
data.softness = GetFloat(constraintMap, "softness", 0) * scale;
|
||||
data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1;
|
||||
data.compress = GetBoolean(constraintMap, "compress", false);
|
||||
data.stretch = GetBoolean(constraintMap, "stretch", false);
|
||||
@ -648,6 +649,7 @@ namespace Spine {
|
||||
frameIndex,
|
||||
GetFloat(valueMap, "time", 0),
|
||||
GetFloat(valueMap, "mix", 1),
|
||||
GetFloat(valueMap, "softness", 0) * scale,
|
||||
GetBoolean(valueMap, "bendPositive", true) ? 1 : -1,
|
||||
GetBoolean(valueMap, "compress", true),
|
||||
GetBoolean(valueMap, "stretch", false)
|
||||
|
||||
@ -224,7 +224,7 @@ namespace Spine.Unity.Modules {
|
||||
static IkConstraintTimeline GetFillerTimeline (IkConstraintTimeline timeline, SkeletonData skeletonData) {
|
||||
var t = new IkConstraintTimeline(1);
|
||||
var ikConstraintData = skeletonData.ikConstraints.Items[timeline.ikConstraintIndex];
|
||||
t.SetFrame(0, 0, ikConstraintData.mix, ikConstraintData.bendDirection, ikConstraintData.compress, ikConstraintData.stretch);
|
||||
t.SetFrame(0, 0, ikConstraintData.mix, ikConstraintData.softness, ikConstraintData.bendDirection, ikConstraintData.compress, ikConstraintData.stretch);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
@ -518,6 +518,7 @@ namespace Spine {
|
||||
case TimelineType.IkConstraint:
|
||||
ikc = skeleton.ikConstraints.Items[i];
|
||||
ikc.mix = ikc.data.mix;
|
||||
ikc.softness = ikc.data.softness;
|
||||
ikc.bendDirection = ikc.data.bendDirection;
|
||||
ikc.stretch = ikc.data.stretch;
|
||||
break;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user