diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java index 2f9d6e561..124a83ef0 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java @@ -50,6 +50,8 @@ public class Bone implements Updatable { float c, d, worldY; float worldSignX, worldSignY; + boolean sorted; + /** @param parent May be null. */ public Bone (BoneData data, Skeleton skeleton, Bone parent) { if (data == null) throw new IllegalArgumentException("data cannot be null."); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java index 2c126492e..4b50b5055 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java @@ -42,6 +42,8 @@ public class IkConstraint implements Updatable { float mix = 1; int bendDirection; + int level; + public IkConstraint (IkConstraintData data, Skeleton skeleton) { if (data == null) throw new IllegalArgumentException("data cannot be null."); if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); @@ -135,6 +137,10 @@ public class IkConstraint implements Updatable { else if (rotationIK < -180) rotationIK += 360; bone.updateWorldTransform(bone.x, bone.y, bone.rotation + (rotationIK - bone.rotation) * alpha, bone.appliedScaleX, bone.appliedScaleY, bone.shearX, bone.shearY); + /* + * parents of this thing this thing (bone) ik constraint + */ + } /** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The @@ -142,7 +148,7 @@ public class IkConstraint implements Updatable { * @param child A direct descendant of the parent bone. */ static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, float alpha) { if (alpha == 0) return; - float px = parent.x, py = parent.y, psx = parent.appliedScaleX, psy = parent.appliedScaleY; + float px = parent.x, py = parent.y, psx = parent.appliedScaleX, psy = parent.appliedScaleY, csx = child.appliedScaleX; int os1, os2, s2; if (psx < 0) { psx = -psx; @@ -156,25 +162,32 @@ public class IkConstraint implements Updatable { psy = -psy; s2 = -s2; } - float cx = child.x, cy = child.y, csx = child.appliedScaleX; - boolean u = Math.abs(psx - psy) <= 0.0001f; - if (!u && cy != 0) { - child.worldX = parent.a * cx + parent.worldX; - child.worldY = parent.c * cx + parent.worldY; - cy = 0; - } if (csx < 0) { csx = -csx; os2 = 180; } else os2 = 0; + float cx = child.x, cy, cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d; + boolean u = Math.abs(psx - psy) <= 0.0001f; + if (!u) { + cy = 0; + cwx = a * cx + parent.worldX; + cwy = c * cx + parent.worldY; + } else { + cy = child.y; + cwx = a * cx + b * cy + parent.worldX; + cwy = c * cx + d * cy + parent.worldY; + } Bone pp = parent.parent; - float ppa = pp.a, ppb = pp.b, ppc = pp.c, ppd = pp.d, id = 1 / (ppa * ppd - ppb * ppc); - float x = targetX - pp.worldX, y = targetY - pp.worldY; - float tx = (x * ppd - y * ppb) * id - px, ty = (y * ppa - x * ppc) * id - py; - x = child.worldX - pp.worldX; - y = child.worldY - pp.worldY; - float dx = (x * ppd - y * ppb) * id - px, dy = (y * ppa - x * ppc) * id - py; + a = pp.a; + b = pp.b; + c = pp.c; + d = pp.d; + float id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY; + float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py; + x = cwx - pp.worldX; + y = cwy - pp.worldY; + float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; float l1 = (float)Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2; outer: if (u) { @@ -184,18 +197,21 @@ public class IkConstraint implements Updatable { cos = -1; else if (cos > 1) cos = 1; a2 = (float)Math.acos(cos) * bendDir; - float a = l1 + l2 * cos, o = l2 * sin(a2); - a1 = atan2(ty * a - tx * o, tx * a + ty * o); + a = l1 + l2 * cos; + b = l2 * sin(a2); + a1 = atan2(ty * a - tx * b, tx * a + ty * b); } else { - float a = psx * l2, b = psy * l2, ta = atan2(ty, tx); - float aa = a * a, bb = b * b, ll = l1 * l1, dd = tx * tx + ty * ty; - float c0 = bb * ll + aa * dd - aa * bb, c1 = -2 * bb * l1, c2 = bb - aa; - float d = c1 * c1 - 4 * c2 * c0; + a = psx * l2; + b = psy * l2; + float aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = atan2(ty, tx); + c = bb * l1 * l1 + aa * dd - aa * bb; + float c1 = -2 * bb * l1, c2 = bb - aa; + d = c1 * c1 - 4 * c2 * c; if (d >= 0) { float q = (float)Math.sqrt(d); if (c1 < 0) q = -q; q = -(c1 + q) / 2; - float r0 = q / c2, r1 = c0 / q; + float r0 = q / c2, r1 = c / q; float r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; if (r * r <= dd) { y = (float)Math.sqrt(dd - r * r) * bendDir; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index 7d17cc563..cdfe12376 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -140,47 +140,115 @@ public class Skeleton { /** Caches information about bones and constraints. Must be called if bones or constraints are added or removed. */ public void updateCache () { - Array bones = this.bones; Array updateCache = this.updateCache; - Array ikConstraints = this.ikConstraints; - Array transformConstraints = this.transformConstraints; - Array pathConstraints = this.pathConstraints; - int ikConstraintsCount = ikConstraints.size; updateCache.clear(); + Array bones = this.bones; + for (int i = 0, n = bones.size; i < n; i++) + bones.get(i).sorted = false; + + // IK first, in hierarchy depth order. + Array ikConstraints = this.ikConstraints; + int ikCount = ikConstraints.size; + for (int i = 0, level, n = ikCount; i < n; i++) { + IkConstraint ik = ikConstraints.get(i); + Bone bone = ik.bones.first().parent; + for (level = 0; bone != null; level++) + bone = bone.parent; + ik.level = level; + } + for (int i = 1, ii; i < ikCount; i++) { + 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 constrained = constraint.bones; + Bone parent = constrained.first(); + sortBone(parent); + + updateCache.add(constraint); + + reset(target.children); + reset(parent.children); + constrained.peek().sorted = true; + } + + Array pathConstraints = this.pathConstraints; + for (int i = 0, n = pathConstraints.size; i < n; i++) { + PathConstraint constraint = pathConstraints.get(i); + + Bone target = constraint.target.bone; + sortBone(target); + + Array constrained = constraint.bones; + int boneCount = constrained.size; + for (int ii = 0; ii < boneCount; ii++) + sortBone(constrained.get(ii)); + + updateCache.add(constraint); + + resetChildren(constrained); + reset(target.children); + for (int ii = 0; ii < boneCount; ii++) + constrained.get(ii).sorted = true; + } + + Array transformConstraints = this.transformConstraints; + for (int i = 0, n = transformConstraints.size; i < n; i++) { + TransformConstraint constraint = transformConstraints.get(i); + + Bone target = constraint.target; + sortBone(target); + + // BOZO - Update transform constraints to support multiple constrained bones. + // Array constrained = constraint.bones; + // int boneCount = constrained.size; + // for (int ii = 0; ii < boneCount; ii++) + // sortBone(constrained.get(ii)); + sortBone(constraint.bone); + + updateCache.add(constraint); + + // resetChildren(constrained); + reset(constraint.bone.children); // BOZO - Remove. + reset(target.children); + // for (int ii = 0; ii < boneCount; ii++) + // constrained.get(ii).sorted = true; + constraint.bone.sorted = true; // BOZO - Remove. + } + + for (int i = 0, n = bones.size; i < n; i++) + sortBone(bones.get(i)); + } + + private void sortBone (Bone bone) { + if (bone.sorted) return; + Bone parent = bone.parent; + if (parent != null) sortBone(parent); + bone.sorted = true; + updateCache.add(bone); + } + + private void resetChildren (Array bones) { + for (int i = 0, n = bones.size; i < n; i++) + reset(bones.get(i).children); + } + + private void reset (Array bones) { for (int i = 0, n = bones.size; i < n; i++) { Bone bone = bones.get(i); - updateCache.add(bone); - for (int ii = 0; ii < ikConstraintsCount; ii++) { - IkConstraint ikConstraint = ikConstraints.get(ii); - if (bone == ikConstraint.bones.peek()) { - updateCache.add(ikConstraint); - break; - } - } - } - - for (int i = 0, n = pathConstraints.size; i < n; i++) { - PathConstraint pathConstraint = pathConstraints.get(i); - // BOZO! - Fix update order for multiple bones. - Bone bone = pathConstraint.bones.peek(); - for (int ii = updateCache.size - 1; ii >= 0; ii--) { - if (updateCache.get(ii) == bone) { - updateCache.insert(ii + 1, pathConstraint); - break; - } - } - } - - for (int i = 0, n = transformConstraints.size; i < n; i++) { - TransformConstraint transformConstraint = transformConstraints.get(i); - Bone bone = transformConstraint.bone; - for (int ii = updateCache.size - 1; ii >= 0; ii--) { - if (updateCache.get(ii) == bone) { - updateCache.insert(ii + 1, transformConstraint); - break; - } - } + if (bone.sorted) reset(bone.children); + bone.sorted = false; } }