/****************************************************************************** * Spine Runtimes License Agreement * Last updated January 1, 2020. Replaces all prior versions. * * Copyright (c) 2013-2020, Esoteric Software LLC * * Integration of the Spine Runtimes into software or otherwise creating * derivative works of the Spine Runtimes is permitted under the terms and * conditions of Section 2 of the Spine Editor License Agreement: * http://esotericsoftware.com/spine-editor-license * * Otherwise, it is permitted to integrate the Spine Runtimes into software * or otherwise create derivative works of the Spine Runtimes (collectively, * "Products"), provided that each user of the Products must obtain their own * Spine Editor license and redistribution of the Products in any form must * include this license and copyright notice. * * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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 * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #ifdef SPINE_UE4 #include "SpinePluginPrivatePCH.h" #endif #include #include #include using namespace spine; RTTI_IMPL(Bone, Updatable) bool Bone::yDown = false; void Bone::setYDown(bool inValue) { yDown = inValue; } bool Bone::isYDown() { return yDown; } 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(1), _b(0), _worldX(0), _c(0), _d(1), _worldY(0), _sorted(false), _active(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) { float cosine, sine; float pa, pb, pc, pd; Bone *parent = _parent; _ax = x; _ay = y; _arotation = rotation; _ascaleX = scaleX; _ascaleY = scaleY; _ashearX = shearX; _ashearY = shearY; _appliedValid = true; if (!parent) { /* Root bone. */ float rotationY = rotation + 90 + shearY; float sx = _skeleton.getScaleX(); float sy = _skeleton.getScaleY(); _a = MathUtil::cosDeg(rotation + shearX) * scaleX * sx; _b = MathUtil::cosDeg(rotationY) * scaleY * sx; _c = MathUtil::sinDeg(rotation + shearX) * scaleX * sy; _d = MathUtil::sinDeg(rotationY) * scaleY * sy; _worldX = x * sx + _skeleton.getX(); _worldY = y * sy + _skeleton.getY(); return; } 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; 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; float prx, rx, ry, la, lb, lc, ld; if (s > 0.0001f) { s = MathUtil::abs(pa * pd - pb * pc) / s; pb = pc * s; pd = pa * s; prx = MathUtil::atan2(pc, pa) * MathUtil::Rad_Deg; } else { pa = 0; pc = 0; prx = 90 - MathUtil::atan2(pd, pb) * MathUtil::Rad_Deg; } rx = rotation + shearX - prx; ry = rotation + shearY - prx + 90; la = MathUtil::cosDeg(rx) * scaleX; lb = MathUtil::cosDeg(ry) * scaleY; lc = MathUtil::sinDeg(rx) * scaleX; 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 za, zc, s; float r, zb, zd, la, lb, lc, ld; cosine = MathUtil::cosDeg(rotation); sine = MathUtil::sinDeg(rotation); za = (pa * cosine + pb * sine) / _skeleton.getScaleX(); zc = (pc * cosine + pd * sine) / _skeleton.getScaleY(); s = MathUtil::sqrt(za * za + zc * zc); if (s > 0.00001f) s = 1 / s; za *= s; zc *= s; s = MathUtil::sqrt(za * za + zc * zc); if (_data.getTransformMode() == TransformMode_NoScale && (pa * pd - pb * pc < 0) != (_skeleton.getScaleX() < 0 != _skeleton.getScaleY() < 0)) s = -s; r = MathUtil::Pi / 2 + MathUtil::atan2(zc, za); zb = MathUtil::cos(r) * s; zd = MathUtil::sin(r) * s; la = MathUtil::cosDeg(shearX) * scaleX; lb = MathUtil::cosDeg(90 + shearY) * scaleY; lc = MathUtil::sinDeg(shearX) * scaleX; ld = MathUtil::sinDeg(90 + shearY) * scaleY; _a = za * la + zb * lc; _b = za * lb + zb * ld; _c = zc * la + zd * lc; _d = zc * lb + zd * ld; break; } } _a *= _skeleton.getScaleX(); _b *= _skeleton.getScaleX(); _c *= _skeleton.getScaleY(); _d *= _skeleton.getScaleY(); } 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) * MathUtil::Rad_Deg + this->_rotation - this->_shearX; } float Bone::localToWorldRotation(float localRotation) { localRotation -= this->_rotation - this->_shearX; float sin = MathUtil::sinDeg(localRotation); float cos = MathUtil::cosDeg(localRotation); return MathUtil::atan2(cos * _c + sin * _d, cos * _a + sin * _b) * MathUtil::Rad_Deg; } 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) * MathUtil::Rad_Deg; } 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) * MathUtil::Rad_Deg; } BoneData &Bone::getData() { return _data; } Skeleton &Bone::getSkeleton() { return _skeleton; } Bone *Bone::getParent() { return _parent; } Vector &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; } void Bone::setA(float inValue) { _a = inValue; } float Bone::getB() { return _b; } void Bone::setB(float inValue) { _b = inValue; } float Bone::getC() { return _c; } void Bone::setC(float inValue) { _c = inValue; } float Bone::getD() { return _d; } void Bone::setD(float inValue) { _d = inValue; } float Bone::getWorldX() { return _worldX; } void Bone::setWorldX(float inValue) { _worldX = inValue; } float Bone::getWorldY() { return _worldY; } void Bone::setWorldY(float inValue) { _worldY = inValue; } float Bone::getWorldRotationX() { return MathUtil::atan2(_c, _a) * MathUtil::MathUtil::Rad_Deg; } float Bone::getWorldRotationY() { return MathUtil::atan2(_d, _b) * MathUtil::Rad_Deg; } float Bone::getWorldScaleX() { return MathUtil::sqrt(_a * _a + _c * _c); } float Bone::getWorldScaleY() { return MathUtil::sqrt(_b * _b + _d * _d); } bool Bone::isAppliedValid() { return _appliedValid; } void Bone::setAppliedValid(bool valid) { _appliedValid = valid; } void Bone::updateAppliedTransform() { Bone *parent = _parent; _appliedValid = 1; if (!parent) { _ax = _worldX; _ay = _worldY; _arotation = MathUtil::atan2(_c, _a) * MathUtil::Rad_Deg; _ascaleX = MathUtil::sqrt(_a * _a + _c * _c); _ascaleY = MathUtil::sqrt(_b * _b + _d * _d); _ashearX = 0; _ashearY = MathUtil::atan2(_a * _b + _c * _d, _a * _d - _b * _c) * MathUtil::Rad_Deg; } else { 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; 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; _ax = (dx * pd * pid - dy * pb * pid); _ay = (dy * pa * pid - dx * pc * pid); _ashearX = 0; _ascaleX = MathUtil::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) * MathUtil::Rad_Deg; _arotation = MathUtil::atan2(rc, ra) * MathUtil::Rad_Deg; } else { _ascaleX = 0; _ascaleY = MathUtil::sqrt(rb * rb + rd * rd); _ashearY = 0; _arotation = 90 - MathUtil::atan2(rd, rb) * MathUtil::Rad_Deg; } } } bool Bone::isActive() { return _active; } void Bone::setActive(bool inValue) { _active = inValue; }