mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
Constraint order WIP.
This commit is contained in:
parent
2dee5d6103
commit
1459abcb06
@ -44,7 +44,8 @@ public class Bone implements Updatable {
|
|||||||
final Bone parent;
|
final Bone parent;
|
||||||
final Array<Bone> children = new Array();
|
final Array<Bone> children = new Array();
|
||||||
float x, y, rotation, scaleX, scaleY, shearX, shearY;
|
float x, y, rotation, scaleX, scaleY, shearX, shearY;
|
||||||
float appliedRotation;
|
float ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY;
|
||||||
|
boolean appliedValid; // BOZO! - Use everywhere.
|
||||||
|
|
||||||
float a, b, worldX;
|
float a, b, worldX;
|
||||||
float c, d, worldY;
|
float c, d, worldY;
|
||||||
@ -91,7 +92,13 @@ public class Bone implements Updatable {
|
|||||||
|
|
||||||
/** Computes the world transform using the parent bone and the specified local transform. */
|
/** Computes the world transform using the parent bone and the specified local transform. */
|
||||||
public void updateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
|
public void updateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
|
||||||
appliedRotation = rotation;
|
ax = x;
|
||||||
|
ay = y;
|
||||||
|
arotation = rotation;
|
||||||
|
ascaleX = scaleX;
|
||||||
|
ascaleY = scaleY;
|
||||||
|
ashearX = shearX;
|
||||||
|
ashearY = shearY;
|
||||||
|
|
||||||
float rotationY = rotation + 90 + shearY;
|
float rotationY = rotation + 90 + shearY;
|
||||||
float la = cosDeg(rotation + shearX) * scaleX, lb = cosDeg(rotationY) * scaleY;
|
float la = cosDeg(rotation + shearX) * scaleX, lb = cosDeg(rotationY) * scaleY;
|
||||||
@ -160,8 +167,8 @@ public class Bone implements Updatable {
|
|||||||
pc = 0;
|
pc = 0;
|
||||||
pd = 1;
|
pd = 1;
|
||||||
do {
|
do {
|
||||||
float cos = cosDeg(parent.appliedRotation), sin = sinDeg(parent.appliedRotation);
|
float cos = cosDeg(parent.arotation), sin = sinDeg(parent.arotation);
|
||||||
float psx = parent.scaleX, psy = parent.scaleY;
|
float psx = parent.ascaleX, psy = parent.ascaleY;
|
||||||
float za = cos * psx, zb = sin * psy, zc = sin * psx, zd = cos * psy;
|
float za = cos * psx, zb = sin * psy, zc = sin * psx, zd = cos * psy;
|
||||||
float temp = pa * za + pb * zc;
|
float temp = pa * za + pb * zc;
|
||||||
pb = pb * zd - pa * zb;
|
pb = pb * zd - pa * zb;
|
||||||
@ -170,7 +177,8 @@ public class Bone implements Updatable {
|
|||||||
pd = pd * zd - pc * zb;
|
pd = pd * zd - pc * zb;
|
||||||
pc = temp;
|
pc = temp;
|
||||||
|
|
||||||
if (psx >= 0) sin = -sin;
|
if (psx >= 0) // BOZO! - Why? Should always do this? Fix in new code?
|
||||||
|
sin = -sin;
|
||||||
temp = pa * cos + pb * sin;
|
temp = pa * cos + pb * sin;
|
||||||
pb = pb * cos - pa * sin;
|
pb = pb * cos - pa * sin;
|
||||||
pa = temp;
|
pa = temp;
|
||||||
@ -350,14 +358,14 @@ public class Bone implements Updatable {
|
|||||||
|
|
||||||
public float worldToLocalRotationX () {
|
public float worldToLocalRotationX () {
|
||||||
Bone parent = this.parent;
|
Bone parent = this.parent;
|
||||||
if (parent == null) return rotation;
|
if (parent == null) return arotation;
|
||||||
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c;
|
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c;
|
||||||
return atan2(pa * c - pc * a, pd * a - pb * c) * radDeg;
|
return atan2(pa * c - pc * a, pd * a - pb * c) * radDeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float worldToLocalRotationY () {
|
public float worldToLocalRotationY () {
|
||||||
Bone parent = this.parent;
|
Bone parent = this.parent;
|
||||||
if (parent == null) return rotation;
|
if (parent == null) return arotation;
|
||||||
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d;
|
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d;
|
||||||
return atan2(pa * d - pc * b, pd * b - pb * d) * radDeg;
|
return atan2(pa * d - pc * b, pd * b - pb * d) * radDeg;
|
||||||
}
|
}
|
||||||
@ -371,29 +379,27 @@ public class Bone implements Updatable {
|
|||||||
this.d = sin * b + cos * d;
|
this.d = sin * b + cos * d;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Computes the local transform from the world transform. This can be useful to perform processing on the local transform
|
/** Computes the individual applied transform values from the world transform. This can be useful to perform processing using
|
||||||
* after the world transform has been modified directly (eg, by a constraint).
|
* the applied transform after the world transform has been modified directly (eg, by a constraint).
|
||||||
* <p>
|
* <p>
|
||||||
* Some redundant information is lost by the world transform, such as -1,-1 scale versus 180 rotation. The computed local
|
* Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. */
|
||||||
* transform values may differ from the original values but are functionally the same. */
|
public void updateAppliedTransform () {
|
||||||
public void updateLocalTransform () {
|
|
||||||
Bone parent = this.parent;
|
Bone parent = this.parent;
|
||||||
if (parent == null) {
|
if (parent == null) {
|
||||||
x = worldX;
|
ax = worldX;
|
||||||
y = worldY;
|
ay = worldY;
|
||||||
rotation = atan2(c, a) * radDeg;
|
arotation = atan2(c, a) * radDeg;
|
||||||
scaleX = (float)Math.sqrt(a * a + c * c);
|
ascaleX = (float)Math.sqrt(a * a + c * c);
|
||||||
scaleY = (float)Math.sqrt(b * b + d * d);
|
ascaleY = (float)Math.sqrt(b * b + d * d);
|
||||||
float det = a * d - b * c;
|
ashearX = 0;
|
||||||
shearX = 0;
|
ashearY = atan2(a * b + c * d, a * d - b * c) * radDeg;
|
||||||
shearY = atan2(a * b + c * d, det) * radDeg;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||||
float pid = 1 / (pa * pd - pb * pc);
|
float pid = 1 / (pa * pd - pb * pc);
|
||||||
float dx = worldX - parent.worldX, dy = worldY - parent.worldY;
|
float dx = worldX - parent.worldX, dy = worldY - parent.worldY;
|
||||||
x = (dx * pd * pid - dy * pb * pid);
|
ax = (dx * pd * pid - dy * pb * pid);
|
||||||
y = (dy * pa * pid - dx * pc * pid);
|
ay = (dy * pa * pid - dx * pc * pid);
|
||||||
float ia = pid * pd;
|
float ia = pid * pd;
|
||||||
float id = pid * pa;
|
float id = pid * pa;
|
||||||
float ib = pid * pb;
|
float ib = pid * pb;
|
||||||
@ -402,20 +408,19 @@ public class Bone implements Updatable {
|
|||||||
float rb = ia * b - ib * d;
|
float rb = ia * b - ib * d;
|
||||||
float rc = id * c - ic * a;
|
float rc = id * c - ic * a;
|
||||||
float rd = id * d - ic * b;
|
float rd = id * d - ic * b;
|
||||||
shearX = 0;
|
ashearX = 0;
|
||||||
scaleX = (float)Math.sqrt(ra * ra + rc * rc);
|
ascaleX = (float)Math.sqrt(ra * ra + rc * rc);
|
||||||
if (scaleX > 0.0001f) {
|
if (ascaleX > 0.0001f) {
|
||||||
float det = ra * rd - rb * rc;
|
float det = ra * rd - rb * rc;
|
||||||
scaleY = det / scaleX;
|
ascaleY = det / ascaleX;
|
||||||
shearY = atan2(ra * rb + rc * rd, det) * radDeg;
|
ashearY = atan2(ra * rb + rc * rd, det) * radDeg;
|
||||||
rotation = atan2(rc, ra) * radDeg;
|
arotation = atan2(rc, ra) * radDeg;
|
||||||
} else {
|
} else {
|
||||||
scaleX = 0;
|
ascaleX = 0;
|
||||||
scaleY = (float)Math.sqrt(rb * rb + rd * rd);
|
ascaleY = (float)Math.sqrt(rb * rb + rd * rd);
|
||||||
shearY = 0;
|
ashearY = 0;
|
||||||
rotation = 90 - atan2(rd, rb) * radDeg;
|
arotation = 90 - atan2(rd, rb) * radDeg;
|
||||||
}
|
}
|
||||||
appliedRotation = rotation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Matrix3 getWorldTransform (Matrix3 worldTransform) {
|
public Matrix3 getWorldTransform (Matrix3 worldTransform) {
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
package com.esotericsoftware.spine;
|
||||||
|
|
||||||
|
public interface Constraint extends Updatable {
|
||||||
|
public int getOrder ();
|
||||||
|
}
|
||||||
@ -35,15 +35,13 @@ import static com.badlogic.gdx.math.MathUtils.*;
|
|||||||
|
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
|
||||||
public class IkConstraint implements Updatable {
|
public class IkConstraint implements Constraint {
|
||||||
final IkConstraintData data;
|
final IkConstraintData data;
|
||||||
final Array<Bone> bones;
|
final Array<Bone> bones;
|
||||||
Bone target;
|
Bone target;
|
||||||
float mix = 1;
|
float mix = 1;
|
||||||
int bendDirection;
|
int bendDirection;
|
||||||
|
|
||||||
int level;
|
|
||||||
|
|
||||||
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
|
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
|
||||||
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.");
|
||||||
@ -87,6 +85,10 @@ public class IkConstraint implements Updatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getOrder () {
|
||||||
|
return data.order;
|
||||||
|
}
|
||||||
|
|
||||||
public Array<Bone> getBones () {
|
public Array<Bone> getBones () {
|
||||||
return bones;
|
return bones;
|
||||||
}
|
}
|
||||||
@ -126,17 +128,17 @@ public class IkConstraint implements Updatable {
|
|||||||
/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world
|
/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world
|
||||||
* coordinate system. */
|
* coordinate system. */
|
||||||
static public void apply (Bone bone, float targetX, float targetY, float alpha) {
|
static public void apply (Bone bone, float targetX, float targetY, float alpha) {
|
||||||
Bone pp = bone.parent;
|
Bone p = bone.parent;
|
||||||
float id = 1 / (pp.a * pp.d - pp.b * pp.c);
|
float id = 1 / (p.a * p.d - p.b * p.c);
|
||||||
float x = targetX - pp.worldX, y = targetY - pp.worldY;
|
float x = targetX - p.worldX, y = targetY - p.worldY;
|
||||||
float tx = (x * pp.d - y * pp.b) * id - bone.x, ty = (y * pp.a - x * pp.c) * id - bone.y;
|
float tx = (x * p.d - y * p.b) * id - bone.ax, ty = (y * p.a - x * p.c) * id - bone.ay;
|
||||||
float rotationIK = atan2(ty, tx) * radDeg - bone.shearX - bone.rotation;
|
float rotationIK = atan2(ty, tx) * radDeg - bone.ashearX - bone.arotation;
|
||||||
if (bone.scaleX < 0) rotationIK += 180;
|
if (bone.ascaleX < 0) rotationIK += 180;
|
||||||
if (rotationIK > 180)
|
if (rotationIK > 180)
|
||||||
rotationIK -= 360;
|
rotationIK -= 360;
|
||||||
else if (rotationIK < -180) rotationIK += 360;
|
else if (rotationIK < -180) rotationIK += 360;
|
||||||
bone.updateWorldTransform(bone.x, bone.y, bone.rotation + rotationIK * alpha, bone.scaleX, bone.scaleY, bone.shearX,
|
bone.updateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, bone.ascaleX, bone.ascaleY, bone.ashearX,
|
||||||
bone.shearY);
|
bone.ashearY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 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
|
||||||
@ -147,7 +149,12 @@ public class IkConstraint implements Updatable {
|
|||||||
child.updateWorldTransform();
|
child.updateWorldTransform();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX;
|
|
||||||
|
// BOZO! - Only when each bone needs it.
|
||||||
|
// child.updateLocalTransform();
|
||||||
|
// parent.updateLocalTransform();
|
||||||
|
|
||||||
|
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, csx = child.ascaleX;
|
||||||
int os1, os2, s2;
|
int os1, os2, s2;
|
||||||
if (psx < 0) {
|
if (psx < 0) {
|
||||||
psx = -psx;
|
psx = -psx;
|
||||||
@ -166,14 +173,14 @@ public class IkConstraint implements Updatable {
|
|||||||
os2 = 180;
|
os2 = 180;
|
||||||
} else
|
} else
|
||||||
os2 = 0;
|
os2 = 0;
|
||||||
float cx = child.x, cy, cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
|
float cx = child.ax, cy, cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
|
||||||
boolean u = Math.abs(psx - psy) <= 0.0001f;
|
boolean u = Math.abs(psx - psy) <= 0.0001f;
|
||||||
if (!u) {
|
if (!u) {
|
||||||
cy = 0;
|
cy = 0;
|
||||||
cwx = a * cx + parent.worldX;
|
cwx = a * cx + parent.worldX;
|
||||||
cwy = c * cx + parent.worldY;
|
cwy = c * cx + parent.worldY;
|
||||||
} else {
|
} else {
|
||||||
cy = child.y;
|
cy = child.ay;
|
||||||
cwx = a * cx + b * cy + parent.worldX;
|
cwx = a * cx + b * cy + parent.worldX;
|
||||||
cwy = c * cx + d * cy + parent.worldY;
|
cwy = c * cx + d * cy + parent.worldY;
|
||||||
}
|
}
|
||||||
@ -260,17 +267,17 @@ public class IkConstraint implements Updatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
float os = atan2(cy, cx) * s2;
|
float os = atan2(cy, cx) * s2;
|
||||||
float rotation = parent.rotation;
|
float rotation = parent.arotation;
|
||||||
a1 = (a1 - os) * radDeg + os1 - rotation;
|
a1 = (a1 - os) * radDeg + os1 - rotation;
|
||||||
if (a1 > 180)
|
if (a1 > 180)
|
||||||
a1 -= 360;
|
a1 -= 360;
|
||||||
else if (a1 < -180) a1 += 360;
|
else if (a1 < -180) a1 += 360;
|
||||||
parent.updateWorldTransform(px, py, rotation + a1 * alpha, parent.scaleX, parent.scaleY, 0, 0);
|
parent.updateWorldTransform(px, py, rotation + a1 * alpha, parent.ascaleX, parent.ascaleY, 0, 0);
|
||||||
rotation = child.rotation;
|
rotation = child.arotation;
|
||||||
a2 = ((a2 + os) * radDeg - child.shearX) * s2 + os2 - rotation;
|
a2 = ((a2 + os) * radDeg - child.ashearX) * s2 + os2 - rotation;
|
||||||
if (a2 > 180)
|
if (a2 > 180)
|
||||||
a2 -= 360;
|
a2 -= 360;
|
||||||
else if (a2 < -180) a2 += 360;
|
else if (a2 < -180) a2 += 360;
|
||||||
child.updateWorldTransform(cx, cy, rotation + a2 * alpha, child.scaleX, child.scaleY, child.shearX, child.shearY);
|
child.updateWorldTransform(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import com.badlogic.gdx.utils.Array;
|
|||||||
|
|
||||||
public class IkConstraintData {
|
public class IkConstraintData {
|
||||||
final String name;
|
final String name;
|
||||||
|
int order;
|
||||||
final Array<BoneData> bones = new Array();
|
final Array<BoneData> bones = new Array();
|
||||||
BoneData target;
|
BoneData target;
|
||||||
int bendDirection = 1;
|
int bendDirection = 1;
|
||||||
@ -49,6 +50,14 @@ public class IkConstraintData {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getOrder () {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrder (int order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
public Array<BoneData> getBones () {
|
public Array<BoneData> getBones () {
|
||||||
return bones;
|
return bones;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import com.esotericsoftware.spine.PathConstraintData.SpacingMode;
|
|||||||
import com.esotericsoftware.spine.attachments.Attachment;
|
import com.esotericsoftware.spine.attachments.Attachment;
|
||||||
import com.esotericsoftware.spine.attachments.PathAttachment;
|
import com.esotericsoftware.spine.attachments.PathAttachment;
|
||||||
|
|
||||||
public class PathConstraint implements Updatable {
|
public class PathConstraint implements Constraint {
|
||||||
static private final int NONE = -1, BEFORE = -2, AFTER = -3;
|
static private final int NONE = -1, BEFORE = -2, AFTER = -3;
|
||||||
|
|
||||||
final PathConstraintData data;
|
final PathConstraintData data;
|
||||||
@ -382,6 +382,10 @@ public class PathConstraint implements Updatable {
|
|||||||
if (tangents) out[o + 2] = atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
|
if (tangents) out[o + 2] = atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getOrder () {
|
||||||
|
return data.order;
|
||||||
|
}
|
||||||
|
|
||||||
public float getPosition () {
|
public float getPosition () {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.badlogic.gdx.utils.Array;
|
|||||||
|
|
||||||
public class PathConstraintData {
|
public class PathConstraintData {
|
||||||
final String name;
|
final String name;
|
||||||
|
int order;
|
||||||
final Array<BoneData> bones = new Array();
|
final Array<BoneData> bones = new Array();
|
||||||
SlotData target;
|
SlotData target;
|
||||||
PositionMode positionMode;
|
PositionMode positionMode;
|
||||||
@ -18,6 +19,18 @@ public class PathConstraintData {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName () {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrder () {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrder (int order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
public Array<BoneData> getBones () {
|
public Array<BoneData> getBones () {
|
||||||
return bones;
|
return bones;
|
||||||
}
|
}
|
||||||
@ -94,10 +107,6 @@ public class PathConstraintData {
|
|||||||
this.translateMix = translateMix;
|
this.translateMix = translateMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName () {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString () {
|
public String toString () {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
package com.esotericsoftware.spine;
|
package com.esotericsoftware.spine;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
import com.badlogic.gdx.math.Vector2;
|
import com.badlogic.gdx.math.Vector2;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
@ -46,9 +48,10 @@ public class Skeleton {
|
|||||||
final Array<Bone> bones;
|
final Array<Bone> bones;
|
||||||
final Array<Slot> slots;
|
final Array<Slot> slots;
|
||||||
Array<Slot> drawOrder;
|
Array<Slot> drawOrder;
|
||||||
final Array<IkConstraint> ikConstraints, ikConstraintsSorted;
|
final Array<IkConstraint> ikConstraints;
|
||||||
final Array<TransformConstraint> transformConstraints;
|
final Array<TransformConstraint> transformConstraints;
|
||||||
final Array<PathConstraint> pathConstraints;
|
final Array<PathConstraint> pathConstraints;
|
||||||
|
final Array<Constraint> sortedConstraints = new Array();
|
||||||
final Array<Updatable> updateCache = new Array();
|
final Array<Updatable> updateCache = new Array();
|
||||||
Skin skin;
|
Skin skin;
|
||||||
final Color color;
|
final Color color;
|
||||||
@ -56,6 +59,9 @@ public class Skeleton {
|
|||||||
boolean flipX, flipY;
|
boolean flipX, flipY;
|
||||||
float x, y;
|
float x, y;
|
||||||
|
|
||||||
|
final Comparator<Constraint> constraintComparator = new Comparator<Constraint>() {
|
||||||
|
public int compare (Constraint o1,Constraint o2){return o1.getOrder()-o2.getOrder();}};
|
||||||
|
|
||||||
public Skeleton (SkeletonData data) {
|
public Skeleton (SkeletonData data) {
|
||||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
@ -83,7 +89,6 @@ public class Skeleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ikConstraints = new Array(data.ikConstraints.size);
|
ikConstraints = new Array(data.ikConstraints.size);
|
||||||
ikConstraintsSorted = new Array(ikConstraints.size);
|
|
||||||
for (IkConstraintData ikConstraintData : data.ikConstraints)
|
for (IkConstraintData ikConstraintData : data.ikConstraints)
|
||||||
ikConstraints.add(new IkConstraint(ikConstraintData, this));
|
ikConstraints.add(new IkConstraint(ikConstraintData, this));
|
||||||
|
|
||||||
@ -107,15 +112,8 @@ public class Skeleton {
|
|||||||
|
|
||||||
bones = new Array(skeleton.bones.size);
|
bones = new Array(skeleton.bones.size);
|
||||||
for (Bone bone : skeleton.bones) {
|
for (Bone bone : skeleton.bones) {
|
||||||
Bone copy;
|
Bone parent = bone.parent == null ? null : bones.get(bone.parent.data.index);
|
||||||
if (bone.parent == null)
|
bones.add(new Bone(bone, this, parent));
|
||||||
copy = new Bone(bone, this, null);
|
|
||||||
else {
|
|
||||||
Bone parent = bones.get(bone.parent.data.index);
|
|
||||||
copy = new Bone(bone, this, parent);
|
|
||||||
parent.children.add(copy);
|
|
||||||
}
|
|
||||||
bones.add(copy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
slots = new Array(skeleton.slots.size);
|
slots = new Array(skeleton.slots.size);
|
||||||
@ -129,7 +127,6 @@ public class Skeleton {
|
|||||||
drawOrder.add(slots.get(slot.data.index));
|
drawOrder.add(slots.get(slot.data.index));
|
||||||
|
|
||||||
ikConstraints = new Array(skeleton.ikConstraints.size);
|
ikConstraints = new Array(skeleton.ikConstraints.size);
|
||||||
ikConstraintsSorted = new Array(ikConstraints.size);
|
|
||||||
for (IkConstraint ikConstraint : skeleton.ikConstraints)
|
for (IkConstraint ikConstraint : skeleton.ikConstraints)
|
||||||
ikConstraints.add(new IkConstraint(ikConstraint, this));
|
ikConstraints.add(new IkConstraint(ikConstraint, this));
|
||||||
|
|
||||||
@ -160,95 +157,82 @@ public class Skeleton {
|
|||||||
for (int i = 0, n = bones.size; i < n; i++)
|
for (int i = 0, n = bones.size; i < n; i++)
|
||||||
bones.get(i).sorted = false;
|
bones.get(i).sorted = false;
|
||||||
|
|
||||||
// IK first, lowest hierarchy depth first.
|
Array<Constraint> constraints = sortedConstraints;
|
||||||
Array<IkConstraint> ikConstraints = this.ikConstraintsSorted;
|
constraints.addAll(ikConstraints);
|
||||||
ikConstraints.clear();
|
constraints.addAll(transformConstraints);
|
||||||
ikConstraints.addAll(this.ikConstraints);
|
constraints.addAll(pathConstraints);
|
||||||
int ikCount = ikConstraints.size;
|
constraints.sort(constraintComparator);
|
||||||
for (int i = 0, level, n = ikCount; i < n; i++) {
|
for (int i = 0, n = constraints.size; i < n; i++) {
|
||||||
IkConstraint ik = ikConstraints.get(i);
|
Constraint constraint = constraints.get(i);
|
||||||
Bone bone = ik.bones.first().parent;
|
if (constraint instanceof IkConstraint)
|
||||||
for (level = 0; bone != null; level++)
|
sortIkConstraint((IkConstraint)constraint);
|
||||||
bone = bone.parent;
|
else if (constraint instanceof TransformConstraint)
|
||||||
ik.level = level;
|
sortTransformConstraint((TransformConstraint)constraint);
|
||||||
}
|
else
|
||||||
for (int i = 1, ii; i < ikCount; i++) {
|
sortPathConstraint((PathConstraint)constraint);
|
||||||
IkConstraint ik = ikConstraints.get(i);
|
|
||||||
int level = ik.level;
|
|
||||||
for (ii = i - 1; ii >= 0; ii--) {
|
|
||||||
IkConstraint other = ikConstraints.get(ii);
|
|
||||||
if (other.level < level) break;
|
|
||||||
ikConstraints.set(ii + 1, other);
|
|
||||||
}
|
|
||||||
ikConstraints.set(ii + 1, ik);
|
|
||||||
}
|
|
||||||
for (int i = 0, n = ikConstraints.size; i < n; i++) {
|
|
||||||
IkConstraint constraint = ikConstraints.get(i);
|
|
||||||
Bone target = constraint.target;
|
|
||||||
sortBone(target);
|
|
||||||
|
|
||||||
Array<Bone> constrained = constraint.bones;
|
|
||||||
Bone parent = constrained.first();
|
|
||||||
sortBone(parent);
|
|
||||||
|
|
||||||
updateCache.add(constraint);
|
|
||||||
|
|
||||||
sortReset(parent.children);
|
|
||||||
constrained.peek().sorted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array<PathConstraint> pathConstraints = this.pathConstraints;
|
|
||||||
for (int i = 0, n = pathConstraints.size; i < n; i++) {
|
|
||||||
PathConstraint constraint = pathConstraints.get(i);
|
|
||||||
|
|
||||||
Slot slot = constraint.target;
|
|
||||||
int slotIndex = slot.getData().index;
|
|
||||||
Bone slotBone = slot.bone;
|
|
||||||
if (skin != null) sortPathConstraintAttachment(skin, slotIndex, slotBone);
|
|
||||||
if (data.defaultSkin != null && data.defaultSkin != skin)
|
|
||||||
sortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone);
|
|
||||||
for (int ii = 0, nn = data.skins.size; ii < nn; ii++)
|
|
||||||
sortPathConstraintAttachment(data.skins.get(ii), slotIndex, slotBone);
|
|
||||||
|
|
||||||
Attachment attachment = slot.attachment;
|
|
||||||
if (attachment instanceof PathAttachment) sortPathConstraintAttachment(attachment, slotBone);
|
|
||||||
|
|
||||||
Array<Bone> constrained = constraint.bones;
|
|
||||||
int boneCount = constrained.size;
|
|
||||||
for (int ii = 0; ii < boneCount; ii++)
|
|
||||||
sortBone(constrained.get(ii));
|
|
||||||
|
|
||||||
updateCache.add(constraint);
|
|
||||||
|
|
||||||
for (int ii = 0; ii < boneCount; ii++)
|
|
||||||
sortReset(constrained.get(ii).children);
|
|
||||||
for (int ii = 0; ii < boneCount; ii++)
|
|
||||||
constrained.get(ii).sorted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array<TransformConstraint> transformConstraints = this.transformConstraints;
|
|
||||||
for (int i = 0, n = transformConstraints.size; i < n; i++) {
|
|
||||||
TransformConstraint constraint = transformConstraints.get(i);
|
|
||||||
|
|
||||||
sortBone(constraint.target);
|
|
||||||
|
|
||||||
Array<Bone> constrained = constraint.bones;
|
|
||||||
int boneCount = constrained.size;
|
|
||||||
for (int ii = 0; ii < boneCount; ii++)
|
|
||||||
sortBone(constrained.get(ii));
|
|
||||||
|
|
||||||
updateCache.add(constraint);
|
|
||||||
|
|
||||||
for (int ii = 0; ii < boneCount; ii++)
|
|
||||||
sortReset(constrained.get(ii).children);
|
|
||||||
for (int ii = 0; ii < boneCount; ii++)
|
|
||||||
constrained.get(ii).sorted = true;
|
|
||||||
}
|
}
|
||||||
|
constraints.clear();
|
||||||
|
|
||||||
for (int i = 0, n = bones.size; i < n; i++)
|
for (int i = 0, n = bones.size; i < n; i++)
|
||||||
sortBone(bones.get(i));
|
sortBone(bones.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sortIkConstraint (IkConstraint constraint) {
|
||||||
|
Bone target = constraint.target;
|
||||||
|
sortBone(target);
|
||||||
|
|
||||||
|
Array<Bone> constrained = constraint.bones;
|
||||||
|
Bone parent = constrained.first();
|
||||||
|
sortBone(parent);
|
||||||
|
|
||||||
|
updateCache.add(constraint);
|
||||||
|
|
||||||
|
sortReset(parent.children);
|
||||||
|
constrained.peek().sorted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortPathConstraint (PathConstraint constraint) {
|
||||||
|
Slot slot = constraint.target;
|
||||||
|
int slotIndex = slot.getData().index;
|
||||||
|
Bone slotBone = slot.bone;
|
||||||
|
if (skin != null) sortPathConstraintAttachment(skin, slotIndex, slotBone);
|
||||||
|
if (data.defaultSkin != null && data.defaultSkin != skin)
|
||||||
|
sortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone);
|
||||||
|
for (int ii = 0, nn = data.skins.size; ii < nn; ii++)
|
||||||
|
sortPathConstraintAttachment(data.skins.get(ii), slotIndex, slotBone);
|
||||||
|
|
||||||
|
Attachment attachment = slot.attachment;
|
||||||
|
if (attachment instanceof PathAttachment) sortPathConstraintAttachment(attachment, slotBone);
|
||||||
|
|
||||||
|
Array<Bone> constrained = constraint.bones;
|
||||||
|
int boneCount = constrained.size;
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
sortBone(constrained.get(ii));
|
||||||
|
|
||||||
|
updateCache.add(constraint);
|
||||||
|
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
sortReset(constrained.get(ii).children);
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
constrained.get(ii).sorted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortTransformConstraint (TransformConstraint constraint) {
|
||||||
|
sortBone(constraint.target);
|
||||||
|
|
||||||
|
Array<Bone> constrained = constraint.bones;
|
||||||
|
int boneCount = constrained.size;
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
sortBone(constrained.get(ii));
|
||||||
|
|
||||||
|
updateCache.add(constraint);
|
||||||
|
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
sortReset(constrained.get(ii).children);
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
constrained.get(ii).sorted = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void sortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
|
private void sortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
|
||||||
for (Entry<Key, Attachment> entry : skin.attachments.entries())
|
for (Entry<Key, Attachment> entry : skin.attachments.entries())
|
||||||
if (entry.key.slotIndex == slotIndex) sortPathConstraintAttachment(entry.value, slotBone);
|
if (entry.key.slotIndex == slotIndex) sortPathConstraintAttachment(entry.value, slotBone);
|
||||||
|
|||||||
@ -206,6 +206,7 @@ public class SkeletonBinary {
|
|||||||
// IK constraints.
|
// IK constraints.
|
||||||
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
||||||
IkConstraintData data = new IkConstraintData(input.readString());
|
IkConstraintData data = new IkConstraintData(input.readString());
|
||||||
|
data.order = input.readInt(true);
|
||||||
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
|
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
|
||||||
data.bones.add(skeletonData.bones.get(input.readInt(true)));
|
data.bones.add(skeletonData.bones.get(input.readInt(true)));
|
||||||
data.target = skeletonData.bones.get(input.readInt(true));
|
data.target = skeletonData.bones.get(input.readInt(true));
|
||||||
@ -217,6 +218,7 @@ public class SkeletonBinary {
|
|||||||
// Transform constraints.
|
// Transform constraints.
|
||||||
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
||||||
TransformConstraintData data = new TransformConstraintData(input.readString());
|
TransformConstraintData data = new TransformConstraintData(input.readString());
|
||||||
|
data.order = input.readInt(true);
|
||||||
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
|
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
|
||||||
data.bones.add(skeletonData.bones.get(input.readInt(true)));
|
data.bones.add(skeletonData.bones.get(input.readInt(true)));
|
||||||
data.target = skeletonData.bones.get(input.readInt(true));
|
data.target = skeletonData.bones.get(input.readInt(true));
|
||||||
@ -236,6 +238,7 @@ public class SkeletonBinary {
|
|||||||
// Path constraints.
|
// Path constraints.
|
||||||
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
||||||
PathConstraintData data = new PathConstraintData(input.readString());
|
PathConstraintData data = new PathConstraintData(input.readString());
|
||||||
|
data.order = input.readInt(true);
|
||||||
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
|
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
|
||||||
data.bones.add(skeletonData.bones.get(input.readInt(true)));
|
data.bones.add(skeletonData.bones.get(input.readInt(true)));
|
||||||
data.target = skeletonData.slots.get(input.readInt(true));
|
data.target = skeletonData.slots.get(input.readInt(true));
|
||||||
|
|||||||
@ -157,6 +157,7 @@ public class SkeletonJson {
|
|||||||
// IK constraints.
|
// IK constraints.
|
||||||
for (JsonValue constraintMap = root.getChild("ik"); constraintMap != null; constraintMap = constraintMap.next) {
|
for (JsonValue constraintMap = root.getChild("ik"); constraintMap != null; constraintMap = constraintMap.next) {
|
||||||
IkConstraintData data = new IkConstraintData(constraintMap.getString("name"));
|
IkConstraintData data = new IkConstraintData(constraintMap.getString("name"));
|
||||||
|
data.order = constraintMap.getInt("order", 0);
|
||||||
|
|
||||||
for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) {
|
for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) {
|
||||||
String boneName = boneMap.asString();
|
String boneName = boneMap.asString();
|
||||||
@ -178,6 +179,7 @@ public class SkeletonJson {
|
|||||||
// Transform constraints.
|
// Transform constraints.
|
||||||
for (JsonValue constraintMap = root.getChild("transform"); constraintMap != null; constraintMap = constraintMap.next) {
|
for (JsonValue constraintMap = root.getChild("transform"); constraintMap != null; constraintMap = constraintMap.next) {
|
||||||
TransformConstraintData data = new TransformConstraintData(constraintMap.getString("name"));
|
TransformConstraintData data = new TransformConstraintData(constraintMap.getString("name"));
|
||||||
|
data.order = constraintMap.getInt("order", 0);
|
||||||
|
|
||||||
for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) {
|
for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) {
|
||||||
String boneName = boneMap.asString();
|
String boneName = boneMap.asString();
|
||||||
@ -208,6 +210,7 @@ public class SkeletonJson {
|
|||||||
// Path constraints.
|
// Path constraints.
|
||||||
for (JsonValue constraintMap = root.getChild("path"); constraintMap != null; constraintMap = constraintMap.next) {
|
for (JsonValue constraintMap = root.getChild("path"); constraintMap != null; constraintMap = constraintMap.next) {
|
||||||
PathConstraintData data = new PathConstraintData(constraintMap.getString("name"));
|
PathConstraintData data = new PathConstraintData(constraintMap.getString("name"));
|
||||||
|
data.order = constraintMap.getInt("order", 0);
|
||||||
|
|
||||||
for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) {
|
for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) {
|
||||||
String boneName = boneMap.asString();
|
String boneName = boneMap.asString();
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import static com.badlogic.gdx.math.MathUtils.*;
|
|||||||
import com.badlogic.gdx.math.Vector2;
|
import com.badlogic.gdx.math.Vector2;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
|
||||||
public class TransformConstraint implements Updatable {
|
public class TransformConstraint implements Constraint {
|
||||||
final TransformConstraintData data;
|
final TransformConstraintData data;
|
||||||
final Array<Bone> bones;
|
final Array<Bone> bones;
|
||||||
Bone target;
|
Bone target;
|
||||||
@ -54,7 +54,7 @@ public class TransformConstraint implements Updatable {
|
|||||||
for (int i = 0, n = bones.size; i < n; i++) {
|
for (int i = 0, n = bones.size; i < n; i++) {
|
||||||
Bone bone = bones.get(i);
|
Bone bone = bones.get(i);
|
||||||
|
|
||||||
if (rotateMix > 0) {
|
if (rotateMix != 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) + data.offsetRotation * degRad;
|
float r = atan2(tc, ta) - atan2(c, a) + data.offsetRotation * degRad;
|
||||||
if (r > PI)
|
if (r > PI)
|
||||||
@ -68,7 +68,7 @@ public class TransformConstraint implements Updatable {
|
|||||||
bone.d = sin * b + cos * d;
|
bone.d = sin * b + cos * d;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (translateMix > 0) {
|
if (translateMix != 0) {
|
||||||
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) * translateMix;
|
||||||
@ -103,6 +103,10 @@ public class TransformConstraint implements Updatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getOrder () {
|
||||||
|
return data.order;
|
||||||
|
}
|
||||||
|
|
||||||
public Array<Bone> getBones () {
|
public Array<Bone> getBones () {
|
||||||
return bones;
|
return bones;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.badlogic.gdx.utils.Array;
|
|||||||
|
|
||||||
public class TransformConstraintData {
|
public class TransformConstraintData {
|
||||||
final String name;
|
final String name;
|
||||||
|
int order;
|
||||||
final Array<BoneData> bones = new Array();
|
final Array<BoneData> bones = new Array();
|
||||||
BoneData target;
|
BoneData target;
|
||||||
float rotateMix, translateMix, scaleMix, shearMix;
|
float rotateMix, translateMix, scaleMix, shearMix;
|
||||||
@ -19,6 +20,14 @@ public class TransformConstraintData {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getOrder () {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrder (int order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
public Array<BoneData> getBones () {
|
public Array<BoneData> getBones () {
|
||||||
return bones;
|
return bones;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user