mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
Update cache now handles all constraint configurations.
Can also apply constraints in a specific order, except for IK which must always be first.
This commit is contained in:
parent
8538e793d6
commit
5dcf6b3b02
@ -50,6 +50,8 @@ public class Bone implements Updatable {
|
|||||||
float c, d, worldY;
|
float c, d, worldY;
|
||||||
float worldSignX, worldSignY;
|
float worldSignX, worldSignY;
|
||||||
|
|
||||||
|
boolean sorted;
|
||||||
|
|
||||||
/** @param parent May be null. */
|
/** @param parent May be null. */
|
||||||
public Bone (BoneData data, Skeleton skeleton, Bone parent) {
|
public Bone (BoneData data, Skeleton skeleton, Bone parent) {
|
||||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||||
|
|||||||
@ -42,6 +42,8 @@ public class IkConstraint implements Updatable {
|
|||||||
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.");
|
||||||
@ -135,6 +137,10 @@ public class IkConstraint implements Updatable {
|
|||||||
else if (rotationIK < -180) rotationIK += 360;
|
else if (rotationIK < -180) rotationIK += 360;
|
||||||
bone.updateWorldTransform(bone.x, bone.y, bone.rotation + (rotationIK - bone.rotation) * alpha, bone.appliedScaleX,
|
bone.updateWorldTransform(bone.x, bone.y, bone.rotation + (rotationIK - bone.rotation) * alpha, bone.appliedScaleX,
|
||||||
bone.appliedScaleY, bone.shearX, bone.shearY);
|
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
|
/** 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. */
|
* @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) {
|
static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, float alpha) {
|
||||||
if (alpha == 0) return;
|
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;
|
int os1, os2, s2;
|
||||||
if (psx < 0) {
|
if (psx < 0) {
|
||||||
psx = -psx;
|
psx = -psx;
|
||||||
@ -156,25 +162,32 @@ public class IkConstraint implements Updatable {
|
|||||||
psy = -psy;
|
psy = -psy;
|
||||||
s2 = -s2;
|
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) {
|
if (csx < 0) {
|
||||||
csx = -csx;
|
csx = -csx;
|
||||||
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;
|
||||||
|
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;
|
Bone pp = parent.parent;
|
||||||
float ppa = pp.a, ppb = pp.b, ppc = pp.c, ppd = pp.d, id = 1 / (ppa * ppd - ppb * ppc);
|
a = pp.a;
|
||||||
float x = targetX - pp.worldX, y = targetY - pp.worldY;
|
b = pp.b;
|
||||||
float tx = (x * ppd - y * ppb) * id - px, ty = (y * ppa - x * ppc) * id - py;
|
c = pp.c;
|
||||||
x = child.worldX - pp.worldX;
|
d = pp.d;
|
||||||
y = child.worldY - pp.worldY;
|
float id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||||
float dx = (x * ppd - y * ppb) * id - px, dy = (y * ppa - x * ppc) * id - py;
|
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;
|
float l1 = (float)Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
|
||||||
outer:
|
outer:
|
||||||
if (u) {
|
if (u) {
|
||||||
@ -184,18 +197,21 @@ public class IkConstraint implements Updatable {
|
|||||||
cos = -1;
|
cos = -1;
|
||||||
else if (cos > 1) cos = 1;
|
else if (cos > 1) cos = 1;
|
||||||
a2 = (float)Math.acos(cos) * bendDir;
|
a2 = (float)Math.acos(cos) * bendDir;
|
||||||
float a = l1 + l2 * cos, o = l2 * sin(a2);
|
a = l1 + l2 * cos;
|
||||||
a1 = atan2(ty * a - tx * o, tx * a + ty * o);
|
b = l2 * sin(a2);
|
||||||
|
a1 = atan2(ty * a - tx * b, tx * a + ty * b);
|
||||||
} else {
|
} else {
|
||||||
float a = psx * l2, b = psy * l2, ta = atan2(ty, tx);
|
a = psx * l2;
|
||||||
float aa = a * a, bb = b * b, ll = l1 * l1, dd = tx * tx + ty * ty;
|
b = psy * l2;
|
||||||
float c0 = bb * ll + aa * dd - aa * bb, c1 = -2 * bb * l1, c2 = bb - aa;
|
float aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = atan2(ty, tx);
|
||||||
float d = c1 * c1 - 4 * c2 * c0;
|
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) {
|
if (d >= 0) {
|
||||||
float q = (float)Math.sqrt(d);
|
float q = (float)Math.sqrt(d);
|
||||||
if (c1 < 0) q = -q;
|
if (c1 < 0) q = -q;
|
||||||
q = -(c1 + q) / 2;
|
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;
|
float r = Math.abs(r0) < Math.abs(r1) ? r0 : r1;
|
||||||
if (r * r <= dd) {
|
if (r * r <= dd) {
|
||||||
y = (float)Math.sqrt(dd - r * r) * bendDir;
|
y = (float)Math.sqrt(dd - r * r) * bendDir;
|
||||||
|
|||||||
@ -140,47 +140,115 @@ public class Skeleton {
|
|||||||
|
|
||||||
/** Caches information about bones and constraints. Must be called if bones or constraints are added or removed. */
|
/** Caches information about bones and constraints. Must be called if bones or constraints are added or removed. */
|
||||||
public void updateCache () {
|
public void updateCache () {
|
||||||
Array<Bone> bones = this.bones;
|
|
||||||
Array<Updatable> updateCache = this.updateCache;
|
Array<Updatable> updateCache = this.updateCache;
|
||||||
Array<IkConstraint> ikConstraints = this.ikConstraints;
|
|
||||||
Array<TransformConstraint> transformConstraints = this.transformConstraints;
|
|
||||||
Array<PathConstraint> pathConstraints = this.pathConstraints;
|
|
||||||
int ikConstraintsCount = ikConstraints.size;
|
|
||||||
updateCache.clear();
|
updateCache.clear();
|
||||||
|
|
||||||
|
Array<Bone> 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<IkConstraint> 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<Bone> constrained = constraint.bones;
|
||||||
|
Bone parent = constrained.first();
|
||||||
|
sortBone(parent);
|
||||||
|
|
||||||
|
updateCache.add(constraint);
|
||||||
|
|
||||||
|
reset(target.children);
|
||||||
|
reset(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);
|
||||||
|
|
||||||
|
Bone target = constraint.target.bone;
|
||||||
|
sortBone(target);
|
||||||
|
|
||||||
|
Array<Bone> 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<TransformConstraint> 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<Bone> 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<Bone> bones) {
|
||||||
|
for (int i = 0, n = bones.size; i < n; i++)
|
||||||
|
reset(bones.get(i).children);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset (Array<Bone> bones) {
|
||||||
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);
|
||||||
updateCache.add(bone);
|
if (bone.sorted) reset(bone.children);
|
||||||
for (int ii = 0; ii < ikConstraintsCount; ii++) {
|
bone.sorted = false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user