Refactoring bone transforms.

This commit is contained in:
NathanSweet 2016-09-07 22:34:06 +02:00
parent 56797953c2
commit dfe8e3b826
8 changed files with 179 additions and 72 deletions

2
.gitignore vendored
View File

@ -102,3 +102,5 @@ spine-starling/spine-starling-example/lib/.spine-starling.swc.stamp
spine-turbulenz/spine-js/spine.js spine-turbulenz/spine-js/spine.js
spine-threejs/spine-js/spine.js spine-threejs/spine-js/spine.js
spine-ts/.vscode/*

View File

@ -33,7 +33,7 @@ org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
@ -84,6 +84,7 @@ org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedImport=warning org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning org.eclipse.jdt.core.compiler.problem.unusedLocal=warning

View File

@ -721,35 +721,33 @@ public class Animation {
// BOZO - Test. // BOZO - Test.
if (alpha == 1) { if (alpha == 1) {
// Absolute. // Vertex positions or deform offsets, no alpha.
for (int i = 0; i < vertexCount; i++) { for (int i = 0; i < vertexCount; i++) {
float prev = prevVertices[i]; float prev = prevVertices[i];
vertices[i] = prev + (nextVertices[i] - prev) * percent; vertices[i] = prev + (nextVertices[i] - prev) * percent;
} }
} else { } else if (setupPose) {
if (setupPose) { VertexAttachment vertexAttachment = (VertexAttachment)slotAttachment;
VertexAttachment vertexAttachment = (VertexAttachment)slotAttachment; if (vertexAttachment.getBones() == null) {
if (vertexAttachment.getBones() == null) { // Unweighted vertex positions, with alpha.
// Vertex positions. float[] setupVertices = vertexAttachment.getVertices();
float[] setupVertices = vertexAttachment.getVertices(); for (int i = 0; i < vertexCount; i++) {
for (int i = 0; i < vertexCount; i++) { float prev = prevVertices[i], setup = setupVertices[i];
float prev = prevVertices[i], setup = setupVertices[i]; vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
}
} else {
// Deform offsets.
for (int i = 0; i < vertexCount; i++) {
float prev = prevVertices[i];
vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
}
} }
} else { } else {
// Additive. // Weighted deform offsets, with alpha.
for (int i = 0; i < vertexCount; i++) { for (int i = 0; i < vertexCount; i++) {
float prev = prevVertices[i]; float prev = prevVertices[i];
vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
} }
} }
} else {
// Vertex positions or deform offsets, with alpha.
for (int i = 0; i < vertexCount; i++) {
float prev = prevVertices[i];
vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha;
}
} }
} }
} }
@ -918,6 +916,8 @@ public class Animation {
float[] frames = this.frames; float[] frames = this.frames;
if (time < frames[0]) return; // Time is before first frame. if (time < frames[0]) return; // Time is before first frame.
// BOZO - Finish timelines handling setupPose and mixingOut from here down.
IkConstraint constraint = skeleton.ikConstraints.get(ikConstraintIndex); IkConstraint constraint = skeleton.ikConstraints.get(ikConstraintIndex);
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame. if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.

View File

@ -37,6 +37,7 @@ import static com.badlogic.gdx.math.Matrix3.*;
import com.badlogic.gdx.math.Matrix3; import com.badlogic.gdx.math.Matrix3;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.esotericsoftware.spine.BoneData.TransformMode;
public class Bone implements Updatable { public class Bone implements Updatable {
final BoneData data; final BoneData data;
@ -100,12 +101,13 @@ public class Bone implements Updatable {
ashearY = shearY; ashearY = shearY;
appliedValid = true; appliedValid = true;
float rotationY = rotation + 90 + shearY;
float la = cosDeg(rotation + shearX) * scaleX, lb = cosDeg(rotationY) * scaleY;
float lc = sinDeg(rotation + shearX) * scaleX, ld = sinDeg(rotationY) * scaleY;
Bone parent = this.parent; Bone parent = this.parent;
if (parent == null) { // Root bone. if (parent == null) { // Root bone.
float rotationY = rotation + 90 + shearY;
float la = cosDeg(rotation + shearX) * scaleX;
float lb = cosDeg(rotationY) * scaleY;
float lc = sinDeg(rotation + shearX) * scaleX;
float ld = sinDeg(rotationY) * scaleY;
Skeleton skeleton = this.skeleton; Skeleton skeleton = this.skeleton;
if (skeleton.flipX) { if (skeleton.flipX) {
x = -x; x = -x;
@ -130,28 +132,99 @@ public class Bone implements Updatable {
worldX = pa * x + pb * y + parent.worldX; worldX = pa * x + pb * y + parent.worldX;
worldY = pc * x + pd * y + parent.worldY; worldY = pc * x + pd * y + parent.worldY;
if (data.inheritRotation && data.inheritScale) { switch (data.transformMode) {
case normal: {
float rotationY = rotation + 90 + shearY;
float la = cosDeg(rotation + shearX) * scaleX;
float lb = cosDeg(rotationY) * scaleY;
float lc = sinDeg(rotation + shearX) * scaleX;
float ld = sinDeg(rotationY) * scaleY;
a = pa * la + pb * lc; a = pa * la + pb * lc;
b = pa * lb + pb * ld; b = pa * lb + pb * ld;
c = pc * la + pd * lc; c = pc * la + pd * lc;
d = pc * lb + pd * ld; d = pc * lb + pd * ld;
} else { return;
if (data.inheritRotation) { // No scale inheritance. }
float psx = (float)Math.sqrt(pa * pa + pc * pc); case onlyTranslation: {
float psy = (float)Math.sqrt(pb * pb + pd * pd); float rotationY = rotation + 90 + shearY;
if (psx > 0.0001f) { a = cosDeg(rotation + shearX) * scaleX;
pa /= psx; b = cosDeg(rotationY) * scaleY;
pc /= psx; c = sinDeg(rotation + shearX) * scaleX;
} d = sinDeg(rotationY) * scaleY;
if (psy > 0.0001f) { break;
pb /= psy; }
pd /= psy; case noRotation: {
if (false) {
// Summing parent rotations.
// 1) Negative parent scale causes bone to rotate.
float sum = 0;
Bone current = parent;
while (current != null) {
sum += current.arotation;
current = current.parent;
} }
rotation -= sum;
float rotationY = rotation + 90 + shearY;
float la = cosDeg(rotation + shearX) * scaleX;
float lb = cosDeg(rotationY) * scaleY;
float lc = sinDeg(rotation + shearX) * scaleX;
float ld = sinDeg(rotationY) * scaleY;
a = pa * la + pb * lc; a = pa * la + pb * lc;
b = pa * lb + pb * ld; b = pa * lb + pb * ld;
c = pc * la + pd * lc; c = pc * la + pd * lc;
d = pc * lb + pd * ld; d = pc * lb + pd * ld;
} else if (data.inheritScale) { // No rotation inheritance. } else if (true) {
// Old way.
// 1) Immediate parent scale is applied in wrong direction.
// 2) Negative parent scale causes bone to rotate.
pa = 1;
pb = 0;
pc = 0;
pd = 1;
float rotationY, la, lb, lc, ld;
outer:
do {
if (!parent.appliedValid) parent.updateAppliedTransform();
float pr = parent.arotation, psx = parent.ascaleX;
rotationY = pr + 90 + parent.ashearY;
la = cosDeg(pr + parent.shearX);
lb = cosDeg(rotationY);
lc = sinDeg(pr + parent.shearX);
ld = sinDeg(rotationY);
float temp = (pa * la + pb * lc) * psx;
pb = (pb * ld + pa * lb) * parent.ascaleY;
pa = temp;
temp = (pc * la + pd * lc) * psx;
pd = (pd * ld + pc * lb) * parent.ascaleY;
pc = temp;
if (psx < 0) lc = -lc;
temp = pa * la - pb * lc;
pb = pb * ld - pa * lb;
pa = temp;
temp = pc * la - pd * lc;
pd = pd * ld - pc * lb;
pc = temp;
switch (parent.data.transformMode) {
case noScale:
case noScaleOrReflection:
break outer;
}
parent = parent.parent;
} while (parent != null);
rotationY = rotation + 90 + shearY;
la = cosDeg(rotation + shearX) * scaleX;
lb = cosDeg(rotationY) * scaleY;
lc = sinDeg(rotation + shearX) * scaleX;
ld = sinDeg(rotationY) * scaleY;
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
} else {
// New way.
// 1) Negative scale can cause bone to flip.
float psx = (float)Math.sqrt(pa * pa + pc * pc), psy, pr; float psx = (float)Math.sqrt(pa * pa + pc * pc), psy, pr;
if (psx > 0.0001f) { if (psx > 0.0001f) {
float det = pa * pd - pb * pc; float det = pa * pd - pb * pc;
@ -173,24 +246,57 @@ public class Bone implements Updatable {
blend = 1 - (pr - 90) / 90; blend = 1 - (pr - 90) / 90;
pa = psx + (Math.abs(psy) * Math.signum(psx) - psx) * blend; pa = psx + (Math.abs(psy) * Math.signum(psx) - psx) * blend;
pd = psy + (Math.abs(psx) * Math.signum(psy) - psy) * blend; pd = psy + (Math.abs(psx) * Math.signum(psy) - psy) * blend;
a = pa * la; float rotationY = rotation + 90 + shearY;
b = pa * lb; a = pa * cosDeg(rotation + shearX) * scaleX;
c = pd * lc; b = pa * cosDeg(rotationY) * scaleY;
d = pd * ld; c = pd * sinDeg(rotation + shearX) * scaleX;
} else { d = pd * sinDeg(rotationY) * scaleY;
a = la;
b = lb;
c = lc;
d = ld;
} }
if (skeleton.flipX) { break;
a = -a; }
case noScale:
case noScaleOrReflection: {
float cos = cosDeg(rotation), sin = sinDeg(rotation);
float za = pa * cos + pb * sin, zb = za;
float zc = pc * cos + pd * sin, zd = zc;
float s = (float)Math.sqrt(za * za + zc * zc);
if (s > 0.00001f) s = 1 / s;
za *= s;
zc *= s;
s = (float)Math.sqrt(zb * zb + zd * zd);
if (s > 0.00001f) s = 1 / s;
zb *= s;
zd *= s;
float by = atan2(zd, zb), r = PI / 2 - (by - atan2(zc, za));
if (r > PI)
r -= PI2;
else if (r < -PI) r += PI2;
r += by;
s = (float)Math.sqrt(zb * zb + zd * zd);
zb = cos(r) * s;
zd = sin(r) * s;
float la = cosDeg(shearX) * scaleX;
float lb = cosDeg(90 + shearY) * scaleY;
float lc = sinDeg(shearX) * scaleX;
float ld = sinDeg(90 + shearY) * scaleY;
a = za * la + zb * lc;
b = za * lb + zb * ld;
c = zc * la + zd * lc;
d = zc * lb + zd * ld;
if (data.transformMode != TransformMode.noScaleOrReflection ? pa * pd - pb * pc < 0 : skeleton.flipX != skeleton.flipY) {
b = -b; b = -b;
}
if (skeleton.flipY) {
c = -c;
d = -d; d = -d;
} }
return;
}
}
if (skeleton.flipX) {
a = -a;
b = -b;
}
if (skeleton.flipY) {
c = -c;
d = -d;
} }
} }

View File

@ -39,7 +39,7 @@ public class BoneData {
final BoneData parent; final BoneData parent;
float length; float length;
float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY; float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
boolean inheritRotation = true, inheritScale = true; TransformMode transformMode = TransformMode.normal;
// Nonessential. // Nonessential.
final Color color = new Color(0.61f, 0.61f, 0.61f, 1); final Color color = new Color(0.61f, 0.61f, 0.61f, 1);
@ -157,20 +157,12 @@ public class BoneData {
this.shearY = shearY; this.shearY = shearY;
} }
public boolean getInheritRotation () { public TransformMode getTransformMode () {
return inheritRotation; return transformMode;
} }
public void setInheritRotation (boolean inheritRotation) { public void setTransformMode (TransformMode transformMode) {
this.inheritRotation = inheritRotation; this.transformMode = transformMode;
}
public boolean getInheritScale () {
return inheritScale;
}
public void setInheritScale (boolean inheritScale) {
this.inheritScale = inheritScale;
} }
public Color getColor () { public Color getColor () {
@ -180,4 +172,10 @@ public class BoneData {
public String toString () { public String toString () {
return name; return name;
} }
static public enum TransformMode {
normal, onlyTranslation, noRotation, noScale, noScaleOrReflection;
static public final TransformMode[] values = TransformMode.values();
}
} }

View File

@ -58,6 +58,7 @@ import com.esotericsoftware.spine.Animation.ShearTimeline;
import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.Animation.Timeline;
import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
import com.esotericsoftware.spine.Animation.TranslateTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline;
import com.esotericsoftware.spine.BoneData.TransformMode;
import com.esotericsoftware.spine.PathConstraintData.PositionMode; import com.esotericsoftware.spine.PathConstraintData.PositionMode;
import com.esotericsoftware.spine.PathConstraintData.RotateMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode;
import com.esotericsoftware.spine.PathConstraintData.SpacingMode; import com.esotericsoftware.spine.PathConstraintData.SpacingMode;
@ -186,8 +187,7 @@ public class SkeletonBinary {
data.shearX = input.readFloat(); data.shearX = input.readFloat();
data.shearY = input.readFloat(); data.shearY = input.readFloat();
data.length = input.readFloat() * scale; data.length = input.readFloat() * scale;
data.inheritRotation = input.readBoolean(); data.transformMode = TransformMode.values[input.readInt(true)];
data.inheritScale = input.readBoolean();
if (nonessential) Color.rgba8888ToColor(data.color, input.readInt()); if (nonessential) Color.rgba8888ToColor(data.color, input.readInt());
skeletonData.bones.add(data); skeletonData.bones.add(data);
} }

View File

@ -56,6 +56,7 @@ import com.esotericsoftware.spine.Animation.ShearTimeline;
import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.Animation.Timeline;
import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
import com.esotericsoftware.spine.Animation.TranslateTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline;
import com.esotericsoftware.spine.BoneData.TransformMode;
import com.esotericsoftware.spine.PathConstraintData.PositionMode; import com.esotericsoftware.spine.PathConstraintData.PositionMode;
import com.esotericsoftware.spine.PathConstraintData.RotateMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode;
import com.esotericsoftware.spine.PathConstraintData.SpacingMode; import com.esotericsoftware.spine.PathConstraintData.SpacingMode;
@ -129,8 +130,7 @@ public class SkeletonJson {
data.scaleY = boneMap.getFloat("scaleY", 1); data.scaleY = boneMap.getFloat("scaleY", 1);
data.shearX = boneMap.getFloat("shearX", 0); data.shearX = boneMap.getFloat("shearX", 0);
data.shearY = boneMap.getFloat("shearY", 0); data.shearY = boneMap.getFloat("shearY", 0);
data.inheritRotation = boneMap.getBoolean("inheritRotation", true); data.transformMode = TransformMode.valueOf(boneMap.getString("transform", TransformMode.normal.name()));
data.inheritScale = boneMap.getBoolean("inheritScale", true);
String color = boneMap.getString("color", null); String color = boneMap.getString("color", null);
if (color != null) data.getColor().set(Color.valueOf(color)); if (color != null) data.getColor().set(Color.valueOf(color));

View File

@ -79,14 +79,14 @@ public class TransformConstraint implements Constraint {
} }
if (scaleMix > 0) { if (scaleMix > 0) {
float bs = (float)Math.sqrt(bone.a * bone.a + bone.c * bone.c); float s = (float)Math.sqrt(bone.a * bone.a + bone.c * bone.c);
float ts = (float)Math.sqrt(ta * ta + tc * tc); float ts = (float)Math.sqrt(ta * ta + tc * tc);
float s = bs > 0.00001f ? (bs + (ts - bs + data.offsetScaleX) * scaleMix) / bs : 0; if (s > 0.00001f) s = (s + (ts - s + data.offsetScaleX) * scaleMix) / s;
bone.a *= s; bone.a *= s;
bone.c *= s; bone.c *= s;
bs = (float)Math.sqrt(bone.b * bone.b + bone.d * bone.d); s = (float)Math.sqrt(bone.b * bone.b + bone.d * bone.d);
ts = (float)Math.sqrt(tb * tb + td * td); ts = (float)Math.sqrt(tb * tb + td * td);
s = bs > 0.00001f ? (bs + (ts - bs + data.offsetScaleY) * scaleMix) / bs : 0; if (s > 0.00001f) s = (s + (ts - s + data.offsetScaleY) * scaleMix) / s;
bone.b *= s; bone.b *= s;
bone.d *= s; bone.d *= s;
modified = true; modified = true;