This commit is contained in:
Stephen Gowen 2017-10-15 20:53:02 -04:00
parent 8ebb36daa9
commit 210e492d79
4 changed files with 853 additions and 332 deletions

View File

@ -33,361 +33,149 @@
#include <spine/Updatable.h> #include <spine/Updatable.h>
#include <spine/SimpleArray.h>
namespace Spine namespace Spine
{ {
class BoneData;
class Skeleton;
/// Stores a bone's current pose. /// Stores a bone's current pose.
/// ///
/// A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a /// A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a
/// local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a /// local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a
/// constraint or application code modifies the world transform after it was computed from the local transform. /// constraint or application code modifies the world transform after it was computed from the local transform.
///
class Bone : public Updatable class Bone : public Updatable
{ {
friend class RotateTimeline; friend class RotateTimeline;
public: public:
private:
static public bool yDown;
internal BoneData _data;
internal Skeleton _skeleton;
internal Bone _parent;
internal ExposedList<Bone> _children = new ExposedList<Bone>();
internal float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY;
internal float _ax, _ay, _arotation, _ascaleX, _ascaleY, _ashearX, _ashearY;
internal bool _appliedValid;
internal float _a, _b, _worldX;
internal float _c, _d, _worldY;
// internal float worldSignX, worldSignY;
// public float WorldSignX { get { return worldSignX; } }
// public float WorldSignY { get { return worldSignY; } }
internal bool _sorted;
public BoneData Data { get { return data; } }
public Skeleton Skeleton { get { return skeleton; } }
public Bone Parent { get { return parent; } }
public ExposedList<Bone> Children { get { return children; } }
/// The local X translation.
public float X { get { return x; } set { x = value; } }
/// The local Y translation.
public float Y { get { return y; } set { y = value; } }
/// The local rotation.
public float Rotation { get { return rotation; } set { rotation = value; } }
/// The local scaleX.
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
/// The local scaleY.
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
/// The local shearX.
public float ShearX { get { return shearX; } set { shearX = value; } }
/// The local shearY.
public float ShearY { get { return shearY; } set { shearY = value; } }
/// The rotation, as calculated by any constraints.
public float AppliedRotation { get { return arotation; } set { arotation = value; } }
/// The applied local x translation.
public float AX { get { return ax; } set { ax = value; } }
/// The applied local y translation.
public float AY { get { return ay; } set { ay = value; } }
/// The applied local scaleX.
public float AScaleX { get { return ascaleX; } set { ascaleX = value; } }
/// The applied local scaleY.
public float AScaleY { get { return ascaleY; } set { ascaleY = value; } }
/// The applied local shearX.
public float AShearX { get { return ashearX; } set { ashearX = value; } }
/// The applied local shearY.
public float AShearY { get { return ashearY; } set { ashearY = value; } }
public float A { get { return a; } }
public float B { get { return b; } }
public float C { get { return c; } }
public float D { get { return d; } }
public float WorldX { get { return worldX; } }
public float WorldY { get { return worldY; } }
public float WorldRotationX { get { return MathUtils.Atan2(c, a) * MathUtils.RadDeg; } }
public float WorldRotationY { get { return MathUtils.Atan2(d, b) * MathUtils.RadDeg; } }
/// Returns the magnitide (always positive) of the world scale X.
public float WorldScaleX { get { return (float)Math.Sqrt(a * a + c * c); } }
/// Returns the magnitide (always positive) of the world scale Y.
public float WorldScaleY { get { return (float)Math.Sqrt(b * b + d * d); } }
/// @param parent May be null. /// @param parent May be null.
public Bone (BoneData data, Skeleton skeleton, Bone parent) { Bone(BoneData& data, Skeleton& skeleton, Bone* parent);
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
this.data = data;
this.skeleton = skeleton;
this.parent = parent;
SetToSetupPose();
}
/// Same as <see cref="UpdateWorldTransform"/>. This method exists for Bone to implement <see cref="Spine.IUpdatable"/>. /// Same as updateWorldTransform. This method exists for Bone to implement Spine::Updatable.
public void Update () { virtual void update();
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
}
/// Computes the world transform using the parent bone and this bone's local transform. /// Computes the world transform using the parent bone and this bone's local transform.
public void UpdateWorldTransform () { void updateWorldTransform();
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
}
/// 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) { void updateWorldTransform(float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY);
ax = x;
ay = y;
arotation = rotation;
ascaleX = scaleX;
ascaleY = scaleY;
ashearX = shearX;
ashearY = shearY;
appliedValid = true;
Skeleton skeleton = this.skeleton;
Bone parent = this.parent;
if (parent == null) { // Root bone.
float rotationY = rotation + 90 + shearY;
float la = MathUtils.CosDeg(rotation + shearX) * scaleX;
float lb = MathUtils.CosDeg(rotationY) * scaleY;
float lc = MathUtils.SinDeg(rotation + shearX) * scaleX;
float ld = MathUtils.SinDeg(rotationY) * scaleY;
if (skeleton.flipX) {
x = -x;
la = -la;
lb = -lb;
}
if (skeleton.flipY != yDown) {
y = -y;
lc = -lc;
ld = -ld;
}
a = la;
b = lb;
c = lc;
d = ld;
worldX = x + skeleton.x;
worldY = y + skeleton.y;
// worldSignX = Math.Sign(scaleX);
// worldSignY = Math.Sign(scaleY);
return;
}
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
worldX = pa * x + pb * y + parent.worldX;
worldY = pc * x + pd * y + parent.worldY;
// worldSignX = parent.worldSignX * Math.Sign(scaleX);
// worldSignY = parent.worldSignY * Math.Sign(scaleY);
switch (data.transformMode) {
case TransformMode.Normal: {
float rotationY = rotation + 90 + shearY;
float la = MathUtils.CosDeg(rotation + shearX) * scaleX;
float lb = MathUtils.CosDeg(rotationY) * scaleY;
float lc = MathUtils.SinDeg(rotation + shearX) * scaleX;
float ld = MathUtils.SinDeg(rotationY) * scaleY;
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
return;
}
case TransformMode.OnlyTranslation: {
float rotationY = rotation + 90 + shearY;
a = MathUtils.CosDeg(rotation + shearX) * scaleX;
b = MathUtils.CosDeg(rotationY) * scaleY;
c = MathUtils.SinDeg(rotation + shearX) * scaleX;
d = MathUtils.SinDeg(rotationY) * 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 = rotation + shearX - prx;
float ry = rotation + shearY - prx + 90;
float la = MathUtils.CosDeg(rx) * scaleX;
float lb = MathUtils.CosDeg(ry) * scaleY;
float lc = MathUtils.SinDeg(rx) * scaleX;
float ld = MathUtils.SinDeg(ry) * scaleY;
a = pa * la - pb * lc;
b = pa * lb - pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
break;
}
case TransformMode.NoScale:
case TransformMode.NoScaleOrReflection: {
float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(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(shearX) * scaleX;
float lb = MathUtils.CosDeg(90 + shearY) * scaleY;
float lc = MathUtils.SinDeg(shearX) * scaleX;
float ld = MathUtils.SinDeg(90 + shearY) * scaleY;
if (data.transformMode != TransformMode.NoScaleOrReflection? pa * pd - pb* pc< 0 : skeleton.flipX != skeleton.flipY) {
zb = -zb;
zd = -zd;
}
a = za * la + zb * lc;
b = za * lb + zb * ld;
c = zc * la + zd * lc;
d = zc * lb + zd * ld;
return;
}
}
if (skeleton.flipX) {
a = -a;
b = -b;
}
if (skeleton.flipY != Bone.yDown) {
c = -c;
d = -d;
}
}
public void SetToSetupPose () { void setToSetupPose();
BoneData data = this.data;
x = data.x; void worldToLocal(float worldX, float worldY, float& outLocalX, float& outLocalY);
y = data.y;
rotation = data.rotation; void localToWorld(float localX, float localY, float& outWorldX, float& outWorldY);
scaleX = data.scaleX;
scaleY = data.scaleY; float worldToLocalRotation(float worldRotation);
shearX = data.shearX;
shearY = data.shearY; float localToWorldRotation(float localRotation);
}
///
/// Rotates the world transform the specified amount and sets isAppliedValid to false.
///
/// @param degrees Degrees.
void rotateWorld(float degrees);
float getWorldToLocalRotationX();
float getWorldToLocalRotationY();
BoneData& getData();
Skeleton& getSkeleton();
Bone* getParent();
SimpleArray<Bone*>& getChildren();
/// The local X translation.
float getX();
void setX(float inValue);
/// The local Y translation.
float getY();
void setY(float inValue);
/// The local rotation.
float getRotation();
void setRotation(float inValue);
/// The local scaleX.
float getScaleX();
void setScaleX(float inValue);
/// The local scaleY.
float getScaleY();
void setScaleY(float inValue);
/// The local shearX.
float getShearX();
void setShearX(float inValue);
/// The local shearY.
float getShearY();
void setShearY(float inValue);
/// The rotation, as calculated by any constraints.
float getAppliedRotation();
void setAppliedRotation(float inValue);
/// The applied local x translation.
float getAX();
void setAX(float inValue);
/// The applied local y translation.
float getAY();
void setAY(float inValue);
/// The applied local scaleX.
float getAScaleX();
void setAScaleX(float inValue);
/// The applied local scaleY.
float getAScaleY();
void setAScaleY(float inValue);
/// The applied local shearX.
float getAShearX();
void setAShearX(float inValue);
/// The applied local shearY.
float getAShearY();
void setAShearY(float inValue);
float getA();
float getB();
float getC();
float getD();
float getWorldX();
float getWorldY();
float getWorldRotationX();
float getWorldRotationY();
/// Returns the magnitide (always positive) of the world scale X.
float getWorldScaleX();
/// Returns the magnitide (always positive) of the world scale Y.
float getWorldScaleY();
private:
BoneData& _data;
Skeleton& _skeleton;
Bone* _parent;
SimpleArray<Bone*> _children;
float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY;
float _ax, _ay, _arotation, _ascaleX, _ascaleY, _ashearX, _ashearY;
bool _appliedValid;
float _a, _b, _worldX;
float _c, _d, _worldY;
bool _sorted;
///
/// Computes the individual applied transform values from the world transform. This can be useful to perform processing using /// Computes the individual applied transform values from the world transform. This can be useful to perform processing using
/// the applied transform 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)..
/// ///
/// Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. /// Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation.
/// void updateAppliedTransform();
internal void UpdateAppliedTransform () {
appliedValid = true;
Bone parent = this.parent;
if (parent == null) {
ax = worldX;
ay = worldY;
arotation = MathUtils.Atan2(c, a) * MathUtils.RadDeg;
ascaleX = (float)Math.Sqrt(a * a + c * c);
ascaleY = (float)Math.Sqrt(b * b + d * d);
ashearX = 0;
ashearY = MathUtils.Atan2(a * b + c * d, a * d - b * c) * MathUtils.RadDeg;
return;
}
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
float pid = 1 / (pa * pd - pb * pc);
float dx = worldX - parent.worldX, dy = worldY - parent.worldY;
ax = (dx * pd * pid - dy * pb * pid);
ay = (dy * pa * pid - dx * pc * pid);
float ia = pid * pd;
float id = pid * pa;
float ib = pid * pb;
float ic = pid * pc;
float ra = ia * a - ib * c;
float rb = ia * b - ib * d;
float rc = id * c - ic * a;
float rd = id * d - ic * b;
ashearX = 0;
ascaleX = (float)Math.Sqrt(ra * ra + rc * rc);
if (ascaleX > 0.0001f) {
float det = ra * rd - rb * rc;
ascaleY = det / ascaleX;
ashearY = MathUtils.Atan2(ra * rb + rc * rd, det) * MathUtils.RadDeg;
arotation = MathUtils.Atan2(rc, ra) * MathUtils.RadDeg;
} else {
ascaleX = 0;
ascaleY = (float)Math.Sqrt(rb * rb + rd * rd);
ashearY = 0;
arotation = 90 - MathUtils.Atan2(rd, rb) * MathUtils.RadDeg;
}
}
public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
float a = this.a, b = this.b, c = this.c, d = this.d;
float invDet = 1 / (a * d - b * c);
float x = worldX - this.worldX, y = worldY - this.worldY;
localX = (x * d * invDet - y * b * invDet);
localY = (y * a * invDet - x * c * invDet);
}
public void LocalToWorld (float localX, float localY, out float worldX, out float worldY) {
worldX = localX * a + localY * b + this.worldX;
worldY = localX * c + localY * d + this.worldY;
}
public float WorldToLocalRotationX {
get {
Bone parent = this.parent;
if (parent == null) return arotation;
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c;
return MathUtils.Atan2(pa * c - pc * a, pd * a - pb * c) * MathUtils.RadDeg;
}
}
public float WorldToLocalRotationY {
get {
Bone parent = this.parent;
if (parent == null) return arotation;
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d;
return MathUtils.Atan2(pa * d - pc * b, pd * b - pb * d) * MathUtils.RadDeg;
}
}
public float WorldToLocalRotation (float worldRotation) {
float sin = MathUtils.SinDeg(worldRotation), cos = MathUtils.CosDeg(worldRotation);
return MathUtils.Atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.RadDeg;
}
public float LocalToWorldRotation (float localRotation) {
float sin = MathUtils.SinDeg(localRotation), cos = MathUtils.CosDeg(localRotation);
return MathUtils.Atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.RadDeg;
}
///
/// Rotates the world transform the specified amount and sets isAppliedValid to false.
///
/// @param degrees Degrees.
public void RotateWorld (float degrees)
{
float a = this.a, b = this.b, c = this.c, d = this.d;
float cos = MathUtils.CosDeg(degrees), sin = MathUtils.SinDeg(degrees);
this.a = cos * a - sin * c;
this.b = cos * b - sin * d;
this.c = sin * a + cos * c;
this.d = sin * b + cos * d;
appliedValid = false;
}
}; };
} }

View File

@ -32,9 +32,69 @@
#define Spine_MathUtil_h #define Spine_MathUtil_h
#include <math.h> #include <math.h>
#include <float.h>
#define SPINE_PI 3.1415927f
#define SPINE_PI_2 PI * 2
#define RadDeg 180.0f / SPINE_PI
#define DegRad SPINE_PI / 180.0f
#define SIN_BITS 14 // 16KB. Adjust for accuracy.
#define SIN_MASK ~(-(1 << SIN_BITS))
#define SIN_COUNT SIN_MASK + 1
#define RadFull SPINE_PI * 2
#define DegFull 360
#define RadToIndex SIN_COUNT / RadFull
#define DegToIndex SIN_COUNT / DegFull
namespace Spine namespace Spine
{ {
inline bool areFloatsPracticallyEqual(float A, float B, float maxDiff = 0.0000000000000001f, float maxRelDiff = FLT_EPSILON)
{
// Check if the numbers are really close -- needed
// when comparing numbers near zero.
float diff = fabs(A - B);
if (diff <= maxDiff)
{
return true;
}
A = fabs(A);
B = fabs(B);
float largest = (B > A) ? B : A;
if (diff <= largest * maxRelDiff)
{
return true;
}
return false;
}
class MathUtil
{
public:
static float SIN_TABLE[SIN_COUNT];
MathUtil();
/// Returns the sine in radians from a lookup table.
static float sin(float radians);
/// Returns the cosine in radians from a lookup table.
static float cos(float radians);
/// Returns the sine in radians from a lookup table.
static float sinDeg(float degrees);
/// Returns the cosine in radians from a lookup table.
static float cosDeg(float degrees);
/// Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323
/// degrees), largest error of 0.00488 radians (0.2796 degrees).
static float atan2(float y, float x);
};
inline float clamp(float x, float lower, float upper) inline float clamp(float x, float lower, float upper)
{ {
return fminf(upper, fmaxf(x, lower)); return fminf(upper, fmaxf(x, lower));

View File

@ -30,7 +30,570 @@
#include <spine/Bone.h> #include <spine/Bone.h>
#include <spine/BoneData.h>
#include <spine/Skeleton.h>
#include <spine/MathUtil.h>
#include <spine/TransformMode.h>
namespace Spine namespace Spine
{ {
// TODO Bone::Bone(BoneData& data, Skeleton& skeleton, Bone* parent) : Updatable(),
_data(data),
_skeleton(skeleton),
_parent(parent),
_x(0),
_y(0),
_rotation(0),
_scaleX(0),
_scaleY(0),
_shearX(0),
_shearY(0),
_ax(0),
_ay(0),
_arotation(0),
_ascaleX(0),
_ascaleY(0),
_ashearX(0),
_ashearY(0),
_appliedValid(false),
_a(0),
_b(0),
_worldX(0),
_c(0),
_d(0),
_worldY(0),
_sorted(false)
{
setToSetupPose();
}
void Bone::update()
{
updateWorldTransform(_x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY);
}
void Bone::updateWorldTransform()
{
updateWorldTransform(_x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY);
}
void Bone::updateWorldTransform(float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY)
{
_ax = x;
_ay = y;
_arotation = rotation;
_ascaleX = scaleX;
_ascaleY = scaleY;
_ashearX = shearX;
_ashearY = shearY;
_appliedValid = true;
Skeleton& skeleton = _skeleton;
Bone* parent = _parent;
if (!parent)
{
// Root bone.
float rotationY = rotation + 90 + shearY;
float la = MathUtil::cosDeg(rotation + shearX) * scaleX;
float lb = MathUtil::cosDeg(rotationY) * scaleY;
float lc = MathUtil::sinDeg(rotation + shearX) * scaleX;
float ld = MathUtil::sinDeg(rotationY) * scaleY;
if (_skeleton.isFlipX())
{
x = -x;
la = -la;
lb = -lb;
}
_a = la;
_b = lb;
_c = lc;
_d = ld;
_worldX = x + _skeleton.getX();
_worldY = y + _skeleton.getY();
return;
}
float pa = parent->_a;
float pb = parent->_b;
float pc = parent->_c;
float pd = parent->_d;
_worldX = pa * x + pb * y + parent->_worldX;
_worldY = pc * x + pd * y + parent->_worldY;
switch (_data.getTransformMode())
{
case TransformMode_Normal:
{
float rotationY = rotation + 90 + shearY;
float la = MathUtil::cosDeg(rotation + shearX) * scaleX;
float lb = MathUtil::cosDeg(rotationY) * scaleY;
float lc = MathUtil::sinDeg(rotation + shearX) * scaleX;
float ld = MathUtil::sinDeg(rotationY) * scaleY;
_a = pa * la + pb * lc;
_b = pa * lb + pb * ld;
_c = pc * la + pd * lc;
_d = pc * lb + pd * ld;
return;
}
case TransformMode_OnlyTranslation:
{
float rotationY = rotation + 90 + shearY;
_a = MathUtil::cosDeg(rotation + shearX) * scaleX;
_b = MathUtil::cosDeg(rotationY) * scaleY;
_c = MathUtil::sinDeg(rotation + shearX) * scaleX;
_d = MathUtil::sinDeg(rotationY) * scaleY;
break;
}
case TransformMode_NoRotationOrReflection:
{
float s = pa * pa + pc * pc, prx;
if (s > 0.0001f)
{
s = fabs(pa * pd - pb * pc) / s;
pb = pc * s;
pd = pa * s;
prx = MathUtil::atan2(pc, pa) * RadDeg;
}
else
{
pa = 0;
pc = 0;
prx = 90 - MathUtil::atan2(pd, pb) * RadDeg;
}
float rx = rotation + shearX - prx;
float ry = rotation + shearY - prx + 90;
float la = MathUtil::cosDeg(rx) * scaleX;
float lb = MathUtil::cosDeg(ry) * scaleY;
float lc = MathUtil::sinDeg(rx) * scaleX;
float ld = MathUtil::sinDeg(ry) * scaleY;
_a = pa * la - pb * lc;
_b = pa * lb - pb * ld;
_c = pc * la + pd * lc;
_d = pc * lb + pd * ld;
break;
}
case TransformMode_NoScale:
case TransformMode_NoScaleOrReflection:
{
float cos = MathUtil::cosDeg(rotation);
float sin = MathUtil::sinDeg(rotation);
float za = pa * cos + pb * sin;
float zc = pc * cos + pd * sin;
float s = sqrt(za * za + zc * zc);
if (s > 0.00001f)
{
s = 1 / s;
}
za *= s;
zc *= s;
s = sqrt(za * za + zc * zc);
float r = SPINE_PI / 2 + MathUtil::atan2(zc, za);
float zb = MathUtil::cos(r) * s;
float zd = MathUtil::sin(r) * s;
float la = MathUtil::cosDeg(shearX) * scaleX;
float lb = MathUtil::cosDeg(90 + shearY) * scaleY;
float lc = MathUtil::sinDeg(shearX) * scaleX;
float ld = MathUtil::sinDeg(90 + shearY) * scaleY;
if (_data.getTransformMode() != TransformMode_NoScaleOrReflection ? pa * pd - pb * pc < 0 : _skeleton.isFlipX() != _skeleton.isFlipY())
{
zb = -zb;
zd = -zd;
}
_a = za * la + zb * lc;
_b = za * lb + zb * ld;
_c = zc * la + zd * lc;
_d = zc * lb + zd * ld;
return;
}
}
if (_skeleton.isFlipX())
{
_a = -_a;
_b = -_b;
}
}
void Bone::setToSetupPose()
{
BoneData& data = _data;
_x = data.getX();
_y = data.getY();
_rotation = data.getRotation();
_scaleX = data.getScaleX();
_scaleY = data.getScaleY();
_shearX = data.getShearX();
_shearY = data.getShearY();
}
void Bone::worldToLocal(float worldX, float worldY, float& outLocalX, float& outLocalY)
{
float a = _a;
float b = _b;
float c = _c;
float d = _d;
float invDet = 1 / (a * d - b * c);
float x = worldX - _worldX;
float y = worldY - _worldY;
outLocalX = (x * d * invDet - y * b * invDet);
outLocalY = (y * a * invDet - x * c * invDet);
}
void Bone::localToWorld(float localX, float localY, float& outWorldX, float& outWorldY)
{
outWorldX = localX * _a + localY * _b + _worldX;
outWorldY = localX * _c + localY * _d + _worldY;
}
float Bone::worldToLocalRotation(float worldRotation)
{
float sin = MathUtil::sinDeg(worldRotation);
float cos = MathUtil::cosDeg(worldRotation);
return MathUtil::atan2(_a * sin - _c * cos, _d * cos - _b * sin) * RadDeg;
}
float Bone::localToWorldRotation(float localRotation)
{
float sin = MathUtil::sinDeg(localRotation);
float cos = MathUtil::cosDeg(localRotation);
return MathUtil::atan2(cos * _c + sin * _d, cos * _a + sin * _b) * RadDeg;
}
void Bone::rotateWorld(float degrees)
{
float a = _a;
float b = _b;
float c = _c;
float d = _d;
float cos = MathUtil::cosDeg(degrees);
float sin = MathUtil::sinDeg(degrees);
_a = cos * a - sin * c;
_b = cos * b - sin * d;
_c = sin * a + cos * c;
_d = sin * b + cos * d;
_appliedValid = false;
}
float Bone::getWorldToLocalRotationX()
{
Bone* parent = _parent;
if (!parent)
{
return _arotation;
}
float pa = parent->_a;
float pb = parent->_b;
float pc = parent->_c;
float pd = parent->_d;
float a = _a;
float c = _c;
return MathUtil::atan2(pa * c - pc * a, pd * a - pb * c) * RadDeg;
}
float Bone::getWorldToLocalRotationY()
{
Bone* parent = _parent;
if (!parent)
{
return _arotation;
}
float pa = parent->_a;
float pb = parent->_b;
float pc = parent->_c;
float pd = parent->_d;
float b = _b;
float d = _d;
return MathUtil::atan2(pa * d - pc * b, pd * b - pb * d) * RadDeg;
}
BoneData& Bone::getData()
{
return _data;
}
Skeleton& Bone::getSkeleton()
{
return _skeleton;
}
Bone* Bone::getParent()
{
return _parent;
}
SimpleArray<Bone*>& Bone::getChildren()
{
return _children;
}
float Bone::getX()
{
return _x;
}
void Bone::setX(float inValue)
{
_x = inValue;
}
float Bone::getY()
{
return _y;
}
void Bone::setY(float inValue)
{
_y = inValue;
}
float Bone::getRotation()
{
return _rotation;
}
void Bone::setRotation(float inValue)
{
_rotation = inValue;
}
float Bone::getScaleX()
{
return _scaleX;
}
void Bone::setScaleX(float inValue)
{
_scaleX = inValue;
}
float Bone::getScaleY()
{
return _scaleY;
}
void Bone::setScaleY(float inValue)
{
_scaleY = inValue;
}
float Bone::getShearX()
{
return _shearX;
}
void Bone::setShearX(float inValue)
{
_shearX = inValue;
}
float Bone::getShearY()
{
return _shearY;
}
void Bone::setShearY(float inValue)
{
_shearY = inValue;
}
float Bone::getAppliedRotation()
{
return _arotation;
}
void Bone::setAppliedRotation(float inValue)
{
_arotation = inValue;
}
float Bone::getAX()
{
return _ax;
}
void Bone::setAX(float inValue)
{
_ax = inValue;
}
float Bone::getAY()
{
return _ay;
}
void Bone::setAY(float inValue)
{
_ay = inValue;
}
float Bone::getAScaleX()
{
return _ascaleX;
}
void Bone::setAScaleX(float inValue)
{
_ascaleX = inValue;
}
float Bone::getAScaleY()
{
return _ascaleY;
}
void Bone::setAScaleY(float inValue)
{
_ascaleY = inValue;
}
float Bone::getAShearX()
{
return _ashearX;
}
void Bone::setAShearX(float inValue)
{
_ashearX = inValue;
}
float Bone::getAShearY()
{
return _ashearY;
}
void Bone::setAShearY(float inValue)
{
_ashearY = inValue;
}
float Bone::getA()
{
return _a;
}
float Bone::getB()
{
return _b;
}
float Bone::getC()
{
return _c;
}
float Bone::getD()
{
return _d;
}
float Bone::getWorldX()
{
return _worldX;
}
float Bone::getWorldY()
{
return _worldY;
}
float Bone::getWorldRotationX()
{
return MathUtil::atan2(_c, _a) * RadDeg;
}
float Bone::getWorldRotationY()
{
return MathUtil::atan2(_d, _b) * RadDeg;
}
float Bone::getWorldScaleX()
{
return sqrt(_a * _a + _c * _c);
}
float Bone::getWorldScaleY()
{
return sqrt(_b * _b + _d * _d);
}
void Bone::updateAppliedTransform()
{
_appliedValid = true;
Bone* parent = _parent;
if (!parent)
{
_ax = _worldX;
_ay = _worldY;
_arotation = MathUtil::atan2(_c, _a) * RadDeg;
_ascaleX = sqrt(_a * _a + _c * _c);
_ascaleY = sqrt(_b * _b + _d * _d);
_ashearX = 0;
_ashearY = MathUtil::atan2(_a * _b + _c * _d, _a * _d - _b * _c) * RadDeg;
return;
}
float pa = parent->_a;
float pb = parent->_b;
float pc = parent->_c;
float pd = parent->_d;
float pid = 1 / (pa * pd - pb * pc);
float dx = _worldX - parent->_worldX;
float dy = _worldY - parent->_worldY;
_ax = (dx * pd * pid - dy * pb * pid);
_ay = (dy * pa * pid - dx * pc * pid);
float ia = pid * pd;
float id = pid * pa;
float ib = pid * pb;
float ic = pid * pc;
float ra = ia * _a - ib * _c;
float rb = ia * _b - ib * _d;
float rc = id * _c - ic * _a;
float rd = id * _d - ic * _b;
_ashearX = 0;
_ascaleX = sqrt(ra * ra + rc * rc);
if (_ascaleX > 0.0001f)
{
float det = ra * rd - rb * rc;
_ascaleY = det / _ascaleX;
_ashearY = MathUtil::atan2(ra * rb + rc * rd, det) * RadDeg;
_arotation = MathUtil::atan2(rc, ra) * RadDeg;
}
else
{
_ascaleX = 0;
_ascaleY = sqrt(rb * rb + rd * rd);
_ashearY = 0;
_arotation = 90 - MathUtil::atan2(rd, rb) * RadDeg;
}
}
} }

View File

@ -0,0 +1,110 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, Esoteric Software
* All rights reserved.
*
* You are granted a perpetual, non-exclusive, non-sublicensable, and
* non-transferable license to use, install, execute, and perform the Spine
* Runtimes software and derivative works solely for personal or internal
* use. Without the written permission of Esoteric Software (see Section 2 of
* the Spine Software License Agreement), you may not (a) modify, translate,
* adapt, or develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes or (b) remove,
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
* or other intellectual property or proprietary rights notices on or in the
* Software, including any copy thereof. Redistributions in binary or source
* form must include this license and terms.
*
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <spine/MathUtil.h>
namespace Spine
{
float MathUtil::SIN_TABLE[SIN_COUNT] = {0.0f};
MathUtil::MathUtil()
{
for (int i = 0; i < SIN_COUNT; ++i)
{
SIN_TABLE[i] = (float)sin((i + 0.5f) / SIN_COUNT * RadFull);
}
for (int i = 0; i < 360; i += 90)
{
SIN_TABLE[(int)(i * DegToIndex) & SIN_MASK] = (float)sin(i * DegRad);
}
}
/// Returns the sine in radians from a lookup table.
float MathUtil::sin(float radians)
{
return SIN_TABLE[(int)(radians * RadToIndex) & SIN_MASK];
}
/// Returns the cosine in radians from a lookup table.
float MathUtil::cos(float radians)
{
return SIN_TABLE[(int)((radians + SPINE_PI / 2) * RadToIndex) & SIN_MASK];
}
/// Returns the sine in radians from a lookup table.
float MathUtil::sinDeg(float degrees)
{
return SIN_TABLE[(int)(degrees * DegToIndex) & SIN_MASK];
}
/// Returns the cosine in radians from a lookup table.
float MathUtil::cosDeg(float degrees)
{
return SIN_TABLE[(int)((degrees + 90) * DegToIndex) & SIN_MASK];
}
/// Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323
/// degrees), largest error of 0.00488 radians (0.2796 degrees).
float MathUtil::atan2(float y, float x)
{
if (areFloatsPracticallyEqual(x, 0.0f))
{
if (y > 0.0f)
{
return SPINE_PI / 2;
}
if (areFloatsPracticallyEqual(y, 0.0f))
{
return 0.0f;
}
return -SPINE_PI / 2;
}
float atan, z = y / x;
if (fabs(z) < 1.0f)
{
atan = z / (1.0f + 0.28f * z * z);
if (x < 0.0f)
{
return atan + (y < 0.0f ? -SPINE_PI : SPINE_PI);
}
return atan;
}
atan = SPINE_PI / 2 - z / (z * z + 0.28f);
return y < 0.0f ? atan - SPINE_PI : atan;
}
}