mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-09 16:48:43 +08:00
[libgdx] Added bone transform inheritance timeline.
* Renamed TransformMode to Inherit. * Inherit has setup pose and skeleton instance values. * Skip 2-bone IK for non-normal inheritance.
This commit is contained in:
parent
c83a867868
commit
f4f22cd5a9
@ -39,6 +39,7 @@ import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
import com.badlogic.gdx.utils.ObjectSet;
|
||||
|
||||
import com.esotericsoftware.spine.BoneData.Inherit;
|
||||
import com.esotericsoftware.spine.attachments.Attachment;
|
||||
import com.esotericsoftware.spine.attachments.HasTextureRegion;
|
||||
import com.esotericsoftware.spine.attachments.Sequence;
|
||||
@ -173,7 +174,7 @@ public class Animation {
|
||||
}
|
||||
|
||||
static private enum Property {
|
||||
rotate, x, y, scaleX, scaleY, shearX, shearY, //
|
||||
rotate, x, y, scaleX, scaleY, shearX, shearY, inherit, //
|
||||
rgb, alpha, rgb2, //
|
||||
attachment, deform, //
|
||||
event, drawOrder, //
|
||||
@ -944,6 +945,50 @@ public class Animation {
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a bone's {@link Bone#getInherit()}. */
|
||||
static public class InheritTimeline extends Timeline implements BoneTimeline {
|
||||
static public final int ENTRIES = 2;
|
||||
static private final int INHERIT = 1;
|
||||
|
||||
final int boneIndex;
|
||||
|
||||
public InheritTimeline (int frameCount, int boneIndex) {
|
||||
super(frameCount, Property.inherit.ordinal() + "|" + boneIndex);
|
||||
this.boneIndex = boneIndex;
|
||||
}
|
||||
|
||||
public int getBoneIndex () {
|
||||
return boneIndex;
|
||||
}
|
||||
|
||||
public int getFrameEntries () {
|
||||
return ENTRIES;
|
||||
}
|
||||
|
||||
/** Sets the transform mode for the specified frame.
|
||||
* @param frame Between 0 and <code>frameCount</code>, inclusive.
|
||||
* @param time The frame time in seconds. */
|
||||
public void setFrame (int frame, float time, Inherit inherit) {
|
||||
frame *= ENTRIES;
|
||||
frames[frame] = time;
|
||||
frames[frame + INHERIT] = inherit.ordinal();
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) {
|
||||
if (blend == setup || blend == first) bone.inherit = bone.data.inherit;
|
||||
return;
|
||||
}
|
||||
bone.inherit = Inherit.values[(int)frames[search(frames, time, ENTRIES) + INHERIT]];
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a slot's {@link Slot#getColor()}. */
|
||||
static public class RGBATimeline extends CurveTimeline implements SlotTimeline {
|
||||
static public final int ENTRIES = 5;
|
||||
|
||||
@ -37,7 +37,7 @@ import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
import com.esotericsoftware.spine.BoneData.TransformMode;
|
||||
import com.esotericsoftware.spine.BoneData.Inherit;
|
||||
import com.esotericsoftware.spine.Skeleton.Physics;
|
||||
|
||||
/** Stores a bone's current pose.
|
||||
@ -54,6 +54,7 @@ public class Bone implements Updatable {
|
||||
float ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY;
|
||||
float a, b, worldX;
|
||||
float c, d, worldY;
|
||||
Inherit inherit;
|
||||
|
||||
boolean sorted, active;
|
||||
|
||||
@ -80,6 +81,7 @@ public class Bone implements Updatable {
|
||||
scaleY = bone.scaleY;
|
||||
shearX = bone.shearX;
|
||||
shearY = bone.shearY;
|
||||
inherit = bone.inherit;
|
||||
}
|
||||
|
||||
/** Computes the world transform using the parent bone and this bone's local applied transform. */
|
||||
@ -127,7 +129,7 @@ public class Bone implements Updatable {
|
||||
worldX = pa * x + pb * y + parent.worldX;
|
||||
worldY = pc * x + pd * y + parent.worldY;
|
||||
|
||||
switch (data.transformMode) {
|
||||
switch (inherit) {
|
||||
case normal: {
|
||||
float rx = (rotation + shearX) * degRad;
|
||||
float ry = (rotation + 90 + shearY) * degRad;
|
||||
@ -187,8 +189,7 @@ public class Bone implements Updatable {
|
||||
za *= s;
|
||||
zc *= s;
|
||||
s = (float)Math.sqrt(za * za + zc * zc);
|
||||
if (data.transformMode == TransformMode.noScale
|
||||
&& (pa * pd - pb * pc < 0) != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
if (inherit == Inherit.noScale && (pa * pd - pb * pc < 0) != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
rotation = PI / 2 + atan2(zc, za);
|
||||
float zb = cos(rotation) * s;
|
||||
float zd = sin(rotation) * s;
|
||||
@ -219,6 +220,7 @@ public class Bone implements Updatable {
|
||||
scaleY = data.scaleY;
|
||||
shearX = data.shearX;
|
||||
shearY = data.shearY;
|
||||
inherit = data.inherit;
|
||||
}
|
||||
|
||||
/** The bone's setup pose data. */
|
||||
@ -325,6 +327,16 @@ public class Bone implements Updatable {
|
||||
this.shearY = shearY;
|
||||
}
|
||||
|
||||
/** Controls how parent world transforms affect this bone. */
|
||||
public Inherit getInherit () {
|
||||
return inherit;
|
||||
}
|
||||
|
||||
public void setInherit (Inherit inherit) {
|
||||
if (inherit == null) throw new IllegalArgumentException("inherit cannot be null.");
|
||||
this.inherit = inherit;
|
||||
}
|
||||
|
||||
// -- Applied transform
|
||||
|
||||
/** The applied local x translation. */
|
||||
@ -420,13 +432,13 @@ public class Bone implements Updatable {
|
||||
ay = (dy * id - dx * ic);
|
||||
|
||||
float ra, rb, rc, rd;
|
||||
if (data.transformMode == TransformMode.onlyTranslation) {
|
||||
if (inherit == Inherit.onlyTranslation) {
|
||||
ra = a;
|
||||
rb = b;
|
||||
rc = c;
|
||||
rd = d;
|
||||
} else {
|
||||
switch (data.transformMode) {
|
||||
switch (inherit) {
|
||||
case noRotationOrReflection: {
|
||||
float s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
|
||||
float sa = pa / skeleton.scaleX;
|
||||
@ -448,7 +460,7 @@ public class Bone implements Updatable {
|
||||
pa *= s;
|
||||
pc *= s;
|
||||
s = (float)Math.sqrt(pa * pa + pc * pc);
|
||||
if (data.transformMode == TransformMode.noScale && pid < 0 != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
if (inherit == Inherit.noScale && pid < 0 != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
r = PI / 2 + atan2(pc, pa);
|
||||
pb = cos(r) * s;
|
||||
pd = sin(r) * s;
|
||||
|
||||
@ -41,7 +41,7 @@ public class BoneData {
|
||||
@Null final BoneData parent;
|
||||
float length;
|
||||
float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
|
||||
TransformMode transformMode = TransformMode.normal;
|
||||
Inherit inherit = Inherit.normal;
|
||||
boolean skinRequired;
|
||||
|
||||
// Nonessential.
|
||||
@ -169,14 +169,14 @@ public class BoneData {
|
||||
this.shearY = shearY;
|
||||
}
|
||||
|
||||
/** The transform mode for how parent world transforms affect this bone. */
|
||||
public TransformMode getTransformMode () {
|
||||
return transformMode;
|
||||
/** Determines how parent world transforms affect this bone. */
|
||||
public Inherit getInherit () {
|
||||
return inherit;
|
||||
}
|
||||
|
||||
public void setTransformMode (TransformMode transformMode) {
|
||||
if (transformMode == null) throw new IllegalArgumentException("transformMode cannot be null.");
|
||||
this.transformMode = transformMode;
|
||||
public void setInherit (Inherit inherit) {
|
||||
if (inherit == null) throw new IllegalArgumentException("inherit cannot be null.");
|
||||
this.inherit = inherit;
|
||||
}
|
||||
|
||||
/** When true, {@link Skeleton#updateWorldTransform(Physics)} only updates this bone if the {@link Skeleton#getSkin()} contains
|
||||
@ -220,9 +220,9 @@ public class BoneData {
|
||||
}
|
||||
|
||||
/** Determines how a bone inherits world transforms from parent bones. */
|
||||
static public enum TransformMode {
|
||||
static public enum Inherit {
|
||||
normal, onlyTranslation, noRotationOrReflection, noScale, noScaleOrReflection;
|
||||
|
||||
static public final TransformMode[] values = TransformMode.values();
|
||||
static public final Inherit[] values = Inherit.values();
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ import static com.esotericsoftware.spine.utils.SpineUtils.*;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
import com.esotericsoftware.spine.BoneData.Inherit;
|
||||
import com.esotericsoftware.spine.Skeleton.Physics;
|
||||
|
||||
/** Stores the current pose for an IK constraint. An IK constraint adjusts the rotation of 1 or 2 constrained bones so the tip of
|
||||
@ -189,7 +190,7 @@ public class IkConstraint implements Updatable {
|
||||
Bone p = bone.parent;
|
||||
float pa = p.a, pb = p.b, pc = p.c, pd = p.d;
|
||||
float rotationIK = -bone.ashearX - bone.arotation, tx, ty;
|
||||
switch (bone.data.transformMode) {
|
||||
switch (bone.inherit) {
|
||||
case onlyTranslation:
|
||||
tx = (targetX - bone.worldX) * Math.signum(bone.skeleton.scaleX);
|
||||
ty = (targetY - bone.worldY) * Math.signum(bone.skeleton.scaleY);
|
||||
@ -221,7 +222,7 @@ public class IkConstraint implements Updatable {
|
||||
rotationIK += 360;
|
||||
float sx = bone.ascaleX, sy = bone.ascaleY;
|
||||
if (compress || stretch) {
|
||||
switch (bone.data.transformMode) {
|
||||
switch (bone.inherit) {
|
||||
case noScale:
|
||||
case noScaleOrReflection:
|
||||
tx = targetX - bone.worldX;
|
||||
@ -246,6 +247,7 @@ public class IkConstraint implements Updatable {
|
||||
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 (parent.inherit != Inherit.normal || child.inherit != Inherit.normal) return;
|
||||
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, sx = psx, sy = psy, csx = child.ascaleX;
|
||||
int os1, os2, s2;
|
||||
if (psx < 0) {
|
||||
|
||||
@ -52,6 +52,7 @@ import com.esotericsoftware.spine.Animation.DeformTimeline;
|
||||
import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
|
||||
import com.esotericsoftware.spine.Animation.EventTimeline;
|
||||
import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
|
||||
import com.esotericsoftware.spine.Animation.InheritTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintMixTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintPositionTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintSpacingTimeline;
|
||||
@ -80,7 +81,7 @@ import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
|
||||
import com.esotericsoftware.spine.Animation.TranslateTimeline;
|
||||
import com.esotericsoftware.spine.Animation.TranslateXTimeline;
|
||||
import com.esotericsoftware.spine.Animation.TranslateYTimeline;
|
||||
import com.esotericsoftware.spine.BoneData.TransformMode;
|
||||
import com.esotericsoftware.spine.BoneData.Inherit;
|
||||
import com.esotericsoftware.spine.PathConstraintData.PositionMode;
|
||||
import com.esotericsoftware.spine.PathConstraintData.RotateMode;
|
||||
import com.esotericsoftware.spine.PathConstraintData.SpacingMode;
|
||||
@ -113,6 +114,7 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
static public final int BONE_SHEAR = 7;
|
||||
static public final int BONE_SHEARX = 8;
|
||||
static public final int BONE_SHEARY = 9;
|
||||
static public final int BONE_INHERIT = 10;
|
||||
|
||||
static public final int SLOT_ATTACHMENT = 0;
|
||||
static public final int SLOT_RGBA = 1;
|
||||
@ -209,7 +211,7 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
data.shearX = input.readFloat();
|
||||
data.shearY = input.readFloat();
|
||||
data.length = input.readFloat() * scale;
|
||||
data.transformMode = TransformMode.values[input.readInt(true)];
|
||||
data.inherit = Inherit.values[input.readByte()];
|
||||
data.skinRequired = input.readBoolean();
|
||||
if (nonessential) {
|
||||
Color.rgba8888ToColor(data.color, input.readInt());
|
||||
@ -844,7 +846,15 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
||||
int boneIndex = input.readInt(true);
|
||||
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
|
||||
int type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true);
|
||||
int type = input.readByte(), frameCount = input.readInt(true);
|
||||
if (type == BONE_INHERIT) {
|
||||
InheritTimeline timeline = new InheritTimeline(frameCount, boneIndex);
|
||||
for (int frame = 0; frame < frameCount; frame++)
|
||||
timeline.setFrame(frame, input.readFloat(), Inherit.values[input.readByte()]);
|
||||
timelines.add(timeline);
|
||||
continue;
|
||||
}
|
||||
int bezierCount = input.readInt(true);
|
||||
switch (type) {
|
||||
case BONE_ROTATE:
|
||||
readTimeline(input, timelines, new RotateTimeline(frameCount, bezierCount, boneIndex), 1);
|
||||
|
||||
@ -53,6 +53,7 @@ import com.esotericsoftware.spine.Animation.DeformTimeline;
|
||||
import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
|
||||
import com.esotericsoftware.spine.Animation.EventTimeline;
|
||||
import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
|
||||
import com.esotericsoftware.spine.Animation.InheritTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintMixTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintPositionTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintSpacingTimeline;
|
||||
@ -81,7 +82,7 @@ import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
|
||||
import com.esotericsoftware.spine.Animation.TranslateTimeline;
|
||||
import com.esotericsoftware.spine.Animation.TranslateXTimeline;
|
||||
import com.esotericsoftware.spine.Animation.TranslateYTimeline;
|
||||
import com.esotericsoftware.spine.BoneData.TransformMode;
|
||||
import com.esotericsoftware.spine.BoneData.Inherit;
|
||||
import com.esotericsoftware.spine.PathConstraintData.PositionMode;
|
||||
import com.esotericsoftware.spine.PathConstraintData.RotateMode;
|
||||
import com.esotericsoftware.spine.PathConstraintData.SpacingMode;
|
||||
@ -166,7 +167,7 @@ public class SkeletonJson extends SkeletonLoader {
|
||||
data.scaleY = boneMap.getFloat("scaleY", 1);
|
||||
data.shearX = boneMap.getFloat("shearX", 0);
|
||||
data.shearY = boneMap.getFloat("shearY", 0);
|
||||
data.transformMode = TransformMode.valueOf(boneMap.getString("transform", TransformMode.normal.name()));
|
||||
data.inherit = Inherit.valueOf(boneMap.getString("inherit", Inherit.normal.name()));
|
||||
data.skinRequired = boneMap.getBoolean("skin", false);
|
||||
|
||||
String color = boneMap.getString("color", null);
|
||||
@ -812,7 +813,14 @@ public class SkeletonJson extends SkeletonLoader {
|
||||
timelines.add(readTimeline(keyMap, new ShearXTimeline(frames, frames, bone.index), 0, 1));
|
||||
else if (timelineName.equals("sheary"))
|
||||
timelines.add(readTimeline(keyMap, new ShearYTimeline(frames, frames, bone.index), 0, 1));
|
||||
else
|
||||
else if (timelineName.equals("inherit")) {
|
||||
InheritTimeline timeline = new InheritTimeline(frames, bone.index);
|
||||
for (int frame = 0; keyMap != null; keyMap = keyMap.next, frame++) {
|
||||
float time = keyMap.getFloat("time", 0);
|
||||
timeline.setFrame(frame, time, Inherit.valueOf(keyMap.getString("inherit", Inherit.normal.name())));
|
||||
}
|
||||
timelines.add(timeline);
|
||||
} else
|
||||
throw new RuntimeException("Invalid timeline type for a bone: " + timelineName + " (" + boneMap.name + ")");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user