diff --git a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs
index 00797dc25..841209869 100644
--- a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs
+++ b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs
@@ -283,8 +283,150 @@ namespace Spine.Unity {
}
namespace Spine {
+ using System;
using System.Collections.Generic;
+ public struct BoneMatrix {
+ public float a, b, c, d, x, y;
+
+ /// Recursively calculates a worldspace bone matrix based on BoneData.
+ public static BoneMatrix CalculateSetupWorld (BoneData boneData) {
+ if (boneData == null)
+ return default(BoneMatrix);
+
+ // End condition: isRootBone
+ if (boneData.parent == null)
+ return GetInheritedInternal(boneData, default(BoneMatrix));
+
+ BoneMatrix result = CalculateSetupWorld(boneData.parent);
+ return GetInheritedInternal(boneData, result);
+ }
+
+ static BoneMatrix GetInheritedInternal (BoneData boneData, BoneMatrix parentMatrix) {
+ var parent = boneData.parent;
+ if (parent == null) return new BoneMatrix(boneData); // isRootBone
+
+ float pa = parentMatrix.a, pb = parentMatrix.b, pc = parentMatrix.c, pd = parentMatrix.d;
+ BoneMatrix result = default(BoneMatrix);
+ result.x = pa * boneData.x + pb * boneData.y + parentMatrix.x;
+ result.y = pc * boneData.x + pd * boneData.y + parentMatrix.y;
+
+ switch (boneData.transformMode) {
+ case TransformMode.Normal: {
+ float rotationY = boneData.rotation + 90 + boneData.shearY;
+ float la = MathUtils.CosDeg(boneData.rotation + boneData.shearX) * boneData.scaleX;
+ float lb = MathUtils.CosDeg(rotationY) * boneData.scaleY;
+ float lc = MathUtils.SinDeg(boneData.rotation + boneData.shearX) * boneData.scaleX;
+ float ld = MathUtils.SinDeg(rotationY) * boneData.scaleY;
+ result.a = pa * la + pb * lc;
+ result.b = pa * lb + pb * ld;
+ result.c = pc * la + pd * lc;
+ result.d = pc * lb + pd * ld;
+ break;
+ }
+ case TransformMode.OnlyTranslation: {
+ float rotationY = boneData.rotation + 90 + boneData.shearY;
+ result.a = MathUtils.CosDeg(boneData.rotation + boneData.shearX) * boneData.scaleX;
+ result.b = MathUtils.CosDeg(rotationY) * boneData.scaleY;
+ result.c = MathUtils.SinDeg(boneData.rotation + boneData.shearX) * boneData.scaleX;
+ result.d = MathUtils.SinDeg(rotationY) * boneData.scaleY;
+ break;
+ }
+ case TransformMode.NoRotationOrReflection: {
+ float s = pa * pa + pc * pc, prx;
+ if (s > 0.0001f) {
+ s = Math.Abs(pa * pd - pb * pc) / s;
+ pb = pc * s;
+ pd = pa * s;
+ prx = MathUtils.Atan2(pc, pa) * MathUtils.RadDeg;
+ } else {
+ pa = 0;
+ pc = 0;
+ prx = 90 - MathUtils.Atan2(pd, pb) * MathUtils.RadDeg;
+ }
+ float rx = boneData.rotation + boneData.shearX - prx;
+ float ry = boneData.rotation + boneData.shearY - prx + 90;
+ float la = MathUtils.CosDeg(rx) * boneData.scaleX;
+ float lb = MathUtils.CosDeg(ry) * boneData.scaleY;
+ float lc = MathUtils.SinDeg(rx) * boneData.scaleX;
+ float ld = MathUtils.SinDeg(ry) * boneData.scaleY;
+ result.a = pa * la - pb * lc;
+ result.b = pa * lb - pb * ld;
+ result.c = pc * la + pd * lc;
+ result.d = pc * lb + pd * ld;
+ break;
+ }
+ case TransformMode.NoScale:
+ case TransformMode.NoScaleOrReflection: {
+ float cos = MathUtils.CosDeg(boneData.rotation), sin = MathUtils.SinDeg(boneData.rotation);
+ float za = pa * cos + pb * sin;
+ float zc = pc * cos + pd * sin;
+ float s = (float)Math.Sqrt(za * za + zc * zc);
+ if (s > 0.00001f)
+ s = 1 / s;
+ za *= s;
+ zc *= s;
+ s = (float)Math.Sqrt(za * za + zc * zc);
+ float r = MathUtils.PI / 2 + MathUtils.Atan2(zc, za);
+ float zb = MathUtils.Cos(r) * s;
+ float zd = MathUtils.Sin(r) * s;
+ float la = MathUtils.CosDeg(boneData.shearX) * boneData.scaleX;
+ float lb = MathUtils.CosDeg(90 + boneData.shearY) * boneData.scaleY;
+ float lc = MathUtils.SinDeg(boneData.shearX) * boneData.scaleX;
+ float ld = MathUtils.SinDeg(90 + boneData.shearY) * boneData.scaleY;
+ if (boneData.transformMode != TransformMode.NoScaleOrReflection ? pa * pd - pb * pc < 0 : false) {
+ zb = -zb;
+ zd = -zd;
+ }
+ result.a = za * la + zb * lc;
+ result.b = za * lb + zb * ld;
+ result.c = zc * la + zd * lc;
+ result.d = zc * lb + zd * ld;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ /// Constructor for a local bone matrix based on Setup Pose BoneData.
+ public BoneMatrix (BoneData boneData) {
+ float rotationY = boneData.rotation + 90 + boneData.shearY;
+ float rotationX = boneData.rotation + boneData.shearX;
+
+ a = MathUtils.CosDeg(rotationX) * boneData.scaleX;
+ c = MathUtils.SinDeg(rotationX) * boneData.scaleX;
+ b = MathUtils.CosDeg(rotationY) * boneData.scaleY;
+ d = MathUtils.SinDeg(rotationY) * boneData.scaleY;
+ x = boneData.x;
+ y = boneData.y;
+ }
+
+ /// Constructor for a local bone matrix based on a bone instance's current pose.
+ public BoneMatrix (Bone bone) {
+ float rotationY = bone.rotation + 90 + bone.shearY;
+ float rotationX = bone.rotation + bone.shearX;
+
+ a = MathUtils.CosDeg(rotationX) * bone.scaleX;
+ c = MathUtils.SinDeg(rotationX) * bone.scaleX;
+ b = MathUtils.CosDeg(rotationY) * bone.scaleY;
+ d = MathUtils.SinDeg(rotationY) * bone.scaleY;
+ x = bone.x;
+ y = bone.y;
+ }
+
+ public BoneMatrix TransformMatrix (BoneMatrix local) {
+ return new BoneMatrix {
+ a = this.a * local.a + this.b * local.c,
+ b = this.a * local.b + this.b * local.d,
+ c = this.c * local.a + this.d * local.c,
+ d = this.c * local.b + this.d * local.d,
+ x = this.a * local.x + this.b * local.y + this.x,
+ y = this.c * local.x + this.d * local.y + this.y
+ };
+ }
+ }
+
public static class SkeletonExtensions {
public static bool IsWeighted (this VertexAttachment va) {
return va.bones != null && va.bones.Length > 0;