diff --git a/spine-cpp/spine-cpp/include/spine/ColorTimeline.h b/spine-cpp/spine-cpp/include/spine/ColorTimeline.h index 3760b1bf0..2f35524b4 100644 --- a/spine-cpp/spine-cpp/include/spine/ColorTimeline.h +++ b/spine-cpp/spine-cpp/include/spine/ColorTimeline.h @@ -30,10 +30,12 @@ #ifndef Spine_ColorTimeline_h #define Spine_ColorTimeline_h -#include +#include +#include namespace spine { - class SP_API RGBATimeline : public CurveTimeline { + /// Changes a slot's SlotPose::getColor(). + class SP_API RGBATimeline : public SlotCurveTimeline { friend class SkeletonBinary; friend class SkeletonJson; @@ -45,19 +47,15 @@ namespace spine { virtual ~RGBATimeline(); - virtual void - apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, MixBlend blend, - MixDirection direction); + int getFrameEntries(); - /// Sets the time and value of the specified keyframe. + /// Sets the time and color for the specified frame. + /// @param frame Between 0 and frameCount, inclusive. + /// @param time The frame time in seconds. void setFrame(int frame, float time, float r, float g, float b, float a); - int getSlotIndex() { return _slotIndex; }; - - void setSlotIndex(int inValue) { _slotIndex = inValue; } - protected: - int _slotIndex; + virtual void apply(Slot& slot, SlotPose& pose, float time, float alpha, MixBlend blend) override; static const int ENTRIES = 5; static const int R = 1; @@ -66,7 +64,8 @@ namespace spine { static const int A = 4; }; - class SP_API RGBTimeline : public CurveTimeline { + /// Changes the RGB for a slot's SlotPose::getColor(). + class SP_API RGBTimeline : public SlotCurveTimeline { friend class SkeletonBinary; friend class SkeletonJson; @@ -78,19 +77,15 @@ namespace spine { virtual ~RGBTimeline(); - virtual void - apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, MixBlend blend, - MixDirection direction); + int getFrameEntries(); - /// Sets the time and value of the specified keyframe. + /// Sets the time and color for the specified frame. + /// @param frame Between 0 and frameCount, inclusive. + /// @param time The frame time in seconds. void setFrame(int frame, float time, float r, float g, float b); - int getSlotIndex() { return _slotIndex; }; - - void setSlotIndex(int inValue) { _slotIndex = inValue; } - protected: - int _slotIndex; + virtual void apply(Slot& slot, SlotPose& pose, float time, float alpha, MixBlend blend) override; static const int ENTRIES = 4; static const int R = 1; @@ -98,7 +93,7 @@ namespace spine { static const int B = 3; }; - class SP_API AlphaTimeline : public CurveTimeline1 { + class SP_API AlphaTimeline : public CurveTimeline1, public SlotTimeline { friend class SkeletonBinary; friend class SkeletonJson; @@ -112,17 +107,16 @@ namespace spine { virtual void apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, MixBlend blend, - MixDirection direction); + MixDirection direction, bool appliedPose) override; - int getSlotIndex() { return _slotIndex; }; - - void setSlotIndex(int inValue) { _slotIndex = inValue; } + virtual int getSlotIndex() override; protected: int _slotIndex; }; - class SP_API RGBA2Timeline : public CurveTimeline { + /// Changes a slot's SlotPose::getColor() and SlotPose::getDarkColor() for two color tinting. + class SP_API RGBA2Timeline : public SlotCurveTimeline { friend class SkeletonBinary; friend class SkeletonJson; @@ -134,19 +128,15 @@ namespace spine { virtual ~RGBA2Timeline(); - virtual void - apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, MixBlend blend, - MixDirection direction); + int getFrameEntries(); - /// Sets the time and value of the specified keyframe. + /// Sets the time, light color, and dark color for the specified frame. + /// @param frame Between 0 and frameCount, inclusive. + /// @param time The frame time in seconds. void setFrame(int frame, float time, float r, float g, float b, float a, float r2, float g2, float b2); - int getSlotIndex() { return _slotIndex; }; - - void setSlotIndex(int inValue) { _slotIndex = inValue; } - protected: - int _slotIndex; + virtual void apply(Slot& slot, SlotPose& pose, float time, float alpha, MixBlend blend) override; static const int ENTRIES = 8; static const int R = 1; @@ -158,7 +148,8 @@ namespace spine { static const int B2 = 7; }; - class SP_API RGB2Timeline : public CurveTimeline { + /// Changes the RGB for a slot's SlotPose::getColor() and SlotPose::getDarkColor() for two color tinting. + class SP_API RGB2Timeline : public SlotCurveTimeline { friend class SkeletonBinary; friend class SkeletonJson; @@ -170,19 +161,15 @@ namespace spine { virtual ~RGB2Timeline(); - virtual void - apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, MixBlend blend, - MixDirection direction); + int getFrameEntries(); - /// Sets the time and value of the specified keyframe. + /// Sets the time, light color, and dark color for the specified frame. + /// @param frame Between 0 and frameCount, inclusive. + /// @param time The frame time in seconds. void setFrame(int frame, float time, float r, float g, float b, float r2, float g2, float b2); - int getSlotIndex() { return _slotIndex; }; - - void setSlotIndex(int inValue) { _slotIndex = inValue; } - protected: - int _slotIndex; + virtual void apply(Slot& slot, SlotPose& pose, float time, float alpha, MixBlend blend) override; static const int ENTRIES = 7; static const int R = 1; diff --git a/spine-cpp/spine-cpp/include/spine/PosedActive.h b/spine-cpp/spine-cpp/include/spine/PosedActive.h index a75951942..71c328f66 100644 --- a/spine-cpp/spine-cpp/include/spine/PosedActive.h +++ b/spine-cpp/spine-cpp/include/spine/PosedActive.h @@ -36,6 +36,8 @@ namespace spine { template class SP_API PosedActive : public Posed { + friend class SlotCurveTimeline; + protected: bool _active; diff --git a/spine-cpp/spine-cpp/include/spine/Skeleton.h b/spine-cpp/spine-cpp/include/spine/Skeleton.h index fdef28a35..135714c6b 100644 --- a/spine-cpp/spine-cpp/include/spine/Skeleton.h +++ b/spine-cpp/spine-cpp/include/spine/Skeleton.h @@ -67,6 +67,8 @@ namespace spine { friend class SkeletonClipping; + friend class SlotCurveTimeline; + friend class AttachmentTimeline; friend class RGBATimeline; diff --git a/spine-cpp/spine-cpp/include/spine/Slot.h b/spine-cpp/spine-cpp/include/spine/Slot.h index 1c828ed44..1b4e7d0f2 100644 --- a/spine-cpp/spine-cpp/include/spine/Slot.h +++ b/spine-cpp/spine-cpp/include/spine/Slot.h @@ -52,6 +52,8 @@ namespace spine { friend class SkeletonClipping; + friend class SlotCurveTimeline; + friend class AttachmentTimeline; friend class RGBATimeline; diff --git a/spine-cpp/spine-cpp/include/spine/SlotCurveTimeline.h b/spine-cpp/spine-cpp/include/spine/SlotCurveTimeline.h new file mode 100644 index 000000000..9622c136b --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/SlotCurveTimeline.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * 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_SlotCurveTimeline_h +#define Spine_SlotCurveTimeline_h + +#include +#include + +namespace spine { + class Slot; + class SlotPose; + + /// Base class for slot timelines that use curves. + class SP_API SlotCurveTimeline : public CurveTimeline, public SlotTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL + + public: + SlotCurveTimeline(size_t frameCount, size_t frameEntries, size_t bezierCount, int slotIndex); + + virtual ~SlotCurveTimeline(); + + virtual int getSlotIndex() override; + + virtual void apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction, bool appliedPose) override; + + protected: + /// Applies the timeline to the slot pose. + virtual void apply(Slot& slot, SlotPose& pose, float time, float alpha, MixBlend blend) = 0; + + int _slotIndex; + }; +} + +#endif /* Spine_SlotCurveTimeline_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 index d6a0a8562..100223f21 100644 --- a/spine-cpp/spine-cpp/include/spine/SlotPose.h +++ b/spine-cpp/spine-cpp/include/spine/SlotPose.h @@ -41,6 +41,13 @@ namespace spine { class VertexAttachment; class SP_API SlotPose : public Pose { + friend class SlotCurveTimeline; + friend class RGBATimeline; + friend class RGBTimeline; + friend class AlphaTimeline; + friend class RGBA2Timeline; + friend class RGB2Timeline; + RTTI_DECL private: diff --git a/spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp b/spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp index ce7ed255f..e74bdf29d 100644 --- a/spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp +++ b/spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp @@ -29,23 +29,21 @@ #include -#include -#include - #include #include +#include #include +#include #include #include +#include using namespace spine; -RTTI_IMPL(RGBATimeline, CurveTimeline) +RTTI_IMPL(RGBATimeline, SlotCurveTimeline) -RGBATimeline::RGBATimeline(size_t frameCount, size_t bezierCount, int slotIndex) : CurveTimeline(frameCount, - RGBATimeline::ENTRIES, - bezierCount), - _slotIndex(slotIndex) { +RGBATimeline::RGBATimeline(size_t frameCount, size_t bezierCount, int slotIndex) + : SlotCurveTimeline(frameCount, ENTRIES, bezierCount, slotIndex) { PropertyId ids[] = {((PropertyId) Property_Rgb << 32) | slotIndex, ((PropertyId) Property_Alpha << 32) | slotIndex}; setPropertyIds(ids, 2); @@ -54,71 +52,8 @@ RGBATimeline::RGBATimeline(size_t frameCount, size_t bezierCount, int slotIndex) RGBATimeline::~RGBATimeline() { } -void RGBATimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, - MixBlend blend, MixDirection direction) { - SP_UNUSED(lastTime); - SP_UNUSED(pEvents); - SP_UNUSED(direction); - - Slot *slot = skeleton._slots[_slotIndex]; - if (!slot->_bone._active) return; - - if (time < _frames[0]) { - Color &color = slot->_color, &setup = slot->_data._color; - switch (blend) { - case MixBlend_Setup: - color.set(setup); - return; - case MixBlend_First: - color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, - (setup.a - color.a) * alpha); - default: { - } - } - return; - } - - float r = 0, g = 0, b = 0, a = 0; - int i = Animation::search(_frames, time, RGBATimeline::ENTRIES); - int curveType = (int) _curves[i / RGBATimeline::ENTRIES]; - switch (curveType) { - case RGBATimeline::LINEAR: { - float before = _frames[i]; - r = _frames[i + RGBATimeline::R]; - g = _frames[i + RGBATimeline::G]; - b = _frames[i + RGBATimeline::B]; - a = _frames[i + RGBATimeline::A]; - float t = (time - before) / (_frames[i + RGBATimeline::ENTRIES] - before); - r += (_frames[i + RGBATimeline::ENTRIES + RGBATimeline::R] - r) * t; - g += (_frames[i + RGBATimeline::ENTRIES + RGBATimeline::G] - g) * t; - b += (_frames[i + RGBATimeline::ENTRIES + RGBATimeline::B] - b) * t; - a += (_frames[i + RGBATimeline::ENTRIES + RGBATimeline::A] - a) * t; - break; - } - case RGBATimeline::STEPPED: { - r = _frames[i + RGBATimeline::R]; - g = _frames[i + RGBATimeline::G]; - b = _frames[i + RGBATimeline::B]; - a = _frames[i + RGBATimeline::A]; - break; - } - default: { - r = getBezierValue(time, i, RGBATimeline::R, curveType - RGBATimeline::BEZIER); - g = getBezierValue(time, i, RGBATimeline::G, - curveType + RGBATimeline::BEZIER_SIZE - RGBATimeline::BEZIER); - b = getBezierValue(time, i, RGBATimeline::B, - curveType + RGBATimeline::BEZIER_SIZE * 2 - RGBATimeline::BEZIER); - a = getBezierValue(time, i, RGBATimeline::A, - curveType + RGBATimeline::BEZIER_SIZE * 3 - RGBATimeline::BEZIER); - } - } - Color &color = slot->_color; - if (alpha == 1) - color.set(r, g, b, a); - else { - if (blend == MixBlend_Setup) color.set(slot->_data._color); - color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); - } +int RGBATimeline::getFrameEntries() { + return ENTRIES; } void RGBATimeline::setFrame(int frame, float time, float r, float g, float b, float a) { @@ -130,12 +65,69 @@ void RGBATimeline::setFrame(int frame, float time, float r, float g, float b, fl _frames[frame + A] = a; } -RTTI_IMPL(RGBTimeline, CurveTimeline) +void RGBATimeline::apply(Slot& slot, SlotPose& pose, float time, float alpha, MixBlend blend) { + Color& color = pose._color; + if (time < _frames[0]) { + Color& setup = slot._data._setup->_color; + switch (blend) { + case MixBlend_Setup: + color.set(setup); + return; + case MixBlend_First: + color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, + (setup.b - color.b) * alpha, (setup.a - color.a) * alpha); + return; + default: + return; + } + } -RGBTimeline::RGBTimeline(size_t frameCount, size_t bezierCount, int slotIndex) : CurveTimeline(frameCount, - RGBTimeline::ENTRIES, - bezierCount), - _slotIndex(slotIndex) { + float r, g, b, a; + int i = Animation::search(_frames, time, ENTRIES); + int curveType = (int) _curves[i / ENTRIES]; + switch (curveType) { + case LINEAR: { + float before = _frames[i]; + r = _frames[i + R]; + g = _frames[i + G]; + b = _frames[i + B]; + a = _frames[i + A]; + float t = (time - before) / (_frames[i + ENTRIES] - before); + r += (_frames[i + ENTRIES + R] - r) * t; + g += (_frames[i + ENTRIES + G] - g) * t; + b += (_frames[i + ENTRIES + B] - b) * t; + a += (_frames[i + ENTRIES + A] - a) * t; + break; + } + case STEPPED: { + r = _frames[i + R]; + g = _frames[i + G]; + b = _frames[i + B]; + a = _frames[i + A]; + break; + } + default: { + r = getBezierValue(time, i, R, curveType - BEZIER); + g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER); + b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER); + a = getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER); + break; + } + } + + if (alpha == 1) + color.set(r, g, b, a); + else { + if (blend == MixBlend_Setup) color.set(slot._data._setup->_color); + color.add((r - color.r) * alpha, (g - color.g) * alpha, + (b - color.b) * alpha, (a - color.a) * alpha); + } +} + +RTTI_IMPL(RGBTimeline, SlotCurveTimeline) + +RGBTimeline::RGBTimeline(size_t frameCount, size_t bezierCount, int slotIndex) + : SlotCurveTimeline(frameCount, ENTRIES, bezierCount, slotIndex) { PropertyId ids[] = {((PropertyId) Property_Rgb << 32) | slotIndex}; setPropertyIds(ids, 1); } @@ -143,82 +135,88 @@ RGBTimeline::RGBTimeline(size_t frameCount, size_t bezierCount, int slotIndex) : RGBTimeline::~RGBTimeline() { } -void RGBTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, - MixBlend blend, MixDirection direction) { - SP_UNUSED(lastTime); - SP_UNUSED(pEvents); - SP_UNUSED(direction); - - Slot *slot = skeleton._slots[_slotIndex]; - if (!slot->_bone._active) return; - - if (time < _frames[0]) { - Color &color = slot->_color, &setup = slot->_data._color; - switch (blend) { - case MixBlend_Setup: - color.set(setup); - return; - case MixBlend_First: - color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, - (setup.a - color.a) * alpha); - default: { - } - } - return; - } - - float r = 0, g = 0, b = 0; - int i = Animation::search(_frames, time, RGBTimeline::ENTRIES); - int curveType = (int) _curves[i / RGBTimeline::ENTRIES]; - switch (curveType) { - case RGBTimeline::LINEAR: { - float before = _frames[i]; - r = _frames[i + RGBTimeline::R]; - g = _frames[i + RGBTimeline::G]; - b = _frames[i + RGBTimeline::B]; - float t = (time - before) / (_frames[i + RGBTimeline::ENTRIES] - before); - r += (_frames[i + RGBTimeline::ENTRIES + RGBTimeline::R] - r) * t; - g += (_frames[i + RGBTimeline::ENTRIES + RGBTimeline::G] - g) * t; - b += (_frames[i + RGBTimeline::ENTRIES + RGBTimeline::B] - b) * t; - break; - } - case RGBTimeline::STEPPED: { - r = _frames[i + RGBTimeline::R]; - g = _frames[i + RGBTimeline::G]; - b = _frames[i + RGBTimeline::B]; - break; - } - default: { - r = getBezierValue(time, i, RGBTimeline::R, curveType - RGBTimeline::BEZIER); - g = getBezierValue(time, i, RGBTimeline::G, - curveType + RGBTimeline::BEZIER_SIZE - RGBTimeline::BEZIER); - b = getBezierValue(time, i, RGBTimeline::B, - curveType + RGBTimeline::BEZIER_SIZE * 2 - RGBTimeline::BEZIER); - } - } - Color &color = slot->_color; - if (alpha == 1) - color.set(r, g, b); - else { - Color &setup = slot->_data._color; - if (blend == MixBlend_Setup) color.set(setup.r, setup.g, setup.b); - color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha); - } +int RGBTimeline::getFrameEntries() { + return ENTRIES; } void RGBTimeline::setFrame(int frame, float time, float r, float g, float b) { - frame *= ENTRIES; + frame <<= 2; _frames[frame] = time; _frames[frame + R] = r; _frames[frame + G] = g; _frames[frame + B] = b; } +void RGBTimeline::apply(Slot& slot, SlotPose& pose, float time, float alpha, MixBlend blend) { + Color& color = pose._color; + if (time < _frames[0]) { + Color& setup = slot._data._setup->_color; + switch (blend) { + case MixBlend_Setup: + color.r = setup.r; + color.g = setup.g; + color.b = setup.b; + return; + case MixBlend_First: + color.r += (setup.r - color.r) * alpha; + color.g += (setup.g - color.g) * alpha; + color.b += (setup.b - color.b) * alpha; + return; + default: + return; + } + } + + float r, g, b; + int i = Animation::search(_frames, time, ENTRIES); + int curveType = (int) _curves[i >> 2]; + switch (curveType) { + case LINEAR: { + float before = _frames[i]; + r = _frames[i + R]; + g = _frames[i + G]; + b = _frames[i + B]; + float t = (time - before) / (_frames[i + ENTRIES] - before); + r += (_frames[i + ENTRIES + R] - r) * t; + g += (_frames[i + ENTRIES + G] - g) * t; + b += (_frames[i + ENTRIES + B] - b) * t; + break; + } + case STEPPED: { + r = _frames[i + R]; + g = _frames[i + G]; + b = _frames[i + B]; + break; + } + default: { + r = getBezierValue(time, i, R, curveType - BEZIER); + g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER); + b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER); + break; + } + } + + if (alpha == 1) { + color.r = r; + color.g = g; + color.b = b; + } else { + if (blend == MixBlend_Setup) { + Color& setup = slot._data._setup->_color; + color.r = setup.r; + color.g = setup.g; + color.b = setup.b; + } + color.r += (r - color.r) * alpha; + color.g += (g - color.g) * alpha; + color.b += (b - color.b) * alpha; + } +} + RTTI_IMPL(AlphaTimeline, CurveTimeline1) -AlphaTimeline::AlphaTimeline(size_t frameCount, size_t bezierCount, int slotIndex) : CurveTimeline1(frameCount, - bezierCount), - _slotIndex(slotIndex) { +AlphaTimeline::AlphaTimeline(size_t frameCount, size_t bezierCount, int slotIndex) + : CurveTimeline1(frameCount, bezierCount), _slotIndex(slotIndex) { PropertyId ids[] = {((PropertyId) Property_Alpha << 32) | slotIndex}; setPropertyIds(ids, 1); } @@ -227,7 +225,7 @@ AlphaTimeline::~AlphaTimeline() { } void AlphaTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, - MixBlend blend, MixDirection direction) { + MixBlend blend, MixDirection direction, bool appliedPose) { SP_UNUSED(lastTime); SP_UNUSED(pEvents); SP_UNUSED(direction); @@ -235,35 +233,38 @@ void AlphaTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector Slot *slot = skeleton._slots[_slotIndex]; if (!slot->_bone._active) return; - if (time < _frames[0]) {// Time is before first frame. - Color &color = slot->_color, &setup = slot->_data._color; + Color& color = (appliedPose ? slot->_applied : slot->_pose)._color; + if (time < _frames[0]) { + Color& setup = slot->_data._setup->_color; switch (blend) { case MixBlend_Setup: color.a = setup.a; return; case MixBlend_First: color.a += (setup.a - color.a) * alpha; - default: { - } + return; + default: + return; } - return; } float a = getCurveValue(time); if (alpha == 1) - slot->_color.a = a; + color.a = a; else { - if (blend == MixBlend_Setup) slot->_color.a = slot->_data._color.a; - slot->_color.a += (a - slot->_color.a) * alpha; + if (blend == MixBlend_Setup) color.a = slot->_data._setup->_color.a; + color.a += (a - color.a) * alpha; } } -RTTI_IMPL(RGBA2Timeline, CurveTimeline) +int AlphaTimeline::getSlotIndex() { + return _slotIndex; +} -RGBA2Timeline::RGBA2Timeline(size_t frameCount, size_t bezierCount, int slotIndex) : CurveTimeline(frameCount, - RGBA2Timeline::ENTRIES, - bezierCount), - _slotIndex(slotIndex) { +RTTI_IMPL(RGBA2Timeline, SlotCurveTimeline) + +RGBA2Timeline::RGBA2Timeline(size_t frameCount, size_t bezierCount, int slotIndex) + : SlotCurveTimeline(frameCount, ENTRIES, bezierCount, slotIndex) { PropertyId ids[] = {((PropertyId) Property_Rgb << 32) | slotIndex, ((PropertyId) Property_Alpha << 32) | slotIndex, ((PropertyId) Property_Rgb2 << 32) | slotIndex}; @@ -273,102 +274,12 @@ RGBA2Timeline::RGBA2Timeline(size_t frameCount, size_t bezierCount, int slotInde RGBA2Timeline::~RGBA2Timeline() { } -void RGBA2Timeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, - MixBlend blend, MixDirection direction) { - SP_UNUSED(lastTime); - SP_UNUSED(pEvents); - SP_UNUSED(direction); - - Slot *slot = skeleton._slots[_slotIndex]; - if (!slot->_bone._active) return; - - if (time < _frames[0]) { - Color &light = slot->_color, &dark = slot->_darkColor, &setupLight = slot->_data._color, &setupDark = slot->_data._darkColor; - switch (blend) { - case MixBlend_Setup: - light.set(setupLight); - dark.set(setupDark.r, setupDark.g, setupDark.b); - return; - case MixBlend_First: - light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, - (setupLight.b - light.b) * alpha, - (setupLight.a - light.a) * alpha); - dark.r += (setupDark.r - dark.r) * alpha; - dark.g += (setupDark.g - dark.g) * alpha; - dark.b += (setupDark.b - dark.b) * alpha; - default: { - } - } - return; - } - - float r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0; - int i = Animation::search(_frames, time, RGBA2Timeline::ENTRIES); - int curveType = (int) _curves[i / RGBA2Timeline::ENTRIES]; - switch (curveType) { - case RGBA2Timeline::LINEAR: { - float before = _frames[i]; - r = _frames[i + RGBA2Timeline::R]; - g = _frames[i + RGBA2Timeline::G]; - b = _frames[i + RGBA2Timeline::B]; - a = _frames[i + RGBA2Timeline::A]; - r2 = _frames[i + RGBA2Timeline::R2]; - g2 = _frames[i + RGBA2Timeline::G2]; - b2 = _frames[i + RGBA2Timeline::B2]; - float t = (time - before) / (_frames[i + RGBA2Timeline::ENTRIES] - before); - r += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::R] - r) * t; - g += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::G] - g) * t; - b += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::B] - b) * t; - a += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::A] - a) * t; - r2 += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::R2] - r2) * t; - g2 += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::G2] - g2) * t; - b2 += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::B2] - b2) * t; - break; - } - case RGBA2Timeline::STEPPED: { - r = _frames[i + RGBA2Timeline::R]; - g = _frames[i + RGBA2Timeline::G]; - b = _frames[i + RGBA2Timeline::B]; - a = _frames[i + RGBA2Timeline::A]; - r2 = _frames[i + RGBA2Timeline::R2]; - g2 = _frames[i + RGBA2Timeline::G2]; - b2 = _frames[i + RGBA2Timeline::B2]; - break; - } - default: { - r = getBezierValue(time, i, RGBA2Timeline::R, curveType - RGBA2Timeline::BEZIER); - g = getBezierValue(time, i, RGBA2Timeline::G, - curveType + RGBA2Timeline::BEZIER_SIZE - RGBA2Timeline::BEZIER); - b = getBezierValue(time, i, RGBA2Timeline::B, - curveType + RGBA2Timeline::BEZIER_SIZE * 2 - RGBA2Timeline::BEZIER); - a = getBezierValue(time, i, RGBA2Timeline::A, - curveType + RGBA2Timeline::BEZIER_SIZE * 3 - RGBA2Timeline::BEZIER); - r2 = getBezierValue(time, i, RGBA2Timeline::R2, - curveType + RGBA2Timeline::BEZIER_SIZE * 4 - RGBA2Timeline::BEZIER); - g2 = getBezierValue(time, i, RGBA2Timeline::G2, - curveType + RGBA2Timeline::BEZIER_SIZE * 5 - RGBA2Timeline::BEZIER); - b2 = getBezierValue(time, i, RGBA2Timeline::B2, - curveType + RGBA2Timeline::BEZIER_SIZE * 6 - RGBA2Timeline::BEZIER); - } - } - Color &light = slot->_color, &dark = slot->_darkColor; - if (alpha == 1) { - light.set(r, g, b, a); - dark.set(r2, g2, b2); - } else { - if (blend == MixBlend_Setup) { - light.set(slot->_data._color); - dark.set(slot->_data._darkColor); - } - light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha); - dark.r += (r2 - dark.r) * alpha; - dark.g += (g2 - dark.g) * alpha; - dark.b += (b2 - dark.b) * alpha; - } +int RGBA2Timeline::getFrameEntries() { + return ENTRIES; } void RGBA2Timeline::setFrame(int frame, float time, float r, float g, float b, float a, float r2, float g2, float b2) { - frame *= ENTRIES; + frame <<= 3; _frames[frame] = time; _frames[frame + R] = r; _frames[frame + G] = g; @@ -379,12 +290,103 @@ void RGBA2Timeline::setFrame(int frame, float time, float r, float g, float b, f _frames[frame + B2] = b2; } -RTTI_IMPL(RGB2Timeline, CurveTimeline) +void RGBA2Timeline::apply(Slot& slot, SlotPose& pose, float time, float alpha, MixBlend blend) { + Color& light = pose._color; + Color& dark = pose._darkColor; + if (time < _frames[0]) { + SlotPose& setup = *slot._data._setup; + Color& setupLight = setup._color; + Color& setupDark = setup._darkColor; + switch (blend) { + case MixBlend_Setup: + light.set(setupLight); + dark.r = setupDark.r; + dark.g = setupDark.g; + dark.b = setupDark.b; + return; + case MixBlend_First: + light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, + (setupLight.b - light.b) * alpha, (setupLight.a - light.a) * alpha); + dark.r += (setupDark.r - dark.r) * alpha; + dark.g += (setupDark.g - dark.g) * alpha; + dark.b += (setupDark.b - dark.b) * alpha; + return; + default: + return; + } + } -RGB2Timeline::RGB2Timeline(size_t frameCount, size_t bezierCount, int slotIndex) : CurveTimeline(frameCount, - RGB2Timeline::ENTRIES, - bezierCount), - _slotIndex(slotIndex) { + float r, g, b, a, r2, g2, b2; + int i = Animation::search(_frames, time, ENTRIES); + int curveType = (int) _curves[i >> 3]; + switch (curveType) { + case LINEAR: { + float before = _frames[i]; + r = _frames[i + R]; + g = _frames[i + G]; + b = _frames[i + B]; + a = _frames[i + A]; + r2 = _frames[i + R2]; + g2 = _frames[i + G2]; + b2 = _frames[i + B2]; + float t = (time - before) / (_frames[i + ENTRIES] - before); + r += (_frames[i + ENTRIES + R] - r) * t; + g += (_frames[i + ENTRIES + G] - g) * t; + b += (_frames[i + ENTRIES + B] - b) * t; + a += (_frames[i + ENTRIES + A] - a) * t; + r2 += (_frames[i + ENTRIES + R2] - r2) * t; + g2 += (_frames[i + ENTRIES + G2] - g2) * t; + b2 += (_frames[i + ENTRIES + B2] - b2) * t; + break; + } + case STEPPED: { + r = _frames[i + R]; + g = _frames[i + G]; + b = _frames[i + B]; + a = _frames[i + A]; + r2 = _frames[i + R2]; + g2 = _frames[i + G2]; + b2 = _frames[i + B2]; + break; + } + default: { + r = getBezierValue(time, i, R, curveType - BEZIER); + g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER); + b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER); + a = getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER); + r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 4 - BEZIER); + g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 5 - BEZIER); + b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 6 - BEZIER); + break; + } + } + + if (alpha == 1) { + light.set(r, g, b, a); + dark.r = r2; + dark.g = g2; + dark.b = b2; + } else { + if (blend == MixBlend_Setup) { + SlotPose& setup = *slot._data._setup; + light.set(setup._color); + Color& setupDark = setup._darkColor; + dark.r = setupDark.r; + dark.g = setupDark.g; + dark.b = setupDark.b; + } + light.add((r - light.r) * alpha, (g - light.g) * alpha, + (b - light.b) * alpha, (a - light.a) * alpha); + dark.r += (r2 - dark.r) * alpha; + dark.g += (g2 - dark.g) * alpha; + dark.b += (b2 - dark.b) * alpha; + } +} + +RTTI_IMPL(RGB2Timeline, SlotCurveTimeline) + +RGB2Timeline::RGB2Timeline(size_t frameCount, size_t bezierCount, int slotIndex) + : SlotCurveTimeline(frameCount, ENTRIES, bezierCount, slotIndex) { PropertyId ids[] = {((PropertyId) Property_Rgb << 32) | slotIndex, ((PropertyId) Property_Rgb2 << 32) | slotIndex}; setPropertyIds(ids, 2); @@ -393,92 +395,8 @@ RGB2Timeline::RGB2Timeline(size_t frameCount, size_t bezierCount, int slotIndex) RGB2Timeline::~RGB2Timeline() { } -void RGB2Timeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, - MixBlend blend, MixDirection direction) { - SP_UNUSED(lastTime); - SP_UNUSED(pEvents); - SP_UNUSED(direction); - - Slot *slot = skeleton._slots[_slotIndex]; - if (!slot->_bone._active) return; - - if (time < _frames[0]) { - Color &light = slot->_color, &dark = slot->_darkColor, &setupLight = slot->_data._color, &setupDark = slot->_data._darkColor; - switch (blend) { - case MixBlend_Setup: - light.set(setupLight.r, setupLight.g, setupLight.b); - dark.set(setupDark.r, setupDark.g, setupDark.b); - return; - case MixBlend_First: - light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, - (setupLight.b - light.b) * alpha); - dark.r += (setupDark.r - dark.r) * alpha; - dark.g += (setupDark.g - dark.g) * alpha; - dark.b += (setupDark.b - dark.b) * alpha; - default: { - } - } - return; - } - - float r = 0, g = 0, b = 0, r2 = 0, g2 = 0, b2 = 0; - int i = Animation::search(_frames, time, RGB2Timeline::ENTRIES); - int curveType = (int) _curves[i / RGB2Timeline::ENTRIES]; - switch (curveType) { - case RGB2Timeline::LINEAR: { - float before = _frames[i]; - r = _frames[i + RGB2Timeline::R]; - g = _frames[i + RGB2Timeline::G]; - b = _frames[i + RGB2Timeline::B]; - r2 = _frames[i + RGB2Timeline::R2]; - g2 = _frames[i + RGB2Timeline::G2]; - b2 = _frames[i + RGB2Timeline::B2]; - float t = (time - before) / (_frames[i + RGB2Timeline::ENTRIES] - before); - r += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::R] - r) * t; - g += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::G] - g) * t; - b += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::B] - b) * t; - r2 += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::R2] - r2) * t; - g2 += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::G2] - g2) * t; - b2 += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::B2] - b2) * t; - break; - } - case RGB2Timeline::STEPPED: { - r = _frames[i + RGB2Timeline::R]; - g = _frames[i + RGB2Timeline::G]; - b = _frames[i + RGB2Timeline::B]; - r2 = _frames[i + RGB2Timeline::R2]; - g2 = _frames[i + RGB2Timeline::G2]; - b2 = _frames[i + RGB2Timeline::B2]; - break; - } - default: { - r = getBezierValue(time, i, RGB2Timeline::R, curveType - RGB2Timeline::BEZIER); - g = getBezierValue(time, i, RGB2Timeline::G, - curveType + RGB2Timeline::BEZIER_SIZE - RGB2Timeline::BEZIER); - b = getBezierValue(time, i, RGB2Timeline::B, - curveType + RGB2Timeline::BEZIER_SIZE * 2 - RGB2Timeline::BEZIER); - r2 = getBezierValue(time, i, RGB2Timeline::R2, - curveType + RGB2Timeline::BEZIER_SIZE * 4 - RGB2Timeline::BEZIER); - g2 = getBezierValue(time, i, RGB2Timeline::G2, - curveType + RGB2Timeline::BEZIER_SIZE * 5 - RGB2Timeline::BEZIER); - b2 = getBezierValue(time, i, RGB2Timeline::B2, - curveType + RGB2Timeline::BEZIER_SIZE * 6 - RGB2Timeline::BEZIER); - } - } - Color &light = slot->_color, &dark = slot->_darkColor; - if (alpha == 1) { - light.set(r, g, b); - dark.set(r2, g2, b2); - } else { - if (blend == MixBlend_Setup) { - light.set(slot->_data._color.r, slot->_data._color.g, slot->_data._color.b); - dark.set(slot->_data._darkColor); - } - light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha); - dark.r += (r2 - dark.r) * alpha; - dark.g += (g2 - dark.g) * alpha; - dark.b += (b2 - dark.b) * alpha; - } +int RGB2Timeline::getFrameEntries() { + return ENTRIES; } void RGB2Timeline::setFrame(int frame, float time, float r, float g, float b, float r2, float g2, float b2) { @@ -491,3 +409,99 @@ void RGB2Timeline::setFrame(int frame, float time, float r, float g, float b, fl _frames[frame + G2] = g2; _frames[frame + B2] = b2; } + +void RGB2Timeline::apply(Slot& slot, SlotPose& pose, float time, float alpha, MixBlend blend) { + Color& light = pose._color; + Color& dark = pose._darkColor; + if (time < _frames[0]) { + SlotPose& setup = *slot._data._setup; + Color& setupLight = setup._color; + Color& setupDark = setup._darkColor; + switch (blend) { + case MixBlend_Setup: + light.r = setupLight.r; + light.g = setupLight.g; + light.b = setupLight.b; + dark.r = setupDark.r; + dark.g = setupDark.g; + dark.b = setupDark.b; + return; + case MixBlend_First: + light.r += (setupLight.r - light.r) * alpha; + light.g += (setupLight.g - light.g) * alpha; + light.b += (setupLight.b - light.b) * alpha; + dark.r += (setupDark.r - dark.r) * alpha; + dark.g += (setupDark.g - dark.g) * alpha; + dark.b += (setupDark.b - dark.b) * alpha; + return; + default: + return; + } + } + + float r, g, b, r2, g2, b2; + int i = Animation::search(_frames, time, ENTRIES); + int curveType = (int) _curves[i / ENTRIES]; + switch (curveType) { + case LINEAR: { + float before = _frames[i]; + r = _frames[i + R]; + g = _frames[i + G]; + b = _frames[i + B]; + r2 = _frames[i + R2]; + g2 = _frames[i + G2]; + b2 = _frames[i + B2]; + float t = (time - before) / (_frames[i + ENTRIES] - before); + r += (_frames[i + ENTRIES + R] - r) * t; + g += (_frames[i + ENTRIES + G] - g) * t; + b += (_frames[i + ENTRIES + B] - b) * t; + r2 += (_frames[i + ENTRIES + R2] - r2) * t; + g2 += (_frames[i + ENTRIES + G2] - g2) * t; + b2 += (_frames[i + ENTRIES + B2] - b2) * t; + break; + } + case STEPPED: { + r = _frames[i + R]; + g = _frames[i + G]; + b = _frames[i + B]; + r2 = _frames[i + R2]; + g2 = _frames[i + G2]; + b2 = _frames[i + B2]; + break; + } + default: { + r = getBezierValue(time, i, R, curveType - BEZIER); + g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER); + b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER); + r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 3 - BEZIER); + g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 4 - BEZIER); + b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 5 - BEZIER); + break; + } + } + + if (alpha == 1) { + light.r = r; + light.g = g; + light.b = b; + dark.r = r2; + dark.g = g2; + dark.b = b2; + } else { + if (blend == MixBlend_Setup) { + SlotPose& setup = *slot._data._setup; + light.r = setup._color.r; + light.g = setup._color.g; + light.b = setup._color.b; + dark.r = setup._darkColor.r; + dark.g = setup._darkColor.g; + dark.b = setup._darkColor.b; + } + light.r += (r - light.r) * alpha; + light.g += (g - light.g) * alpha; + light.b += (b - light.b) * alpha; + dark.r += (r2 - dark.r) * alpha; + dark.g += (g2 - dark.g) * alpha; + dark.b += (b2 - dark.b) * alpha; + } +} \ No newline at end of file diff --git a/spine-cpp/spine-cpp/src/spine/SlotCurveTimeline.cpp b/spine-cpp/spine-cpp/src/spine/SlotCurveTimeline.cpp new file mode 100644 index 000000000..5493b9d45 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/SlotCurveTimeline.cpp @@ -0,0 +1,60 @@ +/****************************************************************************** + * 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 +#include + +using namespace spine; + +RTTI_IMPL(SlotCurveTimeline, CurveTimeline) + +SlotCurveTimeline::SlotCurveTimeline(size_t frameCount, size_t frameEntries, size_t bezierCount, int slotIndex) + : CurveTimeline(frameCount, frameEntries, bezierCount), _slotIndex(slotIndex) { +} + +SlotCurveTimeline::~SlotCurveTimeline() { +} + +int SlotCurveTimeline::getSlotIndex() { + return _slotIndex; +} + +void SlotCurveTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector *pEvents, float alpha, + MixBlend blend, MixDirection direction, bool appliedPose) { + SP_UNUSED(lastTime); + SP_UNUSED(pEvents); + SP_UNUSED(direction); + + Slot *slot = skeleton._slots[_slotIndex]; + if (slot->_bone._active) apply(*slot, appliedPose ? slot->_applied : slot->_pose, time, alpha, blend); +} \ No newline at end of file