[cpp] Initial pass at physics, incomplete

This commit is contained in:
Mario Zechner 2023-12-22 17:26:49 +01:00
parent 0b9bab2392
commit 5ffdcdb1e6
25 changed files with 1345 additions and 165 deletions

View File

@ -209,16 +209,22 @@ namespace spine {
/// When the mix percentage (mix time / mix duration) is less than the attachment threshold, attachment timelines for the
/// animation being mixed out will be applied. Defaults to 0, so attachment timelines are not applied for an animation being
/// mixed out.
float getAttachmentThreshold();
float getMixAttachmentThreshold();
void setAttachmentThreshold(float inValue);
void setMixAttachmentThreshold(float inValue);
/// When getAlpha() is greater than alphaAttachmentThreshold, attachment timelines are applied.
/// Defaults to 0, so attachment timelines are always applied. */
float getAlphaAttachmentThreshold();
void setAlphaAttachmentThreshold(float inValue);
/// When the mix percentage (mix time / mix duration) is less than the draw order threshold, draw order timelines for the
/// animation being mixed out will be applied. Defaults to 0, so draw order timelines are not applied for an animation being
/// mixed out.
float getDrawOrderThreshold();
float getMixDrawOrderThreshold();
void setDrawOrderThreshold(float inValue);
void setMixDrawOrderThreshold(float inValue);
/// The animation queued to start after this animation, or NULL.
TrackEntry *getNext();
@ -271,6 +277,8 @@ namespace spine {
void setListener(AnimationStateListenerObject *listener);
bool wasApplied();
private:
Animation *_animation;
TrackEntry *_previous;
@ -280,7 +288,7 @@ namespace spine {
int _trackIndex;
bool _loop, _holdPrevious, _reverse, _shortestRotation;
float _eventThreshold, _attachmentThreshold, _drawOrderThreshold;
float _eventThreshold, _mixAttachmentThreshold, _alphaAttachmentThreshold, _mixDrawOrderThreshold;
float _animationStart, _animationEnd, _animationLast, _nextAnimationLast;
float _delay, _trackTime, _trackLast, _nextTrackLast, _trackEnd, _timeScale;
float _alpha, _mixTime, _mixDuration, _interruptAlpha, _totalAlpha;

View File

@ -57,6 +57,8 @@ namespace spine {
friend class PathConstraint;
friend class PhysicsConstraint;
friend class Skeleton;
friend class RegionAttachment;
@ -104,7 +106,7 @@ namespace spine {
Bone(BoneData &data, Skeleton &skeleton, Bone *parent = NULL);
/// Same as updateWorldTransform. This method exists for Bone to implement Spine::Updatable.
virtual void update();
virtual void update(Physics physics);
/// Computes the world transform using the parent bone and this bone's local transform.
void updateWorldTransform();

View File

@ -125,6 +125,14 @@ namespace spine {
Color &getColor();
const String &getIcon();
void setIcon(const String &icon);
bool isVisible();
void setVisible(bool inValue);
private:
const int _index;
const String _name;
@ -134,6 +142,8 @@ namespace spine {
TransformMode _transformMode;
bool _skinRequired;
Color _color;
String _icon;
bool _visible;
};
}

View File

@ -64,7 +64,7 @@ namespace spine {
IkConstraint(IkConstraintData &data, Skeleton &skeleton);
virtual void update();
virtual void update(Physics physics);
virtual int getOrder();
@ -100,6 +100,8 @@ namespace spine {
void setActive(bool inValue);
void setToSetupPose();
private:
IkConstraintData &_data;
Vector<Bone *> _bones;

View File

@ -46,6 +46,7 @@ namespace spine {
public:
static const float Pi;
static const float Pi_2;
static const float InvPi_2;
static const float Deg_Rad;
static const float Rad_Deg;
@ -77,6 +78,8 @@ namespace spine {
/// degrees), largest error of 0.00488 radians (0.2796 degrees).
static float atan2(float y, float x);
static float atan2Deg(float x, float y);
static float acos(float v);
static float sqrt(float v);
@ -92,6 +95,8 @@ namespace spine {
static float randomTriangular(float min, float max, float mode);
static float pow(float a, float b);
static float ceil(float v);
};
struct SP_API Interpolation {

View File

@ -59,7 +59,7 @@ namespace spine {
public:
PathConstraint(PathConstraintData &data, Skeleton &skeleton);
virtual void update();
virtual void update(Physics physics);
virtual int getOrder();
@ -95,6 +95,8 @@ namespace spine {
void setActive(bool inValue);
void setToSetupPose();
private:
static const float EPSILON;
static const int NONE;

View File

@ -0,0 +1,49 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated July 28, 2023. Replaces all prior versions.
*
* Copyright (c) 2013-2023, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software or
* otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Physics_h
#define Spine_Physics_h
/** Determines how physics and other non-deterministic updates are applied. */
namespace spine {
enum Physics {
/** Physics are not updated or applied. */
none,
/** Physics are reset to the current pose. */
reset,
/** Physics are updated and the pose from physics is applied. */
update,
/** Physics are not updated but the pose from physics is applied. */
pose
};
}
#endif

View File

@ -0,0 +1,170 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated July 28, 2023. Replaces all prior versions.
*
* Copyright (c) 2013-2023, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software or
* otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PhysicsConstraint_h
#define Spine_PhysicsConstraint_h
#include <spine/ConstraintData.h>
#include <spine/Vector.h>
namespace spine {
class PhysicsConstraintData;
class Skeleton;
class Bone;
class SP_API PhysicsConstraint : public Updatable {
RTTI_DECL
public:
explicit PhysicsConstraint(PhysicsConstraintData& data, Skeleton& skeleton);
void setBone(Bone* bone);
Bone* getBone() const;
void setInertia(float value);
float getInertia() const;
void setStrength(float value);
float getStrength() const;
void setDamping(float value);
float getDamping() const;
void setMassInverse(float value);
float getMassInverse() const;
void setWind(float value);
float getWind() const;
void setGravity(float value);
float getGravity() const;
void setMix(float value);
float getMix() const;
void setReset(bool value);
bool getReset() const;
void setUx(float value);
float getUx() const;
void setUy(float value);
float getUy() const;
void setCx(float value);
float getCx() const;
void setCy(float value);
float getCy() const;
void setTx(float value);
float getTx() const;
void setTy(float value);
float getTy() const;
void setXOffset(float value);
float getXOffset() const;
void setXVelocity(float value);
float getXVelocity() const;
void setYOffset(float value);
float getYOffset() const;
void setYVelocity(float value);
float getYVelocity() const;
void setRotateOffset(float value);
float getRotateOffset() const;
void setRotateVelocity(float value);
float getRotateVelocity() const;
void setScaleOffset(float value);
float getScaleOffset() const;
void setScaleVelocity(float value);
float getScaleVelocity() const;
void setActive(bool value);
bool isActive() const;
void setRemaining(float value);
float getRemaining() const;
void setLastTime(float value);
float getLastTime() const;
void reset();
void setToSetupPose();
void update(Physics physics) override;
private:
const PhysicsConstraintData& _data;
Bone* _bone;
float _inertia;
float _strength;
float _damping;
float _massInverse;
float _wind;
float _gravity;
float _mix;
bool _reset;
float _ux;
float _uy;
float _cx;
float _cy;
float _tx;
float _ty;
float _xOffset;
float _xVelocity;
float _yOffset;
float _yVelocity;
float _rotateOffset;
float _rotateVelocity;
float _scaleOffset;
float _scaleVelocity;
bool _active;
Skeleton& _skeleton;
float _remaining;
float _lastTime;
};
}
#endif /* Spine_PhysicsConstraint_h */

View File

@ -0,0 +1,147 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated July 28, 2023. Replaces all prior versions.
*
* Copyright (c) 2013-2023, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software or
* otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PhysicsConstraintData_h
#define Spine_PhysicsConstraintData_h
#include <spine/Vector.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/ConstraintData.h>
namespace spine {
class BoneData;
class SP_API PhysicsConstraintData : public ConstraintData {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class Skeleton;
friend class PhysicsConstraint;
public:
RTTI_DECL
explicit PhysicsConstraintData(const String &name);
void setBone(BoneData* bone);
BoneData* getBone() const;
void setX(float x);
float getX() const;
void setY(float y);
float getY() const;
void setRotate(float rotate);
float getRotate() const;
void setScaleX(float scaleX);
float getScaleX() const;
void setShearX(float shearX);
float getShearX() const;
void setStep(float step);
float getStep() const;
void setInertia(float inertia);
float getInertia() const;
void setStrength(float strength);
float getStrength() const;
void setDamping(float damping);
float getDamping() const;
void setMassInverse(float massInverse);
float getMassInverse() const;
void setWind(float wind);
float getWind() const;
void setGravity(float gravity);
float getGravity() const;
void setMix(float mix);
float getMix() const;
void setInertiaGlobal(bool inertiaGlobal);
bool isInertiaGlobal() const;
void setStrengthGlobal(bool strengthGlobal);
bool isStrengthGlobal() const;
void setDampingGlobal(bool dampingGlobal);
bool isDampingGlobal() const;
void setMassGlobal(bool massGlobal);
bool isMassGlobal() const;
void setWindGlobal(bool windGlobal);
bool isWindGlobal() const;
void setGravityGlobal(bool gravityGlobal);
bool isGravityGlobal() const;
void setMixGlobal(bool mixGlobal);
bool isMixGlobal() const;
private:
BoneData *_bone;
float _x, _y, _rotate, _scaleX, _shearX;
float _step, _inertia, _strength, _damping, _massInverse, _wind, _gravity, _mix;
bool _inertiaGlobal, _strengthGlobal, _dampingGlobal, _massGlobal, _windGlobal, _gravityGlobal, _mixGlobal;
};
}
#endif /* Spine_PhysicsConstraintData_h */

View File

@ -49,6 +49,8 @@ namespace spine {
class PathConstraint;
class PhysicsConstraint;
class TransformConstraint;
class Skin;
@ -220,6 +222,12 @@ namespace spine {
void setScaleY(float inValue);
float getTime();
float setTime(float time);
void update(float delta);
private:
SkeletonData *_data;
Vector<Bone *> _bones;
@ -233,11 +241,14 @@ namespace spine {
Color _color;
float _scaleX, _scaleY;
float _x, _y;
float _time;
void sortIkConstraint(IkConstraint *constraint);
void sortPathConstraint(PathConstraint *constraint);
void sortPhysicsConstraint(PhysicsConstraint *constraint);
void sortTransformConstraint(TransformConstraint *constraint);
void sortPathConstraintAttachment(Skin *skin, size_t slotIndex, Bone &slotBone);

View File

@ -105,6 +105,10 @@ namespace spine {
void setBlendMode(BlendMode inValue);
bool isVisible();
void setVisible(bool inValue);
private:
const int _index;
String _name;
@ -115,6 +119,7 @@ namespace spine {
bool _hasDarkColor;
String _attachmentName;
BlendMode _blendMode;
bool _visible;
};
}

View File

@ -51,7 +51,7 @@ namespace spine {
public:
TransformConstraint(TransformConstraintData &data, Skeleton &skeleton);
virtual void update();
virtual void update(Physics physics);
virtual int getOrder();
@ -91,6 +91,8 @@ namespace spine {
void setActive(bool inValue);
void setToSetupPose();
private:
TransformConstraintData &_data;
Vector<Bone *> _bones;

View File

@ -32,6 +32,7 @@
#include <spine/RTTI.h>
#include <spine/SpineObject.h>
#include <spine/Physics.h>
namespace spine {
class SP_API Updatable : public SpineObject {
@ -42,7 +43,7 @@ namespace spine {
virtual ~Updatable();
virtual void update() = 0;
virtual void update(Physics physics) = 0;
virtual bool isActive() = 0;

View File

@ -56,7 +56,7 @@ void dummyOnAnimationEventFunc(AnimationState *state, spine::EventType type, Tra
TrackEntry::TrackEntry() : _animation(NULL), _previous(NULL), _next(NULL), _mixingFrom(NULL), _mixingTo(0),
_trackIndex(0), _loop(false), _holdPrevious(false), _reverse(false),
_shortestRotation(false),
_eventThreshold(0), _attachmentThreshold(0), _drawOrderThreshold(0), _animationStart(0),
_eventThreshold(0), _mixAttachmentThreshold(0), _alphaAttachmentThreshold(0), _mixDrawOrderThreshold(0), _animationStart(0),
_animationEnd(0), _animationLast(0), _nextAnimationLast(0), _delay(0), _trackTime(0),
_trackLast(0), _nextTrackLast(0), _trackEnd(0), _timeScale(1.0f), _alpha(0), _mixTime(0),
_mixDuration(0), _interruptAlpha(0), _totalAlpha(0), _mixBlend(MixBlend_Replace),
@ -136,13 +136,17 @@ float TrackEntry::getEventThreshold() { return _eventThreshold; }
void TrackEntry::setEventThreshold(float inValue) { _eventThreshold = inValue; }
float TrackEntry::getAttachmentThreshold() { return _attachmentThreshold; }
float TrackEntry::getMixAttachmentThreshold() { return _mixAttachmentThreshold; }
void TrackEntry::setAttachmentThreshold(float inValue) { _attachmentThreshold = inValue; }
void TrackEntry::setMixAttachmentThreshold(float inValue) { _mixAttachmentThreshold = inValue; }
float TrackEntry::getDrawOrderThreshold() { return _drawOrderThreshold; }
float TrackEntry::getAlphaAttachmentThreshold() { return _alphaAttachmentThreshold; }
void TrackEntry::setDrawOrderThreshold(float inValue) { _drawOrderThreshold = inValue; }
void TrackEntry::setAlphaAttachmentThreshold(float inValue) { _alphaAttachmentThreshold = inValue; }
float TrackEntry::getMixDrawOrderThreshold() { return _mixDrawOrderThreshold; }
void TrackEntry::setMixDrawOrderThreshold(float inValue) { _mixDrawOrderThreshold = inValue; }
TrackEntry *TrackEntry::getNext() { return _next; }
@ -206,6 +210,10 @@ float TrackEntry::getTrackComplete() {
return _trackTime;// Next update.
}
bool TrackEntry::wasApplied() {
return _nextTrackLast != -1;
}
EventQueueEntry::EventQueueEntry(EventType eventType, TrackEntry *trackEntry, Event *event) : _type(eventType),
_entry(trackEntry),
_event(event) {
@ -428,14 +436,16 @@ bool AnimationState::apply(Skeleton &skeleton) {
MixBlend blend = i == 0 ? MixBlend_First : current._mixBlend;
// apply mixing from entries first.
float mix = current._alpha;
float alpha = current._alpha;
if (current._mixingFrom != NULL) {
mix *= applyMixingFrom(currentP, skeleton, blend);
alpha *= applyMixingFrom(currentP, skeleton, blend);
} else if (current._trackTime >= current._trackEnd && current._next == NULL) {
mix = 0;// Set to setup pose the last time the entry will be applied.
alpha = 0;// Set to setup pose the last time the entry will be applied.
}
bool attachments = alpha >= current._alphaAttachmentThreshold;
// apply current entry.
// apply current entry.
float animationLast = current._animationLast, animationTime = current.getAnimationTime();
float applyTime = animationTime;
Vector<Event *> *applyEvents = &_events;
@ -445,14 +455,15 @@ bool AnimationState::apply(Skeleton &skeleton) {
}
size_t timelineCount = current._animation->_timelines.size();
Vector<Timeline *> &timelines = current._animation->_timelines;
if ((i == 0 && mix == 1) || blend == MixBlend_Add) {
for (size_t ii = 0; ii < timelineCount; ++ii) {
if ((i == 0 && alpha == 1) || blend == MixBlend_Add) {
if (i == 0) attachments = true;
for (size_t ii = 0; ii < timelineCount; ++ii) {
Timeline *timeline = timelines[ii];
if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, applyTime, blend,
true);
attachments);
else
timeline->apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection_In);
timeline->apply(skeleton, animationLast, applyTime, applyEvents, alpha, blend, MixDirection_In);
}
} else {
Vector<int> &timelineMode = current._timelineMode;
@ -469,14 +480,14 @@ bool AnimationState::apply(Skeleton &skeleton) {
MixBlend timelineBlend = timelineMode[ii] == Subsequent ? blend : MixBlend_Setup;
if (!shortestRotation && timeline->getRTTI().isExactly(RotateTimeline::rtti))
applyRotateTimeline(static_cast<RotateTimeline *>(timeline), skeleton, applyTime, mix,
timelineBlend, timelinesRotation, ii << 1, firstFrame);
applyRotateTimeline(static_cast<RotateTimeline *>(timeline), skeleton, applyTime, alpha,
timelineBlend, timelinesRotation, ii << 1, firstFrame);
else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, applyTime,
timelineBlend, true);
else
timeline->apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend,
MixDirection_In);
timeline->apply(skeleton, animationLast, applyTime, applyEvents, alpha, timelineBlend,
MixDirection_In);
}
}
@ -733,8 +744,8 @@ void AnimationState::applyRotateTimeline(RotateTimeline *rotateTimeline, Skeleto
// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
float total, diff = r2 - r1;
diff -= (16384 - (int) (16384.499999999996 - diff / 360)) * 360;
if (diff == 0) {
diff -= MathUtil::ceil(diff / 360 - 0.5) * 360;
if (diff == 0) {
total = timelinesRotation[i];
} else {
float lastTotal, lastDiff;
@ -812,7 +823,7 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle
if (blend != MixBlend_First) blend = from->_mixBlend;
}
bool attachments = mix < from->_attachmentThreshold, drawOrder = mix < from->_drawOrderThreshold;
bool attachments = mix < from->_mixAttachmentThreshold, drawOrder = mix < from->_mixDrawOrderThreshold;
Vector<Timeline *> &timelines = from->_animation->_timelines;
size_t timelineCount = timelines.size();
float alphaHold = from->_alpha * to->_interruptAlpha, alphaMix = alphaHold * (1 - mix);
@ -874,7 +885,7 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle
timelinesRotation, i << 1, firstFrame);
} else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) {
applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, applyTime, timelineBlend,
attachments);
attachments && alpha >= from->_alphaAttachmentThreshold);
} else {
if (drawOrder && timeline->getRTTI().isExactly(DrawOrderTimeline::rtti) &&
timelineBlend == MixBlend_Setup)
@ -974,8 +985,9 @@ TrackEntry *AnimationState::newTrackEntry(size_t trackIndex, Animation *animatio
entry._shortestRotation = false;
entry._eventThreshold = 0;
entry._attachmentThreshold = 0;
entry._drawOrderThreshold = 0;
entry._mixAttachmentThreshold = 0;
entry._alphaAttachmentThreshold = 0;
entry._mixDrawOrderThreshold = 0;
entry._animationStart = 0;
entry._animationEnd = animation->getDuration();

View File

@ -75,7 +75,7 @@ Bone::Bone(BoneData &data, Skeleton &skeleton, Bone *parent) : Updatable(),
setToSetupPose();
}
void Bone::update() {
void Bone::update(Physics physics) {
updateWorldTransform(_ax, _ay, _arotation, _ascaleX, _ascaleY, _ashearX, _ashearY);
}
@ -84,7 +84,6 @@ void Bone::updateWorldTransform() {
}
void Bone::updateWorldTransform(float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
float cosine, sine;
float pa, pb, pc, pd;
Bone *parent = _parent;
@ -97,13 +96,15 @@ void Bone::updateWorldTransform(float x, float y, float rotation, float scaleX,
_ashearY = shearY;
if (!parent) { /* Root bone. */
float rotationY = rotation + 90 + shearY;
float sx = _skeleton.getScaleX();
float sy = _skeleton.getScaleY();
_a = MathUtil::cosDeg(rotation + shearX) * scaleX * sx;
_b = MathUtil::cosDeg(rotationY) * scaleY * sx;
_c = MathUtil::sinDeg(rotation + shearX) * scaleX * sy;
_d = MathUtil::sinDeg(rotationY) * scaleY * sy;
auto skeleton = this->_skeleton;
float sx = skeleton.getScaleX();
float 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;
@ -119,11 +120,12 @@ void Bone::updateWorldTransform(float x, float y, float rotation, float scaleX,
switch (_data.getTransformMode()) {
case TransformMode_Normal: {
float rotationY = rotation + 90 + shearY;
float la = MathUtil::cosDeg(rotation + shearX) * scaleX;
float lb = MathUtil::cosDeg(rotationY) * scaleY;
float lc = MathUtil::sinDeg(rotation + shearX) * scaleX;
float ld = MathUtil::sinDeg(rotationY) * scaleY;
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;
@ -131,16 +133,17 @@ void Bone::updateWorldTransform(float x, float y, float rotation, float scaleX,
return;
}
case TransformMode_OnlyTranslation: {
float rotationY = rotation + 90 + shearY;
_a = MathUtil::cosDeg(rotation + shearX) * scaleX;
_b = MathUtil::cosDeg(rotationY) * scaleY;
_c = MathUtil::sinDeg(rotation + shearX) * scaleX;
_d = MathUtil::sinDeg(rotationY) * scaleY;
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 TransformMode_NoRotationOrReflection: {
float s = pa * pa + pc * pc;
float prx, rx, ry, la, lb, lc, ld;
float prx;
if (s > 0.0001f) {
s = MathUtil::abs(pa * pd - pb * pc) / s;
pa /= _skeleton.getScaleX();
@ -153,12 +156,12 @@ void Bone::updateWorldTransform(float x, float y, float rotation, float scaleX,
pc = 0;
prx = 90 - MathUtil::atan2(pd, pb) * MathUtil::Rad_Deg;
}
rx = rotation + shearX - prx;
ry = rotation + shearY - prx + 90;
la = MathUtil::cosDeg(rx) * scaleX;
lb = MathUtil::cosDeg(ry) * scaleY;
lc = MathUtil::sinDeg(rx) * scaleX;
ld = MathUtil::sinDeg(ry) * scaleY;
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;
@ -167,13 +170,12 @@ void Bone::updateWorldTransform(float x, float y, float rotation, float scaleX,
}
case TransformMode_NoScale:
case TransformMode_NoScaleOrReflection: {
float za, zc, s;
float r, zb, zd, la, lb, lc, ld;
cosine = MathUtil::cosDeg(rotation);
sine = MathUtil::sinDeg(rotation);
za = (pa * cosine + pb * sine) / _skeleton.getScaleX();
zc = (pc * cosine + pd * sine) / _skeleton.getScaleY();
s = MathUtil::sqrt(za * za + zc * zc);
rotation *= MathUtil::Deg_Rad;
float cosine = MathUtil::cos(rotation);
float sine = MathUtil::sin(rotation);
float za = (pa * cosine + pb * sine) / _skeleton.getScaleX();
float zc = (pc * cosine + pd * sine) / _skeleton.getScaleY();
float s = MathUtil::sqrt(za * za + zc * zc);
if (s > 0.00001f) s = 1 / s;
za *= s;
zc *= s;
@ -181,13 +183,15 @@ void Bone::updateWorldTransform(float x, float y, float rotation, float scaleX,
if (_data.getTransformMode() == TransformMode_NoScale &&
(pa * pd - pb * pc < 0) != (_skeleton.getScaleX() < 0 != _skeleton.getScaleY() < 0))
s = -s;
r = MathUtil::Pi / 2 + MathUtil::atan2(zc, za);
zb = MathUtil::cos(r) * s;
zd = MathUtil::sin(r) * s;
la = MathUtil::cosDeg(shearX) * scaleX;
lb = MathUtil::cosDeg(90 + shearY) * scaleY;
lc = MathUtil::sinDeg(shearX) * scaleX;
ld = MathUtil::sinDeg(90 + shearY) * scaleY;
rotation = MathUtil::Pi / 2 + MathUtil::atan2(zc, za);
float zb = MathUtil::cos(rotation) * s;
float zd = MathUtil::sin(rotation) * s;
shearX *= MathUtil::Deg_Rad;
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;
@ -247,18 +251,13 @@ float Bone::localToWorldRotation(float localRotation) {
}
void Bone::rotateWorld(float degrees) {
float a = _a;
float b = _b;
float c = _c;
float d = _d;
float cos = MathUtil::cosDeg(degrees);
float sin = MathUtil::sinDeg(degrees);
_a = cos * a - sin * c;
_b = cos * b - sin * d;
_c = sin * a + cos * c;
_d = sin * b + cos * d;
degrees *= MathUtil::Deg_Rad;
float sine = MathUtil::sin(degrees), cosine = MathUtil::cos(degrees);
float ra = _a, rb = _b;
_a = cosine * ra - sine * _c;
_b = cosine * rb - sine * _d;
_c = sine * ra + cosine * _c;
_d = sine * rb + cosine * _d;
}
float Bone::getWorldToLocalRotationX() {
@ -498,67 +497,64 @@ void Bone::updateAppliedTransform() {
}
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;
float ia = pd * pid, ib = pb * pid, ic = pc * pid, id = pa * pid;
float dx = _worldX - parent->_worldX, dy = _worldY - parent->_worldY;
_ax = (dx * ia - dy * ib);
_ay = (dy * id - dx * ic);
float ra, rb, rc, rd;
if (_data.getTransformMode() == TransformMode_OnlyTranslation) {
ra = _a;
rb = _b;
rc = _c;
rd = _d;
} else {
switch (_data.getTransformMode()) {
case TransformMode_NoRotationOrReflection: {
float s = MathUtil::abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
float sa = pa / _skeleton.getScaleX();
float sc = pc / _skeleton.getScaleY();
pb = -sc * s * _skeleton.getScaleX();
pd = sa * s * _skeleton.getScaleY();
pid = 1 / (pa * pd - pb * pc);
ia = pd * pid;
ib = pb * pid;
break;
}
case TransformMode_NoScale:
case TransformMode_NoScaleOrReflection: {
float cos = MathUtil::cosDeg(_rotation), sin = MathUtil::sinDeg(_rotation);
pa = (pa * cos + pb * sin) / _skeleton.getScaleX();
pc = (pc * cos + pd * sin) / _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 (_data.getTransformMode() == TransformMode_NoScale && pid < 0 != (_skeleton.getScaleX() < 0 != _skeleton.getScaleY() < 0)) s = -s;
float r = MathUtil::Pi / 2 + MathUtil::atan2(pc, pa);
pb = MathUtil::cos(r) * s;
pd = MathUtil::sin(r) * s;
pid = 1 / (pa * pd - pb * pc);
ia = pd * pid;
ib = pb * pid;
ic = pc * pid;
id = pa * pid;
break;
}
default:
break;
}
ra = ia * _a - ib * _c;
rb = ia * _b - ib * _d;
rc = id * _c - ic * _a;
rd = id * _d - ic * _b;
}
float ra, rb, rc, rd;
if (_data.getTransformMode() == TransformMode_OnlyTranslation) {
ra = _a;
rb = _b;
rc = _c;
rd = _d;
} else {
switch (_data.getTransformMode()) {
case TransformMode_NoRotationOrReflection: {
float s = MathUtil::abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
float sa = pa / _skeleton.getScaleX();
float sc = pc / _skeleton.getScaleY();
pb = -sc * s * _skeleton.getScaleX();
pd = sa * s * _skeleton.getScaleY();
pid = 1 / (pa * pd - pb * pc);
ia = pd * pid;
ib = pb * pid;
break;
}
case TransformMode_NoScale:
case TransformMode_NoScaleOrReflection:
float cos = MathUtil::cosDeg(_rotation), sin = MathUtil::sinDeg(_rotation);
pa = (pa * cos + pb * sin) / _skeleton.getScaleX();
pc = (pc * cos + pd * sin) / _skeleton.getScaleY();
float s = MathUtil::sqrt(pa * pa + pc * pc);
if (s > 0.00001) s = 1 / s;
pa *= s;
pc *= s;
s = MathUtil::sqrt(pa * pa + pc * pc);
if (_data.getTransformMode() == TransformMode_NoScale && pid < 0 != (_skeleton.getScaleX() < 0 != _skeleton.getScaleY() < 0)) s = -s;
float 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;
}
_ashearX = 0;
_ashearX = 0;
_ascaleX = MathUtil::sqrt(ra * ra + rc * rc);
if (_ascaleX > 0.0001f) {
float det = ra * rd - rb * rc;
_ascaleY = det / _ascaleX;
_ashearY = -MathUtil::atan2(ra * rb + rc * rd, det) * MathUtil::Rad_Deg;
_arotation = MathUtil::atan2(rc, ra) * MathUtil::Rad_Deg;
_ashearY = -MathUtil::atan2(ra * rb + rc * rd, det) * MathUtil::Rad_Deg;
_arotation = MathUtil::atan2(rc, ra) * MathUtil::Rad_Deg;
} else {
_ascaleX = 0;
_ascaleY = MathUtil::sqrt(rb * rb + rd * rd);

View File

@ -46,7 +46,9 @@ BoneData::BoneData(int index, const String &name, BoneData *parent) : _index(ind
_shearY(0),
_transformMode(TransformMode_Normal),
_skinRequired(false),
_color() {
_color(),
_icon(),
_visible(true){
assert(index >= 0);
assert(_name.length() > 0);
}
@ -146,3 +148,20 @@ void BoneData::setSkinRequired(bool inValue) {
Color &BoneData::getColor() {
return _color;
}
const String &BoneData::getIcon() {
return _icon;
}
void BoneData::setIcon(const String &icon) {
this->_icon = icon;
}
bool BoneData::isVisible() {
return _visible;
}
void BoneData::setVisible(bool inValue) {
this->_visible = inValue;
}

View File

@ -84,12 +84,16 @@ void IkConstraint::apply(Bone &bone, float targetX, float targetY, bool compress
ty = targetY - bone._worldY;
default:;
}
float b = bone._data.getLength() * sx, dd = MathUtil::sqrt(tx * tx + ty * ty);
if (((compress && dd < b) || (stretch && dd > b)) && (b > 0.0001f)) {
float s = (dd / b - 1) * alpha + 1;
sx *= s;
if (uniform) sy *= s;
}
float b = bone._data.getLength() * sx;
if (b > 0.0001) {
float dd = tx * tx + ty * ty;
if ((compress && dd < b * b) || (stretch && dd > b * b)) {
float s = (MathUtil::sqrt(dd) / b - 1) * alpha + 1;
sx *= s;
if (uniform) sy *= s;
}
}
}
bone.updateWorldTransform(bone._ax, bone._ay, bone._arotation + rotationIK * alpha, sx, sy, bone._ashearX,
bone._ashearY);
@ -279,7 +283,7 @@ IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) : Updatab
}
}
void IkConstraint::update() {
void IkConstraint::update(Physics physics) {
if (_mix == 0) return;
switch (_bones.size()) {
case 1: {
@ -363,3 +367,12 @@ float IkConstraint::getSoftness() {
void IkConstraint::setSoftness(float inValue) {
_softness = inValue;
}
void IkConstraint::setToSetupPose() {
auto data = this->_data;
this->_mix = data._mix;
this->_softness = data._softness;
this->_bendDirection = data._bendDirection;
this->_compress = data._compress;
this->_stretch = data._stretch;
}

View File

@ -40,6 +40,7 @@ using namespace spine;
const float MathUtil::Pi = 3.1415926535897932385f;
const float MathUtil::Pi_2 = 3.1415926535897932385f * 2;
const float MathUtil::InvPi_2 = 1 / MathUtil::Pi_2;
const float MathUtil::Deg_Rad = (3.1415926535897932385f / 180.0f);
const float MathUtil::Rad_Deg = (180.0f / 3.1415926535897932385f);
@ -66,6 +67,10 @@ float MathUtil::atan2(float y, float x) {
return (float) ::atan2(y, x);
}
float MathUtil::atan2Deg(float x, float y) {
return MathUtil::atan2(x, y) * MathUtil::Deg_Rad;
}
/// Returns the cosine in radians from a lookup table.
float MathUtil::cos(float radians) {
return (float) ::cos(radians);
@ -122,3 +127,7 @@ float MathUtil::randomTriangular(float min, float max, float mode) {
float MathUtil::pow(float a, float b) {
return (float) ::pow(a, b);
}
float MathUtil::ceil(float v) {
return ::ceil(v);
}

View File

@ -66,7 +66,7 @@ PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) : U
_segments.setSize(10, 0);
}
void PathConstraint::update() {
void PathConstraint::update(Physics physics) {
Attachment *baseAttachment = _target->getAttachment();
if (baseAttachment == NULL || !baseAttachment->getRTTI().instanceOf(PathAttachment::rtti)) {
return;
@ -91,13 +91,11 @@ void PathConstraint::update() {
Bone *boneP = _bones[i];
Bone &bone = *boneP;
float setupLength = bone._data.getLength();
if (setupLength < PathConstraint::EPSILON) {
_lengths[i] = 0;
} else {
float x = setupLength * bone._a, y = setupLength * bone._c;
_lengths[i] = MathUtil::sqrt(x * x + y * y);
}
}
float x = setupLength * bone._a;
float y = setupLength * bone._c;
_lengths[i] = MathUtil::sqrt(x * x + y * y);
}
}
for (size_t i = 1; i < spacesCount; ++i) {
_spaces[i] = spacing;
@ -580,3 +578,12 @@ bool PathConstraint::isActive() {
void PathConstraint::setActive(bool inValue) {
_active = inValue;
}
void PathConstraint::setToSetupPose() {
auto data = this->_data;
this->_position = data._position;
this->_spacing = data._spacing;
this->_mixRotate = data._mixRotate;
this->_mixX = data._mixX;
this->_mixY = data._mixY;
}

View File

@ -0,0 +1,464 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated July 28, 2023. Replaces all prior versions.
*
* Copyright (c) 2013-2023, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software or
* otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <spine/PhysicsConstraint.h>
#include <spine/PhysicsConstraintData.h>
#include <spine/Bone.h>
#include <spine/Skeleton.h>
#include <spine/BoneData.h>
using namespace spine;
RTTI_IMPL(PhysicsConstraint, Updatable)
PhysicsConstraint::PhysicsConstraint(PhysicsConstraintData& data, Skeleton& skeleton)
: _data(data), _skeleton(skeleton) {
// Assuming 'bones' is a vector or similar container in 'Skeleton'
// and 'index' is an accessible member of 'BoneData' in 'PhysicsConstraintData'
_bone = skeleton.getBones()[data.getBone()->getIndex()]; // Adjust based on actual data structure
_inertia = data.getInertia();
_strength = data.getStrength();
_damping = data.getDamping();
_massInverse = data.getMassInverse();
_wind = data.getWind();
_gravity = data.getGravity();
_mix = data.getMix();
_reset = true;
_ux = 0;
_uy = 0;
_cx = 0;
_cy = 0;
_tx = 0;
_ty = 0;
_xOffset = 0;
_xVelocity = 0;
_yOffset = 0;
_yVelocity = 0;
_rotateOffset = 0;
_rotateVelocity = 0;
_scaleOffset = 0;
_scaleVelocity = 0;
_active = false;
_remaining = 0;
_lastTime = 0;
}
void PhysicsConstraint::setBone(Bone* bone) {
_bone = bone;
}
Bone* PhysicsConstraint::getBone() const {
return _bone;
}
void PhysicsConstraint::setInertia(float value) {
_inertia = value;
}
float PhysicsConstraint::getInertia() const {
return _inertia;
}
void PhysicsConstraint::setStrength(float value) {
_strength = value;
}
float PhysicsConstraint::getStrength() const {
return _strength;
}
void PhysicsConstraint::setDamping(float value) {
_damping = value;
}
float PhysicsConstraint::getDamping() const {
return _damping;
}
void PhysicsConstraint::setMassInverse(float value) {
_massInverse = value;
}
float PhysicsConstraint::getMassInverse() const {
return _massInverse;
}
void PhysicsConstraint::setWind(float value) {
_wind = value;
}
float PhysicsConstraint::getWind() const {
return _wind;
}
void PhysicsConstraint::setGravity(float value) {
_gravity = value;
}
float PhysicsConstraint::getGravity() const {
return _gravity;
}
void PhysicsConstraint::setMix(float value) {
_mix = value;
}
float PhysicsConstraint::getMix() const {
return _mix;
}
void PhysicsConstraint::setReset(bool value) {
_reset = value;
}
bool PhysicsConstraint::getReset() const {
return _reset;
}
void PhysicsConstraint::setUx(float value) {
_ux = value;
}
float PhysicsConstraint::getUx() const {
return _ux;
}
void PhysicsConstraint::setUy(float value) {
_uy = value;
}
float PhysicsConstraint::getUy() const {
return _uy;
}
void PhysicsConstraint::setCx(float value) {
_cx = value;
}
float PhysicsConstraint::getCx() const {
return _cx;
}
void PhysicsConstraint::setCy(float value) {
_cy = value;
}
float PhysicsConstraint::getCy() const {
return _cy;
}
void PhysicsConstraint::setTx(float value) {
_tx = value;
}
float PhysicsConstraint::getTx() const {
return _tx;
}
void PhysicsConstraint::setTy(float value) {
_ty = value;
}
float PhysicsConstraint::getTy() const {
return _ty;
}
void PhysicsConstraint::setXOffset(float value) {
_xOffset = value;
}
float PhysicsConstraint::getXOffset() const {
return _xOffset;
}
void PhysicsConstraint::setXVelocity(float value) {
_xVelocity = value;
}
float PhysicsConstraint::getXVelocity() const {
return _xVelocity;
}
void PhysicsConstraint::setYOffset(float value) {
_yOffset = value;
}
float PhysicsConstraint::getYOffset() const {
return _yOffset;
}
void PhysicsConstraint::setYVelocity(float value) {
_yVelocity = value;
}
float PhysicsConstraint::getYVelocity() const {
return _yVelocity;
}
void PhysicsConstraint::setRotateOffset(float value) {
_rotateOffset = value;
}
float PhysicsConstraint::getRotateOffset() const {
return _rotateOffset;
}
void PhysicsConstraint::setRotateVelocity(float value) {
_rotateVelocity = value;
}
float PhysicsConstraint::getRotateVelocity() const {
return _rotateVelocity;
}
void PhysicsConstraint::setScaleOffset(float value) {
_scaleOffset = value;
}
float PhysicsConstraint::getScaleOffset() const {
return _scaleOffset;
}
void PhysicsConstraint::setScaleVelocity(float value) {
_scaleVelocity = value;
}
float PhysicsConstraint::getScaleVelocity() const {
return _scaleVelocity;
}
void PhysicsConstraint::setActive(bool value) {
_active = value;
}
bool PhysicsConstraint::isActive() const {
return _active;
}
void PhysicsConstraint::setRemaining(float value) {
_remaining = value;
}
float PhysicsConstraint::getRemaining() const {
return _remaining;
}
void PhysicsConstraint::setLastTime(float value) {
_lastTime = value;
}
float PhysicsConstraint::getLastTime() const {
return _lastTime;
}
void PhysicsConstraint::reset() {
_remaining = 0;
_lastTime = _skeleton.getTime(); // Assuming Skeleton has a method getTime()
_reset = true;
_xOffset = 0;
_xVelocity = 0;
_yOffset = 0;
_yVelocity = 0;
_rotateOffset = 0;
_rotateVelocity = 0;
_scaleOffset = 0;
_scaleVelocity = 0;
}
void PhysicsConstraint::setToSetupPose() {
_inertia = _data.getInertia();
_strength = _data.getStrength();
_damping = _data.getDamping();
_massInverse = _data.getMassInverse();
_wind = _data.getWind();
_gravity = _data.getGravity();
_mix = _data.getMix();
}
void PhysicsConstraint::update(Physics physics) {
float mix = _mix;
if (mix == 0) return;
bool x = _data._x > 0;
bool y = _data._y > 0;
bool rotateOrShearX = _data._rotate > 0 || _data._shearX > 0;
bool scaleX = _data._scaleX > 0;
Bone* bone = _bone;
float l = bone->_data.getLength(); // Direct access to Bone's length
switch (physics) {
case Physics::none:
return;
case Physics::reset:
reset();
// Fall through.
case Physics::update: {
_remaining += MathUtil::max(_skeleton.getTime() - _lastTime, 0.0f);
_lastTime = _skeleton.getTime();
float bx = bone->_worldX, by = bone->_worldY;
if (_reset) {
_reset = false;
_ux = bx;
_uy = by;
} else {
float remaining = _remaining, i = _inertia, step = _data._step;
if (x || y) {
if (x) {
_xOffset += (_ux - bx) * i;
_ux = bx;
}
if (y) {
_yOffset += (_uy - by) * i;
_uy = by;
}
if (remaining >= step) {
float m = _massInverse * step, e = _strength, w = _wind * 100, g = _gravity * -100;
float d = MathUtil::pow(_damping, 60 * step);
do {
if (x) {
_xVelocity += (w - _xOffset * e) * m;
_xOffset += _xVelocity * step;
_xVelocity *= d;
}
if (y) {
_yVelocity += (g - _yOffset * e) * m;
_yOffset += _yVelocity * step;
_yVelocity *= d;
}
remaining -= step;
} while (remaining >= step);
}
if (x) bone->_worldX += _xOffset * mix * _data._x;
if (y) bone->_worldY += _yOffset * mix * _data._y;
}
if (rotateOrShearX || scaleX) {
float ca = MathUtil::atan2(bone->_c, bone->_a), c = 0, s = 0, mr = 0;
if (rotateOrShearX) {
mr = (_data._rotate + _data._shearX) * mix;
float dx = _cx - bone->_worldX, dy = _cy - bone->_worldY;
float r = MathUtil::atan2(dy + _ty, dx + _tx) - ca - _rotateOffset * mr;
_rotateOffset += (r - MathUtil::ceil(r * MathUtil::InvPi_2 - 0.5) * MathUtil::Pi_2) * i;
r = _rotateOffset * mr + ca;
c = MathUtil::cos(r);
s = MathUtil::sin(r);
if (scaleX) {
r = l * bone->getWorldScaleX();
if (r > 0) _scaleOffset += (dx * c + dy * s) * i / r;
}
} else {
c = MathUtil::cos(ca);
s = MathUtil::sin(ca);
float r = l * bone->getWorldScaleX();
if (r > 0) _scaleOffset += ((this->_cx - bone->_worldX) * c + (this->_cy - bone->_worldY) * s) * i / r;
}
remaining = _remaining;
if (remaining >= step) {
float m = _massInverse * step, e = _strength;
float d = MathUtil::pow(_damping, 60 * step);
while(true) {
remaining -= step;
if (scaleX) {
_scaleVelocity += (_wind * c - _gravity * s - _scaleOffset * e) * m;
_scaleOffset += _scaleVelocity * step;
_scaleVelocity *= d;
}
if (rotateOrShearX) {
_rotateVelocity += (-0.01f * l * (_wind * s + _gravity * c) - _rotateOffset * e) * m;
_rotateOffset += _rotateVelocity * step;
_rotateVelocity *= d;
if (remaining < step) break;
float r = _rotateOffset * mr + ca;
c = MathUtil::cos(r);
s = MathUtil::sin(r);
} else if (remaining < step) //
break;
}
}
}
_remaining = remaining;
}
_cx = bone->_worldX;
_cy = bone->_worldY;
break;
}
case Physics::pose: {
if (x) bone->_worldX += _xOffset * mix * _data._x;
if (y) bone->_worldY += _yOffset * mix * _data._y;
break;
}
}
if (rotateOrShearX) {
float o = _rotateOffset * mix, s = 0, c = 0, a = 0;
if (_data._shearX > 0) {
float r = 0;
if (_data._rotate > 0) {
r = o * _data._rotate;
s = MathUtil::sin(r);
c = MathUtil::cos(r);
a = bone->_b;
bone->_b = c * a - s * bone->_d;
bone->_d = s * a + c * bone->_d;
}
r += o * _data._shearX;
s = MathUtil::sin(r);
c = MathUtil::cos(r);
a = bone->_a;
bone->_a = c * a - s * bone->_c;
bone->_c = s * a + c * bone->_c;
} else {
o *= _data._rotate;
s = MathUtil::sin(o);
c = MathUtil::cos(o);
a = bone->_a;
bone->_a = c * a - s * bone->_c;
bone->_c = s * a + c * bone->_c;
a = bone->_b;
bone->_b = c * a - s * bone->_d;
bone->_d = s * a + c * bone->_d;
}
}
if (scaleX) {
float s = 1 + _scaleOffset * mix * _data._scaleX;
bone->_a *= s;
bone->_c *= s;
}
if (physics != Physics::pose) {
_tx = l * bone->_a;
_ty = l * bone->_c;
}
bone->updateAppliedTransform();
}

View File

@ -0,0 +1,216 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated July 28, 2023. Replaces all prior versions.
*
* Copyright (c) 2013-2023, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software or
* otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <spine/PhysicsConstraintData.h>
#include <spine/BoneData.h>
#include <assert.h>
using namespace spine;
RTTI_IMPL(PhysicsConstraintData, ConstraintData)
PhysicsConstraintData::PhysicsConstraintData(const String &name) : ConstraintData(name),
_bone(nullptr),
_x(0), _y(0), _rotate(0), _scaleX(0), _shearX(0),
_step(0), _inertia(0), _strength(0), _damping(0), _massInverse(0), _wind(0), _gravity(0), _mix(0),
_inertiaGlobal(false), _strengthGlobal(false), _dampingGlobal(false), _massGlobal(false),
_windGlobal(false), _gravityGlobal(false), _mixGlobal(false)
{
}
void PhysicsConstraintData::setBone(BoneData* bone) {
_bone = bone;
}
BoneData* PhysicsConstraintData::getBone() const {
return _bone;
}
void PhysicsConstraintData::setX(float x) {
_x = x;
}
float PhysicsConstraintData::getX() const {
return _x;
}
void PhysicsConstraintData::setY(float y) {
_y = y;
}
float PhysicsConstraintData::getY() const {
return _y;
}
void PhysicsConstraintData::setRotate(float rotate) {
_rotate = rotate;
}
float PhysicsConstraintData::getRotate() const {
return _rotate;
}
void PhysicsConstraintData::setScaleX(float scaleX) {
_scaleX = scaleX;
}
float PhysicsConstraintData::getScaleX() const {
return _scaleX;
}
void PhysicsConstraintData::setShearX(float shearX) {
_shearX = shearX;
}
float PhysicsConstraintData::getShearX() const {
return _shearX;
}
void PhysicsConstraintData::setStep(float step) {
_step = step;
}
float PhysicsConstraintData::getStep() const {
return _step;
}
void PhysicsConstraintData::setInertia(float inertia) {
_inertia = inertia;
}
float PhysicsConstraintData::getInertia() const {
return _inertia;
}
void PhysicsConstraintData::setStrength(float strength) {
_strength = strength;
}
float PhysicsConstraintData::getStrength() const {
return _strength;
}
void PhysicsConstraintData::setDamping(float damping) {
_damping = damping;
}
float PhysicsConstraintData::getDamping() const {
return _damping;
}
void PhysicsConstraintData::setMassInverse(float massInverse) {
_massInverse = massInverse;
}
float PhysicsConstraintData::getMassInverse() const {
return _massInverse;
}
void PhysicsConstraintData::setWind(float wind) {
_wind = wind;
}
float PhysicsConstraintData::getWind() const {
return _wind;
}
void PhysicsConstraintData::setGravity(float gravity) {
_gravity = gravity;
}
float PhysicsConstraintData::getGravity() const {
return _gravity;
}
void PhysicsConstraintData::setMix(float mix) {
_mix = mix;
}
float PhysicsConstraintData::getMix() const {
return _mix;
}
void PhysicsConstraintData::setInertiaGlobal(bool inertiaGlobal) {
_inertiaGlobal = inertiaGlobal;
}
bool PhysicsConstraintData::isInertiaGlobal() const {
return _inertiaGlobal;
}
void PhysicsConstraintData::setStrengthGlobal(bool strengthGlobal) {
_strengthGlobal = strengthGlobal;
}
bool PhysicsConstraintData::isStrengthGlobal() const {
return _strengthGlobal;
}
void PhysicsConstraintData::setDampingGlobal(bool dampingGlobal) {
_dampingGlobal = dampingGlobal;
}
bool PhysicsConstraintData::isDampingGlobal() const {
return _dampingGlobal;
}
void PhysicsConstraintData::setMassGlobal(bool massGlobal) {
_massGlobal = massGlobal;
}
bool PhysicsConstraintData::isMassGlobal() const {
return _massGlobal;
}
void PhysicsConstraintData::setWindGlobal(bool windGlobal) {
_windGlobal = windGlobal;
}
bool PhysicsConstraintData::isWindGlobal() const {
return _windGlobal;
}
void PhysicsConstraintData::setGravityGlobal(bool gravityGlobal) {
_gravityGlobal = gravityGlobal;
}
bool PhysicsConstraintData::isGravityGlobal() const {
return _gravityGlobal;
}
void PhysicsConstraintData::setMixGlobal(bool mixGlobal) {
_mixGlobal = mixGlobal;
}
bool PhysicsConstraintData::isMixGlobal() const {
return _mixGlobal;
}

View File

@ -45,12 +45,10 @@ void PointAttachment::computeWorldPosition(Bone &bone, float &ox, float &oy) {
}
float PointAttachment::computeWorldRotation(Bone &bone) {
float cos = MathUtil::cosDeg(_rotation);
float sin = MathUtil::sinDeg(_rotation);
float ix = cos * bone._a + sin * bone._b;
float iy = cos * bone._c + sin * bone._d;
return MathUtil::atan2(iy, ix) * MathUtil::Rad_Deg;
float r = _rotation * MathUtil::Deg_Rad, cosine = MathUtil::cos(r), sine = MathUtil::sin(r);
float x = cosine * bone._a + sine * bone._b;
float y = cosine * bone._c + sine * bone._d;
return MathUtil::atan2Deg(y, x);
}
float PointAttachment::getX() {

View File

@ -59,7 +59,8 @@ Skeleton::Skeleton(SkeletonData *skeletonData) : _data(skeletonData),
_scaleX(1),
_scaleY(1),
_x(0),
_y(0) {
_y(0),
_time(0){
_bones.ensureCapacity(_data->getBones().size());
for (size_t i = 0; i < _data->getBones().size(); ++i) {
BoneData *data = _data->getBones()[i];
@ -683,3 +684,15 @@ void Skeleton::sortReset(Vector<Bone *> &bones) {
bone->_sorted = false;
}
}
float Skeleton::getTime() {
return _time;
}
float Skeleton::setTime(float time) {
_time = time;
}
void Skeleton::update(float delta) {
_time += delta;
}

View File

@ -40,7 +40,8 @@ SlotData::SlotData(int index, const String &name, BoneData &boneData) : _index(i
_darkColor(0, 0, 0, 0),
_hasDarkColor(false),
_attachmentName(),
_blendMode(BlendMode_Normal) {
_blendMode(BlendMode_Normal),
_visible(true) {
assert(_index >= 0);
assert(_name.length() > 0);
}
@ -88,3 +89,11 @@ BlendMode SlotData::getBlendMode() {
void SlotData::setBlendMode(BlendMode inValue) {
_blendMode = inValue;
}
bool SlotData::isVisible() {
return _visible;
}
void SlotData::setVisible(bool inValue) {
this->_visible = inValue;
}

View File

@ -61,7 +61,7 @@ TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton
}
}
void TransformConstraint::update() {
void TransformConstraint::update(Physics physics) {
if (_mixRotate == 0 && _mixX == 0 && _mixY == 0 && _mixScaleX == 0 && _mixScaleY == 0 && _mixShearY == 0) return;
if (_data.isLocal()) {
@ -287,7 +287,7 @@ void TransformConstraint::applyAbsoluteLocal() {
float rotation = bone._arotation;
if (mixRotate != 0) {
float r = target._arotation - rotation + _data._offsetRotation;
r -= (16384 - (int) (16384.499999999996 - r / 360)) * 360;
r -= MathUtil::ceil(r / 360 - 0.5) * 360;
rotation += r * mixRotate;
}
@ -304,7 +304,7 @@ void TransformConstraint::applyAbsoluteLocal() {
float shearY = bone._ashearY;
if (mixShearY != 0) {
float r = target._ashearY - shearY + _data._offsetShearY;
r -= (16384 - (int) (16384.499999999996 - r / 360)) * 360;
r -= MathUtil::ceil(r / 360 - 0.5) * 360;
bone._shearY += r * mixShearY;
}
@ -338,3 +338,13 @@ bool TransformConstraint::isActive() {
void TransformConstraint::setActive(bool inValue) {
_active = inValue;
}
void TransformConstraint::setToSetupPose() {
auto data = this->_data;
this->_mixRotate = data._mixRotate;
this->_mixX = data._mixX;
this->_mixY = data._mixY;
this->_mixScaleX = data._mixScaleX;
this->_mixScaleY = data._mixScaleY;
this->_mixShearY = data._mixShearY;
}