Posed hierarchy changes

This commit is contained in:
Mario Zechner 2025-06-12 22:04:32 +02:00
parent 922e94485c
commit f2e6b9095f
42 changed files with 1219 additions and 709 deletions

View File

@ -123,6 +123,8 @@ namespace spine {
static int search(Vector<float> &values, float target);
static int search(Vector<float> &values, float target, int step);
Vector<int> &getBones();
private:
Vector<Timeline *> _timelines;
HashMap<PropertyId, bool> _timelineIds;

View File

@ -42,7 +42,7 @@ namespace spine {
/// A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a
/// local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a
/// constraint or application code modifies the world transform after it was computed from the local transform.
class SP_API Bone : public PosedActive<BoneData, BoneLocal, BonePose> {
class SP_API Bone : public PosedActiveGeneric<BoneData, BoneLocal, BonePose> {
friend class AnimationState;
friend class RotateTimeline;
friend class IkConstraint;

View File

@ -33,6 +33,7 @@
#include <spine/PosedActive.h>
#include <spine/Update.h>
#include <spine/RTTI.h>
#include <spine/ConstraintData.h>
namespace spine {
class Skeleton;
@ -42,14 +43,16 @@ namespace spine {
public:
Constraint();
virtual ~Constraint();
ConstraintData &getData();
};
template<class T, class D, class P>
class SP_API ConstraintGeneric : public PosedActive<D, P, P>, public Constraint {
class SP_API ConstraintGeneric : public PosedActiveGeneric<D, P, P>, public Constraint {
RTTI_DECL
public:
ConstraintGeneric(D& data) : PosedActive<D, P, P>(data), Constraint() {
ConstraintGeneric(D& data) : PosedActiveGeneric<D, P, P>(data), Constraint() {
}
virtual ~ConstraintGeneric() {

View File

@ -55,20 +55,14 @@ namespace spine {
friend class SkeletonJson;
public:
ConstraintDataGeneric(const String &name);
virtual ~ConstraintDataGeneric();
ConstraintDataGeneric(const String &name) : PosedData<P>(name), ConstraintData(name) {
}
virtual ~ConstraintDataGeneric() {
}
/// Creates a constraint instance.
virtual T* create(Skeleton& skeleton) = 0;
};
template<class T, class P>
ConstraintDataGeneric<T, P>::ConstraintDataGeneric(const String &name) : PosedData<P>(name), ConstraintData(name) {
}
template<class T, class P>
ConstraintDataGeneric<T, P>::~ConstraintDataGeneric() {
}
}
#endif /* Spine_ConstraintData_h */

View File

@ -38,6 +38,12 @@ namespace spine {
public:
Posed() {}
virtual ~Posed() {}
virtual void resetApplied() = 0;
virtual void pose() = 0;
virtual void constrained() = 0;
};
template<class D, class P, class A>
@ -72,16 +78,10 @@ namespace spine {
friend class InheritTimeline;
public:
Posed(D& data) : _data(data), _pose(), _constrained() {
static_assert(std::is_base_of<P, A>::value, "A must extend P");
// Match Java behavior: applied initially points to pose
// Note: Both _pose and _constrained are stored as type A to match Java's
// "new BonePose(), new BonePose()" pattern. For most classes P==A, but
// Bone uses P=BoneLocal, A=BonePose where BonePose extends BoneLocal.
_applied = &_pose;
PosedGeneric(D &data) : _data(data), _pose(), _constrained(), _applied(&_pose) {
}
virtual ~Posed() {
virtual ~PosedGeneric() {
}
void setupPose() {
@ -103,12 +103,24 @@ namespace spine {
return *_applied;
}
virtual void resetApplied() {
_constrained.set(_pose);
}
virtual void pose() {
_applied = &_pose;
}
virtual void constrained() {
_applied = &_constrained;
}
protected:
D &_data;
A _pose; ///< Stored as A type (concrete pose type) to match Java behavior
A _constrained;///< Stored as A type (concrete pose type) to match Java behavior
A *_applied; ///< Points to either _pose or _constrained, reassignable like Java
};
}
}// namespace spine
#endif /* Spine_Posed_h */

View File

@ -35,33 +35,22 @@
namespace spine {
class SP_API PosedActive {
public:
PosedActive() {}
virtual ~PosedActive() {}
/// Returns false when this constraint won't be updated by
/// Skeleton::updateWorldTransform() because a skin is required and the
/// active skin does not contain this item.
/// @see Skin::getBones()
/// @see Skin::getConstraints()
/// @see PosedData::getSkinRequired()
/// @see Skeleton::updateCache()
bool isActive();
protected:
bool _active;
};
template<class D, class P, class A>
class SP_API PosedActiveGeneric : public PosedGeneric<D, P, A> {
friend class SlotCurveTimeline;
public:
PosedActive(D &data): PosedGeneric<D, P, A>(data), _active(false) {
PosedActiveGeneric(D &data): PosedGeneric<D, P, A>(data), _active(false) {
this->setupPose();
}
virtual ~PosedActive() {}
virtual ~PosedActiveGeneric() {}
bool isActive() {
return _active;
}
protected:
bool _active;
};
}// namespace spine

View File

@ -298,21 +298,20 @@ namespace spine {
void update(float delta);
protected:
Vector<Update *> _updateCache;
private:
SkeletonData &_data;
Vector<Bone *> _bones;
Vector<Slot *> _slots;
Vector<Slot *> _drawOrder;
Vector<Constraint *> _constraints;
Vector<PhysicsConstraint *> _physics;
Vector<Update *> _updateCache;
Vector<Posed *> _resetCache;
Skin *_skin;
Color _color;
float _scaleX, _scaleY;
float _x, _y;
float _time;
float _scaleX, _scaleY;
float _windX, _windY, _gravityX, _gravityY;
float _time;
int _update;
};
}// namespace spine

View File

@ -118,8 +118,7 @@ void Animation::setTimelines(Vector<Timeline *> &timelines) {
_timelineIds.put(propertyIds[ii], true);
}
BoneTimeline *boneTimeline = timeline->getRTTI().instanceOf(BoneTimeline1::rtti) ?
static_cast<BoneTimeline1 *>(timeline) : NULL;
BoneTimeline *boneTimeline = timeline->getRTTI().instanceOf(BoneTimeline1::rtti) ? static_cast<BoneTimeline1 *>(timeline) : NULL;
if (boneTimeline) {
int boneIndex = boneTimeline->getBoneIndex();
if (!boneSet.containsKey(boneIndex)) {

View File

@ -36,7 +36,7 @@ using namespace spine;
RTTI_IMPL_NOPARENT(Bone)
Bone::Bone(BoneData& data, Bone* parent) : PosedActive(data),
Bone::Bone(BoneData &data, Bone *parent) : PosedActiveGeneric(data),
_parent(parent),
_children(),
_sorted(false) {

View File

@ -42,8 +42,7 @@ RTTI_IMPL_NOPARENT(BoneTimeline)
RTTI_IMPL(BoneTimeline1, CurveTimeline1)
BoneTimeline1::BoneTimeline1(size_t frameCount, size_t bezierCount, int boneIndex, Property property) :
BoneTimeline(boneIndex),
BoneTimeline1::BoneTimeline1(size_t frameCount, size_t bezierCount, int boneIndex, Property property) : BoneTimeline(boneIndex),
CurveTimeline1(frameCount, bezierCount) {
PropertyId ids[] = {((PropertyId) property << 32) | boneIndex};
setPropertyIds(ids, 1);
@ -62,8 +61,7 @@ void BoneTimeline1::apply(Skeleton &skeleton, float lastTime, float time, Vector
RTTI_IMPL(BoneTimeline2, CurveTimeline2)
BoneTimeline2::BoneTimeline2(size_t frameCount, size_t bezierCount, int boneIndex, Property property1, Property property2) :
BoneTimeline(boneIndex),
BoneTimeline2::BoneTimeline2(size_t frameCount, size_t bezierCount, int boneIndex, Property property1, Property property2) : BoneTimeline(boneIndex),
CurveTimeline2(frameCount, bezierCount) {
PropertyId ids[] = {((PropertyId) property1 << 32) | boneIndex, ((PropertyId) property2 << 32) | boneIndex};
setPropertyIds(ids, 2);

View File

@ -249,8 +249,7 @@ void IkConstraint::apply(Skeleton& skeleton, BonePose& parent, BonePose& child,
child._rotation += a2 * mix;
}
IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) :
ConstraintGeneric<IkConstraint, IkConstraintData, IkConstraintPose>(data),
IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) : ConstraintGeneric<IkConstraint, IkConstraintData, IkConstraintPose>(data),
_target(skeleton.findBone(data.getTarget()->getName())) {
_bones.ensureCapacity(data.getBones().size());
@ -299,8 +298,8 @@ void IkConstraint::sort(Skeleton& skeleton) {
skeleton._updateCache.add(this);
parent->_sorted = false;
skeleton.sortReset(parent->_children);
skeleton.constrained(parent);
if (_bones.size() > 1) skeleton.constrained(_bones[1]->_bone);
skeleton.constrained(*parent);
if (_bones.size() > 1) skeleton.constrained(*_bones[1]->_bone);
}
bool IkConstraint::isSourceActive() {

View File

@ -51,8 +51,7 @@ const int PathConstraint::NONE = -1;
const int PathConstraint::BEFORE = -2;
const int PathConstraint::AFTER = -3;
PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) :
ConstraintGeneric<PathConstraint, PathConstraintData, PathConstraintPose>(data),
PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) : ConstraintGeneric<PathConstraint, PathConstraintData, PathConstraintPose>(data),
_slot(skeleton._slots.buffer()[data._slot->_index]) {
_bones.ensureCapacity(data.getBones().size());

View File

@ -41,8 +41,7 @@ using namespace spine;
RTTI_IMPL(PhysicsConstraint, Constraint)
PhysicsConstraint::PhysicsConstraint(PhysicsConstraintData &data, Skeleton &skeleton) :
ConstraintGeneric<PhysicsConstraint, PhysicsConstraintData, PhysicsConstraintPose>(data),
PhysicsConstraint::PhysicsConstraint(PhysicsConstraintData &data, Skeleton &skeleton) : ConstraintGeneric<PhysicsConstraint, PhysicsConstraintData, PhysicsConstraintPose>(data),
_reset(true), _ux(0), _uy(0), _cx(0), _cy(0), _tx(0), _ty(0),
_xOffset(0), _xLag(0), _xVelocity(0), _yOffset(0), _yLag(0), _yVelocity(0),
_rotateOffset(0), _rotateLag(0), _rotateVelocity(0), _scaleOffset(0), _scaleLag(0), _scaleVelocity(0),
@ -60,9 +59,9 @@ PhysicsConstraint* PhysicsConstraint::copy(Skeleton& skeleton) {
void PhysicsConstraint::sort(Skeleton &skeleton) {
Bone *bone = _bone->_bone;
skeleton.sortBone(bone);
skeleton.updateCache.add(this);
skeleton._updateCache.add(this);
skeleton.sortReset(bone->_children);
skeleton.constrained(bone);
skeleton.constrained(*bone);
}
bool PhysicsConstraint::isSourceActive() {
@ -127,12 +126,14 @@ void PhysicsConstraint::update(Skeleton& skeleton, Physics physics) {
if (x || y) {
if (x) {
float u = (_ux - bx) * i;
_xOffset += u > qx ? qx : u < -qx ? -qx : u;
_xOffset += u > qx ? qx : u < -qx ? -qx
: u;
_ux = bx;
}
if (y) {
float u = (_uy - by) * i;
_yOffset += u > qy ? qy : u < -qy ? -qy : u;
_yOffset += u > qy ? qy : u < -qy ? -qy
: u;
_uy = by;
}
if (a >= t) {

View File

@ -35,8 +35,7 @@ using namespace spine;
RTTI_IMPL(RotateTimeline, BoneTimeline1)
RotateTimeline::RotateTimeline(size_t frameCount, size_t bezierCount, int boneIndex) :
BoneTimeline1(frameCount, bezierCount, boneIndex, Property_Rotate) {
RotateTimeline::RotateTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : BoneTimeline1(frameCount, bezierCount, boneIndex, Property_Rotate) {
}
void RotateTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float alpha, MixBlend blend,

View File

@ -42,8 +42,7 @@ using namespace spine;
RTTI_IMPL(ScaleTimeline, BoneTimeline2)
ScaleTimeline::ScaleTimeline(size_t frameCount, size_t bezierCount, int boneIndex) :
BoneTimeline2(frameCount, bezierCount, boneIndex, Property_ScaleX, Property_ScaleY) {
ScaleTimeline::ScaleTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : BoneTimeline2(frameCount, bezierCount, boneIndex, Property_ScaleX, Property_ScaleY) {
}
void ScaleTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float alpha, MixBlend blend,
@ -144,8 +143,7 @@ void ScaleTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float a
RTTI_IMPL(ScaleXTimeline, BoneTimeline1)
ScaleXTimeline::ScaleXTimeline(size_t frameCount, size_t bezierCount, int boneIndex) :
BoneTimeline1(frameCount, bezierCount, boneIndex, Property_ScaleX) {
ScaleXTimeline::ScaleXTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : BoneTimeline1(frameCount, bezierCount, boneIndex, Property_ScaleX) {
}
void ScaleXTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float alpha, MixBlend blend,
@ -155,8 +153,7 @@ void ScaleXTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float
RTTI_IMPL(ScaleYTimeline, BoneTimeline1)
ScaleYTimeline::ScaleYTimeline(size_t frameCount, size_t bezierCount, int boneIndex) :
BoneTimeline1(frameCount, bezierCount, boneIndex, Property_ScaleY) {
ScaleYTimeline::ScaleYTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : BoneTimeline1(frameCount, bezierCount, boneIndex, Property_ScaleY) {
}
void ScaleYTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float alpha, MixBlend blend,

View File

@ -42,8 +42,7 @@ using namespace spine;
RTTI_IMPL(ShearTimeline, BoneTimeline2)
ShearTimeline::ShearTimeline(size_t frameCount, size_t bezierCount, int boneIndex) :
BoneTimeline2(frameCount, bezierCount, boneIndex, Property_ShearX, Property_ShearY) {
ShearTimeline::ShearTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : BoneTimeline2(frameCount, bezierCount, boneIndex, Property_ShearX, Property_ShearY) {
}
void ShearTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float alpha, MixBlend blend,
@ -106,8 +105,7 @@ void ShearTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float a
RTTI_IMPL(ShearXTimeline, BoneTimeline1)
ShearXTimeline::ShearXTimeline(size_t frameCount, size_t bezierCount, int boneIndex) :
BoneTimeline1(frameCount, bezierCount, boneIndex, Property_ShearX) {
ShearXTimeline::ShearXTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : BoneTimeline1(frameCount, bezierCount, boneIndex, Property_ShearX) {
}
void ShearXTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float alpha, MixBlend blend,
@ -117,8 +115,7 @@ void ShearXTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float
RTTI_IMPL(ShearYTimeline, BoneTimeline1)
ShearYTimeline::ShearYTimeline(size_t frameCount, size_t bezierCount, int boneIndex) :
BoneTimeline1(frameCount, bezierCount, boneIndex, Property_ShearY) {
ShearYTimeline::ShearYTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : BoneTimeline1(frameCount, bezierCount, boneIndex, Property_ShearY) {
}
void ShearYTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float alpha, MixBlend blend,

View File

@ -0,0 +1,569 @@
/******************************************************************************
* 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/Skeleton.h>
#include <spine/SkeletonData.h>
#include <spine/BoneData.h>
#include <spine/SlotData.h>
#include <spine/ConstraintData.h>
#include <spine/PhysicsConstraintData.h>
#include <spine/IkConstraintData.h>
#include <spine/PathConstraintData.h>
#include <spine/TransformConstraintData.h>
#include <spine/Bone.h>
#include <spine/Slot.h>
#include <spine/IkConstraint.h>
#include <spine/PathConstraint.h>
#include <spine/PhysicsConstraint.h>
#include <spine/TransformConstraint.h>
#include <spine/Skin.h>
#include <spine/Attachment.h>
#include <spine/RegionAttachment.h>
#include <spine/MeshAttachment.h>
#include <spine/ClippingAttachment.h>
#include <spine/SkeletonClipping.h>
#include <spine/MathUtil.h>
#include <spine/BonePose.h>
#include <spine/SlotPose.h>
#include <spine/Posed.h>
#include <spine/Update.h>
#include <float.h>
using namespace spine;
static const unsigned short quadTriangles[] = {0, 1, 2, 2, 3, 0};
Skeleton::Skeleton(SkeletonData &skeletonData) : _data(skeletonData), _skin(NULL), _color(1, 1, 1, 1),
_scaleX(1), _scaleY(1), _x(0), _y(0), _time(0), _windX(1), _windY(0), _gravityX(0), _gravityY(1), _update(0) {
// Create bones
_bones.ensureCapacity(_data.getBones().size());
Vector<BoneData *> &boneDataArray = _data.getBones();
for (size_t i = 0; i < boneDataArray.size(); ++i) {
BoneData *boneData = boneDataArray[i];
Bone *bone;
if (boneData->getParent() == NULL) {
bone = new (__FILE__, __LINE__) Bone(*boneData, NULL);
} else {
Bone *parent = _bones[boneData->getParent()->getIndex()];
bone = new (__FILE__, __LINE__) Bone(*boneData, parent);
parent->getChildren().add(bone);
}
_bones.add(bone);
}
// Create slots
_slots.ensureCapacity(_data.getSlots().size());
_drawOrder.ensureCapacity(_data.getSlots().size());
Vector<SlotData *> &slotDataArray = _data.getSlots();
for (size_t i = 0; i < slotDataArray.size(); ++i) {
SlotData *slotData = slotDataArray[i];
Slot *slot = new (__FILE__, __LINE__) Slot(*slotData, *this);
_slots.add(slot);
_drawOrder.add(slot);
}
// Create constraints and physics
_physics.ensureCapacity(8);
_constraints.ensureCapacity(_data.getConstraints().size());
Vector<ConstraintData *> &constraintDataArray = _data.getConstraints();
for (size_t i = 0; i < constraintDataArray.size(); ++i) {
ConstraintData *constraintData = constraintDataArray[i];
Constraint *constraint = constraintData->create(*this);
PhysicsConstraint *physicsConstraint = dynamic_cast<PhysicsConstraint *>(constraint);
if (physicsConstraint != NULL) {
_physics.add(physicsConstraint);
}
_constraints.add(constraint);
}
_physics.shrink();
updateCache();
}
Skeleton::~Skeleton() {
for (size_t i = 0; i < _bones.size(); ++i) {
delete _bones[i];
}
for (size_t i = 0; i < _slots.size(); ++i) {
delete _slots[i];
}
for (size_t i = 0; i < _constraints.size(); ++i) {
delete _constraints[i];
}
}
void Skeleton::updateCache() {
_updateCache.clear();
// Reset slot applied poses to current pose
for (size_t i = 0; i < _slots.size(); ++i) {
Slot *slot = _slots[i];
slot->getAppliedPose().set(slot->getPose());
}
// Mark bones based on skin requirements
size_t boneCount = _bones.size();
for (size_t i = 0; i < boneCount; ++i) {
Bone *bone = _bones[i];
bone->_sorted = bone->getData().getSkinRequired();
bone->_active = !bone->_sorted;
bone->getAppliedPose().set(bone->getPose());
}
// Activate bones required by skin
if (_skin != NULL) {
Vector<BoneData *> &skinBones = _skin->getBones();
for (size_t i = 0; i < skinBones.size(); ++i) {
Bone *bone = _bones[skinBones[i]->getIndex()];
do {
bone->_sorted = false;
bone->_active = true;
bone = bone->getParent();
} while (bone != NULL);
}
}
// Set constraint applied poses and mark active constraints
for (size_t i = 0; i < _constraints.size(); ++i) {
Constraint *constraint = _constraints[i];
constraint->getAppliedPose().set(constraint->getPose());
}
for (size_t i = 0; i < _constraints.size(); ++i) {
Constraint *constraint = _constraints[i];
constraint->_active = constraint->isSourceActive() &&
(!constraint->getData().getSkinRequired() || (_skin != NULL && _skin->getConstraints().contains(&constraint->getData(), true)));
if (constraint->_active) {
constraint->sort(*this);
}
}
// Sort all bones
for (size_t i = 0; i < boneCount; ++i) {
sortBone(_bones[i]);
}
// Replace bone references in update cache with their applied poses
for (size_t i = 0; i < _updateCache.size(); ++i) {
Update *updateItem = _updateCache[i];
Bone *bone = dynamic_cast<Bone *>(updateItem);
if (bone != NULL) {
_updateCache[i] = &bone->getAppliedPose();
}
}
}
void Skeleton::constrained(Posed &object) {
if (&object.getPose() == &object.getAppliedPose()) {
object.setAppliedPose(object.getConstrainedPose());
// Add to reset cache - this would need a resetCache member like Java
}
}
void Skeleton::sortBone(Bone *bone) {
if (bone->_sorted || !bone->_active) return;
Bone *parent = bone->getParent();
if (parent != NULL) sortBone(parent);
bone->_sorted = true;
_updateCache.add(bone);
}
void Skeleton::sortReset(Vector<Bone *> &bones) {
for (size_t i = 0; i < bones.size(); ++i) {
Bone *bone = bones[i];
if (bone->_active) {
if (bone->_sorted) sortReset(bone->getChildren());
bone->_sorted = false;
}
}
}
void Skeleton::updateWorldTransform(Physics physics) {
_update++;
// Reset all applied poses to their base poses
// This would require a resetCache implementation like Java has
// Update all items in update cache
for (size_t i = 0; i < _updateCache.size(); ++i) {
Update *updateItem = _updateCache[i];
updateItem->update(*this, physics);
}
}
void Skeleton::updateWorldTransform(Physics physics, BonePose *parent) {
if (parent == NULL) return;
_update++;
// Reset all applied poses to their base poses
// This would require a resetCache implementation like Java has
// Apply the parent bone transform to the root bone
BonePose &rootBone = getRootBone()->getAppliedPose();
float pa = parent->getA(), pb = parent->getB(), pc = parent->getC(), pd = parent->getD();
rootBone.setWorldX(pa * _x + pb * _y + parent->getWorldX());
rootBone.setWorldY(pc * _x + pd * _y + parent->getWorldY());
float rx = (rootBone.getRotation() + rootBone.getShearX()) * MathUtil::Deg_Rad;
float ry = (rootBone.getRotation() + 90 + rootBone.getShearY()) * MathUtil::Deg_Rad;
float la = MathUtil::cos(rx) * rootBone.getScaleX();
float lb = MathUtil::cos(ry) * rootBone.getScaleY();
float lc = MathUtil::sin(rx) * rootBone.getScaleX();
float ld = MathUtil::sin(ry) * rootBone.getScaleY();
rootBone.setA((pa * la + pb * lc) * _scaleX);
rootBone.setB((pa * lb + pb * ld) * _scaleX);
rootBone.setC((pc * la + pd * lc) * _scaleY);
rootBone.setD((pc * lb + pd * ld) * _scaleY);
// Update everything except root bone
for (size_t i = 0; i < _updateCache.size(); ++i) {
Update *updateItem = _updateCache[i];
if (updateItem != &rootBone) {
updateItem->update(*this, physics);
}
}
}
void Skeleton::setupPose() {
setupPoseBones();
setupPoseSlots();
}
void Skeleton::setupPoseBones() {
for (size_t i = 0; i < _bones.size(); ++i) {
_bones[i]->setupPose();
}
for (size_t i = 0; i < _constraints.size(); ++i) {
_constraints[i]->setupPose();
}
}
void Skeleton::setupPoseSlots() {
// Copy slots to draw order in setup pose order
for (size_t i = 0; i < _slots.size(); ++i) {
_drawOrder[i] = _slots[i];
}
for (size_t i = 0; i < _slots.size(); ++i) {
_slots[i]->setupPose();
}
}
SkeletonData *Skeleton::getData() {
return &_data;
}
Vector<Bone *> &Skeleton::getBones() {
return _bones;
}
Vector<Update *> &Skeleton::getUpdateCache() {
return _updateCache;
}
Bone *Skeleton::getRootBone() {
return _bones.size() == 0 ? NULL : _bones[0];
}
Bone *Skeleton::findBone(const String &boneName) {
if (boneName.isEmpty()) return NULL;
for (size_t i = 0; i < _bones.size(); ++i) {
if (_bones[i]->getData().getName() == boneName) {
return _bones[i];
}
}
return NULL;
}
Vector<Slot *> &Skeleton::getSlots() {
return _slots;
}
Slot *Skeleton::findSlot(const String &slotName) {
if (slotName.isEmpty()) return NULL;
for (size_t i = 0; i < _slots.size(); ++i) {
if (_slots[i]->getData().getName() == slotName) {
return _slots[i];
}
}
return NULL;
}
Vector<Slot *> &Skeleton::getDrawOrder() {
return _drawOrder;
}
Skin *Skeleton::getSkin() {
return _skin;
}
void Skeleton::setSkin(const String &skinName) {
Skin *skin = _data.findSkin(skinName);
if (skin == NULL && !skinName.isEmpty()) {
// Handle error - skin not found
return;
}
setSkin(skin);
}
void Skeleton::setSkin(Skin *newSkin) {
if (newSkin == _skin) return;
if (newSkin != NULL) {
if (_skin != NULL) {
newSkin->attachAll(*this, *_skin);
} else {
for (size_t i = 0; i < _slots.size(); ++i) {
Slot *slot = _slots[i];
const String &name = slot->getData().getAttachmentName();
if (!name.isEmpty()) {
Attachment *attachment = newSkin->getAttachment((int) i, name);
if (attachment != NULL) {
slot->getPose().setAttachment(attachment);
}
}
}
}
}
_skin = newSkin;
updateCache();
}
Attachment *Skeleton::getAttachment(const String &slotName, const String &attachmentName) {
SlotData *slotData = _data.findSlot(slotName);
if (slotData == NULL) return NULL;
return getAttachment(slotData->getIndex(), attachmentName);
}
Attachment *Skeleton::getAttachment(int slotIndex, const String &attachmentName) {
if (attachmentName.isEmpty()) return NULL;
if (_skin != NULL) {
Attachment *attachment = _skin->getAttachment(slotIndex, attachmentName);
if (attachment != NULL) return attachment;
}
if (_data.getDefaultSkin() != NULL) {
return _data.getDefaultSkin()->getAttachment(slotIndex, attachmentName);
}
return NULL;
}
void Skeleton::setAttachment(const String &slotName, const String &attachmentName) {
if (slotName.isEmpty()) return;
Slot *slot = findSlot(slotName);
if (slot == NULL) return;
Attachment *attachment = NULL;
if (!attachmentName.isEmpty()) {
attachment = getAttachment(slot->getData().getIndex(), attachmentName);
if (attachment == NULL) {
// Handle error - attachment not found
return;
}
}
slot->getPose().setAttachment(attachment);
}
Vector<Constraint *> &Skeleton::getConstraints() {
return _constraints;
}
Vector<PhysicsConstraint *> &Skeleton::getPhysicsConstraints() {
return _physics;
}
void Skeleton::getBounds(float &outX, float &outY, float &outWidth, float &outHeight, Vector<float> &outVertexBuffer) {
getBounds(outX, outY, outWidth, outHeight, outVertexBuffer, NULL);
}
void Skeleton::getBounds(float &outX, float &outY, float &outWidth, float &outHeight, Vector<float> &outVertexBuffer, SkeletonClipping *clipper) {
float minX = FLT_MAX, minY = FLT_MAX, maxX = -FLT_MAX, maxY = -FLT_MAX;
for (size_t i = 0; i < _drawOrder.size(); ++i) {
Slot *slot = _drawOrder[i];
if (!slot->getBone().isActive()) continue;
size_t verticesLength = 0;
float *vertices = NULL;
const unsigned short *triangles = NULL;
Attachment *attachment = slot->getPose().getAttachment();
if (attachment != NULL) {
RegionAttachment *region = dynamic_cast<RegionAttachment *>(attachment);
if (region != NULL) {
verticesLength = 8;
outVertexBuffer.setSize(8);
vertices = outVertexBuffer.buffer();
region->computeWorldVertices(*slot, vertices, 0, 2);
triangles = quadTriangles;
} else {
MeshAttachment *mesh = dynamic_cast<MeshAttachment *>(attachment);
if (mesh != NULL) {
verticesLength = mesh->getWorldVerticesLength();
outVertexBuffer.setSize(verticesLength);
vertices = outVertexBuffer.buffer();
mesh->computeWorldVertices(*this, *slot, 0, verticesLength, vertices, 0, 2);
triangles = mesh->getTriangles().buffer();
} else {
ClippingAttachment *clip = dynamic_cast<ClippingAttachment *>(attachment);
if (clip != NULL && clipper != NULL) {
clipper->clipEnd(*slot);
clipper->clipStart(*this, *slot, *clip);
continue;
}
}
}
if (vertices != NULL) {
if (clipper != NULL && clipper->isClipping() && clipper->clipTriangles(vertices, triangles, 6)) {
vertices = clipper->getClippedVertices().buffer();
verticesLength = clipper->getClippedVertices().size();
}
for (size_t ii = 0; ii < verticesLength; ii += 2) {
float x = vertices[ii], y = vertices[ii + 1];
minX = MathUtil::min(minX, x);
minY = MathUtil::min(minY, y);
maxX = MathUtil::max(maxX, x);
maxY = MathUtil::max(maxY, y);
}
}
}
if (clipper != NULL) clipper->clipEnd(*slot);
}
if (clipper != NULL) clipper->clipEnd();
outX = minX;
outY = minY;
outWidth = maxX - minX;
outHeight = maxY - minY;
}
Color &Skeleton::getColor() {
return _color;
}
void Skeleton::setColor(Color &color) {
_color = color;
}
float Skeleton::getScaleX() {
return _scaleX;
}
void Skeleton::setScaleX(float inValue) {
_scaleX = inValue;
}
float Skeleton::getScaleY() {
return _scaleY;
}
void Skeleton::setScaleY(float inValue) {
_scaleY = inValue;
}
void Skeleton::setScale(float scaleX, float scaleY) {
_scaleX = scaleX;
_scaleY = scaleY;
}
float Skeleton::getX() {
return _x;
}
void Skeleton::setX(float inValue) {
_x = inValue;
}
float Skeleton::getY() {
return _y;
}
void Skeleton::setY(float inValue) {
_y = inValue;
}
void Skeleton::setPosition(float x, float y) {
_x = x;
_y = y;
}
float Skeleton::getWindX() {
return _windX;
}
void Skeleton::setWindX(float windX) {
_windX = windX;
}
float Skeleton::getWindY() {
return _windY;
}
void Skeleton::setWindY(float windY) {
_windY = windY;
}
float Skeleton::getGravityX() {
return _gravityX;
}
void Skeleton::setGravityX(float gravityX) {
_gravityX = gravityX;
}
float Skeleton::getGravityY() {
return _gravityY;
}
void Skeleton::setGravityY(float gravityY) {
_gravityY = gravityY;
}
void Skeleton::physicsTranslate(float x, float y) {
for (size_t i = 0; i < _physics.size(); ++i) {
_physics[i]->translate(x, y);
}
}
void Skeleton::physicsRotate(float x, float y, float degrees) {
for (size_t i = 0; i < _physics.size(); ++i) {
_physics[i]->rotate(x, y, degrees);
}
}
float Skeleton::getTime() {
return _time;
}
void Skeleton::setTime(float time) {
_time = time;
}
void Skeleton::update(float delta) {
_time += delta;
}

View File

@ -57,50 +57,11 @@ Slider* Slider::copy(Skeleton& skeleton) {
}
void Slider::update(Skeleton &skeleton, Physics physics) {
SliderPose& p = *_applied;
if (p.getMix() == 0) return;
Animation* animation = _data.getAnimation();
if (_bone != NULL) {
if (!_bone->isActive()) return;
if (_data.getLocal()) _bone->getAppliedPose().validateLocalTransform(skeleton);
p.setTime((_data.getProperty()->value(_bone->getAppliedPose(), _data.getLocal(), _offsets) - _data.getProperty()->offset) * _data.getScale());
if (_data.getLoop()) {
// TODO: Implement when Animation is ported
p.setTime(animation->getDuration() + MathUtil::fmod(p.getTime(), animation->getDuration()));
} else {
p.setTime(MathUtil::max(0.0f, p.getTime()));
}
}
// TODO: Implement when Animation is ported
// Vector<Bone*>& bones = skeleton.getBones();
// Vector<size_t>& indices = animation->getBones();
// for (size_t i = 0, n = indices.size(); i < n; i++) {
// bones[indices[i]]->getAppliedPose().modifyLocal(skeleton);
// }
// TODO: Implement when Animation is ported
// animation->apply(skeleton, p.getTime(), p.getTime(), _data.getLoop(), NULL, p.getMix(),
// _data.getAdditive() ? MixBlend_Add : MixBlend_Replace, MixDirection_In);
}
void Slider::sort(Skeleton &skeleton) {
// TODO: Implement when Animation is ported
// if (_bone != NULL && !_data.getLocal()) skeleton.sortBone(_bone);
skeleton.getUpdateCacheList().add(this);
// TODO: Implement when Animation is ported
// Vector<Bone*>& bones = skeleton.getBones();
// Vector<size_t>& indices = _data.getAnimation()->getBones();
// for (size_t i = 0, n = indices.size(); i < n; i++) {
// Bone* bone = bones[indices[i]];
// bone->setSorted(false);
// skeleton.sortReset(bone->getChildren());
// skeleton.constrained(bone);
// }
// TODO: Implement when Animation is ported - timeline processing
}
Bone *Slider::getBone() {

View File

@ -37,8 +37,7 @@
using namespace spine;
Slot::Slot(SlotData &data, Skeleton &skeleton) :
Posed<SlotData, SlotPose, SlotPose>(data),
Slot::Slot(SlotData &data, Skeleton &skeleton) : Posed<SlotData, SlotPose, SlotPose>(data),
_skeleton(skeleton),
_bone(*skeleton.getBones()[data.getBoneData().getIndex()]),
_attachmentState(0) {

View File

@ -35,8 +35,7 @@
using namespace spine;
SlotData::SlotData(int index, const String& name, BoneData& boneData) :
PosedData<SlotPose>(name),
SlotData::SlotData(int index, const String &name, BoneData &boneData) : PosedData<SlotPose>(name),
_index(index),
_boneData(boneData),
_attachmentName(),

View File

@ -41,8 +41,7 @@ using namespace spine;
RTTI_IMPL(TransformConstraint, Constraint)
TransformConstraint::TransformConstraint(TransformConstraintData& data, Skeleton& skeleton) :
ConstraintGeneric<TransformConstraint, TransformConstraintData, TransformConstraintPose>(data) {
TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton &skeleton) : ConstraintGeneric<TransformConstraint, TransformConstraintData, TransformConstraintPose>(data) {
_bones.ensureCapacity(data.getBones().size());
for (size_t i = 0; i < data.getBones().size(); i++) {
@ -74,7 +73,7 @@ void TransformConstraint::update(Skeleton& skeleton, Physics physics) {
Vector<FromProperty *> &properties = data.getProperties();
FromProperty **fromItems = properties.buffer();
size_t fn = properties.size();
int update = 1; // TODO: Add skeleton.update field
int update = skeleton._update;
BonePose **bones = _bones.buffer();
for (size_t i = 0, n = _bones.size(); i < n; i++) {
BonePose *bone = bones[i];
@ -106,22 +105,22 @@ void TransformConstraint::update(Skeleton& skeleton, Physics physics) {
}
void TransformConstraint::sort(Skeleton &skeleton) {
// if (!_data.getLocalSource()) skeleton.sortBone(_source); // TODO: sortBone is private, need friend access
if (!_data.getLocalSource()) skeleton.sortBone(_source);
BonePose **bones = _bones.buffer();
size_t boneCount = _bones.size();
bool worldTarget = !_data.getLocalTarget();
// if (worldTarget) {
// for (size_t i = 0; i < boneCount; i++)
// skeleton.sortBone(bones[i]->_bone); // TODO: sortBone is private, need friend access
// }
// skeleton._updateCache.add(this); // TODO: _updateCache is private, need friend access
// for (size_t i = 0; i < boneCount; i++) {
// Bone* bone = bones[i]->_bone;
// skeleton.sortReset(bone->getChildren()); // TODO: sortReset is private, need friend access
// // skeleton.constrained(bone); // TODO: Add constrained() method to Skeleton class
// }
// for (size_t i = 0; i < boneCount; i++)
// bones[i]->_bone->_sorted = worldTarget; // TODO: _sorted is private, need friend access
if (worldTarget) {
for (size_t i = 0; i < boneCount; i++)
skeleton.sortBone(bones[i]->_bone);
}
skeleton._updateCache.add(this);
for (size_t i = 0; i < boneCount; i++) {
Bone* bone = bones[i]->_bone;
skeleton.sortReset(bone->getChildren());
skeleton.constrained(*bone);
}
for (size_t i = 0; i < boneCount; i++)
bones[i]->_bone->_sorted = worldTarget;
}
bool TransformConstraint::isSourceActive() {

View File

@ -169,8 +169,7 @@ ToProperty::~ToProperty() {
float FromRotate::value(BonePose &source, bool local, float *offsets) {
if (local) return source.getRotation() + offsets[TransformConstraintData::ROTATION];
float value = MathUtil::atan2(source._c, source._a) * MathUtil::Rad_Deg
+ (source._a * source._d - source._b * source._c > 0 ? offsets[TransformConstraintData::ROTATION] : -offsets[TransformConstraintData::ROTATION]);
float value = MathUtil::atan2(source._c, source._a) * MathUtil::Rad_Deg + (source._a * source._d - source._b * source._c > 0 ? offsets[TransformConstraintData::ROTATION] : -offsets[TransformConstraintData::ROTATION]);
if (value < 0) value += 360;
return value;
}

View File

@ -31,8 +31,7 @@
using namespace spine;
TransformConstraintPose::TransformConstraintPose() :
_mixRotate(0), _mixX(0), _mixY(0), _mixScaleX(0), _mixScaleY(0), _mixShearY(0) {
TransformConstraintPose::TransformConstraintPose() : _mixRotate(0), _mixX(0), _mixY(0), _mixScaleX(0), _mixScaleY(0), _mixShearY(0) {
}
TransformConstraintPose::~TransformConstraintPose() {

View File

@ -42,8 +42,7 @@ using namespace spine;
RTTI_IMPL(TranslateTimeline, BoneTimeline2)
TranslateTimeline::TranslateTimeline(size_t frameCount, size_t bezierCount, int boneIndex) :
BoneTimeline2(frameCount, bezierCount, boneIndex, Property_X, Property_Y) {
TranslateTimeline::TranslateTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : BoneTimeline2(frameCount, bezierCount, boneIndex, Property_X, Property_Y) {
}
void TranslateTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float alpha, MixBlend blend,
@ -106,8 +105,7 @@ void TranslateTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, flo
RTTI_IMPL(TranslateXTimeline, BoneTimeline1)
TranslateXTimeline::TranslateXTimeline(size_t frameCount, size_t bezierCount, int boneIndex) :
BoneTimeline1(frameCount, bezierCount, boneIndex, Property_X) {
TranslateXTimeline::TranslateXTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : BoneTimeline1(frameCount, bezierCount, boneIndex, Property_X) {
}
void TranslateXTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float alpha, MixBlend blend,
@ -117,8 +115,7 @@ void TranslateXTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, fl
RTTI_IMPL(TranslateYTimeline, BoneTimeline1)
TranslateYTimeline::TranslateYTimeline(size_t frameCount, size_t bezierCount, int boneIndex) :
BoneTimeline1(frameCount, bezierCount, boneIndex, Property_Y) {
TranslateYTimeline::TranslateYTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : BoneTimeline1(frameCount, bezierCount, boneIndex, Property_Y) {
}
void TranslateYTimeline::apply(BoneLocal &pose, BoneLocal &setup, float time, float alpha, MixBlend blend,