[cpp] 4.3 porting WIP

This commit is contained in:
Mario Zechner 2025-06-11 17:27:18 +02:00
parent c41da0c788
commit 0699f41696
8 changed files with 526 additions and 387 deletions

View File

@ -30,10 +30,12 @@
#ifndef Spine_ColorTimeline_h
#define Spine_ColorTimeline_h
#include <spine/CurveTimeline.h>
#include <spine/SlotCurveTimeline.h>
#include <spine/SlotTimeline.h>
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<Event *> *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<Event *> *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<Event *> *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<Event *> *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<Event *> *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;

View File

@ -36,6 +36,8 @@
namespace spine {
template<class D, class P, class A>
class SP_API PosedActive : public Posed<D, P, A> {
friend class SlotCurveTimeline;
protected:
bool _active;

View File

@ -67,6 +67,8 @@ namespace spine {
friend class SkeletonClipping;
friend class SlotCurveTimeline;
friend class AttachmentTimeline;
friend class RGBATimeline;

View File

@ -52,6 +52,8 @@ namespace spine {
friend class SkeletonClipping;
friend class SlotCurveTimeline;
friend class AttachmentTimeline;
friend class RGBATimeline;

View File

@ -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 <spine/CurveTimeline.h>
#include <spine/SlotTimeline.h>
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<Event *> *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 */

View File

@ -41,6 +41,13 @@ namespace spine {
class VertexAttachment;
class SP_API SlotPose : public Pose<SlotPose> {
friend class SlotCurveTimeline;
friend class RGBATimeline;
friend class RGBTimeline;
friend class AlphaTimeline;
friend class RGBA2Timeline;
friend class RGB2Timeline;
RTTI_DECL
private:

View File

@ -29,23 +29,21 @@
#include <spine/ColorTimeline.h>
#include <spine/Event.h>
#include <spine/Skeleton.h>
#include <spine/Animation.h>
#include <spine/Bone.h>
#include <spine/Event.h>
#include <spine/Property.h>
#include <spine/Skeleton.h>
#include <spine/Slot.h>
#include <spine/SlotData.h>
#include <spine/SlotPose.h>
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<Event *> *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<Event *> *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<Event *> *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<Event *> *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<Event *> *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;
}
}

View File

@ -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 <spine/SlotCurveTimeline.h>
#include <spine/Bone.h>
#include <spine/Skeleton.h>
#include <spine/Slot.h>
#include <spine/SlotPose.h>
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<Event *> *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);
}