diff --git a/spine-cpp/spine-cpp/include/spine/Bone.h b/spine-cpp/spine-cpp/include/spine/Bone.h index 845fc58df..db1bc42d8 100644 --- a/spine-cpp/spine-cpp/include/spine/Bone.h +++ b/spine-cpp/spine-cpp/include/spine/Bone.h @@ -30,7 +30,7 @@ #ifndef Spine_Bone_h #define Spine_Bone_h -#include +#include #include #include #include @@ -45,7 +45,7 @@ namespace spine { /// 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 /// constraint or application code modifies the world transform after it was computed from the local transform. - class SP_API Bone : public Updatable { + class SP_API Bone : public Update { friend class AnimationState; friend class RotateTimeline; diff --git a/spine-cpp/spine-cpp/include/spine/BoneLocal.h b/spine-cpp/spine-cpp/include/spine/BoneLocal.h index 2d0c40eb2..35f0a6d01 100644 --- a/spine-cpp/spine-cpp/include/spine/BoneLocal.h +++ b/spine-cpp/spine-cpp/include/spine/BoneLocal.h @@ -40,7 +40,7 @@ namespace spine { class SP_API BoneLocal : public Pose { RTTI_DECL - private: + protected: float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY; Inherit _inherit; diff --git a/spine-cpp/spine-cpp/include/spine/BonePose.h b/spine-cpp/spine-cpp/include/spine/BonePose.h new file mode 100644 index 000000000..e95981855 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/BonePose.h @@ -0,0 +1,141 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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. + *****************************************************************************/ + +#ifndef SPINE_BONEPOSE_H_ +#define SPINE_BONEPOSE_H_ + +#include +#include +#include + +namespace spine { + class Bone; + class Skeleton; + + class SP_API BonePose : public BoneLocal, public Update { + RTTI_DECL + + public: + Bone* _bone; + float _a, _b, _worldX; + float _c, _d, _worldY; + int _world, _local; + + BonePose(); + virtual ~BonePose(); + + /// Called by Skeleton::updateCache() to compute the world transform, if needed. + virtual void update(Skeleton& skeleton, Physics physics) override; + + /// Computes the world transform using the parent bone's applied pose and this pose. Child bones are not updated. + /// + /// See World transforms in the Spine + /// Runtimes Guide. + void updateWorldTransform(Skeleton& skeleton); + + /// Computes the local transform values from the world transform. + /// + /// If the world transform is modified (by a constraint, rotateWorld(), etc) then this method should be called so + /// the local transform matches the world transform. The local transform may be needed by other code (eg to apply another + /// constraint). + /// + /// Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The local transform after + /// calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. + void updateLocalTransform(Skeleton& skeleton); + + /// If the world transform has been modified and the local transform no longer matches, updateLocalTransform() is called. + void validateLocalTransform(Skeleton& skeleton); + + void modifyLocal(Skeleton& skeleton); + void modifyWorld(int update); + void resetWorld(int update); + + /// Part of the world transform matrix for the X axis. If changed, updateLocalTransform() should be called. + float getA(); + void setA(float a); + + /// Part of the world transform matrix for the Y axis. If changed, updateLocalTransform() should be called. + float getB(); + void setB(float b); + + /// Part of the world transform matrix for the X axis. If changed, updateLocalTransform() should be called. + float getC(); + void setC(float c); + + /// Part of the world transform matrix for the Y axis. If changed, updateLocalTransform() should be called. + float getD(); + void setD(float d); + + /// The world X position. If changed, updateLocalTransform() should be called. + float getWorldX(); + void setWorldX(float worldX); + + /// The world Y position. If changed, updateLocalTransform() should be called. + float getWorldY(); + void setWorldY(float worldY); + + /// The world rotation for the X axis, calculated using a and c. + float getWorldRotationX(); + + /// The world rotation for the Y axis, calculated using b and d. + float getWorldRotationY(); + + /// The magnitude (always positive) of the world scale X, calculated using a and c. + float getWorldScaleX(); + + /// The magnitude (always positive) of the world scale Y, calculated using b and d. + float getWorldScaleY(); + + /// Transforms a point from world coordinates to the bone's local coordinates. + void worldToLocal(float worldX, float worldY, float& outLocalX, float& outLocalY); + + /// Transforms a point from the bone's local coordinates to world coordinates. + void localToWorld(float localX, float localY, float& outWorldX, float& outWorldY); + + /// Transforms a point from world coordinates to the parent bone's local coordinates. + void worldToParent(float worldX, float worldY, float& outParentX, float& outParentY); + + /// Transforms a point from the parent bone's coordinates to world coordinates. + void parentToWorld(float parentX, float parentY, float& outWorldX, float& outWorldY); + + /// Transforms a world rotation to a local rotation. + float worldToLocalRotation(float worldRotation); + + /// Transforms a local rotation to a world rotation. + float localToWorldRotation(float localRotation); + + /// Rotates the world transform the specified amount. + /// + /// After changes are made to the world transform, updateLocalTransform() should be called on this bone and any + /// child bones, recursively. + void rotateWorld(float degrees); + }; +} + +#endif /* SPINE_BONEPOSE_H_ */ \ No newline at end of file diff --git a/spine-cpp/spine-cpp/include/spine/FromProperty.h b/spine-cpp/spine-cpp/include/spine/FromProperty.h new file mode 100644 index 000000000..9de7a72d6 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/FromProperty.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated July 28, 2023. Replaces all prior versions. + * + * Copyright (c) 2013-2023, 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. + *****************************************************************************/ + +#ifndef SPINE_FROMPROPERTY_H_ +#define SPINE_FROMPROPERTY_H_ + +#include +#include + +namespace spine { + class ToProperty; + class BonePose; + + /// Source property for a TransformConstraint. + class SP_API FromProperty { + public: + FromProperty(); + virtual ~FromProperty(); + + /// The value of this property that corresponds to ToProperty::offset. + float offset; + + /// Constrained properties. + Vector to; + + /// Reads this property from the specified bone. + virtual float value(BonePose& source, bool local, Vector& offsets) = 0; + }; +} + +#endif /* SPINE_FROMPROPERTY_H_ */ \ No newline at end of file diff --git a/spine-cpp/spine-cpp/include/spine/spine.h b/spine-cpp/spine-cpp/include/spine/spine.h index dbd8db972..32304c78e 100644 --- a/spine-cpp/spine-cpp/include/spine/spine.h +++ b/spine-cpp/spine-cpp/include/spine/spine.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ #include #include #include +#include #include #include #include diff --git a/spine-cpp/spine-cpp/src/spine/BonePose.cpp b/spine-cpp/spine-cpp/src/spine/BonePose.cpp new file mode 100644 index 000000000..2cd8e1a1d --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/BonePose.cpp @@ -0,0 +1,388 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated July 28, 2023. Replaces all prior versions. + * + * Copyright (c) 2013-2023, 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. + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +using namespace spine; + +RTTI_IMPL(BonePose, BoneLocal) + +BonePose::BonePose() : BoneLocal(), _bone(nullptr), _a(0), _b(0), _worldX(0), _c(0), _d(0), _worldY(0), _world(0), _local(0) { +} + +BonePose::~BonePose() { +} + +void BonePose::update(Skeleton& skeleton, Physics physics) { + if (_world != skeleton.getUpdate()) updateWorldTransform(skeleton); +} + +void BonePose::updateWorldTransform(Skeleton& skeleton) { + if (_local == skeleton.getUpdate()) + updateLocalTransform(skeleton); + else + _world = skeleton.getUpdate(); + + if (_bone->getParent() == nullptr) { // Root bone. + float sx = skeleton.getScaleX(), sy = skeleton.getScaleY(); + float rx = (_rotation + _shearX) * MathUtil::Deg_Rad; + float ry = (_rotation + 90 + _shearY) * MathUtil::Deg_Rad; + _a = MathUtil::cos(rx) * _scaleX * sx; + _b = MathUtil::cos(ry) * _scaleY * sx; + _c = MathUtil::sin(rx) * _scaleX * sy; + _d = MathUtil::sin(ry) * _scaleY * sy; + _worldX = _x * sx + skeleton.getX(); + _worldY = _y * sy + skeleton.getY(); + return; + } + + BonePose& parent = _bone->getParent()->getApplied(); + 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; + + switch (_inherit) { + case Inherit_Normal: { + float rx = (_rotation + _shearX) * MathUtil::Deg_Rad; + float ry = (_rotation + 90 + _shearY) * MathUtil::Deg_Rad; + float la = MathUtil::cos(rx) * _scaleX; + float lb = MathUtil::cos(ry) * _scaleY; + float lc = MathUtil::sin(rx) * _scaleX; + float ld = MathUtil::sin(ry) * _scaleY; + _a = pa * la + pb * lc; + _b = pa * lb + pb * ld; + _c = pc * la + pd * lc; + _d = pc * lb + pd * ld; + return; + } + case Inherit_OnlyTranslation: { + float rx = (_rotation + _shearX) * MathUtil::Deg_Rad; + float ry = (_rotation + 90 + _shearY) * MathUtil::Deg_Rad; + _a = MathUtil::cos(rx) * _scaleX; + _b = MathUtil::cos(ry) * _scaleY; + _c = MathUtil::sin(rx) * _scaleX; + _d = MathUtil::sin(ry) * _scaleY; + break; + } + case Inherit_NoRotationOrReflection: { + float sx = 1 / skeleton.getScaleX(), sy = 1 / skeleton.getScaleY(); + pa *= sx; + pc *= sy; + float s = pa * pa + pc * pc, prx; + if (s > 0.0001f) { + s = MathUtil::abs(pa * pd * sy - pb * sx * 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; + } + float rx = (_rotation + _shearX - prx) * MathUtil::Deg_Rad; + float ry = (_rotation + _shearY - prx + 90) * MathUtil::Deg_Rad; + float la = MathUtil::cos(rx) * _scaleX; + float lb = MathUtil::cos(ry) * _scaleY; + float lc = MathUtil::sin(rx) * _scaleX; + float ld = MathUtil::sin(ry) * _scaleY; + _a = pa * la - pb * lc; + _b = pa * lb - pb * ld; + _c = pc * la + pd * lc; + _d = pc * lb + pd * ld; + break; + } + case Inherit_NoScale: + case Inherit_NoScaleOrReflection: { + float rot = _rotation * MathUtil::Deg_Rad; + float cosRot = MathUtil::cos(rot), sinRot = MathUtil::sin(rot); + float za = (pa * cosRot + pb * sinRot) / skeleton.getScaleX(); + float zc = (pc * cosRot + pd * sinRot) / skeleton.getScaleY(); + float 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 (_inherit == Inherit_NoScale && (pa * pd - pb * pc < 0) != (skeleton.getScaleX() < 0 != skeleton.getScaleY() < 0)) s = -s; + rot = MathUtil::Pi / 2 + MathUtil::atan2(zc, za); + float zb = MathUtil::cos(rot) * s; + float zd = MathUtil::sin(rot) * s; + float _shearXRad = _shearX * MathUtil::Deg_Rad; + float _shearYRad = (90 + _shearY) * MathUtil::Deg_Rad; + float la = MathUtil::cos(_shearXRad) * _scaleX; + float lb = MathUtil::cos(_shearYRad) * _scaleY; + float lc = MathUtil::sin(_shearXRad) * _scaleX; + float ld = MathUtil::sin(_shearYRad) * _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 BonePose::updateLocalTransform(Skeleton& skeleton) { + _local = 0; + _world = skeleton.getUpdate(); + + if (_bone->getParent() == nullptr) { + _x = _worldX - skeleton.getX(); + _y = _worldY - skeleton.getY(); + float _a = this->_a, _b = this->_b, _c = this->_c, _d = this->_d; + _rotation = MathUtil::atan2(_c, _a) * MathUtil::Rad_Deg; + _scaleX = MathUtil::sqrt(_a * _a + _c * _c); + _scaleY = MathUtil::sqrt(_b * _b + _d * _d); + _shearX = 0; + _shearY = MathUtil::atan2(_a * _b + _c * _d, _a * _d - _b * _c) * MathUtil::Rad_Deg; + return; + } + + BonePose& parent = _bone->getParent()->getApplied(); + float pa = parent._a, pb = parent._b, pc = parent._c, pd = parent._d; + float pid = 1 / (pa * pd - pb * pc); + float ia = pd * pid, ib = pb * pid, ic = pc * pid, id = pa * pid; + float dx = _worldX - parent._worldX, dy = _worldY - parent._worldY; + _x = (dx * ia - dy * ib); + _y = (dy * id - dx * ic); + + float ra, rb, rc, rd; + if (_inherit == Inherit_OnlyTranslation) { + ra = _a; + rb = _b; + rc = _c; + rd = _d; + } else { + switch (_inherit) { + case Inherit_NoRotationOrReflection: { + float s = MathUtil::abs(pa * pd - pb * pc) / (pa * pa + pc * pc); + pb = -pc * skeleton.getScaleX() * s / skeleton.getScaleY(); + pd = pa * skeleton.getScaleY() * s / skeleton.getScaleX(); + pid = 1 / (pa * pd - pb * pc); + ia = pd * pid; + ib = pb * pid; + break; + } + case Inherit_NoScale: + case Inherit_NoScaleOrReflection: { + float r = _rotation * MathUtil::Deg_Rad, cosR = MathUtil::cos(r), sinR = MathUtil::sin(r); + pa = (pa * cosR + pb * sinR) / skeleton.getScaleX(); + pc = (pc * cosR + pd * sinR) / skeleton.getScaleY(); + float s = MathUtil::sqrt(pa * pa + pc * pc); + if (s > 0.00001f) s = 1 / s; + pa *= s; + pc *= s; + s = MathUtil::sqrt(pa * pa + pc * pc); + if (_inherit == Inherit_NoScale && pid < 0 != (skeleton.getScaleX() < 0 != skeleton.getScaleY() < 0)) s = -s; + r = MathUtil::Pi / 2 + MathUtil::atan2(pc, pa); + pb = MathUtil::cos(r) * s; + pd = MathUtil::sin(r) * s; + pid = 1 / (pa * pd - pb * pc); + ia = pd * pid; + ib = pb * pid; + ic = pc * pid; + id = pa * pid; + break; + } + default: + break; + } + ra = ia * _a - ib * _c; + rb = ia * _b - ib * _d; + rc = id * _c - ic * _a; + rd = id * _d - ic * _b; + } + + _shearX = 0; + _scaleX = MathUtil::sqrt(ra * ra + rc * rc); + if (_scaleX > 0.0001f) { + float det = ra * rd - rb * rc; + _scaleY = det / _scaleX; + _shearY = -MathUtil::atan2(ra * rb + rc * rd, det) * MathUtil::Rad_Deg; + _rotation = MathUtil::atan2(rc, ra) * MathUtil::Rad_Deg; + } else { + _scaleX = 0; + _scaleY = MathUtil::sqrt(rb * rb + rd * rd); + _shearY = 0; + _rotation = 90 - MathUtil::atan2(rd, rb) * MathUtil::Rad_Deg; + } +} + +void BonePose::validateLocalTransform(Skeleton& skeleton) { + if (_local == skeleton.getUpdate()) updateLocalTransform(skeleton); +} + +void BonePose::modifyLocal(Skeleton& skeleton) { + if (_local == skeleton.getUpdate()) updateLocalTransform(skeleton); + _world = 0; + resetWorld(skeleton.getUpdate()); +} + +void BonePose::modifyWorld(int update) { + _local = update; + _world = update; + resetWorld(update); +} + +void BonePose::resetWorld(int update) { + Vector& children = _bone->getChildren(); + for (size_t i = 0, n = children.size(); i < n; i++) { + BonePose& child = children[i]->getApplied(); + if (child._world == update) { + child._world = 0; + child._local = 0; + child.resetWorld(update); + } + } +} + +float BonePose::getA() { + return _a; +} + +void BonePose::setA(float a) { + this->_a = a; +} + +float BonePose::getB() { + return _b; +} + +void BonePose::setB(float b) { + this->_b = b; +} + +float BonePose::getC() { + return _c; +} + +void BonePose::setC(float c) { + this->_c = c; +} + +float BonePose::getD() { + return _d; +} + +void BonePose::setD(float d) { + this->_d = d; +} + +float BonePose::getWorldX() { + return _worldX; +} + +void BonePose::setWorldX(float _worldX) { + this->_worldX = _worldX; +} + +float BonePose::getWorldY() { + return _worldY; +} + +void BonePose::setWorldY(float _worldY) { + this->_worldY = _worldY; +} + +float BonePose::getWorldRotationX() { + return MathUtil::atan2(_c, _a) * MathUtil::Rad_Deg; +} + +float BonePose::getWorldRotationY() { + return MathUtil::atan2(_d, _b) * MathUtil::Rad_Deg; +} + +float BonePose::getWorldScaleX() { + return MathUtil::sqrt(_a * _a + _c * _c); +} + +float BonePose::getWorldScaleY() { + return MathUtil::sqrt(_b * _b + _d * _d); +} + + +void BonePose::worldToLocal(float worldX, float worldY, float& outLocalX, float& outLocalY) { + float det = _a * _d - _b * _c; + float _x = worldX - _worldX, _y = worldY - _worldY; + outLocalX = (_x * _d - _y * _b) / det; + outLocalY = (_y * _a - _x * _c) / det; +} + +void BonePose::localToWorld(float localX, float localY, float& outWorldX, float& outWorldY) { + outWorldX = localX * _a + localY * _b + _worldX; + outWorldY = localX * _c + localY * _d + _worldY; +} + +void BonePose::worldToParent(float worldX, float worldY, float& outParentX, float& outParentY) { + if (_bone->getParent() == nullptr) { + outParentX = worldX; + outParentY = worldY; + } else { + _bone->getParent()->getApplied().worldToLocal(worldX, worldY, outParentX, outParentY); + } +} + +void BonePose::parentToWorld(float parentX, float parentY, float& outWorldX, float& outWorldY) { + if (_bone->getParent() == nullptr) { + outWorldX = parentX; + outWorldY = parentY; + } else { + _bone->getParent()->getApplied().localToWorld(parentX, parentY, outWorldX, outWorldY); + } +} + +float BonePose::worldToLocalRotation(float worldRotation) { + worldRotation *= MathUtil::Deg_Rad; + float sinRot = MathUtil::sin(worldRotation), cosRot = MathUtil::cos(worldRotation); + return MathUtil::atan2(_a * sinRot - _c * cosRot, _d * cosRot - _b * sinRot) * MathUtil::Rad_Deg + _rotation - _shearX; +} + +float BonePose::localToWorldRotation(float localRotation) { + localRotation = (localRotation - _rotation - _shearX) * MathUtil::Deg_Rad; + float sinRot = MathUtil::sin(localRotation), cosRot = MathUtil::cos(localRotation); + return MathUtil::atan2(cosRot * _c + sinRot * _d, cosRot * _a + sinRot * _b) * MathUtil::Rad_Deg; +} + +void BonePose::rotateWorld(float degrees) { + degrees *= MathUtil::Deg_Rad; + float sinRot = MathUtil::sin(degrees), cosRot = MathUtil::cos(degrees); + float ra = _a, rb = _b; + _a = cosRot * ra - sinRot * _c; + _b = cosRot * rb - sinRot * _d; + _c = sinRot * ra + cosRot * _c; + _d = sinRot * rb + cosRot * _d; +} \ No newline at end of file diff --git a/spine-cpp/spine-cpp/src/spine/FromProperty.cpp b/spine-cpp/spine-cpp/src/spine/FromProperty.cpp new file mode 100644 index 000000000..0e371e4dc --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/FromProperty.cpp @@ -0,0 +1,38 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated July 28, 2023. Replaces all prior versions. + * + * Copyright (c) 2013-2023, 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. + *****************************************************************************/ + +#include + +using namespace spine; + +FromProperty::FromProperty() : offset(0) { +} + +FromProperty::~FromProperty() { +} \ No newline at end of file