[cpp] Ported skin bones/constraints changes. See #1346.

This commit is contained in:
badlogic 2019-06-06 14:13:23 +02:00
parent 625f9b076a
commit 29322c5c51
49 changed files with 379 additions and 151 deletions

View File

@ -223,6 +223,10 @@ public:
bool isAppliedValid();
void setAppliedValid(bool valid);
bool isActive();
void setActive(bool inValue);
private:
static bool yDown;
@ -236,6 +240,7 @@ private:
float _a, _b, _worldX;
float _c, _d, _worldY;
bool _sorted;
bool _active;
/// Computes the individual applied transform values from the world transform. This can be useful to perform processing using
/// the applied transform after the world transform has been modified directly (eg, by a constraint)..

View File

@ -106,6 +106,9 @@ public:
void setTransformMode(TransformMode inValue);
bool isSkinRequired();
void setSkinRequired(bool inValue);
private:
const int _index;
const String _name;
@ -113,6 +116,7 @@ private:
float _length;
float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY;
TransformMode _transformMode;
bool _skinRequired;
};
}

View File

@ -31,21 +31,32 @@
#define Spine_Constraint_h
#include <spine/Updatable.h>
#include <spine/SpineString.h>
namespace spine {
/// The interface for all constraints.
class SP_API Constraint : public Updatable {
RTTI_DECL
class SP_API ConstraintData : public SpineObject {
public:
Constraint();
ConstraintData(const String& name);
virtual ~Constraint();
virtual ~ConstraintData();
virtual void update() = 0;
/// The IK constraint's name, which is unique within the skeleton.
const String& getName();
/// The ordinal for the order a skeleton's constraints will be applied.
virtual int getOrder() = 0;
size_t getOrder();
void setOrder(size_t inValue);
/// Whether the constraint is only active for a specific skin.
bool isSkinRequired();
void setSkinRequired(bool inValue);
private:
const String _name;
size_t _order;
bool _skinRequired;
};
}

View File

@ -55,6 +55,7 @@ public:
void reportLeaks() {
for (std::map<void*, Allocation>::iterator it = _allocated.begin(); it != _allocated.end(); it++) {
void* addr = it->second.address;
printf("\"%s:%i (%zu bytes at %p)\n", it->second.fileName, it->second.line, it->second.size, it->second.address);
}
printf("allocations: %zu, reallocations: %zu, frees: %zu\n", _allocations, _reallocations, _frees);

View File

@ -30,7 +30,7 @@
#ifndef Spine_IkConstraint_h
#define Spine_IkConstraint_h
#include <spine/Constraint.h>
#include <spine/ConstraintData.h>
#include <spine/Vector.h>
@ -41,7 +41,7 @@ class Skeleton;
class Bone;
class SP_API IkConstraint : public Constraint {
class SP_API IkConstraint : public Updatable {
friend class Skeleton;
friend class IkConstraintTimeline;
@ -91,6 +91,10 @@ public:
void setMix(float inValue);
bool isActive();
void setActive(bool inValue);
private:
IkConstraintData &_data;
Vector<Bone *> _bones;
@ -99,6 +103,7 @@ private:
bool _stretch;
float _mix;
Bone *_target;
bool _active;
};
}

View File

@ -33,11 +33,12 @@
#include <spine/Vector.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/ConstraintData.h>
namespace spine {
class BoneData;
class SP_API IkConstraintData : public SpineObject {
class SP_API IkConstraintData : public ConstraintData {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class IkConstraint;
@ -47,12 +48,6 @@ namespace spine {
public:
explicit IkConstraintData(const String& name);
/// The IK constraint's name, which is unique within the skeleton.
const String& getName();
size_t getOrder();
void setOrder(size_t inValue);
/// The bones that are constrained by this IK Constraint.
Vector<BoneData*>& getBones();
@ -77,8 +72,6 @@ namespace spine {
void setMix(float inValue);
private:
const String _name;
size_t _order;
Vector<BoneData*> _bones;
BoneData* _target;
int _bendDirection;

View File

@ -60,6 +60,8 @@ public:
static int getInt(Json *object, const char *name, int defaultValue);
static bool getBoolean(Json *object, const char *name, bool defaultValue);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Json_create() returns 0. 0 when Json_create() succeeds. */
static const char *getError();

View File

@ -30,7 +30,7 @@
#ifndef Spine_PathConstraint_h
#define Spine_PathConstraint_h
#include <spine/Constraint.h>
#include <spine/ConstraintData.h>
#include <spine/Vector.h>
@ -41,7 +41,7 @@ namespace spine {
class Bone;
class Slot;
class SP_API PathConstraint : public Constraint {
class SP_API PathConstraint : public Updatable {
friend class Skeleton;
friend class PathConstraintMixTimeline;
friend class PathConstraintPositionTimeline;
@ -77,6 +77,10 @@ namespace spine {
void setTarget(Slot* inValue);
PathConstraintData& getData();
bool isActive();
void setActive(bool inValue);
private:
static const float EPSILON;
@ -95,6 +99,8 @@ namespace spine {
Vector<float> _curves;
Vector<float> _lengths;
Vector<float> _segments;
bool _active;
Vector<float>& computeWorldPositions(PathAttachment& path, int spacesCount, bool tangents, bool percentPosition, bool percentSpacing);

View File

@ -36,12 +36,13 @@
#include <spine/Vector.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/ConstraintData.h>
namespace spine {
class BoneData;
class SlotData;
class SP_API PathConstraintData : public SpineObject {
class SP_API PathConstraintData : public ConstraintData {
friend class SkeletonBinary;
friend class SkeletonJson;
@ -53,11 +54,6 @@ namespace spine {
public:
explicit PathConstraintData(const String& name);
const String& getName();
int getOrder();
void setOrder(int inValue);
Vector<BoneData*>& getBones();
@ -89,8 +85,6 @@ namespace spine {
void setTranslateMix(float inValue);
private:
const String _name;
int _order;
Vector<BoneData*> _bones;
SlotData* _target;
PositionMode _positionMode;

View File

@ -95,6 +95,8 @@ namespace spine {
void setError(const char* value1, const char* value2);
char* readString(DataInput* input);
char* readStringRef(DataInput* input, SkeletonData* skeletonData);
float readFloat(DataInput* input);
@ -110,7 +112,7 @@ namespace spine {
int readVarint(DataInput* input, bool optimizePositive);
Skin* readSkin(DataInput* input, const String& skinName, SkeletonData* skeletonData, bool nonessential);
Skin* readSkin(DataInput* input, bool defaultSkin, SkeletonData* skeletonData, bool nonessential);
Attachment* readAttachment(DataInput* input, Skin* skin, int slotIndex, const String& attachmentName, SkeletonData* skeletonData, bool nonessential);

View File

@ -172,6 +172,7 @@ private:
float _x, _y, _width, _height;
String _version;
String _hash;
Vector<char*> _strings;
// Nonessential.
float _fps;

View File

@ -37,6 +37,8 @@ namespace spine {
class Attachment;
class Skeleton;
class BoneData;
class ConstraintData;
/// Stores attachments by slot index and attachment name.
/// See SkeletonData::getDefaultSkin, Skeleton::getSkin, and
@ -142,9 +144,14 @@ public:
AttachmentMap::Entries getAttachments();
Vector<BoneData*>& getBones();
Vector<ConstraintData*>& getConstraints();
private:
const String _name;
AttachmentMap _attachments;
Vector<BoneData*> _bones;
Vector<ConstraintData*> _constraints;
/// Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached.
void attachAll(Skeleton &skeleton, Skin &oldSkin);

View File

@ -30,7 +30,7 @@
#ifndef Spine_TransformConstraint_h
#define Spine_TransformConstraint_h
#include <spine/Constraint.h>
#include <spine/ConstraintData.h>
#include <spine/Vector.h>
@ -39,7 +39,7 @@ namespace spine {
class Skeleton;
class Bone;
class SP_API TransformConstraint : public Constraint {
class SP_API TransformConstraint : public Updatable {
friend class Skeleton;
friend class TransformConstraintTimeline;
@ -72,12 +72,17 @@ namespace spine {
float getShearMix();
void setShearMix(float inValue);
bool isActive();
void setActive(bool inValue);
private:
TransformConstraintData& _data;
Vector<Bone*> _bones;
Bone* _target;
float _rotateMix, _translateMix, _scaleMix, _shearMix;
bool _active;
void applyAbsoluteWorld();

View File

@ -33,11 +33,12 @@
#include <spine/Vector.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/ConstraintData.h>
namespace spine {
class BoneData;
class SP_API TransformConstraintData : public SpineObject {
class SP_API TransformConstraintData : public ConstraintData {
friend class SkeletonBinary;
friend class SkeletonJson;
@ -48,8 +49,6 @@ namespace spine {
public:
explicit TransformConstraintData(const String& name);
const String& getName();
int getOrder();
Vector<BoneData*>& getBones();
BoneData* getTarget();
float getRotateMix();
@ -68,8 +67,6 @@ namespace spine {
bool isLocal();
private:
const String _name;
int _order;
Vector<BoneData*> _bones;
BoneData* _target;
float _rotateMix, _translateMix, _scaleMix, _shearMix;

View File

@ -43,6 +43,10 @@ public:
virtual ~Updatable();
virtual void update() = 0;
virtual bool isActive() = 0;
virtual void setActive(bool inValue) = 0;
};
}

View File

@ -46,7 +46,7 @@
#include <spine/ClippingAttachment.h>
#include <spine/Color.h>
#include <spine/ColorTimeline.h>
#include <spine/Constraint.h>
#include <spine/ConstraintData.h>
#include <spine/ContainerUtil.h>
#include <spine/CurveTimeline.h>
#include <spine/Debug.h>

View File

@ -668,6 +668,7 @@ void AnimationState::applyRotateTimeline(RotateTimeline *rotateTimeline, Skeleto
}
Bone *bone = skeleton._bones[rotateTimeline->_boneIndex];
if (!bone->isActive()) return;
Vector<float>& frames = rotateTimeline->_frames;
float r1, r2;
if (time < frames[0]) {

View File

@ -37,6 +37,7 @@
#include <spine/Event.h>
#include <spine/Animation.h>
#include <spine/Bone.h>
#include <spine/TimelineType.h>
#include <spine/Slot.h>
#include <spine/SlotData.h>
@ -67,6 +68,8 @@ void AttachmentTimeline::apply(Skeleton &skeleton, float lastTime, float time, V
String *attachmentName;
Slot *slotP = skeleton._slots[_slotIndex];
Slot &slot = *slotP;
if (!slot._bone.isActive()) return;
if (direction == MixDirection_Out && blend == MixBlend_Setup) {
attachmentName = &slot._data._attachmentName;
slot.setAttachment(attachmentName->length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName));

View File

@ -75,7 +75,8 @@ Bone::Bone(BoneData &data, Skeleton &skeleton, Bone *parent) : Updatable(),
_c(0),
_d(1),
_worldY(0),
_sorted(false) {
_sorted(false),
_active(false) {
setToSetupPose();
}
@ -538,3 +539,11 @@ void Bone::updateAppliedTransform() {
}
}
}
bool Bone::isActive() {
return _active;
}
void Bone::setActive(bool inValue) {
_active = inValue;
}

View File

@ -49,7 +49,8 @@ BoneData::BoneData(int index, const String &name, BoneData *parent) :
_scaleY(1),
_shearX(0),
_shearY(0),
_transformMode(TransformMode_Normal) {
_transformMode(TransformMode_Normal),
_skinRequired(false) {
assert(index >= 0);
assert(_name.length() > 0);
}
@ -137,3 +138,11 @@ TransformMode BoneData::getTransformMode() {
void BoneData::setTransformMode(TransformMode inValue) {
_transformMode = inValue;
}
bool BoneData::isSkinRequired() {
return _skinRequired;
}
void BoneData::setSkinRequired(bool inValue) {
_skinRequired = inValue;
}

View File

@ -37,6 +37,7 @@
#include <spine/Event.h>
#include <spine/Animation.h>
#include <spine/Bone.h>
#include <spine/TimelineType.h>
#include <spine/Slot.h>
#include <spine/SlotData.h>
@ -68,6 +69,7 @@ void ColorTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector
Slot *slotP = skeleton._slots[_slotIndex];
Slot &slot = *slotP;
if (!slot._bone.isActive()) return;
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:

View File

@ -31,14 +31,32 @@
#include "SpinePluginPrivatePCH.h"
#endif
#include <spine/Constraint.h>
#include <spine/ConstraintData.h>
using namespace spine;
RTTI_IMPL(Constraint, Updatable)
Constraint::Constraint() {
ConstraintData::ConstraintData(const String& name): _name(name), _order(0), _skinRequired(false) {
}
Constraint::~Constraint() {
ConstraintData::~ConstraintData() {
}
const String& ConstraintData::getName() {
return _name;
}
size_t ConstraintData::getOrder() {
return _order;
}
void ConstraintData::setOrder(size_t inValue) {
_order = inValue;
}
bool ConstraintData::isSkinRequired() {
return _skinRequired;
}
void ConstraintData::setSkinRequired(bool inValue) {
_skinRequired = inValue;
}

View File

@ -41,6 +41,7 @@
#include <spine/Animation.h>
#include <spine/TimelineType.h>
#include <spine/Slot.h>
#include <spine/Bone.h>
#include <spine/SlotData.h>
using namespace spine;
@ -67,6 +68,7 @@ void DeformTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
Slot *slotP = skeleton._slots[_slotIndex];
Slot &slot = *slotP;
if (!slot._bone.isActive()) return;
Attachment *slotAttachment = slot.getAttachment();
if (slotAttachment == NULL || !slotAttachment->getRTTI().instanceOf(VertexAttachment::rtti)) {

View File

@ -41,7 +41,7 @@
using namespace spine;
RTTI_IMPL(IkConstraint, Constraint)
RTTI_IMPL(IkConstraint, Updatable)
void IkConstraint::apply(Bone &bone, float targetX, float targetY, bool compress, bool stretch, bool uniform, float alpha) {
Bone *p = bone.getParent();
@ -207,14 +207,15 @@ void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY
}
}
IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) : Constraint(),
IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) : Updatable(),
_data(data),
_bendDirection(data.getBendDirection()),
_compress(data.getCompress()),
_stretch(data.getStretch()),
_mix(data.getMix()),
_target(skeleton.findBone(
data.getTarget()->getName())) {
data.getTarget()->getName())),
_active(false) {
_bones.ensureCapacity(_data.getBones().size());
for (size_t i = 0; i < _data.getBones().size(); i++) {
BoneData *boneData = _data.getBones()[i];
@ -294,3 +295,12 @@ bool IkConstraint::getCompress() {
void IkConstraint::setCompress(bool inValue) {
_compress = inValue;
}
bool IkConstraint::isActive() {
return _active;
}
void IkConstraint::setActive(bool inValue) {
_active = inValue;
}

View File

@ -38,8 +38,7 @@
using namespace spine;
IkConstraintData::IkConstraintData(const String &name) :
_name(name),
_order(0),
ConstraintData(name),
_target(NULL),
_bendDirection(1),
_compress(false),
@ -48,18 +47,6 @@ IkConstraintData::IkConstraintData(const String &name) :
_mix(1) {
}
const String &IkConstraintData::getName() {
return _name;
}
size_t IkConstraintData::getOrder() {
return _order;
}
void IkConstraintData::setOrder(size_t inValue) {
_order = inValue;
}
Vector<BoneData *> &IkConstraintData::getBones() {
return _bones;
}

View File

@ -69,6 +69,8 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
IkConstraint *constraintP = skeleton._ikConstraints[_ikConstraintIndex];
IkConstraint &constraint = *constraintP;
if (!constraint.isActive()) return;
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:

View File

@ -90,6 +90,18 @@ int Json::getInt(Json *value, const char *name, int defaultValue) {
return value ? value->_valueInt : defaultValue;
}
bool Json::getBoolean(spine::Json *value, const char *name, bool defaultValue) {
value = getItem(value, name);
if (value) {
if (value->_valueString) return strcmp(value->_valueString, "true") == 0;
if (value->_type == JSON_NULL) return false;
if (value->_type == JSON_NUMBER) return value->_valueFloat != 0;
return defaultValue;
} else {
return defaultValue;
}
}
const char *Json::getError() {
return _error;
}

View File

@ -44,21 +44,22 @@
using namespace spine;
RTTI_IMPL(PathConstraint, Constraint)
RTTI_IMPL(PathConstraint, Updatable)
const float PathConstraint::EPSILON = 0.00001f;
const int PathConstraint::NONE = -1;
const int PathConstraint::BEFORE = -2;
const int PathConstraint::AFTER = -3;
PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) : Constraint(),
PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) : Updatable(),
_data(data),
_target(skeleton.findSlot(
data.getTarget()->getName())),
_position(data.getPosition()),
_spacing(data.getSpacing()),
_rotateMix(data.getRotateMix()),
_translateMix(data.getTranslateMix()) {
_translateMix(data.getTranslateMix()),
_active(false) {
_bones.ensureCapacity(_data.getBones().size());
for (size_t i = 0; i < _data.getBones().size(); i++) {
BoneData *boneData = _data.getBones()[i];
@ -568,3 +569,11 @@ void PathConstraint::addCurvePosition(float p, float x1, float y1, float cx1, fl
}
}
}
bool PathConstraint::isActive() {
return _active;
}
void PathConstraint::setActive(bool inValue) {
_active = inValue;
}

View File

@ -41,8 +41,7 @@
using namespace spine;
PathConstraintData::PathConstraintData(const String &name) :
_name(name),
_order(0),
ConstraintData(name),
_target(NULL),
_positionMode(PositionMode_Fixed),
_spacingMode(SpacingMode_Length),
@ -52,19 +51,6 @@ PathConstraintData::PathConstraintData(const String &name) :
_spacing(0),
_rotateMix(0),
_translateMix(0) {
assert(_name.length() > 0);
}
const String &PathConstraintData::getName() {
return _name;
}
int PathConstraintData::getOrder() {
return _order;
}
void PathConstraintData::setOrder(int inValue) {
_order = inValue;
}
Vector<BoneData *> &PathConstraintData::getBones() {

View File

@ -68,6 +68,8 @@ PathConstraintMixTimeline::apply(Skeleton &skeleton, float lastTime, float time,
PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex];
PathConstraint &constraint = *constraintP;
if (!constraint.isActive()) return;
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:

View File

@ -68,6 +68,8 @@ void PathConstraintPositionTimeline::apply(Skeleton &skeleton, float lastTime, f
PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex];
PathConstraint &constraint = *constraintP;
if (!constraint.isActive()) return;
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:

View File

@ -59,6 +59,8 @@ void PathConstraintSpacingTimeline::apply(Skeleton &skeleton, float lastTime, fl
PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex];
PathConstraint &constraint = *constraintP;
if (!constraint.isActive()) return;
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:

View File

@ -303,4 +303,4 @@ Attachment* RegionAttachment::copy() {
copy->_vertexOffset.clearAndAddAll(_vertexOffset);
copy->_color.set(_color);
return copy;
}
}

View File

@ -56,6 +56,7 @@ void RotateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
SP_UNUSED(direction);
Bone *bone = skeleton.getBones()[_boneIndex];
if (!bone->_active) return;
if (time < _frames[0]) {
switch (blend) {

View File

@ -55,6 +55,8 @@ void ScaleTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector
Bone *boneP = skeleton._bones[_boneIndex];
Bone &bone = *boneP;
if (!bone._active) return;
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:

View File

@ -56,6 +56,7 @@ void ShearTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector
Bone *boneP = skeleton._bones[_boneIndex];
Bone &bone = *boneP;
if (!bone._active) return;
if (time < _frames[0]) {
switch (blend) {

View File

@ -135,7 +135,21 @@ void Skeleton::updateCache() {
_updateCacheReset.clear();
for (size_t i = 0, n = _bones.size(); i < n; ++i) {
_bones[i]->_sorted = false;
Bone* bone = _bones[i];
bone->_sorted = bone->_data.isSkinRequired();
bone->_active = !bone->_sorted;
}
if (_skin) {
Vector<BoneData*>& skinBones = _skin->getBones();
for (size_t i = 0, n = skinBones.size(); i < n; i++) {
Bone* bone = _bones[skinBones[i]->getIndex()];
do {
bone->_sorted = false;
bone->_active = true;
bone = bone->_parent;
} while (bone);
}
}
size_t ikCount = _ikConstraints.size();
@ -158,7 +172,7 @@ void Skeleton::updateCache() {
for (size_t ii = 0; ii < transformCount; ++ii) {
TransformConstraint *constraint = _transformConstraints[ii];
if (constraint->getData().getOrder() == (int)i) {
if (constraint->getData().getOrder() == i) {
sortTransformConstraint(constraint);
i++;
goto continue_outer;
@ -167,7 +181,7 @@ void Skeleton::updateCache() {
for (size_t ii = 0; ii < pathCount; ++ii) {
PathConstraint *constraint = _pathConstraints[ii];
if (constraint->getData().getOrder() == (int)i) {
if (constraint->getData().getOrder() == i) {
sortPathConstraint(constraint);
i++;
goto continue_outer;
@ -407,6 +421,7 @@ void Skeleton::getBounds(float &outX, float &outY, float &outWidth, float &outHe
for (size_t i = 0; i < _drawOrder.size(); ++i) {
Slot *slot = _drawOrder[i];
if (!slot->_bone._active) continue;
size_t verticesLength = 0;
Attachment *attachment = slot->getAttachment();
@ -536,6 +551,9 @@ void Skeleton::setScaleY(float inValue) {
}
void Skeleton::sortIkConstraint(IkConstraint *constraint) {
constraint->_active = constraint->_target->_active && (!constraint->_data.isSkinRequired() || (_skin && _skin->_constraints.contains(&constraint->_data)));
if (!constraint->_active) return;
Bone *target = constraint->getTarget();
sortBone(target);
@ -555,6 +573,9 @@ void Skeleton::sortIkConstraint(IkConstraint *constraint) {
}
void Skeleton::sortPathConstraint(PathConstraint *constraint) {
constraint->_active = constraint->_target->_bone._active && (!constraint->_data.isSkinRequired() || (_skin && _skin->_constraints.contains(&constraint->_data)));
if (!constraint->_active) return;
Slot *slot = constraint->getTarget();
int slotIndex = slot->getData().getIndex();
Bone &slotBone = slot->getBone();
@ -583,6 +604,9 @@ void Skeleton::sortPathConstraint(PathConstraint *constraint) {
}
void Skeleton::sortTransformConstraint(TransformConstraint *constraint) {
constraint->_active = constraint->_target->_active && (!constraint->_data.isSkinRequired() || (_skin && _skin->_constraints.contains(&constraint->_data)));
if (!constraint->_active) return;
sortBone(constraint->getTarget());
Vector<Bone *> &constrained = constraint->getBones();
@ -646,6 +670,7 @@ void Skeleton::sortBone(Bone *bone) {
void Skeleton::sortReset(Vector<Bone *> &bones) {
for (size_t i = 0, n = bones.size(); i < n; ++i) {
Bone *bone = bones[i];
if (!bone->_active) continue;
if (bone->_sorted) sortReset(bone->getChildren());
bone->_sorted = false;
}

View File

@ -144,6 +144,10 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
skeletonData->_audioPath.own(readString(input));
}
int numStrings = readVarint(input, true);
for (int i = 0; i < numStrings; i++)
skeletonData->_strings.add(readString(input));
/* Bones. */
int numBones = readVarint(input, true);
skeletonData->_bones.setSize(numBones, 0);
@ -160,6 +164,7 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
data->_shearY = readFloat(input);
data->_length = readFloat(input) * _scale;
data->_transformMode = static_cast<TransformMode>(readVarint(input, true));
data->_skinRequired = readBoolean(input);
if (nonessential) {
/* Skip bone color. */
readInt(input);
@ -184,7 +189,7 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
slotData->getDarkColor().set(r / 255.0f, g / 255.0f, b / 255.0f, 1);
slotData->setHasDarkColor(true);
}
slotData->_attachmentName.own(readString(input));
slotData->_attachmentName = readStringRef(input, skeletonData);
slotData->_blendMode = static_cast<BlendMode>(readVarint(input, true));
skeletonData->_slots[i] = slotData;
}
@ -195,7 +200,8 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
for (int i = 0; i < ikConstraintsCount; ++i) {
const char *name = readString(input);
IkConstraintData *data = new(__FILE__, __LINE__) IkConstraintData(String(name, true));
data->_order = readVarint(input, true);
data->setOrder(readVarint(input, true));
data->setSkinRequired(readBoolean(input));
int bonesCount = readVarint(input, true);
data->_bones.setSize(bonesCount, 0);
for (int ii = 0; ii < bonesCount; ++ii) {
@ -216,7 +222,8 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
for (int i = 0; i < transformConstraintsCount; ++i) {
const char *name = readString(input);
TransformConstraintData *data = new(__FILE__, __LINE__) TransformConstraintData(String(name, true));
data->_order = readVarint(input, true);
data->setOrder(readVarint(input, true));
data->setSkinRequired(readBoolean(input));
int bonesCount = readVarint(input, true);
data->_bones.setSize(bonesCount, 0);
for (int ii = 0; ii < bonesCount; ++ii) {
@ -244,7 +251,8 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
for (int i = 0; i < pathConstraintsCount; ++i) {
const char *name = readString(input);
PathConstraintData *data = new(__FILE__, __LINE__) PathConstraintData(String(name, true));
data->_order = readVarint(input, true);
data->setOrder(readVarint(input, true));
data->setSkinRequired(readBoolean(input));
int bonesCount = readVarint(input, true);
data->_bones.setSize(bonesCount, 0);
for (int ii = 0; ii < bonesCount; ++ii) {
@ -266,20 +274,15 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
}
/* Default skin. */
skeletonData->_defaultSkin = readSkin(input, "default", skeletonData, nonessential);
int skinsCount = readVarint(input, true);
if (skeletonData->_defaultSkin) {
++skinsCount;
}
skeletonData->_skins.setSize(skinsCount, 0);
if (skeletonData->_defaultSkin) {
skeletonData->_skins[0] = skeletonData->_defaultSkin;
Skin* defaultSkin = readSkin(input, true, skeletonData, nonessential);
if (defaultSkin) {
skeletonData->_defaultSkin = defaultSkin;
skeletonData->_skins.add(defaultSkin);
}
/* Skins. */
for (size_t i = skeletonData->_defaultSkin ? 1 : 0; i < skeletonData->_skins.size(); ++i) {
String skinName(readString(input), true);
skeletonData->_skins[i] = readSkin(input, skinName, skeletonData, nonessential);
for (size_t i = 0, n = (size_t)readVarint(input, true); i < n; ++i) {
skeletonData->_skins.add(readSkin(input, false, skeletonData, nonessential));
}
/* Linked meshes. */
@ -312,8 +315,8 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
int eventsCount = readVarint(input, true);
skeletonData->_events.setSize(eventsCount, 0);
for (int i = 0; i < eventsCount; ++i) {
const char *name = readString(input);
EventData *eventData = new(__FILE__, __LINE__) EventData(String(name, true));
const char *name = readStringRef(input, skeletonData);
EventData *eventData = new(__FILE__, __LINE__) EventData(String(name));
eventData->_intValue = readVarint(input, false);
eventData->_floatValue = readFloat(input);
eventData->_stringValue.own(readString(input));
@ -381,6 +384,11 @@ char *SkeletonBinary::readString(DataInput *input) {
return string;
}
char* SkeletonBinary::readStringRef(DataInput* input, SkeletonData* skeletonData) {
int index = readVarint(input, true);
return index == 0 ? nullptr : skeletonData->_strings[index - 1];
}
float SkeletonBinary::readFloat(DataInput *input) {
union {
int intValue;
@ -447,14 +455,27 @@ int SkeletonBinary::readVarint(DataInput *input, bool optimizePositive) {
}
Skin *
SkeletonBinary::readSkin(DataInput *input, const String &skinName, SkeletonData *skeletonData, bool nonessential) {
int slotCount = readVarint(input, true);
if (slotCount == 0) return NULL;
Skin *skin = new(__FILE__, __LINE__) Skin(skinName);
for (int i = 0; i < slotCount; ++i) {
SkeletonBinary::readSkin(DataInput *input, bool defaultSkin, SkeletonData *skeletonData, bool nonessential) {
Skin *skin = new(__FILE__, __LINE__) Skin(defaultSkin ? "default" : readStringRef(input, skeletonData));
if (!defaultSkin) {
for (int i = 0, n = readVarint(input, true); i < n; i++)
skin->getBones().add(skeletonData->_bones[readVarint(input, true)]);
for (int i = 0, n = readVarint(input, true); i < n; i++)
skin->getConstraints().add(skeletonData->_ikConstraints[readVarint(input, true)]);
for (int i = 0, n = readVarint(input, true); i < n; i++)
skin->getConstraints().add(skeletonData->_transformConstraints[readVarint(input, true)]);
for (int i = 0, n = readVarint(input, true); i < n; i++)
skin->getConstraints().add(skeletonData->_pathConstraints[readVarint(input, true)]);
}
for (int i = 0, n = readVarint(input, true); i < n; ++i) {
int slotIndex = readVarint(input, true);
for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
String name(readString(input), true);
String name(readStringRef(input, skeletonData));
Attachment *attachment = readAttachment(input, skin, slotIndex, name, skeletonData, nonessential);
if (attachment) skin->setAttachment(slotIndex, String(name), attachment);
}
@ -464,13 +485,13 @@ SkeletonBinary::readSkin(DataInput *input, const String &skinName, SkeletonData
Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slotIndex, const String &attachmentName,
SkeletonData *skeletonData, bool nonessential) {
String name(readString(input), true);
String name(readStringRef(input, skeletonData));
if (name.isEmpty()) name = attachmentName;
AttachmentType type = static_cast<AttachmentType>(readByte(input));
switch (type) {
case AttachmentType_Region: {
String path(readString(input), true);
String path(readStringRef(input, skeletonData));
if (path.isEmpty()) path = name;
RegionAttachment *region = _attachmentLoader->newRegionAttachment(*skin, String(name), String(path));
region->_path = path;
@ -500,7 +521,7 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
case AttachmentType_Mesh: {
int vertexCount;
MeshAttachment *mesh;
String path(readString(input), true);
String path(readStringRef(input, skeletonData));
if (path.isEmpty()) path = name;
mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path));
@ -524,14 +545,14 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
return mesh;
}
case AttachmentType_Linkedmesh: {
String path(readString(input), true);
String path(readStringRef(input, skeletonData));
if (path.isEmpty()) path = name;
MeshAttachment *mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path));
mesh->_path = path;
readColor(input, mesh->getColor());
String skinName(readString(input), true);
String parent(readString(input), true);
String skinName(readStringRef(input, skeletonData));
String parent(readStringRef(input, skeletonData));
bool inheritDeform = readBoolean(input);
if (nonessential) {
mesh->_width = readFloat(input) * _scale;
@ -661,7 +682,7 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
timeline->_slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
String attachmentName = String(readString(input), true);
String attachmentName(readStringRef(input, skeletonData));
timeline->setFrame(frameIndex, time, attachmentName);
}
timelines.add(timeline);
@ -888,8 +909,8 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
int slotIndex = readVarint(input, true);
for (int iii = 0, nnn = readVarint(input, true); iii < nnn; iii++) {
const char *attachmentName = readString(input);
Attachment *baseAttachment = skin->getAttachment(slotIndex, String(attachmentName, true));
const char *attachmentName = readStringRef(input, skeletonData);
Attachment *baseAttachment = skin->getAttachment(slotIndex, String(attachmentName));
if (!baseAttachment) {
ContainerUtil::cleanUpVectorOfPointers(timelines);

View File

@ -71,6 +71,9 @@ SkeletonData::~SkeletonData() {
ContainerUtil::cleanUpVectorOfPointers(_ikConstraints);
ContainerUtil::cleanUpVectorOfPointers(_transformConstraints);
ContainerUtil::cleanUpVectorOfPointers(_pathConstraints);
for (size_t i = 0; i < _strings.size(); i++) {
SpineExtension::free(_strings[i], __FILE__, __LINE__);
}
}
BoneData *SkeletonData::findBone(const String &boneName) {

View File

@ -189,6 +189,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
if (strcmp(transformMode, "noScaleOrReflection") == 0) {
data->_transformMode = TransformMode_NoScaleOrReflection;
}
data->_skinRequired = Json::getBoolean(boneMap, "skin", false);
skeletonData->_bones[i] = data;
bonesCount++;
@ -267,7 +268,8 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
IkConstraintData *data = new(__FILE__, __LINE__) IkConstraintData(
Json::getString(constraintMap, "name", 0));
data->_order = Json::getInt(constraintMap, "order", 0);
data->setOrder(Json::getInt(constraintMap, "order", 0));
data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false));
boneMap = Json::getItem(constraintMap, "bones");
data->_bones.ensureCapacity(boneMap->_size);
@ -311,7 +313,8 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
TransformConstraintData *data = new(__FILE__, __LINE__) TransformConstraintData(
Json::getString(constraintMap, "name", 0));
data->_order = Json::getInt(constraintMap, "order", 0);
data->setOrder(Json::getInt(constraintMap, "order", 0));
data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false));
boneMap = Json::getItem(constraintMap, "bones");
data->_bones.ensureCapacity(boneMap->_size);
@ -364,7 +367,8 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
PathConstraintData *data = new(__FILE__, __LINE__) PathConstraintData(
Json::getString(constraintMap, "name", 0));
data->_order = Json::getInt(constraintMap, "order", 0);
data->setOrder(Json::getInt(constraintMap, "order", 0));
data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false));
boneMap = Json::getItem(constraintMap, "bones");
data->_bones.ensureCapacity(boneMap->_size);
@ -438,15 +442,67 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
Json *attachmentsMap;
Json *curves;
Skin *skin = new(__FILE__, __LINE__) Skin(skinMap->_name);
Skin *skin = new(__FILE__, __LINE__) Skin(Json::getString(skinMap, "name", ""));
Json *item = Json::getItem(skinMap, "bones");
if (item) {
for (item = item->_child; item; item = item->_next) {
BoneData* data = skeletonData->findBone(item->_valueString);
if (!data) {
delete skeletonData;
setError(root, String("Skin bone not found: "), item->_valueString);
return NULL;
}
skin->getBones().add(data);
}
}
item = Json::getItem(skinMap, "ik");
if (item) {
for (item = item->_child; item; item = item->_next) {
IkConstraintData* data = skeletonData->findIkConstraint(item->_valueString);
if (!data) {
delete skeletonData;
setError(root, String("Skin IK constraint not found: "), item->_valueString);
return NULL;
}
skin->getConstraints().add(data);
}
}
item = Json::getItem(skinMap, "transform");
if (item) {
for (item = item->_child; item; item = item->_next) {
TransformConstraintData* data = skeletonData->findTransformConstraint(item->_valueString);
if (!data) {
delete skeletonData;
setError(root, String("Skin transform constraint not found: "), item->_valueString);
return NULL;
}
skin->getConstraints().add(data);
}
}
item = Json::getItem(skinMap, "path");
if (item) {
for (item = item->_child; item; item = item->_next) {
PathConstraintData* data = skeletonData->findPathConstraint(item->_valueString);
if (!data) {
delete skeletonData;
setError(root, String("Skin path constraint not found: "), item->_valueString);
return NULL;
}
skin->getConstraints().add(data);
}
}
skeletonData->_skins[skinsIndex++] = skin;
if (strcmp(skinMap->_name, "default") == 0) {
if (strcmp(Json::getString(skinMap, "name", ""), "default") == 0) {
skeletonData->_defaultSkin = skin;
}
for (attachmentsMap = skinMap->_child; attachmentsMap; attachmentsMap = attachmentsMap->_next) {
int slotIndex = skeletonData->findSlotIndex(attachmentsMap->_name);
for (attachmentsMap = Json::getItem(skinMap, "attachments")->_child; attachmentsMap; attachmentsMap = attachmentsMap->_next) {
SlotData* slot = skeletonData->findSlot(attachmentsMap->_name);
Json *attachmentMap;
for (attachmentMap = attachmentsMap->_child; attachmentMap; attachmentMap = attachmentMap->_next) {
@ -574,7 +630,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
String(Json::getString(
attachmentMap,
"skin", 0)),
slotIndex,
slot->getIndex(),
String(entry->_valueString), inheritDeform);
_linkedMeshes.add(linkedMesh);
}
@ -640,7 +696,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
}
}
skin->setAttachment(slotIndex, skinAttachmentName, attachment);
skin->setAttachment(slot->getIndex(), skinAttachmentName, attachment);
}
}
}
@ -746,13 +802,12 @@ void SkeletonJson::readCurve(Json *frame, CurveTimeline *timeline, size_t frameI
}
if (curve->_type == Json::JSON_STRING && strcmp(curve->_valueString, "stepped") == 0) {
timeline->setStepped(frameIndex);
} else if (curve->_type == Json::JSON_ARRAY) {
Json *child0 = curve->_child;
Json *child1 = child0->_next;
Json *child2 = child1->_next;
Json *child3 = child2->_next;
timeline->setCurve(frameIndex, child0->_valueFloat, child1->_valueFloat, child2->_valueFloat,
child3->_valueFloat);
} else {
float c1 = Json::getFloat(frame, "curve", 0);
float c2 = Json::getFloat(frame, "c2", 0);
float c3 = Json::getFloat(frame, "c3", 1);
float c4 = Json::getFloat(frame, "c4", 1);
timeline->setCurve(frameIndex, c1, c2, c3, c4);
}
}
@ -905,9 +960,11 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
int isShear = strcmp(timelineMap->_name, "shear") == 0;
if (isScale || isTranslate || isShear) {
float timelineScale = isTranslate ? _scale : 1;
float defaultValue = 0;
TranslateTimeline *timeline = 0;
if (isScale) {
timeline = new(__FILE__, __LINE__) ScaleTimeline(timelineMap->_size);
defaultValue = 1;
} else if (isTranslate) {
timeline = new(__FILE__, __LINE__) TranslateTimeline(timelineMap->_size);
} else if (isShear) {
@ -917,8 +974,8 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0),
Json::getFloat(valueMap, "x", 0) * timelineScale,
Json::getFloat(valueMap, "y", 0) * timelineScale);
Json::getFloat(valueMap, "x", defaultValue) * timelineScale,
Json::getFloat(valueMap, "y", defaultValue) * timelineScale);
readCurve(valueMap, timeline, frameIndex);
}

View File

@ -37,6 +37,7 @@
#include <spine/Skeleton.h>
#include <spine/Slot.h>
#include <spine/ConstraintData.h>
#include <assert.h>
@ -157,6 +158,12 @@ void Skin::attachAll(Skeleton &skeleton, Skin &oldSkin) {
}
void Skin::addSkin(Skin* other) {
for (int i = 0; i < other->getBones().size(); i++)
if (!_bones.contains(other->getBones()[i])) _bones.add(other->getBones()[i]);
for (int i = 0; i < other->getConstraints().size(); i++)
if (!_constraints.contains(other->getConstraints()[i])) _constraints.add(other->getConstraints()[i]);
AttachmentMap::Entries entries = other->getAttachments();
while(entries.hasNext()) {
AttachmentMap::Entry& entry = entries.next();
@ -165,6 +172,12 @@ void Skin::addSkin(Skin* other) {
}
void Skin::copySkin(Skin* other) {
for (int i = 0; i < other->getBones().size(); i++)
if (!_bones.contains(other->getBones()[i])) _bones.add(other->getBones()[i]);
for (int i = 0; i < other->getConstraints().size(); i++)
if (!_constraints.contains(other->getConstraints()[i])) _constraints.add(other->getConstraints()[i]);
AttachmentMap::Entries entries = other->getAttachments();
while(entries.hasNext()) {
AttachmentMap::Entry& entry = entries.next();
@ -175,3 +188,11 @@ void Skin::copySkin(Skin* other) {
}
}
}
Vector<ConstraintData*>& Skin::getConstraints() {
return _constraints;
}
Vector<BoneData*>& Skin::getBones() {
return _bones;
}

View File

@ -41,9 +41,9 @@
using namespace spine;
RTTI_IMPL(TransformConstraint, Constraint)
RTTI_IMPL(TransformConstraint, Updatable)
TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton &skeleton) : Constraint(),
TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton &skeleton) : Updatable(),
_data(data),
_target(skeleton.findBone(
data.getTarget()->getName())),
@ -54,7 +54,8 @@ TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton
_scaleMix(
data.getScaleMix()),
_shearMix(
data.getShearMix()) {
data.getShearMix()),
_active(false) {
_bones.ensureCapacity(_data.getBones().size());
for (size_t i = 0; i < _data.getBones().size(); ++i) {
BoneData *boneData = _data.getBones()[i];
@ -380,3 +381,12 @@ void TransformConstraint::applyRelativeLocal() {
bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone._ashearX, shearY);
}
}
bool TransformConstraint::isActive() {
return _active;
}
void TransformConstraint::setActive(bool inValue) {
_active = inValue;
}

View File

@ -39,8 +39,7 @@
using namespace spine;
TransformConstraintData::TransformConstraintData(const String &name) :
_name(name),
_order(0),
ConstraintData(name),
_target(NULL),
_rotateMix(0),
_translateMix(0),
@ -54,15 +53,6 @@ TransformConstraintData::TransformConstraintData(const String &name) :
_offsetShearY(0),
_relative(false),
_local(false) {
assert(_name.length() > 0);
}
const String &TransformConstraintData::getName() {
return _name;
}
int TransformConstraintData::getOrder() {
return _order;
}
Vector<BoneData *> &TransformConstraintData::getBones() {

View File

@ -71,6 +71,7 @@ void TransformConstraintTimeline::apply(Skeleton &skeleton, float lastTime, floa
TransformConstraint *constraintP = skeleton._transformConstraints[_transformConstraintIndex];
TransformConstraint &constraint = *constraintP;
if (!constraint.isActive()) return;
if (time < _frames[0]) {
switch (blend) {

View File

@ -68,6 +68,7 @@ void TranslateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Ve
Bone *boneP = skeleton._bones[_boneIndex];
Bone &bone = *boneP;
if (!bone._active) return;
if (time < _frames[0]) {
switch (blend) {

View File

@ -37,6 +37,7 @@
#include <spine/Event.h>
#include <spine/Animation.h>
#include <spine/Bone.h>
#include <spine/TimelineType.h>
#include <spine/Slot.h>
#include <spine/SlotData.h>
@ -75,6 +76,7 @@ void TwoColorTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vec
Slot *slotP = skeleton._slots[_slotIndex];
Slot &slot = *slotP;
if (!slot._bone.isActive()) return;
if (time < _frames[0]) {
// Time is before first frame.

View File

@ -128,7 +128,7 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
if (!attachment) continue;
// Early out if slot is invisible
if (slot->color.a == 0) {
if (slot->color.a == 0 || !slot->bone->active) {
spSkeletonClipping_clipEnd(clipper, slot);
continue;
}

View File

@ -473,7 +473,7 @@ int main () {
SpineExtension::setInstance(&dbgExtension);
testcase(goblins, "data/goblins-pro.json", "data/goblins-pro.skel", "data/goblins-pma.atlas", 1.4f);
/*testcase(owl, "data/owl-pro.json", "data/owl-pro.skel", "data/owl-pma.atlas", 0.5f);
testcase(owl, "data/owl-pro.json", "data/owl-pro.skel", "data/owl-pma.atlas", 0.5f);
testcase(spineboy, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.6f);
testcase(stretchymanStrechyIk, "data/stretchyman-stretchy-ik-pro.json", "data/stretchyman-stretchy-ik-pro.skel", "data/stretchyman-pma.atlas", 0.6f);
testcase(raptor, "data/raptor-pro.json", "data/raptor-pro.skel", "data/raptor-pma.atlas", 0.5f);
@ -481,7 +481,7 @@ int main () {
testcase(vine, "data/vine-pro.json", "data/vine-pro.skel", "data/vine-pma.atlas", 0.5f);
testcase(tank, "data/tank-pro.json", "data/tank-pro.skel", "data/tank-pma.atlas", 0.2f);
testcase(raptor, "data/raptor-pro.json", "data/raptor-pro.skel", "data/raptor-pma.atlas", 0.5f);
testcase(stretchyman, "data/stretchyman-pro.json", "data/stretchyman-pro.skel", "data/stretchyman-pma.atlas", 0.6f);*/
testcase(stretchyman, "data/stretchyman-pro.json", "data/stretchyman-pro.skel", "data/stretchyman-pma.atlas", 0.6f);
dbgExtension.reportLeaks();
return 0;

View File

@ -102,8 +102,8 @@ void SkeletonDrawable::draw(RenderTarget &target, RenderStates states) const {
Attachment *attachment = slot.getAttachment();
if (!attachment) continue;
// Early out if the slot color is 0
if (slot.getColor().a == 0) {
// Early out if the slot color is 0 or the bone is not active
if (slot.getColor().a == 0 || !slot.getBone().isActive()) {
clipper.clipEnd(slot);
continue;
}