diff --git a/spine-cpp/spine-cpp/include/spine/BoneLocal.h b/spine-cpp/spine-cpp/include/spine/BoneLocal.h new file mode 100644 index 000000000..c408ee168 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/BoneLocal.h @@ -0,0 +1,90 @@ +/****************************************************************************** + * 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_BoneLocal_h +#define Spine_BoneLocal_h + +#include +#include +#include + +namespace spine { + /// Stores a bone's local pose. + class SP_API BoneLocal : public SpineObject, public Pose { + public: + BoneLocal(); + + virtual ~BoneLocal(); + + virtual void set(const BoneLocal& pose) override; + + /// The local x translation. + float getX() const; + void setX(float x); + + /// The local y translation. + float getY() const; + void setY(float y); + + void setPosition(float x, float y); + + /// The local rotation in degrees, counter clockwise. + float getRotation() const; + void setRotation(float rotation); + + /// The local scaleX. + float getScaleX() const; + void setScaleX(float scaleX); + + /// The local scaleY. + float getScaleY() const; + void setScaleY(float scaleY); + + void setScale(float scaleX, float scaleY); + void setScale(float scale); + + /// The local shearX. + float getShearX() const; + void setShearX(float shearX); + + /// The local shearY. + float getShearY() const; + void setShearY(float shearY); + + /// Determines how parent world transforms affect this bone. + Inherit getInherit() const; + void setInherit(Inherit inherit); + + protected: + float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY; + Inherit _inherit; + }; +} + +#endif /* Spine_BoneLocal_h */ \ No newline at end of file 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..f5f241bb1 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/BonePose.h @@ -0,0 +1,143 @@ +/****************************************************************************** + * 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 + +namespace spine { + class Bone; + class Skeleton; + + /// The applied pose for a bone. This is the Bone pose with constraints applied and the world transform computed by + /// Skeleton::updateWorldTransform(Physics). + class SP_API BonePose : public BoneLocal, public Update { + public: + 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(Skeleton) + /// is called. + void validateLocalTransform(Skeleton& skeleton); + + /// 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(Skeleton) should be called on this bone and any + /// child bones, recursively. + void rotateWorld(float degrees); + + /// Part of the world transform matrix for the X axis. If changed, updateLocalTransform(Skeleton) should be called. + float getA() const; + void setA(float a); + + /// Part of the world transform matrix for the Y axis. If changed, updateLocalTransform(Skeleton) should be called. + float getB() const; + void setB(float b); + + /// Part of the world transform matrix for the X axis. If changed, updateLocalTransform(Skeleton) should be called. + float getC() const; + void setC(float c); + + /// Part of the world transform matrix for the Y axis. If changed, updateLocalTransform(Skeleton) should be called. + float getD() const; + void setD(float d); + + /// The world X position. If changed, updateLocalTransform(Skeleton) should be called. + float getWorldX() const; + void setWorldX(float worldX); + + /// The world Y position. If changed, updateLocalTransform(Skeleton) should be called. + float getWorldY() const; + void setWorldY(float worldY); + + /// The world rotation for the X axis, calculated using a and c. + float getWorldRotationX() const; + + /// The world rotation for the Y axis, calculated using b and d. + float getWorldRotationY() const; + + /// The magnitude (always positive) of the world scale X, calculated using a and c. + float getWorldScaleX() const; + + /// The magnitude (always positive) of the world scale Y, calculated using b and d. + float getWorldScaleY() const; + + private: + void modifyLocal(Skeleton& skeleton); + void modifyWorld(int update); + void resetWorld(int update); + + public: + Bone* _bone; + float _a, _b, _worldX; + float _c, _d, _worldY; + int _world, _local; + }; +} + +#endif /* Spine_BonePose_h */ \ No newline at end of file diff --git a/spine-cpp/spine-cpp/include/spine/SlotPose.h b/spine-cpp/spine-cpp/include/spine/SlotPose.h new file mode 100644 index 000000000..ec58ff8e5 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/SlotPose.h @@ -0,0 +1,93 @@ +/****************************************************************************** + * 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_SlotPose_h +#define Spine_SlotPose_h + +#include +#include +#include +#include + +namespace spine { + class Attachment; + class VertexAttachment; + + /// Stores a slot's pose. Slots organize attachments for Skeleton drawOrder purposes and provide a place to store state + /// for an attachment. State cannot be stored in an attachment itself because attachments are stateless and may be shared across + /// multiple skeletons. + class SP_API SlotPose : public SpineObject, public Pose { + public: + SlotPose(); + + virtual ~SlotPose(); + + virtual void set(const SlotPose& pose) override; + + /// The color used to tint the slot's attachment. If hasDarkColor() is true, this is used as the light color for two + /// color tinting. + Color& getColor(); + + /// The dark color used to tint the slot's attachment for two color tinting. Only use if hasDarkColor() is true. + /// The dark color's alpha is not used. + Color& getDarkColor(); + + bool hasDarkColor(); + void setHasDarkColor(bool hasDarkColor); + + /// The current attachment for the slot, or null if the slot has no attachment. + Attachment* getAttachment(); + + /// Sets the slot's attachment and, if the attachment changed, resets sequenceIndex and clears the deform. + /// The deform is not cleared if the old attachment has the same VertexAttachment::getTimelineAttachment() as the + /// specified attachment. + void setAttachment(Attachment* attachment); + + /// The index of the texture region to display when the slot's attachment has a Sequence. -1 represents the + /// Sequence::getSetupIndex(). + int getSequenceIndex(); + void setSequenceIndex(int sequenceIndex); + + /// Values to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a + /// weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions. + /// + /// See VertexAttachment::computeWorldVertices and DeformTimeline. + Vector& getDeform(); + + private: + Color _color; + Color _darkColor; + bool _hasDarkColor; + Attachment* _attachment; + int _sequenceIndex; + Vector _deform; + }; +} + +#endif /* Spine_SlotPose_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 5f0e60acc..009fcb80f 100644 --- a/spine-cpp/spine-cpp/include/spine/spine.h +++ b/spine-cpp/spine-cpp/include/spine/spine.h @@ -112,6 +112,9 @@ #include #include #include +#include +#include +#include #include #include #include diff --git a/spine-cpp/spine-cpp/src/spine/BoneLocal.cpp b/spine-cpp/spine-cpp/src/spine/BoneLocal.cpp new file mode 100644 index 000000000..764441542 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/BoneLocal.cpp @@ -0,0 +1,128 @@ +/****************************************************************************** + * 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. + *****************************************************************************/ + +#include + +using namespace spine; + +BoneLocal::BoneLocal() : _x(0), _y(0), _rotation(0), _scaleX(1), _scaleY(1), _shearX(0), _shearY(0), _inherit(Inherit_Normal) { +} + +BoneLocal::~BoneLocal() { +} + +void BoneLocal::set(const BoneLocal& pose) { + _x = pose._x; + _y = pose._y; + _rotation = pose._rotation; + _scaleX = pose._scaleX; + _scaleY = pose._scaleY; + _shearX = pose._shearX; + _shearY = pose._shearY; + _inherit = pose._inherit; +} + +float BoneLocal::getX() const { + return _x; +} + +void BoneLocal::setX(float x) { + _x = x; +} + +float BoneLocal::getY() const { + return _y; +} + +void BoneLocal::setY(float y) { + _y = y; +} + +void BoneLocal::setPosition(float x, float y) { + _x = x; + _y = y; +} + +float BoneLocal::getRotation() const { + return _rotation; +} + +void BoneLocal::setRotation(float rotation) { + _rotation = rotation; +} + +float BoneLocal::getScaleX() const { + return _scaleX; +} + +void BoneLocal::setScaleX(float scaleX) { + _scaleX = scaleX; +} + +float BoneLocal::getScaleY() const { + return _scaleY; +} + +void BoneLocal::setScaleY(float scaleY) { + _scaleY = scaleY; +} + +void BoneLocal::setScale(float scaleX, float scaleY) { + _scaleX = scaleX; + _scaleY = scaleY; +} + +void BoneLocal::setScale(float scale) { + _scaleX = scale; + _scaleY = scale; +} + +float BoneLocal::getShearX() const { + return _shearX; +} + +void BoneLocal::setShearX(float shearX) { + _shearX = shearX; +} + +float BoneLocal::getShearY() const { + return _shearY; +} + +void BoneLocal::setShearY(float shearY) { + _shearY = shearY; +} + +Inherit BoneLocal::getInherit() const { + return _inherit; +} + +void BoneLocal::setInherit(Inherit inherit) { + _inherit = inherit; +} \ No newline at end of file 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..ac238a68e --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/BonePose.cpp @@ -0,0 +1,382 @@ +/****************************************************************************** + * 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. + *****************************************************************************/ + +#include +#include +#include +#include + +using namespace spine; + +BonePose::BonePose() : _bone(nullptr), _a(1), _b(0), _worldX(0), _c(0), _d(1), _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) { + 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; + break; + } + 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::atan2Deg(pc, pa); + } else { + pa = 0; + pc = 0; + prx = 90 - MathUtil::atan2Deg(pd, pb); + } + 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 rotation = _rotation * MathUtil::Deg_Rad; + float cosVal = MathUtil::cos(rotation), sinVal = MathUtil::sin(rotation); + float za = (pa * cosVal + pb * sinVal) / skeleton.getScaleX(); + float zc = (pc * cosVal + pd * sinVal) / 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; + rotation = MathUtil::Pi / 2 + MathUtil::atan2(zc, za); + float zb = MathUtil::cos(rotation) * s; + float zd = MathUtil::sin(rotation) * s; + float shearX = _shearX * MathUtil::Deg_Rad; + float shearY = (90 + _shearY) * MathUtil::Deg_Rad; + float la = MathUtil::cos(shearX) * _scaleX; + float lb = MathUtil::cos(shearY) * _scaleY; + float lc = MathUtil::sin(shearX) * _scaleX; + float ld = MathUtil::sin(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 BonePose::updateLocalTransform(Skeleton& skeleton) { + _local = 0; + _world = skeleton.getUpdate(); + + if (_bone->getParent() == nullptr) { + _x = _worldX - skeleton.getX(); + _y = _worldY - skeleton.getY(); + float a = _a, b = _b, c = _c, d = _d; + _rotation = MathUtil::atan2Deg(c, a); + _scaleX = MathUtil::sqrt(a * a + c * c); + _scaleY = MathUtil::sqrt(b * b + d * d); + _shearX = 0; + _shearY = MathUtil::atan2Deg(a * b + c * d, a * d - b * c); + 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, cosVal = MathUtil::cos(r), sinVal = MathUtil::sin(r); + pa = (pa * cosVal + pb * sinVal) / skeleton.getScaleX(); + pc = (pc * cosVal + pd * sinVal) / 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; + } + } + 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::atan2Deg(ra * rb + rc * rd, det); + _rotation = MathUtil::atan2Deg(rc, ra); + } else { + _scaleX = 0; + _scaleY = MathUtil::sqrt(rb * rb + rd * rd); + _shearY = 0; + _rotation = 90 - MathUtil::atan2Deg(rd, rb); + } +} + +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 (int 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); + } + } +} + +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 == nullptr || _bone->getParent() == nullptr) { + outParentX = worldX; + outParentY = worldY; + return; + } + _bone->getParent()->getApplied()->worldToLocal(worldX, worldY, outParentX, outParentY); +} + +void BonePose::parentToWorld(float parentX, float parentY, float& outWorldX, float& outWorldY) { + if (_bone == nullptr || _bone->getParent() == nullptr) { + outWorldX = parentX; + outWorldY = parentY; + return; + } + _bone->getParent()->getApplied()->localToWorld(parentX, parentY, outWorldX, outWorldY); +} + +float BonePose::worldToLocalRotation(float worldRotation) { + worldRotation *= MathUtil::Deg_Rad; + float sinVal = MathUtil::sin(worldRotation), cosVal = MathUtil::cos(worldRotation); + return MathUtil::atan2Deg(_a * sinVal - _c * cosVal, _d * cosVal - _b * sinVal) + _rotation - _shearX; +} + +float BonePose::localToWorldRotation(float localRotation) { + localRotation = (localRotation - _rotation - _shearX) * MathUtil::Deg_Rad; + float sinVal = MathUtil::sin(localRotation), cosVal = MathUtil::cos(localRotation); + return MathUtil::atan2Deg(cosVal * _c + sinVal * _d, cosVal * _a + sinVal * _b); +} + +void BonePose::rotateWorld(float degrees) { + degrees *= MathUtil::Deg_Rad; + float sinVal = MathUtil::sin(degrees), cosVal = MathUtil::cos(degrees); + float ra = _a, rb = _b; + _a = cosVal * ra - sinVal * _c; + _b = cosVal * rb - sinVal * _d; + _c = sinVal * ra + cosVal * _c; + _d = sinVal * rb + cosVal * _d; +} + +float BonePose::getA() const { + return _a; +} + +void BonePose::setA(float a) { + _a = a; +} + +float BonePose::getB() const { + return _b; +} + +void BonePose::setB(float b) { + _b = b; +} + +float BonePose::getC() const { + return _c; +} + +void BonePose::setC(float c) { + _c = c; +} + +float BonePose::getD() const { + return _d; +} + +void BonePose::setD(float d) { + _d = d; +} + +float BonePose::getWorldX() const { + return _worldX; +} + +void BonePose::setWorldX(float worldX) { + _worldX = worldX; +} + +float BonePose::getWorldY() const { + return _worldY; +} + +void BonePose::setWorldY(float worldY) { + _worldY = worldY; +} + +float BonePose::getWorldRotationX() const { + return MathUtil::atan2Deg(_c, _a); +} + +float BonePose::getWorldRotationY() const { + return MathUtil::atan2Deg(_d, _b); +} + +float BonePose::getWorldScaleX() const { + return MathUtil::sqrt(_a * _a + _c * _c); +} + +float BonePose::getWorldScaleY() const { + return MathUtil::sqrt(_b * _b + _d * _d); +} \ No newline at end of file diff --git a/spine-cpp/spine-cpp/src/spine/SlotPose.cpp b/spine-cpp/spine-cpp/src/spine/SlotPose.cpp new file mode 100644 index 000000000..17e40c710 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/SlotPose.cpp @@ -0,0 +1,96 @@ +/****************************************************************************** + * 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. + *****************************************************************************/ + +#include +#include +#include + +using namespace spine; + +SlotPose::SlotPose() : _color(1, 1, 1, 1), _darkColor(0, 0, 0, 1), _hasDarkColor(false), _attachment(nullptr), _sequenceIndex(-1) { +} + +SlotPose::~SlotPose() { +} + +void SlotPose::set(const SlotPose& pose) { + _color.set(pose._color); + if (_hasDarkColor) _darkColor.set(pose._darkColor); + _hasDarkColor = pose._hasDarkColor; + _attachment = pose._attachment; + _sequenceIndex = pose._sequenceIndex; + _deform.clear(); + _deform.addAll(pose._deform); +} + +Color& SlotPose::getColor() { + return _color; +} + +Color& SlotPose::getDarkColor() { + return _darkColor; +} + +bool SlotPose::hasDarkColor() { + return _hasDarkColor; +} + +void SlotPose::setHasDarkColor(bool hasDarkColor) { + _hasDarkColor = hasDarkColor; +} + +Attachment* SlotPose::getAttachment() { + return _attachment; +} + +void SlotPose::setAttachment(Attachment* attachment) { + if (_attachment == attachment) return; + + if (attachment == nullptr || _attachment == nullptr || + !attachment->getRTTI().instanceOf(VertexAttachment::rtti) || + !_attachment->getRTTI().instanceOf(VertexAttachment::rtti) || + ((VertexAttachment*)attachment)->getTimelineAttachment() != ((VertexAttachment*)_attachment)->getTimelineAttachment()) { + _deform.clear(); + } + + _attachment = attachment; + _sequenceIndex = -1; +} + +int SlotPose::getSequenceIndex() { + return _sequenceIndex; +} + +void SlotPose::setSequenceIndex(int sequenceIndex) { + _sequenceIndex = sequenceIndex; +} + +Vector& SlotPose::getDeform() { + return _deform; +} \ No newline at end of file