[cpp] 4.3 porting WIP

This commit is contained in:
Mario Zechner 2025-06-23 16:40:17 +02:00
parent 522785b8fb
commit 2868c79f7c
14 changed files with 86 additions and 644 deletions

View File

@ -37,7 +37,7 @@
#include <spine/RTTI.h>
namespace spine {
class SP_API BoneData : public PosedData<BoneLocal> {
class SP_API BoneData : public PosedDataGeneric<BoneLocal> {
friend class SkeletonBinary;
friend class SkeletonJson;

View File

@ -50,12 +50,12 @@ namespace spine {
/// Generic base class for all constraint data.
template<class T, class P>
class SP_API ConstraintDataGeneric : public PosedData<P>, public ConstraintData {
class SP_API ConstraintDataGeneric : public PosedDataGeneric<P>, public ConstraintData {
friend class SkeletonBinary;
friend class SkeletonJson;
public:
ConstraintDataGeneric(const String &name) : PosedData<P>(name), ConstraintData(name) {
ConstraintDataGeneric(const String &name) : PosedDataGeneric<P>(name), ConstraintData(name) {
}
virtual ~ConstraintDataGeneric() {
}

View File

@ -46,7 +46,7 @@ namespace spine {
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction) override;
MixDirection direction, bool appliedPose) override;
/// Sets the time, mix and bend direction of the specified keyframe.
void setFrame(int frame, float time, float mix, float softness, int bendDirection, bool compress, bool stretch);

View File

@ -39,6 +39,8 @@ namespace spine {
Posed() {}
virtual ~Posed() {}
virtual void setupPose() = 0;
virtual void resetConstrained() = 0;
virtual void pose() = 0;
@ -84,7 +86,7 @@ namespace spine {
virtual ~PosedGeneric() {
}
void setupPose() {
virtual void setupPose() override {
_pose.set(_data.getSetupPose());
}
@ -103,15 +105,15 @@ namespace spine {
return *_applied;
}
virtual void resetConstrained() {
virtual void resetConstrained() override {
_constrained.set(_pose);
}
virtual void pose() {
virtual void pose() override {
_applied = &_pose;
}
virtual void constrained() {
virtual void constrained() override {
_applied = &_constrained;
}

View File

@ -37,8 +37,6 @@ namespace spine {
template<class P>
class Pose;
/// The base class for all constrained datas.
template<class P>
class SP_API PosedData : public SpineObject {
friend class SkeletonBinary;
friend class SkeletonJson;
@ -62,62 +60,66 @@ namespace spine {
friend class TranslateYTimeline;
friend class InheritTimeline;
protected:
spine::String _name;
P _setup;
bool _skinRequired;
public:
PosedData(const spine::String& name);
virtual ~PosedData();
/// The constraint's name, which is unique across all constraints in the skeleton of the same type.
const spine::String& getName();
P& getSetupPose();
const spine::String& getName() { return _name; };
/// When true, Skeleton::updateWorldTransform(Physics) only updates this constraint if the Skeleton::getSkin()
/// contains this constraint.
///
/// See Skin::getConstraints().
bool getSkinRequired();
void setSkinRequired(bool skinRequired);
bool getSkinRequired() { return _skinRequired; };
void setSkinRequired(bool skinRequired) { _skinRequired = skinRequired; };
virtual spine::String toString();
protected:
spine::String _name;
bool _skinRequired;
};
template<class P>
PosedData<P>::PosedData(const spine::String& name) : _name(name), _setup(), _skinRequired(false) {
inline PosedData::PosedData(const spine::String& name) : _name(name), _skinRequired(false) {
}
template<class P>
PosedData<P>::~PosedData() {
inline PosedData::~PosedData() {
}
/// The base class for all constrained datas.
template<class P>
const spine::String& PosedData<P>::getName() {
return _name;
}
class SP_API PosedDataGeneric : public PosedData {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class BoneTimeline1;
friend class BoneTimeline2;
friend class RotateTimeline;
friend class AttachmentTimeline;
friend class RGBATimeline;
friend class RGBTimeline;
friend class AlphaTimeline;
friend class RGBA2Timeline;
friend class RGB2Timeline;
friend class ScaleTimeline;
friend class ScaleXTimeline;
friend class ScaleYTimeline;
friend class ShearTimeline;
friend class ShearXTimeline;
friend class ShearYTimeline;
friend class TranslateTimeline;
friend class TranslateXTimeline;
friend class TranslateYTimeline;
friend class InheritTimeline;
template<class P>
P& PosedData<P>::getSetupPose() {
return _setup;
}
protected:
P _setup;
template<class P>
bool PosedData<P>::getSkinRequired() {
return _skinRequired;
public:
PosedDataGeneric(const spine::String& name): PosedData(name), _setup() {
}
virtual ~PosedDataGeneric() {};
template<class P>
void PosedData<P>::setSkinRequired(bool skinRequired) {
_skinRequired = skinRequired;
}
template<class P>
spine::String PosedData<P>::toString() {
return _name;
}
P& getSetupPose() { return _setup; };
};
}
#endif

View File

@ -222,7 +222,7 @@ namespace spine {
Vector<PhysicsConstraint *> &getPhysicsConstraints();
template<typename T>
template<class T>
T *findConstraint(const String &constraintName) {
if (constraintName.isEmpty()) return NULL;
for (size_t i = 0; i < _constraints.size(); i++) {

View File

@ -141,7 +141,7 @@ namespace spine {
/// Finds a constraint by name and type.
/// @return May be NULL.
template<typename T>
template<class T>
T *findConstraint(const String &constraintName) {
getConstraints(); // Ensure constraints array is populated
for (size_t i = 0, n = _constraints.size(); i < n; i++) {

View File

@ -40,7 +40,7 @@ namespace spine {
class BoneData;
/// Stores the setup pose for a Slot.
class SP_API SlotData : public PosedData<SlotPose> {
class SP_API SlotData : public PosedDataGeneric<SlotPose> {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class PathConstraint;

View File

@ -33,7 +33,7 @@
using namespace spine;
BoneData::BoneData(int index, const String &name, BoneData *parent) : PosedData<BoneLocal>(name),
BoneData::BoneData(int index, const String &name, BoneData *parent) : PosedDataGeneric<BoneLocal>(name),
_index(index),
_parent(parent),
_length(0),

View File

@ -50,9 +50,10 @@ IkConstraintTimeline::IkConstraintTimeline(size_t frameCount, size_t bezierCount
}
void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
MixBlend blend, MixDirection direction) {
MixBlend blend, MixDirection direction, bool appliedPose) {
SP_UNUSED(lastTime);
SP_UNUSED(pEvents);
SP_UNUSED(appliedPose);
IkConstraint *constraintP = (IkConstraint *) skeleton._constraints[_constraintIndex];
IkConstraint &constraint = *constraintP;
@ -61,18 +62,20 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:
constraint._mix = constraint._data._mix;
constraint._softness = constraint._data._softness;
constraint._bendDirection = constraint._data._bendDirection;
constraint._compress = constraint._data._compress;
constraint._stretch = constraint._data._stretch;
constraint.getAppliedPose().setMix(constraint._data.getSetupPose().getMix());
constraint.getAppliedPose().setSoftness(constraint._data.getSetupPose().getSoftness());
constraint.getAppliedPose().setBendDirection(constraint._data.getSetupPose().getBendDirection());
constraint.getAppliedPose().setCompress(constraint._data.getSetupPose().getCompress());
constraint.getAppliedPose().setStretch(constraint._data.getSetupPose().getStretch());
return;
case MixBlend_First:
constraint._mix += (constraint._data._mix - constraint._mix) * alpha;
constraint._softness += (constraint._data._softness - constraint._softness) * alpha;
constraint._bendDirection = constraint._data._bendDirection;
constraint._compress = constraint._data._compress;
constraint._stretch = constraint._data._stretch;
constraint.getAppliedPose().setMix(constraint.getAppliedPose().getMix() +
(constraint._data.getSetupPose().getMix() - constraint.getAppliedPose().getMix()) * alpha);
constraint.getAppliedPose().setSoftness(constraint.getAppliedPose().getSoftness() +
(constraint._data.getSetupPose().getSoftness() - constraint.getAppliedPose().getSoftness()) * alpha);
constraint.getAppliedPose().setBendDirection(constraint._data.getSetupPose().getBendDirection());
constraint.getAppliedPose().setCompress(constraint._data.getSetupPose().getCompress());
constraint.getAppliedPose().setStretch(constraint._data.getSetupPose().getStretch());
return;
default:
return;
@ -106,25 +109,29 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
}
if (blend == MixBlend_Setup) {
constraint._mix = constraint._data._mix + (mix - constraint._data._mix) * alpha;
constraint._softness = constraint._data._softness + (softness - constraint._data._softness) * alpha;
constraint.getAppliedPose().setMix(constraint._data.getSetupPose().getMix() +
(mix - constraint._data.getSetupPose().getMix()) * alpha);
constraint.getAppliedPose().setSoftness(constraint._data.getSetupPose().getSoftness() +
(softness - constraint._data.getSetupPose().getSoftness()) * alpha);
if (direction == MixDirection_Out) {
constraint._bendDirection = constraint._data._bendDirection;
constraint._compress = constraint._data._compress;
constraint._stretch = constraint._data._stretch;
constraint.getAppliedPose().setBendDirection(constraint._data.getSetupPose().getBendDirection());
constraint.getAppliedPose().setCompress(constraint._data.getSetupPose().getCompress());
constraint.getAppliedPose().setStretch(constraint._data.getSetupPose().getStretch());
} else {
constraint._bendDirection = _frames[i + IkConstraintTimeline::BEND_DIRECTION];
constraint._compress = _frames[i + IkConstraintTimeline::COMPRESS] != 0;
constraint._stretch = _frames[i + IkConstraintTimeline::STRETCH] != 0;
constraint.getAppliedPose().setBendDirection(_frames[i + IkConstraintTimeline::BEND_DIRECTION]);
constraint.getAppliedPose().setCompress(_frames[i + IkConstraintTimeline::COMPRESS] != 0);
constraint.getAppliedPose().setStretch(_frames[i + IkConstraintTimeline::STRETCH] != 0);
}
} else {
constraint._mix += (mix - constraint._mix) * alpha;
constraint._softness += (softness - constraint._softness) * alpha;
constraint.getAppliedPose().setMix(constraint.getAppliedPose().getMix() +
(mix - constraint.getAppliedPose().getMix()) * alpha);
constraint.getAppliedPose().setSoftness(constraint.getAppliedPose().getSoftness() +
(softness - constraint.getAppliedPose().getSoftness()) * alpha);
if (direction == MixDirection_In) {
constraint._bendDirection = _frames[i + IkConstraintTimeline::BEND_DIRECTION];
constraint._compress = _frames[i + IkConstraintTimeline::COMPRESS] != 0;
constraint._stretch = _frames[i + IkConstraintTimeline::STRETCH] != 0;
constraint.getAppliedPose().setBendDirection(_frames[i + IkConstraintTimeline::BEND_DIRECTION]);
constraint.getAppliedPose().setCompress(_frames[i + IkConstraintTimeline::COMPRESS] != 0);
constraint.getAppliedPose().setStretch(_frames[i + IkConstraintTimeline::STRETCH] != 0);
}
}
}

View File

@ -133,7 +133,7 @@ void RegionAttachment::computeWorldVertices(Slot &slot, Vector<float> &worldVert
}
void RegionAttachment::computeWorldVertices(Slot &slot, float *worldVertices, size_t offset, size_t stride) {
if (_sequence) _sequence->apply(slot.getAppliedPose(), this);
if (_sequence) _sequence->apply(&slot.getAppliedPose(), this);
BonePose &bone = slot.getBone().getAppliedPose();
float x = bone.getWorldX(), y = bone.getWorldY();

View File

@ -1,569 +0,0 @@
/******************************************************************************
* 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

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

View File

@ -35,7 +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) : PosedDataGeneric<SlotPose>(name),
_index(index),
_boneData(boneData),
_attachmentName(),