[as3] Added soft IK support. See #1383.

This commit is contained in:
badlogic 2019-06-19 14:24:59 +02:00
parent 5776e2467a
commit 442699238a
9 changed files with 46 additions and 13 deletions

View File

@ -36,6 +36,7 @@ package spine {
public var compress: Boolean; public var compress: Boolean;
public var stretch: Boolean; public var stretch: Boolean;
public var mix : Number; public var mix : Number;
public var softness : Number = 0;
public var active : Boolean; public var active : Boolean;
public function IkConstraint(data : IkConstraintData, skeleton : Skeleton) { public function IkConstraint(data : IkConstraintData, skeleton : Skeleton) {
@ -43,6 +44,7 @@ package spine {
if (skeleton == null) throw new ArgumentError("skeleton cannot be null."); if (skeleton == null) throw new ArgumentError("skeleton cannot be null.");
_data = data; _data = data;
mix = data.mix; mix = data.mix;
softness = data.softness;
bendDirection = data.bendDirection; bendDirection = data.bendDirection;
compress = data.compress; compress = data.compress;
stretch = data.stretch; stretch = data.stretch;
@ -67,7 +69,7 @@ package spine {
apply1(bones[0], target.worldX, target.worldY, compress, stretch, _data.uniform, mix); apply1(bones[0], target.worldX, target.worldY, compress, stretch, _data.uniform, mix);
break; break;
case 2: case 2:
apply2(bones[0], bones[1], target.worldX, target.worldY, bendDirection, stretch, mix); apply2(bones[0], bones[1], target.worldX, target.worldY, bendDirection, stretch, softness, mix);
break; break;
} }
} }
@ -109,7 +111,7 @@ package spine {
/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The /** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The
* target is specified in the world coordinate system. * target is specified in the world coordinate system.
* @param child Any descendant bone of the parent. */ * @param child Any descendant bone of the parent. */
static public function apply2(parent : Bone, child : Bone, targetX : Number, targetY : Number, bendDir : int, stretch : Boolean, alpha : Number) : void { static public function apply2(parent : Bone, child : Bone, targetX : Number, targetY : Number, bendDir : int, stretch : Boolean, softness: Number, alpha : Number) : void {
if (alpha == 0) { if (alpha == 0) {
child.updateWorldTransform(); child.updateWorldTransform();
return; return;
@ -151,12 +153,29 @@ package spine {
b = pp.b; b = pp.b;
c = pp.c; c = pp.c;
d = pp.d; d = pp.d;
var id : Number = 1 / (a * d - b * c), x : Number = targetX - pp.worldX, y : Number = targetY - pp.worldY; var id : Number = 1 / (a * d - b * c), x : Number = cwx - pp.worldX, y : Number = cwy - pp.worldY;
var tx : Number = (x * d - y * b) * id - px, ty : Number = (y * a - x * c) * id - py, dd : Number = tx * tx + ty * ty;
x = cwx - pp.worldX;
y = cwy - pp.worldY;
var dx : Number = (x * d - y * b) * id - px, dy : Number = (y * a - x * c) * id - py; var dx : Number = (x * d - y * b) * id - px, dy : Number = (y * a - x * c) * id - py;
var l1 : Number = Math.sqrt(dx * dx + dy * dy), l2 : Number = child.data.length * csx, a1 : Number, a2 : Number; var l1 : Number = Math.sqrt(dx * dx + dy * dy), l2 : Number = child.data.length * csx, a1 : Number, a2 : Number;
if (l1 < 0.0001) {
apply1(parent, targetX, targetY, false, stretch, false, alpha);
child.updateWorldTransformWith(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
return;
}
x = targetX - pp.worldX;
y = targetY - pp.worldY;
var tx : Number = (x * d - y * b) * id - px, ty : Number = (y * a - x * c) * id - py;
var dd : Number = tx * tx + ty * ty;
if (softness != 0) {
softness *= psx * (csx + 1) / 2;
var td : Number = Math.sqrt(dd), sd : Number = td - l1 - l2 * psx + softness;
if (sd > 0) {
var p : Number = 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: outer:
if (u) { if (u) {
l2 *= psx; l2 *= psx;
@ -165,7 +184,7 @@ package spine {
cos = -1; cos = -1;
else if (cos > 1) { else if (cos > 1) {
cos = 1; cos = 1;
if (stretch && l1 + l2 > 0.0001) sx *= (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1; if (stretch) sx *= (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
} }
a2 = Math.acos(cos) * bendDir; a2 = Math.acos(cos) * bendDir;
a = l1 + l2 * cos; a = l1 + l2 * cos;

View File

@ -36,6 +36,7 @@ package spine {
public var compress : Boolean = false; public var compress : Boolean = false;
public var stretch : Boolean = false; public var stretch : Boolean = false;
public var uniform : Boolean = false; public var uniform : Boolean = false;
public var softness : Number = 0;
public function IkConstraintData(name : String) { public function IkConstraintData(name : String) {
super(name, 0, false); super(name, 0, false);

View File

@ -322,6 +322,7 @@ package spine {
for each (var ikConstraint : IkConstraint in ikConstraints) { for each (var ikConstraint : IkConstraint in ikConstraints) {
ikConstraint.mix = ikConstraint._data.mix; ikConstraint.mix = ikConstraint._data.mix;
ikConstraint.softness = ikConstraint._data.softness;
ikConstraint.bendDirection = ikConstraint._data.bendDirection; ikConstraint.bendDirection = ikConstraint._data.bendDirection;
ikConstraint.compress = ikConstraint._data.compress; ikConstraint.compress = ikConstraint._data.compress;
ikConstraint.stretch = ikConstraint._data.stretch; ikConstraint.stretch = ikConstraint._data.stretch;

View File

@ -169,6 +169,7 @@ package spine {
ikData.bones.push(skeletonData.bones[input.readInt(true)]); ikData.bones.push(skeletonData.bones[input.readInt(true)]);
ikData.target = skeletonData.bones[input.readInt(true)]; ikData.target = skeletonData.bones[input.readInt(true)];
ikData.mix = input.readFloat(); ikData.mix = input.readFloat();
ikData.softness = input.readFloat();
ikData.bendDirection = input.readByte(); ikData.bendDirection = input.readByte();
ikData.compress = input.readBoolean(); ikData.compress = input.readBoolean();
ikData.stretch = input.readBoolean(); ikData.stretch = input.readBoolean();
@ -656,7 +657,7 @@ package spine {
frameIndex = 0; frameIndex = 0;
ikConstraintTimeline.ikConstraintIndex = index; ikConstraintTimeline.ikConstraintIndex = index;
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) { for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
ikConstraintTimeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readByte(), input.readBoolean(), ikConstraintTimeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readByte(), input.readBoolean(),
input.readBoolean()); input.readBoolean());
if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, ikConstraintTimeline); if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, ikConstraintTimeline);
} }

View File

@ -169,6 +169,7 @@ package spine {
ikConstraintData.compress = (constraintMap.hasOwnProperty("compress") && constraintMap["compress"]); ikConstraintData.compress = (constraintMap.hasOwnProperty("compress") && constraintMap["compress"]);
ikConstraintData.stretch = (constraintMap.hasOwnProperty("stretch") && constraintMap["stretch"]); ikConstraintData.stretch = (constraintMap.hasOwnProperty("stretch") && constraintMap["stretch"]);
ikConstraintData.uniform = (constraintMap.hasOwnProperty("uniform") && constraintMap["uniform"]); ikConstraintData.uniform = (constraintMap.hasOwnProperty("uniform") && constraintMap["uniform"]);
ikConstraintData.softness = constraintMap.hasOwnProperty("softness") ? constraintMap["softness"] : 0;
ikConstraintData.mix = constraintMap.hasOwnProperty("mix") ? constraintMap["mix"] : 1; ikConstraintData.mix = constraintMap.hasOwnProperty("mix") ? constraintMap["mix"] : 1;
skeletonData.ikConstraints.push(ikConstraintData); skeletonData.ikConstraints.push(ikConstraintData);
@ -588,7 +589,8 @@ package spine {
var bendDirection : int = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1; var bendDirection : int = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1;
var compress : Boolean = (valueMap.hasOwnProperty("compress") && valueMap["compress"]); var compress : Boolean = (valueMap.hasOwnProperty("compress") && valueMap["compress"]);
var stretch : Boolean = (valueMap.hasOwnProperty("stretch") && valueMap["stretch"]); var stretch : Boolean = (valueMap.hasOwnProperty("stretch") && valueMap["stretch"]);
ikTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), mix, bendDirection, compress, stretch); var softness : Number = valueMap.hasOwnProperty("softness") ? valueMap["softness"] : 0;
ikTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), mix, softness, bendDirection, compress, stretch);
readCurve(valueMap, ikTimeline, frameIndex); readCurve(valueMap, ikTimeline, frameIndex);
frameIndex++; frameIndex++;
} }

View File

@ -34,8 +34,8 @@ package spine.animation {
public class IkConstraintTimeline extends CurveTimeline { public class IkConstraintTimeline extends CurveTimeline {
static public const ENTRIES : int = 5; static public const ENTRIES : int = 5;
static internal const PREV_TIME : int = -5, PREV_MIX : int = -4, PREV_BEND_DIRECTION : int = -3, PREV_COMPRESS : int = -2, PREV_STRETCH : int = -1; static internal const PREV_TIME : int = -6, PREV_MIX : int = -5, PREV_SOFTNESS : int = -4, PREV_BEND_DIRECTION : int = -3, PREV_COMPRESS : int = -2, PREV_STRETCH : int = -1;
static internal const MIX : int = 1, BEND_DIRECTION : int = 2, COMPRESS : int = 3, STRETCH : int = 4; static internal const MIX : int = 1, SOFTNESS : int = 2, BEND_DIRECTION : int = 3, COMPRESS : int = 4, STRETCH : int = 5;
public var ikConstraintIndex : int; public var ikConstraintIndex : int;
public var frames : Vector.<Number>; // time, mix, bendDirection, compress, stretch, ... public var frames : Vector.<Number>; // time, mix, bendDirection, compress, stretch, ...
@ -49,10 +49,11 @@ package spine.animation {
} }
/** Sets the time, mix and bend direction of the specified keyframe. */ /** Sets the time, mix and bend direction of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, mix : Number, bendDirection : int, compress: Boolean, stretch: Boolean) : void { public function setFrame(frameIndex : int, time : Number, mix : Number, softness: Number, bendDirection : int, compress: Boolean, stretch: Boolean) : void {
frameIndex *= ENTRIES; frameIndex *= ENTRIES;
frames[frameIndex] = time; frames[frameIndex] = time;
frames[int(frameIndex + MIX)] = mix; frames[int(frameIndex + MIX)] = mix;
frames[int(frameIndex + SOFTNESS)] = softness;
frames[int(frameIndex + BEND_DIRECTION)] = bendDirection; frames[int(frameIndex + BEND_DIRECTION)] = bendDirection;
frames[int(frameIndex + COMPRESS)] = compress ? 1 : 0; frames[int(frameIndex + COMPRESS)] = compress ? 1 : 0;
frames[int(frameIndex + STRETCH)] = stretch ? 1 : 0; frames[int(frameIndex + STRETCH)] = stretch ? 1 : 0;
@ -65,12 +66,14 @@ package spine.animation {
switch (blend) { switch (blend) {
case MixBlend.setup: case MixBlend.setup:
constraint.mix = constraint.data.mix; constraint.mix = constraint.data.mix;
constraint.softness = constraint.data.softness;
constraint.bendDirection = constraint.data.bendDirection; constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress; constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch; constraint.stretch = constraint.data.stretch;
return; return;
case MixBlend.first: case MixBlend.first:
constraint.mix += (constraint.data.mix - constraint.mix) * alpha; constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
constraint.softness += (constraint.data.softness - constraint.softness) * alpha;
constraint.bendDirection = constraint.data.bendDirection; constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress; constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch; constraint.stretch = constraint.data.stretch;
@ -81,7 +84,8 @@ package spine.animation {
if (time >= frames[int(frames.length - ENTRIES)]) { // Time is after last frame. if (time >= frames[int(frames.length - ENTRIES)]) { // Time is after last frame.
if (blend == MixBlend.setup) { if (blend == MixBlend.setup) {
constraint.mix = constraint.data.mix + (frames[frames.length + PREV_MIX] - constraint.data.mix) * alpha; 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) { if (direction == MixDirection.Out) {
constraint.bendDirection = constraint.data.bendDirection; constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress; constraint.compress = constraint.data.compress;
@ -93,6 +97,7 @@ package spine.animation {
} }
} else { } else {
constraint.mix += (frames[frames.length + PREV_MIX] - constraint.mix) * alpha; constraint.mix += (frames[frames.length + PREV_MIX] - constraint.mix) * alpha;
constraint.softness += (frames[frames.length + PREV_SOFTNESS] - constraint.softness) * alpha;
if (direction == MixDirection.In) { if (direction == MixDirection.In) {
constraint.bendDirection = int(frames[frames.length + PREV_BEND_DIRECTION]); constraint.bendDirection = int(frames[frames.length + PREV_BEND_DIRECTION]);
constraint.compress = int(frames[frames.length + PREV_COMPRESS]) != 0; constraint.compress = int(frames[frames.length + PREV_COMPRESS]) != 0;
@ -105,11 +110,14 @@ package spine.animation {
// Interpolate between the previous frame and the current frame. // Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES); var frame : int = Animation.binarySearch(frames, time, ENTRIES);
var mix : Number = frames[int(frame + PREV_MIX)]; var mix : Number = frames[int(frame + PREV_MIX)];
var softness : Number = frames[frame + PREV_SOFTNESS];
var frameTime : Number = frames[frame]; var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
if (blend == MixBlend.setup) { if (blend == MixBlend.setup) {
constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha; 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) { if (direction == MixDirection.Out) {
constraint.bendDirection = constraint.data.bendDirection; constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress; constraint.compress = constraint.data.compress;
@ -121,6 +129,7 @@ package spine.animation {
} }
} else { } else {
constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha; 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) { if (direction == MixDirection.In) {
constraint.bendDirection = int(frames[frame + PREV_BEND_DIRECTION]); constraint.bendDirection = int(frames[frame + PREV_BEND_DIRECTION]);
constraint.compress = int(frames[frame + PREV_COMPRESS]) != 0; constraint.compress = int(frames[frame + PREV_COMPRESS]) != 0;