* Added separate X and Y for transform constraint translate and scale mix.

* Renamed `xxxMix` to `mixXxx` to avoid names like `scaleXMix`.
This commit is contained in:
Nathan Sweet 2020-11-14 18:59:42 -08:00
parent 391917d37b
commit dbd90caa53
13 changed files with 331 additions and 245 deletions

View File

@ -188,7 +188,7 @@ public class Animation {
} }
static private enum Property { static private enum Property {
rotate, translateX, translateY, scaleX, scaleY, shearX, shearY, // rotate, x, y, scaleX, scaleY, shearX, shearY, //
rgb, alpha, rgb2, // rgb, alpha, rgb2, //
attachment, deform, // attachment, deform, //
event, drawOrder, // event, drawOrder, //
@ -316,7 +316,7 @@ public class Animation {
* @param bezier The ordinal of this Bezier curve for this timeline, between 0 and <code>bezierCount - 1</code> (specified * @param bezier The ordinal of this Bezier curve for this timeline, between 0 and <code>bezierCount - 1</code> (specified
* in the constructor), inclusive. * in the constructor), inclusive.
* @param frame Between 0 and <code>frameCount - 1</code>, inclusive. * @param frame Between 0 and <code>frameCount - 1</code>, inclusive.
* @param value The index of the value for this frame that this curve is used for. * @param value The index of the value for the frame this curve is used for.
* @param time1 The time for the first key. * @param time1 The time for the first key.
* @param value1 The value for the first key. * @param value1 The value for the first key.
* @param cx1 The time for the first Bezier handle. * @param cx1 The time for the first Bezier handle.
@ -494,8 +494,8 @@ public class Animation {
public TranslateTimeline (int frameCount, int bezierCount, int boneIndex) { public TranslateTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, // super(frameCount, bezierCount, //
Property.translateX.ordinal() + "|" + boneIndex, // Property.x.ordinal() + "|" + boneIndex, //
Property.translateY.ordinal() + "|" + boneIndex); Property.y.ordinal() + "|" + boneIndex);
this.boneIndex = boneIndex; this.boneIndex = boneIndex;
} }
@ -565,7 +565,7 @@ public class Animation {
final int boneIndex; final int boneIndex;
public TranslateXTimeline (int frameCount, int bezierCount, int boneIndex) { public TranslateXTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, Property.translateX.ordinal() + "|" + boneIndex); super(frameCount, bezierCount, Property.x.ordinal() + "|" + boneIndex);
this.boneIndex = boneIndex; this.boneIndex = boneIndex;
} }
@ -611,7 +611,7 @@ public class Animation {
final int boneIndex; final int boneIndex;
public TranslateYTimeline (int frameCount, int bezierCount, int boneIndex) { public TranslateYTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, Property.translateY.ordinal() + "|" + boneIndex); super(frameCount, bezierCount, Property.y.ordinal() + "|" + boneIndex);
this.boneIndex = boneIndex; this.boneIndex = boneIndex;
} }
@ -2109,11 +2109,12 @@ public class Animation {
} }
} }
/** Changes a transform constraint's {@link TransformConstraint#getRotateMix()}, {@link TransformConstraint#getTranslateMix()}, /** Changes a transform constraint's {@link TransformConstraint#getMixRotate()}, {@link TransformConstraint#getMixX()},
* {@link TransformConstraint#getScaleMix()}, and {@link TransformConstraint#getShearMix()}. */ * {@link TransformConstraint#getMixY()}, {@link TransformConstraint#getMixScaleX()},
* {@link TransformConstraint#getMixScaleY()}, and {@link TransformConstraint#getMixShearY()}. */
static public class TransformConstraintTimeline extends CurveTimeline { static public class TransformConstraintTimeline extends CurveTimeline {
static public final int ENTRIES = 5; static public final int ENTRIES = 7;
static private final int ROTATE = 1, TRANSLATE = 2, SCALE = 3, SHEAR = 4; static private final int ROTATE = 1, X = 2, Y = 3, SCALEX = 4, SCALEY = 5, SHEARY = 6;
final int transformConstraintIndex; final int transformConstraintIndex;
@ -2135,13 +2136,16 @@ public class Animation {
/** Sets the time, rotate mix, translate mix, scale mix, and shear mix for the specified frame. /** Sets the time, rotate mix, translate mix, scale mix, and shear mix for the specified frame.
* @param frame Between 0 and <code>frameCount</code>, inclusive. * @param frame Between 0 and <code>frameCount</code>, inclusive.
* @param time The frame time in seconds. */ * @param time The frame time in seconds. */
public void setFrame (int frame, float time, float rotateMix, float translateMix, float scaleMix, float shearMix) { public void setFrame (int frame, float time, float mixRotate, float mixX, float mixY, float mixScaleX, float mixScaleY,
float mixShearY) {
frame *= ENTRIES; frame *= ENTRIES;
frames[frame] = time; frames[frame] = time;
frames[frame + ROTATE] = rotateMix; frames[frame + ROTATE] = mixRotate;
frames[frame + TRANSLATE] = translateMix; frames[frame + X] = mixX;
frames[frame + SCALE] = scaleMix; frames[frame + Y] = mixY;
frames[frame + SHEAR] = shearMix; frames[frame + SCALEX] = mixScaleX;
frames[frame + SCALEY] = mixScaleY;
frames[frame + SHEARY] = mixShearY;
} }
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend, public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
@ -2155,59 +2159,75 @@ public class Animation {
TransformConstraintData data = constraint.data; TransformConstraintData data = constraint.data;
switch (blend) { switch (blend) {
case setup: case setup:
constraint.rotateMix = data.rotateMix; constraint.mixRotate = data.mixRotate;
constraint.translateMix = data.translateMix; constraint.mixX = data.mixX;
constraint.scaleMix = data.scaleMix; constraint.mixY = data.mixY;
constraint.shearMix = data.shearMix; constraint.mixScaleX = data.mixScaleX;
constraint.mixScaleY = data.mixScaleY;
constraint.mixShearY = data.mixShearY;
return; return;
case first: case first:
constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha; constraint.mixRotate += (data.mixRotate - constraint.mixRotate) * alpha;
constraint.translateMix += (data.translateMix - constraint.translateMix) * alpha; constraint.mixX += (data.mixX - constraint.mixX) * alpha;
constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha; constraint.mixY += (data.mixY - constraint.mixY) * alpha;
constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha; constraint.mixScaleX += (data.mixScaleX - constraint.mixScaleX) * alpha;
constraint.mixScaleY += (data.mixScaleY - constraint.mixScaleY) * alpha;
constraint.mixShearY += (data.mixShearY - constraint.mixShearY) * alpha;
} }
return; return;
} }
float rotate, translate, scale, shear; float rotate, x, y, scaleX, scaleY, shearY;
int i = search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES]; int i = search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
switch (curveType) { switch (curveType) {
case LINEAR: case LINEAR:
float before = frames[i]; float before = frames[i];
rotate = frames[i + ROTATE]; rotate = frames[i + ROTATE];
translate = frames[i + TRANSLATE]; x = frames[i + X];
scale = frames[i + SCALE]; y = frames[i + Y];
shear = frames[i + SHEAR]; scaleX = frames[i + SCALEX];
scaleY = frames[i + SCALEY];
shearY = frames[i + SHEARY];
float t = (time - before) / (frames[i + ENTRIES] - before); float t = (time - before) / (frames[i + ENTRIES] - before);
rotate += (frames[i + ENTRIES + ROTATE] - rotate) * t; rotate += (frames[i + ENTRIES + ROTATE] - rotate) * t;
translate += (frames[i + ENTRIES + TRANSLATE] - translate) * t; x += (frames[i + ENTRIES + X] - x) * t;
scale += (frames[i + ENTRIES + SCALE] - scale) * t; y += (frames[i + ENTRIES + Y] - y) * t;
shear += (frames[i + ENTRIES + SHEAR] - shear) * t; scaleX += (frames[i + ENTRIES + SCALEX] - scaleX) * t;
scaleY += (frames[i + ENTRIES + SCALEY] - scaleY) * t;
shearY += (frames[i + ENTRIES + SHEARY] - shearY) * t;
break; break;
case STEPPED: case STEPPED:
rotate = frames[i + ROTATE]; rotate = frames[i + ROTATE];
translate = frames[i + TRANSLATE]; x = frames[i + X];
scale = frames[i + SCALE]; y = frames[i + Y];
shear = frames[i + SHEAR]; scaleX = frames[i + SCALEX];
scaleY = frames[i + SCALEY];
shearY = frames[i + SHEARY];
break; break;
default: default:
rotate = getBezierValue(time, i, ROTATE, curveType - BEZIER); rotate = getBezierValue(time, i, ROTATE, curveType - BEZIER);
translate = getBezierValue(time, i, TRANSLATE, curveType + BEZIER_SIZE - BEZIER); x = getBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER);
scale = getBezierValue(time, i, TRANSLATE, curveType + BEZIER_SIZE * 2 - BEZIER); y = getBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER);
shear = getBezierValue(time, i, TRANSLATE, curveType + BEZIER_SIZE * 3 - BEZIER); scaleX = getBezierValue(time, i, SCALEX, curveType + BEZIER_SIZE * 3 - BEZIER);
scaleY = getBezierValue(time, i, SCALEY, curveType + BEZIER_SIZE * 4 - BEZIER);
shearY = getBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER);
} }
if (blend == setup) { if (blend == setup) {
TransformConstraintData data = constraint.data; TransformConstraintData data = constraint.data;
constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha; constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha; constraint.mixX = data.mixX + (x - data.mixX) * alpha;
constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha; constraint.mixY = data.mixY + (y - data.mixY) * alpha;
constraint.shearMix = data.shearMix + (shear - data.shearMix) * alpha; constraint.mixScaleX = data.mixScaleX + (scaleX - data.mixScaleX) * alpha;
constraint.mixScaleY = data.mixScaleY + (scaleY - data.mixScaleY) * alpha;
constraint.mixShearY = data.mixShearY + (shearY - data.mixShearY) * alpha;
} else { } else {
constraint.rotateMix += (rotate - constraint.rotateMix) * alpha; constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
constraint.translateMix += (translate - constraint.translateMix) * alpha; constraint.mixX += (x - constraint.mixX) * alpha;
constraint.scaleMix += (scale - constraint.scaleMix) * alpha; constraint.mixY += (y - constraint.mixY) * alpha;
constraint.shearMix += (shear - constraint.shearMix) * alpha; constraint.mixScaleX += (scaleX - constraint.mixScaleX) * alpha;
constraint.mixScaleY += (scaleY - constraint.mixScaleY) * alpha;
constraint.mixShearY += (shearY - constraint.mixShearY) * alpha;
} }
} }
} }
@ -2292,8 +2312,7 @@ public class Animation {
} }
} }
/** Changes a transform constraint's {@link PathConstraint#getRotateMix()} and /** Changes a transform constraint's {@link PathConstraint#getMixRotate()} and {@link PathConstraint#getMixTranslate()}. */
* {@link TransformConstraint#getTranslateMix()}. */
static public class PathConstraintMixTimeline extends CurveTimeline2 { static public class PathConstraintMixTimeline extends CurveTimeline2 {
final int pathConstraintIndex; final int pathConstraintIndex;
@ -2318,12 +2337,12 @@ public class Animation {
if (time < frames[0]) { // Time is before first frame. if (time < frames[0]) { // Time is before first frame.
switch (blend) { switch (blend) {
case setup: case setup:
constraint.rotateMix = constraint.data.rotateMix; constraint.mixRotate = constraint.data.mixRotate;
constraint.translateMix = constraint.data.translateMix; constraint.mixTranslate = constraint.data.mixTranslate;
return; return;
case first: case first:
constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha; constraint.mixRotate += (constraint.data.mixRotate - constraint.mixRotate) * alpha;
constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha; constraint.mixTranslate += (constraint.data.mixTranslate - constraint.mixTranslate) * alpha;
} }
return; return;
} }
@ -2349,11 +2368,11 @@ public class Animation {
} }
if (blend == setup) { if (blend == setup) {
constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha; constraint.mixRotate = constraint.data.mixRotate + (rotate - constraint.data.mixRotate) * alpha;
constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha; constraint.mixTranslate = constraint.data.mixTranslate + (translate - constraint.data.mixTranslate) * alpha;
} else { } else {
constraint.rotateMix += (rotate - constraint.rotateMix) * alpha; constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
constraint.translateMix += (translate - constraint.translateMix) * alpha; constraint.mixTranslate += (translate - constraint.mixTranslate) * alpha;
} }
} }
} }

View File

@ -109,7 +109,7 @@ public class IkConstraint implements Updatable {
this.target = target; this.target = target;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */
public float getMix () { public float getMix () {
return mix; return mix;
} }

View File

@ -60,7 +60,7 @@ public class IkConstraintData extends ConstraintData {
this.target = target; this.target = target;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */
public float getMix () { public float getMix () {
return mix; return mix;
} }

View File

@ -50,7 +50,7 @@ public class PathConstraint implements Updatable {
final PathConstraintData data; final PathConstraintData data;
final Array<Bone> bones; final Array<Bone> bones;
Slot target; Slot target;
float position, spacing, rotateMix, translateMix; float position, spacing, mixRotate, mixTranslate;
boolean active; boolean active;
@ -68,8 +68,8 @@ public class PathConstraint implements Updatable {
target = skeleton.findSlot(data.target.name); target = skeleton.findSlot(data.target.name);
position = data.position; position = data.position;
spacing = data.spacing; spacing = data.spacing;
rotateMix = data.rotateMix; mixRotate = data.mixRotate;
translateMix = data.translateMix; mixTranslate = data.mixTranslate;
} }
/** Copy constructor. */ /** Copy constructor. */
@ -83,8 +83,8 @@ public class PathConstraint implements Updatable {
target = skeleton.slots.get(constraint.target.data.index); target = skeleton.slots.get(constraint.target.data.index);
position = constraint.position; position = constraint.position;
spacing = constraint.spacing; spacing = constraint.spacing;
rotateMix = constraint.rotateMix; mixRotate = constraint.mixRotate;
translateMix = constraint.translateMix; mixTranslate = constraint.mixTranslate;
} }
/** Applies the constraint to the constrained bones. */ /** Applies the constraint to the constrained bones. */
@ -92,8 +92,8 @@ public class PathConstraint implements Updatable {
Attachment attachment = target.attachment; Attachment attachment = target.attachment;
if (!(attachment instanceof PathAttachment)) return; if (!(attachment instanceof PathAttachment)) return;
float rotateMix = this.rotateMix, translateMix = this.translateMix; float mixRotate = this.mixRotate, mixTranslate = this.mixTranslate;
boolean translate = translateMix > 0, rotate = rotateMix > 0; boolean translate = mixTranslate > 0, rotate = mixRotate > 0;
if (!translate && !rotate) return; if (!translate && !rotate) return;
PathConstraintData data = this.data; PathConstraintData data = this.data;
@ -145,13 +145,13 @@ public class PathConstraint implements Updatable {
} }
for (int i = 0, p = 3; i < boneCount; i++, p += 3) { for (int i = 0, p = 3; i < boneCount; i++, p += 3) {
Bone bone = (Bone)bones[i]; Bone bone = (Bone)bones[i];
bone.worldX += (boneX - bone.worldX) * translateMix; bone.worldX += (boneX - bone.worldX) * mixTranslate;
bone.worldY += (boneY - bone.worldY) * translateMix; bone.worldY += (boneY - bone.worldY) * mixTranslate;
float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
if (scale) { if (scale) {
float length = lengths[i]; float length = lengths[i];
if (length >= epsilon) { if (length >= epsilon) {
float s = ((float)Math.sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1; float s = ((float)Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1;
bone.a *= s; bone.a *= s;
bone.c *= s; bone.c *= s;
} }
@ -171,15 +171,15 @@ public class PathConstraint implements Updatable {
cos = (float)Math.cos(r); cos = (float)Math.cos(r);
sin = (float)Math.sin(r); sin = (float)Math.sin(r);
float length = bone.data.length; float length = bone.data.length;
boneX += (length * (cos * a - sin * c) - dx) * rotateMix; boneX += (length * (cos * a - sin * c) - dx) * mixRotate;
boneY += (length * (sin * a + cos * c) - dy) * rotateMix; boneY += (length * (sin * a + cos * c) - dy) * mixRotate;
} else } else
r += offsetRotation; r += offsetRotation;
if (r > SpineUtils.PI) if (r > SpineUtils.PI)
r -= SpineUtils.PI2; r -= SpineUtils.PI2;
else if (r < -SpineUtils.PI) // else if (r < -SpineUtils.PI) //
r += SpineUtils.PI2; r += SpineUtils.PI2;
r *= rotateMix; r *= mixRotate;
cos = (float)Math.cos(r); cos = (float)Math.cos(r);
sin = (float)Math.sin(r); sin = (float)Math.sin(r);
bone.a = cos * a - sin * c; bone.a = cos * a - sin * c;
@ -464,22 +464,22 @@ public class PathConstraint implements Updatable {
this.spacing = spacing; this.spacing = spacing;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */
public float getRotateMix () { public float getMixRotate () {
return rotateMix; return mixRotate;
} }
public void setRotateMix (float rotateMix) { public void setMixRotate (float mixRotate) {
this.rotateMix = rotateMix; this.mixRotate = mixRotate;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation. */
public float getTranslateMix () { public float getMixTranslate () {
return translateMix; return mixTranslate;
} }
public void setTranslateMix (float translateMix) { public void setMixTranslate (float mixTranslate) {
this.translateMix = translateMix; this.mixTranslate = mixTranslate;
} }
/** The bones that will be modified by this path constraint. */ /** The bones that will be modified by this path constraint. */

View File

@ -41,7 +41,7 @@ public class PathConstraintData extends ConstraintData {
SpacingMode spacingMode; SpacingMode spacingMode;
RotateMode rotateMode; RotateMode rotateMode;
float offsetRotation; float offsetRotation;
float position, spacing, rotateMix, translateMix; float position, spacing, mixRotate, mixTranslate;
public PathConstraintData (String name) { public PathConstraintData (String name) {
super(name); super(name);
@ -119,22 +119,22 @@ public class PathConstraintData extends ConstraintData {
this.spacing = spacing; this.spacing = spacing;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */
public float getRotateMix () { public float getMixRotate () {
return rotateMix; return mixRotate;
} }
public void setRotateMix (float rotateMix) { public void setMixRotate (float mixRotate) {
this.rotateMix = rotateMix; this.mixRotate = mixRotate;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation. */
public float getTranslateMix () { public float getMixTranslate () {
return translateMix; return mixTranslate;
} }
public void setTranslateMix (float translateMix) { public void setMixTranslate (float mixTranslate) {
this.translateMix = translateMix; this.mixTranslate = mixTranslate;
} }
/** Controls how the first bone is positioned along the path. /** Controls how the first bone is positioned along the path.

View File

@ -405,10 +405,12 @@ public class Skeleton {
for (int i = 0, n = this.transformConstraints.size; i < n; i++) { for (int i = 0, n = this.transformConstraints.size; i < n; i++) {
TransformConstraint constraint = (TransformConstraint)transformConstraints[i]; TransformConstraint constraint = (TransformConstraint)transformConstraints[i];
TransformConstraintData data = constraint.data; TransformConstraintData data = constraint.data;
constraint.rotateMix = data.rotateMix; constraint.mixRotate = data.mixRotate;
constraint.translateMix = data.translateMix; constraint.mixX = data.mixX;
constraint.scaleMix = data.scaleMix; constraint.mixY = data.mixY;
constraint.shearMix = data.shearMix; constraint.mixScaleX = data.mixScaleX;
constraint.mixScaleY = data.mixScaleY;
constraint.mixShearY = data.mixShearY;
} }
Object[] pathConstraints = this.pathConstraints.items; Object[] pathConstraints = this.pathConstraints.items;
@ -417,8 +419,8 @@ public class Skeleton {
PathConstraintData data = constraint.data; PathConstraintData data = constraint.data;
constraint.position = data.position; constraint.position = data.position;
constraint.spacing = data.spacing; constraint.spacing = data.spacing;
constraint.rotateMix = data.rotateMix; constraint.mixRotate = data.mixRotate;
constraint.translateMix = data.translateMix; constraint.mixTranslate = data.mixTranslate;
} }
} }

View File

@ -239,10 +239,12 @@ public class SkeletonBinary extends SkeletonLoader {
data.offsetScaleX = input.readFloat(); data.offsetScaleX = input.readFloat();
data.offsetScaleY = input.readFloat(); data.offsetScaleY = input.readFloat();
data.offsetShearY = input.readFloat(); data.offsetShearY = input.readFloat();
data.rotateMix = input.readFloat(); data.mixRotate = input.readFloat();
data.translateMix = input.readFloat(); data.mixX = input.readFloat();
data.scaleMix = input.readFloat(); data.mixY = input.readFloat();
data.shearMix = input.readFloat(); data.mixScaleX = input.readFloat();
data.mixScaleY = input.readFloat();
data.mixShearY = input.readFloat();
o[i] = data; o[i] = data;
} }
@ -264,8 +266,8 @@ public class SkeletonBinary extends SkeletonLoader {
if (data.positionMode == PositionMode.fixed) data.position *= scale; if (data.positionMode == PositionMode.fixed) data.position *= scale;
data.spacing = input.readFloat(); data.spacing = input.readFloat();
if (data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed) data.spacing *= scale; if (data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed) data.spacing *= scale;
data.rotateMix = input.readFloat(); data.mixRotate = input.readFloat();
data.translateMix = input.readFloat(); data.mixTranslate = input.readFloat();
o[i] = data; o[i] = data;
} }
@ -810,28 +812,32 @@ public class SkeletonBinary extends SkeletonLoader {
for (int i = 0, n = input.readInt(true); i < n; i++) { for (int i = 0, n = input.readInt(true); i < n; i++) {
int index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1; int index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1;
TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index); TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index);
float time = input.readFloat(), rotateMix = input.readFloat(), translateMix = input.readFloat(), float time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat(),
scaleMix = input.readFloat(), shearMix = input.readFloat(); mixScaleX = input.readFloat(), mixScaleY = input.readFloat(), mixShearY = input.readFloat();
for (int frame = 0, bezier = 0;; frame++) { for (int frame = 0, bezier = 0;; frame++) {
timeline.setFrame(frame, time, rotateMix, translateMix, scaleMix, shearMix); timeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
if (frame == frameLast) break; if (frame == frameLast) break;
float time2 = input.readFloat(), rotateMix2 = input.readFloat(), translateMix2 = input.readFloat(), float time2 = input.readFloat(), mixRotate2 = input.readFloat(), mixX2 = input.readFloat(), mixY2 = input.readFloat(),
scaleMix2 = input.readFloat(), shearMix2 = input.readFloat(); mixScaleX2 = input.readFloat(), mixScaleY2 = input.readFloat(), mixShearY2 = input.readFloat();
switch (input.readByte()) { switch (input.readByte()) {
case CURVE_STEPPED: case CURVE_STEPPED:
timeline.setStepped(frame); timeline.setStepped(frame);
break; break;
case CURVE_BEZIER: case CURVE_BEZIER:
setBezier(input, timeline, bezier++, frame, 0, time, time2, rotateMix, rotateMix2, 1); setBezier(input, timeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
setBezier(input, timeline, bezier++, frame, 1, time, time2, translateMix, translateMix2, 1); setBezier(input, timeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
setBezier(input, timeline, bezier++, frame, 2, time, time2, scaleMix, scaleMix2, 1); setBezier(input, timeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
setBezier(input, timeline, bezier++, frame, 3, time, time2, shearMix, shearMix2, 1); setBezier(input, timeline, bezier++, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
setBezier(input, timeline, bezier++, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
setBezier(input, timeline, bezier++, frame, 5, time, time2, mixShearY, mixShearY2, 1);
} }
time = time2; time = time2;
rotateMix = rotateMix2; mixRotate = mixRotate2;
translateMix = translateMix2; mixX = mixX2;
scaleMix = scaleMix2; mixY = mixY2;
shearMix = shearMix2; mixScaleX = mixScaleX2;
mixScaleY = mixScaleY2;
mixShearY = mixShearY2;
} }
timelines.add(timeline); timelines.add(timeline);
} }

View File

@ -57,7 +57,7 @@ public class SkeletonBounds {
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
Array<BoundingBoxAttachment> boundingBoxes = this.boundingBoxes; Array<BoundingBoxAttachment> boundingBoxes = this.boundingBoxes;
Array<FloatArray> polygons = this.polygons; Array<FloatArray> polygons = this.polygons;
Object[] slots = skeleton.slots.items; Object[] slots = skeleton.slots.items;
int slotCount = skeleton.slots.size; int slotCount = skeleton.slots.size;
boundingBoxes.clear(); boundingBoxes.clear();

View File

@ -226,10 +226,12 @@ public class SkeletonJson extends SkeletonLoader {
data.offsetScaleY = constraintMap.getFloat("scaleY", 0); data.offsetScaleY = constraintMap.getFloat("scaleY", 0);
data.offsetShearY = constraintMap.getFloat("shearY", 0); data.offsetShearY = constraintMap.getFloat("shearY", 0);
data.rotateMix = constraintMap.getFloat("rotateMix", 1); data.mixRotate = constraintMap.getFloat("mixRotate", 1);
data.translateMix = constraintMap.getFloat("translateMix", 1); data.mixX = constraintMap.getFloat("mixX", 1);
data.scaleMix = constraintMap.getFloat("scaleMix", 1); data.mixY = constraintMap.getFloat("mixY", data.mixX);
data.shearMix = constraintMap.getFloat("shearMix", 1); data.mixScaleX = constraintMap.getFloat("mixScaleX", 1);
data.mixScaleY = constraintMap.getFloat("mixScaleY", data.mixScaleX);
data.mixShearY = constraintMap.getFloat("mixShearY", 1);
skeletonData.transformConstraints.add(data); skeletonData.transformConstraints.add(data);
} }
@ -258,8 +260,8 @@ public class SkeletonJson extends SkeletonLoader {
if (data.positionMode == PositionMode.fixed) data.position *= scale; if (data.positionMode == PositionMode.fixed) data.position *= scale;
data.spacing = constraintMap.getFloat("spacing", 0); data.spacing = constraintMap.getFloat("spacing", 0);
if (data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed) data.spacing *= scale; if (data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed) data.spacing *= scale;
data.rotateMix = constraintMap.getFloat("rotateMix", 1); data.mixRotate = constraintMap.getFloat("mixRotate", 1);
data.translateMix = constraintMap.getFloat("translateMix", 1); data.mixTranslate = constraintMap.getFloat("mixTranslate", 1);
skeletonData.pathConstraints.add(data); skeletonData.pathConstraints.add(data);
} }
@ -765,30 +767,36 @@ public class SkeletonJson extends SkeletonLoader {
TransformConstraintTimeline timeline = new TransformConstraintTimeline(timelineMap.size, timelineMap.size << 2, TransformConstraintTimeline timeline = new TransformConstraintTimeline(timelineMap.size, timelineMap.size << 2,
skeletonData.getTransformConstraints().indexOf(constraint, true)); skeletonData.getTransformConstraints().indexOf(constraint, true));
float time = keyMap.getFloat("time", 0); float time = keyMap.getFloat("time", 0);
float rotateMix = keyMap.getFloat("rotateMix", 1), translateMix = keyMap.getFloat("translateMix", 1); float mixRotate = keyMap.getFloat("mixRotate", 1), mixShearY = keyMap.getFloat("mixShearY", 1);
float scaleMix = keyMap.getFloat("scaleMix", 1), shearMix = keyMap.getFloat("shearMix", 1); float mixX = keyMap.getFloat("mixX", 1), mixY = keyMap.getFloat("mixY", mixX);
float mixScaleX = keyMap.getFloat("mixScaleX", 1), mixScaleY = keyMap.getFloat("mixScaleY", mixScaleX);
for (int frame = 0, bezier = 0;; frame++) { for (int frame = 0, bezier = 0;; frame++) {
timeline.setFrame(frame, time, rotateMix, translateMix, scaleMix, shearMix); timeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
JsonValue nextMap = keyMap.next; JsonValue nextMap = keyMap.next;
if (nextMap == null) { if (nextMap == null) {
timeline.shrink(bezier); timeline.shrink(bezier);
break; break;
} }
float time2 = nextMap.getFloat("time", 0); float time2 = nextMap.getFloat("time", 0);
float rotateMix2 = nextMap.getFloat("rotateMix", 1), translateMix2 = nextMap.getFloat("translateMix", 1); float mixRotate2 = nextMap.getFloat("mixRotate", 1), mixShearY2 = nextMap.getFloat("mixShearY", 1);
float scaleMix2 = nextMap.getFloat("scaleMix", 1), shearMix2 = nextMap.getFloat("shearMix", 1); float mixX2 = nextMap.getFloat("mixX", 1), mixY2 = nextMap.getFloat("mixY", mixX2);
float mixScaleX2 = nextMap.getFloat("mixScaleX", 1), mixScaleY2 = nextMap.getFloat("mixScaleY", mixScaleX2);
JsonValue curve = keyMap.get("curve"); JsonValue curve = keyMap.get("curve");
if (curve != null) { if (curve != null) {
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, rotateMix, rotateMix2, 1); bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, translateMix, translateMix2, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, scaleMix, scaleMix2, 1); bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, shearMix, shearMix2, 1); bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, mixShearY, mixShearY2, 1);
} }
time = time2; time = time2;
rotateMix = rotateMix2; mixRotate = mixRotate2;
translateMix = translateMix2; mixX = mixX2;
scaleMix = scaleMix2; mixY = mixY2;
shearMix = shearMix2; mixScaleX = mixScaleX2;
mixScaleY = mixScaleY2;
mixScaleX = mixScaleX2;
keyMap = nextMap; keyMap = nextMap;
} }
timelines.add(timeline); timelines.add(timeline);
@ -812,7 +820,7 @@ public class SkeletonJson extends SkeletonLoader {
data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1)); data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1));
} else if (timelineName.equals("mix")) { } else if (timelineName.equals("mix")) {
CurveTimeline2 timeline = new PathConstraintMixTimeline(timelineMap.size, timelineMap.size << 1, index); CurveTimeline2 timeline = new PathConstraintMixTimeline(timelineMap.size, timelineMap.size << 1, index);
timelines.add(readTimeline(keyMap, timeline, "rotateMix", "translateMix", 1, 1)); timelines.add(readTimeline(keyMap, timeline, "mixRotate", "mixTranslate", 1, 1));
} }
} }
} }

View File

@ -42,7 +42,7 @@ public class TransformConstraint implements Updatable {
final TransformConstraintData data; final TransformConstraintData data;
final Array<Bone> bones; final Array<Bone> bones;
Bone target; Bone target;
float rotateMix, translateMix, scaleMix, shearMix; float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY;
boolean active; boolean active;
final Vector2 temp = new Vector2(); final Vector2 temp = new Vector2();
@ -51,10 +51,12 @@ public class TransformConstraint implements Updatable {
if (data == null) throw new IllegalArgumentException("data cannot be null."); if (data == null) throw new IllegalArgumentException("data cannot be null.");
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
this.data = data; this.data = data;
rotateMix = data.rotateMix; mixRotate = data.mixRotate;
translateMix = data.translateMix; mixX = data.mixX;
scaleMix = data.scaleMix; mixY = data.mixY;
shearMix = data.shearMix; mixScaleX = data.mixScaleX;
mixScaleY = data.mixScaleY;
mixShearY = data.mixShearY;
bones = new Array(data.bones.size); bones = new Array(data.bones.size);
for (BoneData boneData : data.bones) for (BoneData boneData : data.bones)
bones.add(skeleton.findBone(boneData.name)); bones.add(skeleton.findBone(boneData.name));
@ -70,15 +72,17 @@ public class TransformConstraint implements Updatable {
for (Bone bone : constraint.bones) for (Bone bone : constraint.bones)
bones.add(skeleton.bones.get(bone.data.index)); bones.add(skeleton.bones.get(bone.data.index));
target = skeleton.bones.get(constraint.target.data.index); target = skeleton.bones.get(constraint.target.data.index);
rotateMix = constraint.rotateMix; mixRotate = constraint.mixRotate;
translateMix = constraint.translateMix; mixX = constraint.mixX;
scaleMix = constraint.scaleMix; mixY = constraint.mixY;
shearMix = constraint.shearMix; mixScaleX = constraint.mixScaleX;
mixScaleY = constraint.mixScaleY;
mixShearY = constraint.mixShearY;
} }
/** Applies the constraint to the constrained bones. */ /** Applies the constraint to the constrained bones. */
public void update () { public void update () {
if (rotateMix == 0 && translateMix == 0 && scaleMix == 0 && shearMix == 0) return; if (mixRotate == 0 && mixX == 0 && mixY == 0 && mixScaleX == 0 && mixScaleX == 0 && mixShearY == 0) return;
if (data.local) { if (data.local) {
if (data.relative) if (data.relative)
applyRelativeLocal(); applyRelativeLocal();
@ -93,22 +97,27 @@ public class TransformConstraint implements Updatable {
} }
private void applyAbsoluteWorld () { private void applyAbsoluteWorld () {
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
boolean translate = mixX != 0 || mixY != 0;
Bone target = this.target; Bone target = this.target;
float ta = target.a, tb = target.b, tc = target.c, td = target.d; float ta = target.a, tb = target.b, tc = target.c, td = target.d;
float degRadReflect = ta * td - tb * tc > 0 ? degRad : -degRad; float degRadReflect = ta * td - tb * tc > 0 ? degRad : -degRad;
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect; float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
Object[] bones = this.bones.items; Object[] bones = this.bones.items;
for (int i = 0, n = this.bones.size; i < n; i++) { for (int i = 0, n = this.bones.size; i < n; i++) {
Bone bone = (Bone)bones[i]; Bone bone = (Bone)bones[i];
if (rotateMix != 0) { if (mixRotate != 0) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d; float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float r = atan2(tc, ta) - atan2(c, a) + offsetRotation; float r = atan2(tc, ta) - atan2(c, a) + offsetRotation;
if (r > PI) if (r > PI)
r -= PI2; r -= PI2;
else if (r < -PI) r += PI2; else if (r < -PI) //
r *= rotateMix; r += PI2;
r *= mixRotate;
float cos = cos(r), sin = sin(r); float cos = cos(r), sin = sin(r);
bone.a = cos * a - sin * c; bone.a = cos * a - sin * c;
bone.b = cos * b - sin * d; bone.b = cos * b - sin * d;
@ -116,32 +125,35 @@ public class TransformConstraint implements Updatable {
bone.d = sin * b + cos * d; bone.d = sin * b + cos * d;
} }
if (translateMix != 0) { if (translate) {
Vector2 temp = this.temp; Vector2 temp = this.temp;
target.localToWorld(temp.set(data.offsetX, data.offsetY)); target.localToWorld(temp.set(data.offsetX, data.offsetY));
bone.worldX += (temp.x - bone.worldX) * translateMix; bone.worldX += (temp.x - bone.worldX) * mixX;
bone.worldY += (temp.y - bone.worldY) * translateMix; bone.worldY += (temp.y - bone.worldY) * mixY;
} }
if (scaleMix > 0) { if (mixScaleX != 0) {
float s = (float)Math.sqrt(bone.a * bone.a + bone.c * bone.c); float s = (float)Math.sqrt(bone.a * bone.a + bone.c * bone.c);
if (s != 0) s = (s + ((float)Math.sqrt(ta * ta + tc * tc) - s + data.offsetScaleX) * scaleMix) / s; if (s != 0) s = (s + ((float)Math.sqrt(ta * ta + tc * tc) - s + data.offsetScaleX) * mixScaleX) / s;
bone.a *= s; bone.a *= s;
bone.c *= s; bone.c *= s;
s = (float)Math.sqrt(bone.b * bone.b + bone.d * bone.d); }
if (s != 0) s = (s + ((float)Math.sqrt(tb * tb + td * td) - s + data.offsetScaleY) * scaleMix) / s; if (mixScaleY != 0) {
float s = (float)Math.sqrt(bone.b * bone.b + bone.d * bone.d);
if (s != 0) s = (s + ((float)Math.sqrt(tb * tb + td * td) - s + data.offsetScaleY) * mixScaleY) / s;
bone.b *= s; bone.b *= s;
bone.d *= s; bone.d *= s;
} }
if (shearMix > 0) { if (mixShearY > 0) {
float b = bone.b, d = bone.d; float b = bone.b, d = bone.d;
float by = atan2(d, b); float by = atan2(d, b);
float r = atan2(td, tb) - atan2(tc, ta) - (by - atan2(bone.c, bone.a)); float r = atan2(td, tb) - atan2(tc, ta) - (by - atan2(bone.c, bone.a));
if (r > PI) if (r > PI)
r -= PI2; r -= PI2;
else if (r < -PI) r += PI2; else if (r < -PI) //
r = by + (r + offsetShearY) * shearMix; r += PI2;
r = by + (r + offsetShearY) * mixShearY;
float s = (float)Math.sqrt(b * b + d * d); float s = (float)Math.sqrt(b * b + d * d);
bone.b = cos(r) * s; bone.b = cos(r) * s;
bone.d = sin(r) * s; bone.d = sin(r) * s;
@ -152,22 +164,27 @@ public class TransformConstraint implements Updatable {
} }
private void applyRelativeWorld () { private void applyRelativeWorld () {
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
boolean translate = mixX != 0 || mixY != 0;
Bone target = this.target; Bone target = this.target;
float ta = target.a, tb = target.b, tc = target.c, td = target.d; float ta = target.a, tb = target.b, tc = target.c, td = target.d;
float degRadReflect = ta * td - tb * tc > 0 ? degRad : -degRad; float degRadReflect = ta * td - tb * tc > 0 ? degRad : -degRad;
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect; float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
Object[] bones = this.bones.items; Object[] bones = this.bones.items;
for (int i = 0, n = this.bones.size; i < n; i++) { for (int i = 0, n = this.bones.size; i < n; i++) {
Bone bone = (Bone)bones[i]; Bone bone = (Bone)bones[i];
if (rotateMix != 0) { if (mixRotate != 0) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d; float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float r = atan2(tc, ta) + offsetRotation; float r = atan2(tc, ta) + offsetRotation;
if (r > PI) if (r > PI)
r -= PI2; r -= PI2;
else if (r < -PI) r += PI2; else if (r < -PI) //
r *= rotateMix; r += PI2;
r *= mixRotate;
float cos = cos(r), sin = sin(r); float cos = cos(r), sin = sin(r);
bone.a = cos * a - sin * c; bone.a = cos * a - sin * c;
bone.b = cos * b - sin * d; bone.b = cos * b - sin * d;
@ -175,29 +192,32 @@ public class TransformConstraint implements Updatable {
bone.d = sin * b + cos * d; bone.d = sin * b + cos * d;
} }
if (translateMix != 0) { if (translate) {
Vector2 temp = this.temp; Vector2 temp = this.temp;
target.localToWorld(temp.set(data.offsetX, data.offsetY)); target.localToWorld(temp.set(data.offsetX, data.offsetY));
bone.worldX += temp.x * translateMix; bone.worldX += temp.x * mixX;
bone.worldY += temp.y * translateMix; bone.worldY += temp.y * mixY;
} }
if (scaleMix > 0) { if (mixScaleX != 0) {
float s = ((float)Math.sqrt(ta * ta + tc * tc) - 1 + data.offsetScaleX) * scaleMix + 1; float s = ((float)Math.sqrt(ta * ta + tc * tc) - 1 + data.offsetScaleX) * mixScaleX + 1;
bone.a *= s; bone.a *= s;
bone.c *= s; bone.c *= s;
s = ((float)Math.sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * scaleMix + 1; }
if (mixScaleY != 0) {
float s = ((float)Math.sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * mixScaleY + 1;
bone.b *= s; bone.b *= s;
bone.d *= s; bone.d *= s;
} }
if (shearMix > 0) { if (mixShearY > 0) {
float r = atan2(td, tb) - atan2(tc, ta); float r = atan2(td, tb) - atan2(tc, ta);
if (r > PI) if (r > PI)
r -= PI2; r -= PI2;
else if (r < -PI) r += PI2; else if (r < -PI) //
r += PI2;
float b = bone.b, d = bone.d; float b = bone.b, d = bone.d;
r = atan2(d, b) + (r - PI / 2 + offsetShearY) * shearMix; r = atan2(d, b) + (r - PI / 2 + offsetShearY) * mixShearY;
float s = (float)Math.sqrt(b * b + d * d); float s = (float)Math.sqrt(b * b + d * d);
bone.b = cos(r) * s; bone.b = cos(r) * s;
bone.d = sin(r) * s; bone.d = sin(r) * s;
@ -208,38 +228,39 @@ public class TransformConstraint implements Updatable {
} }
private void applyAbsoluteLocal () { private void applyAbsoluteLocal () {
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
Bone target = this.target; Bone target = this.target;
if (!target.appliedValid) target.updateAppliedTransform(); if (!target.appliedValid) target.updateAppliedTransform();
Object[] bones = this.bones.items; Object[] bones = this.bones.items;
for (int i = 0, n = this.bones.size; i < n; i++) { for (int i = 0, n = this.bones.size; i < n; i++) {
Bone bone = (Bone)bones[i]; Bone bone = (Bone)bones[i];
if (!bone.appliedValid) bone.updateAppliedTransform(); if (!bone.appliedValid) bone.updateAppliedTransform();
float rotation = bone.arotation; float rotation = bone.arotation;
if (rotateMix != 0) { if (mixRotate != 0) {
float r = target.arotation - rotation + data.offsetRotation; float r = target.arotation - rotation + data.offsetRotation;
r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
rotation += r * rotateMix; rotation += r * mixRotate;
} }
float x = bone.ax, y = bone.ay; float x = bone.ax, y = bone.ay;
if (translateMix != 0) { x += (target.ax - x + data.offsetX) * mixX;
x += (target.ax - x + data.offsetX) * translateMix; y += (target.ay - y + data.offsetY) * mixY;
y += (target.ay - y + data.offsetY) * translateMix;
}
float scaleX = bone.ascaleX, scaleY = bone.ascaleY; float scaleX = bone.ascaleX, scaleY = bone.ascaleY;
if (scaleMix != 0) { if (mixScaleX != 0 && scaleX != 0)
if (scaleX != 0) scaleX = (scaleX + (target.ascaleX - scaleX + data.offsetScaleX) * scaleMix) / scaleX; scaleX = (scaleX + (target.ascaleX - scaleX + data.offsetScaleX) * mixScaleX) / scaleX;
if (scaleY != 0) scaleY = (scaleY + (target.ascaleY - scaleY + data.offsetScaleY) * scaleMix) / scaleY; if (mixScaleY != 0 && scaleY != 0)
} scaleY = (scaleY + (target.ascaleY - scaleY + data.offsetScaleY) * mixScaleY) / scaleY;
float shearY = bone.ashearY; float shearY = bone.ashearY;
if (shearMix != 0) { if (mixShearY != 0) {
float r = target.ashearY - shearY + data.offsetShearY; float r = target.ashearY - shearY + data.offsetShearY;
r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
shearY += r * shearMix; shearY += r * mixShearY;
} }
bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
@ -247,31 +268,23 @@ public class TransformConstraint implements Updatable {
} }
private void applyRelativeLocal () { private void applyRelativeLocal () {
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
Bone target = this.target; Bone target = this.target;
if (!target.appliedValid) target.updateAppliedTransform(); if (!target.appliedValid) target.updateAppliedTransform();
Object[] bones = this.bones.items; Object[] bones = this.bones.items;
for (int i = 0, n = this.bones.size; i < n; i++) { for (int i = 0, n = this.bones.size; i < n; i++) {
Bone bone = (Bone)bones[i]; Bone bone = (Bone)bones[i];
if (!bone.appliedValid) bone.updateAppliedTransform(); if (!bone.appliedValid) bone.updateAppliedTransform();
float rotation = bone.arotation; float rotation = bone.arotation + (target.arotation + data.offsetRotation) * mixRotate;
if (rotateMix != 0) rotation += (target.arotation + data.offsetRotation) * rotateMix; float x = bone.ax + (target.ax + data.offsetX) * mixX;
float y = bone.ay + (target.ay + data.offsetY) * mixY;
float x = bone.ax, y = bone.ay; float scaleX = (bone.ascaleX * ((target.ascaleX - 1 + data.offsetScaleX) * mixScaleX) + 1);
if (translateMix != 0) { float scaleY = (bone.ascaleY * ((target.ascaleY - 1 + data.offsetScaleY) * mixScaleY) + 1);
x += (target.ax + data.offsetX) * translateMix; float shearY = bone.ashearY + (target.ashearY + data.offsetShearY) * mixShearY;
y += (target.ay + data.offsetY) * translateMix;
}
float scaleX = bone.ascaleX, scaleY = bone.ascaleY;
if (scaleMix != 0) {
scaleX *= ((target.ascaleX - 1 + data.offsetScaleX) * scaleMix) + 1;
scaleY *= ((target.ascaleY - 1 + data.offsetScaleY) * scaleMix) + 1;
}
float shearY = bone.ashearY;
if (shearMix != 0) shearY += (target.ashearY + data.offsetShearY) * shearMix;
bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
} }
@ -292,40 +305,58 @@ public class TransformConstraint implements Updatable {
this.target = target; this.target = target;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */
public float getRotateMix () { public float getMixRotate () {
return rotateMix; return mixRotate;
} }
public void setRotateMix (float rotateMix) { public void setMixRotate (float mixRotate) {
this.rotateMix = rotateMix; this.mixRotate = mixRotate;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */
public float getTranslateMix () { public float getMixX () {
return translateMix; return mixX;
} }
public void setTranslateMix (float translateMix) { public void setMixX (float mixX) {
this.translateMix = translateMix; this.mixX = mixX;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained scales. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */
public float getScaleMix () { public float getMixY () {
return scaleMix; return mixY;
} }
public void setScaleMix (float scaleMix) { public void setMixY (float mixY) {
this.scaleMix = scaleMix; this.mixY = mixY;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained scales. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained scale X. */
public float getShearMix () { public float getMixScaleX () {
return shearMix; return mixScaleX;
} }
public void setShearMix (float shearMix) { public void setMixScaleX (float mixScaleX) {
this.shearMix = shearMix; this.mixScaleX = mixScaleX;
}
/** A percentage (0-1) that controls the mix between the constrained and unconstrained scale X. */
public float getMixScaleY () {
return mixScaleY;
}
public void setMixScaleY (float mixScaleY) {
this.mixScaleY = mixScaleY;
}
/** A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y. */
public float getMixShearY () {
return mixShearY;
}
public void setMixShearY (float mixShearY) {
this.mixShearY = mixShearY;
} }
public boolean isActive () { public boolean isActive () {

View File

@ -37,7 +37,7 @@ import com.badlogic.gdx.utils.Array;
public class TransformConstraintData extends ConstraintData { public class TransformConstraintData extends ConstraintData {
final Array<BoneData> bones = new Array(); final Array<BoneData> bones = new Array();
BoneData target; BoneData target;
float rotateMix, translateMix, scaleMix, shearMix; float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY;
float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
boolean relative, local; boolean relative, local;
@ -60,40 +60,58 @@ public class TransformConstraintData extends ConstraintData {
this.target = target; this.target = target;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */
public float getRotateMix () { public float getMixRotate () {
return rotateMix; return mixRotate;
} }
public void setRotateMix (float rotateMix) { public void setMixRotate (float mixRotate) {
this.rotateMix = rotateMix; this.mixRotate = mixRotate;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */
public float getTranslateMix () { public float getMixX () {
return translateMix; return mixX;
} }
public void setTranslateMix (float translateMix) { public void setMixX (float mixX) {
this.translateMix = translateMix; this.mixX = mixX;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained scales. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */
public float getScaleMix () { public float getMixY () {
return scaleMix; return mixY;
} }
public void setScaleMix (float scaleMix) { public void setMixY (float mixY) {
this.scaleMix = scaleMix; this.mixY = mixY;
} }
/** A percentage (0-1) that controls the mix between the constrained and unconstrained shears. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained scale X. */
public float getShearMix () { public float getMixScaleX () {
return shearMix; return mixScaleX;
} }
public void setShearMix (float shearMix) { public void setMixScaleX (float mixScaleX) {
this.shearMix = shearMix; this.mixScaleX = mixScaleX;
}
/** A percentage (0-1) that controls the mix between the constrained and unconstrained scale Y. */
public float getMixScaleY () {
return mixScaleY;
}
public void setMixScaleY (float mixScaleY) {
this.mixScaleY = mixScaleY;
}
/** A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y. */
public float getMixShearY () {
return mixShearY;
}
public void setMixShearY (float mixShearY) {
this.mixShearY = mixShearY;
} }
/** An offset added to the constrained bone rotation. */ /** An offset added to the constrained bone rotation. */

View File

@ -32,6 +32,7 @@ package com.esotericsoftware.spine.vertexeffects;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.esotericsoftware.spine.Skeleton; import com.esotericsoftware.spine.Skeleton;
import com.esotericsoftware.spine.SkeletonRenderer.VertexEffect; import com.esotericsoftware.spine.SkeletonRenderer.VertexEffect;

View File

@ -33,6 +33,7 @@ import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Interpolation; import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.esotericsoftware.spine.Skeleton; import com.esotericsoftware.spine.Skeleton;
import com.esotericsoftware.spine.SkeletonRenderer.VertexEffect; import com.esotericsoftware.spine.SkeletonRenderer.VertexEffect;
import com.esotericsoftware.spine.utils.SpineUtils; import com.esotericsoftware.spine.utils.SpineUtils;