[cpp] Fix SkeletinBinary, fix memory leaks

This commit is contained in:
Mario Zechner 2024-04-01 13:29:26 +02:00
parent ff4cfbaa05
commit 2dc7335b4e
12 changed files with 393 additions and 176 deletions

View File

@ -37,6 +37,8 @@ namespace spine {
/// The interface for all constraints.
class SP_API ConstraintData : public SpineObject {
friend class SkeletonBinary;
RTTI_DECL
public:

View File

@ -42,12 +42,16 @@ namespace spine {
friend class SkeletonJson;
public:
LinkedMesh(MeshAttachment *mesh, const String &skin, size_t slotIndex, const String &parent,
LinkedMesh(MeshAttachment *mesh, const int skinIndex, size_t slotIndex, const String &parent,
bool inheritTimeline);
LinkedMesh(MeshAttachment *mesh, const String &skin, size_t slotIndex, const String &parent,
bool inheritTimeline);
private:
MeshAttachment *_mesh;
String _skin;
int _skinIndex;
String _skin;
size_t _slotIndex;
String _parent;
bool _inheritTimeline;

View File

@ -75,6 +75,7 @@ namespace spine {
static const int BONE_SHEAR = 7;
static const int BONE_SHEARX = 8;
static const int BONE_SHEARY = 9;
static const int BONE_INHERIT = 10;
static const int SLOT_ATTACHMENT = 0;
static const int SLOT_RGBA = 1;
@ -90,6 +91,15 @@ namespace spine {
static const int PATH_SPACING = 1;
static const int PATH_MIX = 2;
static const int PHYSICS_INERTIA = 0;
static const int PHYSICS_STRENGTH = 1;
static const int PHYSICS_DAMPING = 2;
static const int PHYSICS_MASS = 4;
static const int PHYSICS_WIND = 5;
static const int PHYSICS_GRAVITY = 6;
static const int PHYSICS_MIX = 7;
static const int PHYSICS_RESET = 8;
static const int CURVE_LINEAR = 0;
static const int CURVE_STEPPED = 1;
static const int CURVE_BEZIER = 2;
@ -147,11 +157,11 @@ namespace spine {
Attachment *readAttachment(DataInput *input, Skin *skin, int slotIndex, const String &attachmentName,
SkeletonData *skeletonData, bool nonessential);
void readVertices(DataInput *input, Vector<float> &vertices, Vector<int> &bones, int vertexCount);
int readVertices(DataInput *input, Vector<float> &vertices, Vector<int> &bones, bool weighted);
void readFloatArray(DataInput *input, int n, float scale, Vector<float> &array);
void readShortArray(DataInput *input, Vector<unsigned short> &array);
void readShortArray(DataInput *input, Vector<unsigned short> &array, int n);
Animation *readAnimation(const String &name, DataInput *input, SkeletonData *skeletonData);
@ -159,9 +169,9 @@ namespace spine {
setBezier(DataInput *input, CurveTimeline *timeline, int bezier, int frame, int value, float time1, float time2,
float value1, float value2, float scale);
Timeline *readTimeline(DataInput *input, CurveTimeline1 *timeline, float scale);
void readTimeline(DataInput *input, Vector<Timeline*> &timelines, CurveTimeline1 *timeline, float scale);
Timeline *readTimeline2(DataInput *input, CurveTimeline2 *timeline, float scale);
void readTimeline2(DataInput *input, Vector<Timeline*> &timelines, CurveTimeline2 *timeline, float scale);
};
}

View File

@ -195,7 +195,7 @@ namespace spine {
}
String substring(int startIndex, int length) const {
if (startIndex < 0 || startIndex >= _length || length < 0 || startIndex + length > _length) {
if (startIndex < 0 || startIndex >= (int)_length || length < 0 || startIndex + length > (int)_length) {
return String();
}
char* subStr = SpineExtension::calloc<char>(length + 1, __FILE__, __LINE__);
@ -205,7 +205,7 @@ namespace spine {
}
String substring(int startIndex) const {
if (startIndex < 0 || startIndex >= _length) {
if (startIndex < 0 || startIndex >= (int)_length) {
return String();
}
int length = _length - startIndex;

View File

@ -45,7 +45,7 @@ void IkConstraint::apply(Bone &bone, float targetX, float targetY, bool compress
float rotationIK = -bone._ashearX - bone._arotation;
float tx = 0, ty = 0;
switch (bone._data.getInherit()) {
switch (bone._inherit) {
case Inherit_OnlyTranslation:
tx = (targetX - bone._worldX) * MathUtil::sign(bone.getSkeleton().getScaleX());
ty = (targetY - bone._worldY) * MathUtil::sign(bone.getSkeleton().getScaleY());
@ -77,7 +77,7 @@ void IkConstraint::apply(Bone &bone, float targetX, float targetY, bool compress
float sx = bone._ascaleX;
float sy = bone._ascaleY;
if (compress || stretch) {
switch (bone._data.getInherit()) {
switch (bone._inherit) {
case Inherit_NoScale:
case Inherit_NoScaleOrReflection:
tx = targetX - bone._worldX;
@ -109,7 +109,8 @@ void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY
Bone *pp = parent.getParent();
float tx, ty, dx, dy, dd, l1, l2, a1, a2, r, td, sd, p;
float id, x, y;
px = parent._ax;
if (parent._inherit != Inherit_Normal || child._inherit != Inherit_Normal) return;
px = parent._ax;
py = parent._ay;
psx = parent._ascaleX;
psy = parent._ascaleY;

View File

@ -37,11 +37,11 @@ RTTI_IMPL(IkConstraintData, ConstraintData)
IkConstraintData::IkConstraintData(const String &name) : ConstraintData(name),
_target(NULL),
_bendDirection(1),
_bendDirection(0),
_compress(false),
_stretch(false),
_uniform(false),
_mix(1),
_mix(0),
_softness(0) {
}

View File

@ -33,10 +33,20 @@
using namespace spine;
LinkedMesh::LinkedMesh(MeshAttachment *mesh, const String &skin, size_t slotIndex, const String &parent,
bool inheritTimeline) : _mesh(mesh),
_skin(skin),
_slotIndex(slotIndex),
_parent(parent),
_inheritTimeline(inheritTimeline) {
LinkedMesh::LinkedMesh(MeshAttachment *mesh, const int skinIndex, size_t slotIndex, const String &parent,
bool inheritTimeline) : _mesh(mesh),
_skinIndex(skinIndex),
_skin(""),
_slotIndex(slotIndex),
_parent(parent),
_inheritTimeline(inheritTimeline) {
}
LinkedMesh::LinkedMesh(MeshAttachment *mesh, const String &skin, size_t slotIndex, const String &parent,
bool inheritTimeline) : _mesh(mesh),
_skinIndex(-1),
_skin(skin),
_slotIndex(slotIndex),
_parent(parent),
_inheritTimeline(inheritTimeline) {
}

View File

@ -136,6 +136,7 @@ Skeleton::~Skeleton() {
ContainerUtil::cleanUpVectorOfPointers(_ikConstraints);
ContainerUtil::cleanUpVectorOfPointers(_transformConstraints);
ContainerUtil::cleanUpVectorOfPointers(_pathConstraints);
ContainerUtil::cleanUpVectorOfPointers(_physicsConstraints);
}
void Skeleton::updateCache() {
@ -195,7 +196,7 @@ continue_outer:
}
}
for (size_t ii = 0; ii < pathCount; ++ii) {
for (size_t ii = 0; ii < physicsCount; ++ii) {
PhysicsConstraint *constraint = _physicsConstraints[ii];
if (constraint->getData().getOrder() == i) {
sortPhysicsConstraint(constraint);
@ -222,7 +223,9 @@ void Skeleton::printUpdateCache() {
printf("ik constraint %s\n", ((IkConstraint *) updatable)->getData().getName().buffer());
} else if (updatable->getRTTI().isExactly(PathConstraint::rtti)) {
printf("path constraint %s\n", ((PathConstraint *) updatable)->getData().getName().buffer());
}
} else if (updatable->getRTTI().isExactly(PhysicsConstraint::rtti)) {
printf("physics constraint %s\n", ((PhysicsConstraint *) updatable)->getData().getName().buffer());
}
}
}

View File

@ -53,12 +53,15 @@
#include <spine/EventTimeline.h>
#include <spine/IkConstraintData.h>
#include <spine/IkConstraintTimeline.h>
#include <spine/InheritTimeline.h>
#include <spine/MeshAttachment.h>
#include <spine/PathAttachment.h>
#include <spine/PathConstraintData.h>
#include <spine/PathConstraintMixTimeline.h>
#include <spine/PathConstraintPositionTimeline.h>
#include <spine/PathConstraintSpacingTimeline.h>
#include <spine/PhysicsConstraintData.h>
#include <spine/PhysicsConstraintTimeline.h>
#include <spine/PointAttachment.h>
#include <spine/RegionAttachment.h>
#include <spine/RotateTimeline.h>
@ -129,6 +132,7 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
skeletonData->_y = readFloat(input);
skeletonData->_width = readFloat(input);
skeletonData->_height = readFloat(input);
skeletonData->_referenceScale = readFloat(input) * this->_scale;
nonessential = readBoolean(input);
@ -161,6 +165,8 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
data->_skinRequired = readBoolean(input);
if (nonessential) {
readColor(input, data->getColor());
data->_icon.own(readString(input));
data->_visible = readBoolean(input);
}
skeletonData->_bones[i] = data;
}
@ -169,9 +175,17 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
int slotsCount = readVarint(input, true);
skeletonData->_slots.setSize(slotsCount, 0);
for (int i = 0; i < slotsCount; ++i) {
const char *slotName = readString(input);
String slotName = String(readString(input), true);
String pathName = "";
if (nonessential) {
int slash = slotName.lastIndexOf('/');
if (slash != -1) {
pathName = slotName.substring(0, slash);
slotName = slotName.substring(slash + 1);
}
}
BoneData *boneData = skeletonData->_bones[readVarint(input, true)];
SlotData *slotData = new (__FILE__, __LINE__) SlotData(i, String(slotName, true), *boneData);
SlotData *slotData = new (__FILE__, __LINE__) SlotData(i, slotName, *boneData);
readColor(input, slotData->getColor());
unsigned char a = readByte(input);
@ -184,6 +198,10 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
}
slotData->_attachmentName = readStringRef(input, skeletonData);
slotData->_blendMode = static_cast<BlendMode>(readVarint(input, true));
if (nonessential) {
slotData->_visible = readBoolean(input);
slotData->_path = pathName;
}
skeletonData->_slots[i] = slotData;
}
@ -194,18 +212,20 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
const char *name = readString(input);
IkConstraintData *data = new (__FILE__, __LINE__) IkConstraintData(String(name, 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)
data->_bones[ii] = skeletonData->_bones[readVarint(input, true)];
data->_target = skeletonData->_bones[readVarint(input, true)];
data->_mix = readFloat(input);
data->_softness = readFloat(input) * _scale;
data->_bendDirection = readSByte(input);
data->_compress = readBoolean(input);
data->_stretch = readBoolean(input);
data->_uniform = readBoolean(input);
int flags = readByte(input);
data->_skinRequired = (flags & 1) != 0;
data->_bendDirection = (flags & 2) != 0 ? 1 : -1;
data->_compress = (flags & 4) != 0;
data->_stretch = (flags & 8) != 0;
data->_uniform = (flags & 16) != 0;
if ((flags & 32) != 0) data->_mix = (flags & 64) != 0 ? readFloat(input) : 1;
if ((flags & 128) != 0) data->_softness = readFloat(input) * _scale;
skeletonData->_ikConstraints[i] = data;
}
@ -216,27 +236,30 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
const char *name = readString(input);
TransformConstraintData *data = new (__FILE__, __LINE__) TransformConstraintData(String(name, 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)
data->_bones[ii] = skeletonData->_bones[readVarint(input, true)];
data->_target = skeletonData->_bones[readVarint(input, true)];
data->_local = readBoolean(input);
data->_relative = readBoolean(input);
data->_offsetRotation = readFloat(input);
data->_offsetX = readFloat(input) * _scale;
data->_offsetY = readFloat(input) * _scale;
data->_offsetScaleX = readFloat(input);
data->_offsetScaleY = readFloat(input);
data->_offsetShearY = readFloat(input);
data->_mixRotate = readFloat(input);
data->_mixX = readFloat(input);
data->_mixY = readFloat(input);
data->_mixScaleX = readFloat(input);
data->_mixScaleY = readFloat(input);
data->_mixShearY = readFloat(input);
skeletonData->_transformConstraints[i] = data;
int flags = readByte(input);
data->_skinRequired = (flags & 1) != 0;
data->_local = (flags & 2) != 0;
data->_relative = (flags & 4) != 0;
if ((flags & 8) != 0) data->_offsetRotation = readFloat(input);
if ((flags & 16) != 0) data->_offsetX =readFloat(input) * _scale;
if ((flags & 32) != 0) data->_offsetY = readFloat(input) * _scale;
if ((flags & 64) != 0) data->_offsetScaleX = readFloat(input);
if ((flags & 128) != 0) data->_offsetScaleY = readFloat(input);
flags = readByte(input);
if ((flags & 1) != 0) data->_offsetShearY = readFloat(input);
if ((flags & 2) != 0) data->_mixRotate = readFloat(input);
if ((flags & 4) != 0) data->_mixX = readFloat(input);
if ((flags & 8) != 0) data->_mixY = readFloat(input);
if ((flags & 16) != 0) data->_mixScaleX = readFloat(input);
if ((flags & 32) != 0) data->_mixScaleY = readFloat(input);
if ((flags & 64) != 0) data->_mixShearY = readFloat(input);
skeletonData->_transformConstraints[i] = data;
}
/* Path constraints */
@ -252,10 +275,11 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
for (int ii = 0; ii < bonesCount; ++ii)
data->_bones[ii] = skeletonData->_bones[readVarint(input, true)];
data->_target = skeletonData->_slots[readVarint(input, true)];
data->_positionMode = static_cast<PositionMode>(readVarint(input, true));
data->_spacingMode = static_cast<SpacingMode>(readVarint(input, true));
data->_rotateMode = static_cast<RotateMode>(readVarint(input, true));
data->_offsetRotation = readFloat(input);
int flags = readByte(input);
data->_positionMode = (PositionMode)(flags & 1);
data->_spacingMode = (SpacingMode)((flags >> 1) & 3);
data->_rotateMode = (RotateMode)((flags >> 3) & 3);
if ((flags & 128) != 0) data->_offsetRotation = readFloat(input);
data->_position = readFloat(input);
if (data->_positionMode == PositionMode_Fixed) data->_position *= _scale;
data->_spacing = readFloat(input);
@ -267,6 +291,41 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
skeletonData->_pathConstraints[i] = data;
}
// Physics constraints.
int physicsConstraintsCount = readVarint(input, true);
skeletonData->_physicsConstraints.setSize(physicsConstraintsCount, 0);
for (int i = 0; i < physicsConstraintsCount; i++) {
const char *name = readString(input);
PhysicsConstraintData *data = new (__FILE__, __LINE__) PhysicsConstraintData(String(name, true));
data->_order = readVarint(input, true);
data->_bone = skeletonData->_bones[readVarint(input, true)];
int flags = readByte(input);
data->_skinRequired = (flags & 1) != 0;
if ((flags & 2) != 0) data->_x = readFloat(input);
if ((flags & 4) != 0) data->_y = readFloat(input);
if ((flags & 8) != 0) data->_rotate = readFloat(input);
if ((flags & 16) != 0) data->_scaleX = readFloat(input);
if ((flags & 32) != 0) data->_shearX = readFloat(input);
data->_limit = ((flags & 64) != 0 ? readFloat(input) : 5000) * _scale;
data->_step = 1.f / readByte(input);
data->_inertia = readFloat(input);
data->_strength = readFloat(input);
data->_damping = readFloat(input);
data->_massInverse = (flags & 128) != 0 ? readFloat(input) : 1;
data->_wind = readFloat(input);
data->_gravity = readFloat(input);
flags = readByte(input);
if ((flags & 1) != 0) data->_inertiaGlobal = true;
if ((flags & 2) != 0) data->_strengthGlobal = true;
if ((flags & 4) != 0) data->_dampingGlobal = true;
if ((flags & 8) != 0) data->_massGlobal = true;
if ((flags & 16) != 0) data->_windGlobal = true;
if ((flags & 32) != 0) data->_gravityGlobal = true;
if ((flags & 64) != 0) data->_mixGlobal = true;
data->_mix = (flags & 128) != 0 ? readFloat(input) : 1;
skeletonData->_physicsConstraints[i] = data;
}
/* Default skin. */
Skin *defaultSkin = readSkin(input, true, skeletonData, nonessential);
if (defaultSkin) {
@ -289,13 +348,7 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
/* Linked meshes. */
for (int i = 0, n = (int) _linkedMeshes.size(); i < n; ++i) {
LinkedMesh *linkedMesh = _linkedMeshes[i];
Skin *skin = linkedMesh->_skin.length() == 0 ? skeletonData->getDefaultSkin() : skeletonData->findSkin(linkedMesh->_skin);
if (skin == NULL) {
delete input;
delete skeletonData;
setError("Skin not found: ", linkedMesh->_skin.buffer());
return NULL;
}
Skin *skin = skeletonData->_skins[linkedMesh->_skinIndex];
Attachment *parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent);
if (parent == NULL) {
delete input;
@ -316,8 +369,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 = readStringRef(input, skeletonData);
EventData *eventData = new (__FILE__, __LINE__) EventData(String(name));
const char *name = readString(input);
EventData *eventData = new (__FILE__, __LINE__) EventData(String(name, true));
eventData->_intValue = readVarint(input, false);
eventData->_floatValue = readFloat(input);
eventData->_stringValue.own(readString(input));
@ -452,7 +505,10 @@ Skin *SkeletonBinary::readSkin(DataInput *input, bool defaultSkin, SkeletonData
if (slotCount == 0) return NULL;
skin = new (__FILE__, __LINE__) Skin("default");
} else {
skin = new (__FILE__, __LINE__) Skin(readStringRef(input, skeletonData));
skin = new (__FILE__, __LINE__) Skin(String(readString(input), true));
if (nonessential) readColor(input, skin->getColor());
for (int i = 0, n = readVarint(input, true); i < n; i++) {
int boneIndex = readVarint(input, true);
if (boneIndex >= (int) skeletonData->_bones.size()) return NULL;
@ -476,6 +532,12 @@ Skin *SkeletonBinary::readSkin(DataInput *input, bool defaultSkin, SkeletonData
if (pathIndex >= (int) skeletonData->_pathConstraints.size()) return NULL;
skin->getConstraints().add(skeletonData->_pathConstraints[pathIndex]);
}
for (int i = 0, n = readVarint(input, true); i < n; i++) {
int physicsIndex = readVarint(input, true);
if (physicsIndex >= (int) skeletonData->_physicsConstraints.size()) return NULL;
skin->getConstraints().add(skeletonData->_physicsConstraints[physicsIndex]);
}
slotCount = readVarint(input, true);
}
@ -496,7 +558,6 @@ Skin *SkeletonBinary::readSkin(DataInput *input, bool defaultSkin, SkeletonData
}
Sequence *SkeletonBinary::readSequence(DataInput *input) {
if (!readBoolean(input)) return NULL;
Sequence *sequence = new (__FILE__, __LINE__) Sequence(readVarint(input, true));
sequence->_start = readVarint(input, true);
sequence->_digits = readVarint(input, true);
@ -506,24 +567,23 @@ Sequence *SkeletonBinary::readSequence(DataInput *input) {
Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slotIndex, const String &attachmentName,
SkeletonData *skeletonData, bool nonessential) {
String name(readStringRef(input, skeletonData));
if (name.isEmpty()) name = attachmentName;
AttachmentType type = static_cast<AttachmentType>(readByte(input));
int flags = readByte(input);
String name = (flags & 8) != 0 ? readStringRef(input, skeletonData) : attachmentName;
AttachmentType type = static_cast<AttachmentType>(flags & 0x7);
switch (type) {
case AttachmentType_Region: {
String path(readStringRef(input, skeletonData));
if (path.isEmpty()) path = name;
float rotation = readFloat(input);
String path = (flags & 16) != 0 ? readStringRef(input, skeletonData) : name;
Color color(1, 1, 1, 1);
if ((flags & 32) != 0) readColor(input, color);
Sequence *sequence = (flags & 64) != 0 ? readSequence(input) : nullptr;
float rotation = (flags & 128) != 0 ? readFloat(input) : 0;
float x = readFloat(input) * _scale;
float y = readFloat(input) * _scale;
float scaleX = readFloat(input);
float scaleY = readFloat(input);
float width = readFloat(input) * _scale;
float height = readFloat(input) * _scale;
Color color;
readColor(input, color);
Sequence *sequence = readSequence(input);
RegionAttachment *region = _attachmentLoader->newRegionAttachment(*skin, String(name), String(path), sequence);
if (!region) {
setError("Error reading attachment: ", name.buffer());
@ -544,14 +604,13 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
return region;
}
case AttachmentType_Boundingbox: {
int vertexCount = readVarint(input, true);
BoundingBoxAttachment *box = _attachmentLoader->newBoundingBoxAttachment(*skin, String(name));
if (!box) {
setError("Error reading attachment: ", name.buffer());
return NULL;
}
readVertices(input, box->getVertices(), box->getBones(), vertexCount);
box->setWorldVerticesLength(vertexCount << 1);
readVertices(input, box->getVertices(), box->getBones(), (flags & 16) != 0);
box->setWorldVerticesLength(box->getVertices().size());
if (nonessential) {
readColor(input, box->getColor());
}
@ -559,31 +618,28 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
return box;
}
case AttachmentType_Mesh: {
Color color;
int vertexCount;
Vector<float> uvs;
Vector<unsigned short> triangles;
Vector<float> vertices;
Vector<int> bones;
int hullLength;
Sequence *sequence;
float width = 0;
float height = 0;
Vector<unsigned short> edges;
String path(readStringRef(input, skeletonData));
if (path.isEmpty()) path = name;
readColor(input, color);
vertexCount = readVarint(input, true);
readFloatArray(input, vertexCount << 1, 1, uvs);
readShortArray(input, triangles);
readVertices(input, vertices, bones, vertexCount);
hullLength = readVarint(input, true) << 1;
sequence = readSequence(input);
String path = (flags & 16) != 0 ? readStringRef(input, skeletonData) : name;
Color color(1, 1, 1, 1);
if ((flags & 32) != 0) readColor(input, color);
Sequence *sequence = (flags & 64) != 0 ? readSequence(input) : nullptr;
hullLength = readVarint(input, true);
int verticesLength = readVertices(input, vertices, bones, (flags & 128) != 0);
readFloatArray(input, verticesLength, 1, uvs);
readShortArray(input, triangles, (verticesLength - hullLength - 2) * 3);
if (nonessential) {
readShortArray(input, edges);
width = readFloat(input) * _scale;
height = readFloat(input) * _scale;
readShortArray(input, edges, readVarint(input, true));
width = readFloat(input);
height = readFloat(input);
}
MeshAttachment *mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path), sequence);
@ -595,7 +651,7 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
mesh->_color.set(color);
mesh->_bones.addAll(bones);
mesh->_vertices.addAll(vertices);
mesh->setWorldVerticesLength(vertexCount << 1);
mesh->setWorldVerticesLength(verticesLength);
mesh->_triangles.addAll(triangles);
mesh->_regionUVs.addAll(uvs);
if (sequence == NULL) mesh->updateRegion();
@ -610,16 +666,14 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
return mesh;
}
case AttachmentType_Linkedmesh: {
String path(readStringRef(input, skeletonData));
if (path.isEmpty()) path = name;
Color color;
float width = 0, height = 0;
readColor(input, color);
String skinName(readStringRef(input, skeletonData));
String path = (flags & 16) != 0 ? readStringRef(input, skeletonData) : name;
Color color(1, 1, 1, 1);
if ((flags & 32) != 0) readColor(input, color);
Sequence *sequence = (flags & 64) != 0 ? readSequence(input) : nullptr;
bool inheritTimelines = (flags & 128) != 0;
int skinIndex = readVarint(input, true);
String parent(readStringRef(input, skeletonData));
bool inheritTimelines = readBoolean(input);
Sequence *sequence = readSequence(input);
float width = 0, height = 0;
if (nonessential) {
width = readFloat(input) * _scale;
height = readFloat(input) * _scale;
@ -638,7 +692,7 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
mesh->_height = height;
}
LinkedMesh *linkedMesh = new (__FILE__, __LINE__) LinkedMesh(mesh, String(skinName), slotIndex,
LinkedMesh *linkedMesh = new (__FILE__, __LINE__) LinkedMesh(mesh, skinIndex, slotIndex,
String(parent), inheritTimelines);
_linkedMeshes.add(linkedMesh);
return mesh;
@ -649,12 +703,11 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
setError("Error reading attachment: ", name.buffer());
return NULL;
}
path->_closed = readBoolean(input);
path->_constantSpeed = readBoolean(input);
int vertexCount = readVarint(input, true);
readVertices(input, path->getVertices(), path->getBones(), vertexCount);
path->setWorldVerticesLength(vertexCount << 1);
int lengthsLength = vertexCount / 3;
path->_closed = (flags & 16) != 0;
path->_constantSpeed = (flags & 32) != 0;
int verticesLength = readVertices(input, path->getVertices(), path->getBones(), (flags & 64) != 0);
path->setWorldVerticesLength(verticesLength);
int lengthsLength = verticesLength / 6;
path->_lengths.setSize(lengthsLength, 0);
for (int i = 0; i < lengthsLength; ++i) {
path->_lengths[i] = readFloat(input) * _scale;
@ -683,14 +736,13 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
}
case AttachmentType_Clipping: {
int endSlotIndex = readVarint(input, true);
int vertexCount = readVarint(input, true);
ClippingAttachment *clip = _attachmentLoader->newClippingAttachment(*skin, name);
if (!clip) {
setError("Error reading attachment: ", name.buffer());
return NULL;
}
readVertices(input, clip->getVertices(), clip->getBones(), vertexCount);
clip->setWorldVerticesLength(vertexCount << 1);
int verticesLength = readVertices(input, clip->getVertices(), clip->getBones(), (flags & 16) != 0);
clip->setWorldVerticesLength(verticesLength);
clip->_endSlot = skeletonData->_slots[endSlotIndex];
if (nonessential) {
readColor(input, clip->getColor());
@ -702,18 +754,16 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
return NULL;
}
void SkeletonBinary::readVertices(DataInput *input, Vector<float> &vertices, Vector<int> &bones, int vertexCount) {
int SkeletonBinary::readVertices(DataInput *input, Vector<float> &vertices, Vector<int> &bones, bool weighted) {
float scale = _scale;
int vertexCount = readVarint(input, true);
int verticesLength = vertexCount << 1;
if (!readBoolean(input)) {
readFloatArray(input, verticesLength, scale, vertices);
return;
}
vertices.ensureCapacity(verticesLength * 3 * 3);
bones.ensureCapacity(verticesLength * 3);
if (!weighted) {
readFloatArray(input, verticesLength, scale, vertices);
return verticesLength;
}
vertices.ensureCapacity(verticesLength * 3 * 3);
bones.ensureCapacity(verticesLength * 3);
for (int i = 0; i < vertexCount; ++i) {
int boneCount = readVarint(input, true);
bones.add(boneCount);
@ -724,6 +774,7 @@ void SkeletonBinary::readVertices(DataInput *input, Vector<float> &vertices, Vec
vertices.add(readFloat(input));
}
}
return verticesLength;
}
void SkeletonBinary::readFloatArray(DataInput *input, int n, float scale, Vector<float> &array) {
@ -741,14 +792,10 @@ void SkeletonBinary::readFloatArray(DataInput *input, int n, float scale, Vector
}
}
void SkeletonBinary::readShortArray(DataInput *input, Vector<unsigned short> &array) {
int n = readVarint(input, true);
void SkeletonBinary::readShortArray(DataInput *input, Vector<unsigned short> &array, int n) {
array.setSize(n, 0);
int i;
for (i = 0; i < n; ++i) {
array[i] = readByte(input) << 8;
array[i] |= readByte(input);
for (int i = 0; i < n; ++i) {
array[i] = (short)readVarint(input, true);
}
}
@ -762,7 +809,7 @@ void SkeletonBinary::setBezier(DataInput *input, CurveTimeline *timeline, int be
timeline->setBezier(bezier, frame, value, time1, value1, cx1, cy1 * scale, cx2, cy2 * scale, time2, value2);
}
Timeline *SkeletonBinary::readTimeline(DataInput *input, CurveTimeline1 *timeline, float scale) {
void SkeletonBinary::readTimeline(DataInput *input, Vector<Timeline*> &timelines, CurveTimeline1 *timeline, float scale) {
float time = readFloat(input);
float value = readFloat(input) * scale;
for (int frame = 0, bezier = 0, frameLast = (int) timeline->getFrameCount() - 1;; frame++) {
@ -780,10 +827,10 @@ Timeline *SkeletonBinary::readTimeline(DataInput *input, CurveTimeline1 *timelin
time = time2;
value = value2;
}
return timeline;
timelines.add(timeline);
}
Timeline *SkeletonBinary::readTimeline2(DataInput *input, CurveTimeline2 *timeline, float scale) {
void SkeletonBinary::readTimeline2(DataInput *input, Vector<Timeline*> &timelines, CurveTimeline2 *timeline, float scale) {
float time = readFloat(input);
float value1 = readFloat(input) * scale;
float value2 = readFloat(input) * scale;
@ -805,7 +852,7 @@ Timeline *SkeletonBinary::readTimeline2(DataInput *input, CurveTimeline2 *timeli
value1 = nvalue1;
value2 = nvalue2;
}
return timeline;
timelines.add(timeline);
}
Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, SkeletonData *skeletonData) {
@ -1039,50 +1086,59 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
unsigned char timelineType = readByte(input);
int frameCount = readVarint(input, true);
int bezierCount = readVarint(input, true);
Timeline *timeline = NULL;
if (timelineType == BONE_INHERIT) {
InheritTimeline *timeline = new (__FILE__, __LINE__) InheritTimeline(frameCount, boneIndex);
for (int frame = 0; frame < frameCount; frame++) {
float time = readFloat(input);
Inherit inherit = (Inherit)readByte(input);
timeline->setFrame(frame, time, inherit);
}
timelines.add(timeline);
continue;
}
int bezierCount = readVarint(input, true);
switch (timelineType) {
case BONE_ROTATE:
timeline = readTimeline(input,
readTimeline(input, timelines,
new (__FILE__, __LINE__) RotateTimeline(frameCount, bezierCount, boneIndex),
1);
break;
case BONE_TRANSLATE:
timeline = readTimeline2(input, new (__FILE__, __LINE__) TranslateTimeline(frameCount, bezierCount, boneIndex), scale);
readTimeline2(input, timelines,new (__FILE__, __LINE__) TranslateTimeline(frameCount, bezierCount, boneIndex), scale);
break;
case BONE_TRANSLATEX:
timeline = readTimeline(input, new (__FILE__, __LINE__) TranslateXTimeline(frameCount, bezierCount, boneIndex), scale);
readTimeline(input, timelines, new (__FILE__, __LINE__) TranslateXTimeline(frameCount, bezierCount, boneIndex), scale);
break;
case BONE_TRANSLATEY:
timeline = readTimeline(input, new (__FILE__, __LINE__) TranslateYTimeline(frameCount, bezierCount, boneIndex), scale);
readTimeline(input, timelines, new (__FILE__, __LINE__) TranslateYTimeline(frameCount, bezierCount, boneIndex), scale);
break;
case BONE_SCALE:
timeline = readTimeline2(input,
readTimeline2(input, timelines,
new (__FILE__, __LINE__) ScaleTimeline(frameCount, bezierCount, boneIndex),
1);
break;
case BONE_SCALEX:
timeline = readTimeline(input,
readTimeline(input, timelines,
new (__FILE__, __LINE__) ScaleXTimeline(frameCount, bezierCount, boneIndex),
1);
break;
case BONE_SCALEY:
timeline = readTimeline(input,
readTimeline(input, timelines,
new (__FILE__, __LINE__) ScaleYTimeline(frameCount, bezierCount, boneIndex),
1);
break;
case BONE_SHEAR:
timeline = readTimeline2(input,
readTimeline2(input, timelines,
new (__FILE__, __LINE__) ShearTimeline(frameCount, bezierCount, boneIndex),
1);
break;
case BONE_SHEARX:
timeline = readTimeline(input,
readTimeline(input, timelines,
new (__FILE__, __LINE__) ShearXTimeline(frameCount, bezierCount, boneIndex),
1);
break;
case BONE_SHEARY:
timeline = readTimeline(input,
readTimeline(input, timelines,
new (__FILE__, __LINE__) ShearYTimeline(frameCount, bezierCount, boneIndex),
1);
break;
@ -1092,7 +1148,6 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
return NULL;
}
}
timelines.add(timeline);
}
}
@ -1103,25 +1158,20 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
int frameLast = frameCount - 1;
int bezierCount = readVarint(input, true);
IkConstraintTimeline *timeline = new (__FILE__, __LINE__) IkConstraintTimeline(frameCount, bezierCount, index);
float time = readFloat(input);
float mix = readFloat(input);
float softness = readFloat(input) * scale;
for (int frame = 0, bezier = 0;; frame++) {
int bendDirection = readSByte(input);
bool compress = readBoolean(input);
bool stretch = readBoolean(input);
timeline->setFrame(frame, time, mix, softness, bendDirection, compress, stretch);
int flags = readByte(input);
float time = readFloat(input), mix = (flags & 1) != 0 ? ((flags & 2) != 0 ? readFloat(input) : 1) : 0;
float softness = (flags & 4) != 0 ? readFloat(input) * scale : 0;
for (int frame = 0, bezier = 0;; frame++) {
timeline->setFrame(frame, time, mix, softness, (flags & 8) != 0 ? 1 : -1, (flags & 16) != 0, (flags & 32) != 0);
if (frame == frameLast) break;
float time2 = readFloat(input);
float mix2 = readFloat(input);
float softness2 = readFloat(input) * scale;
switch (readSByte(input)) {
case CURVE_STEPPED:
timeline->setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
flags = readByte(input);
float time2 = readFloat(input), mix2 = (flags & 1) != 0 ? ((flags & 2) != 0 ? readFloat(input) : 1) : 0;
float softness2 = (flags & 4) != 0 ? readFloat(input) * scale : 0;
if ((flags & 64) != 0)
timeline->setStepped(frame);
else if ((flags & 128) != 0) {
setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
}
time = time2;
mix = mix2;
@ -1182,26 +1232,24 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
int index = readVarint(input, true);
PathConstraintData *data = skeletonData->_pathConstraints[index];
for (int ii = 0, nn = readVarint(input, true); ii < nn; ii++) {
int type = readSByte(input);
int type = readByte(input);
int frameCount = readVarint(input, true);
int bezierCount = readVarint(input, true);
switch (type) {
switch (type) {
case PATH_POSITION: {
timelines
.add(readTimeline(input, new PathConstraintPositionTimeline(frameCount, bezierCount, index),
data->_positionMode == PositionMode_Fixed ? scale : 1));
readTimeline(input, timelines, new PathConstraintPositionTimeline(frameCount, bezierCount, index),
data->_positionMode == PositionMode_Fixed ? scale : 1);
break;
}
case PATH_SPACING: {
timelines
.add(readTimeline(input,
readTimeline(input, timelines,
new PathConstraintSpacingTimeline(frameCount,
bezierCount,
index),
data->_spacingMode == SpacingMode_Length ||
data->_spacingMode == SpacingMode_Fixed
? scale
: 1));
: 1);
break;
}
case PATH_MIX:
@ -1236,7 +1284,46 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
}
}
// Deform timelines.
// Physics timelines.
for (int i = 0, n = readVarint(input, true); i < n; i++) {
int index = readVarint(input, true) - 1;
for (int ii = 0, nn = readVarint(input, true); ii < nn; ii++) {
int type = readByte(input);
int frameCount = readVarint(input, true);
if (type == PHYSICS_RESET) {
PhysicsConstraintResetTimeline *timeline = new (__FILE__, __LINE__) PhysicsConstraintResetTimeline(frameCount, index);
for (int frame = 0; frame < frameCount; frame++)
timeline->setFrame(frame, readFloat(input));
timelines.add(timeline);
continue;
}
int bezierCount = readVarint(input, true);
switch (type) {
case PHYSICS_INERTIA:
readTimeline(input, timelines, new PhysicsConstraintInertiaTimeline(frameCount, bezierCount, index), 1);
break;
case PHYSICS_STRENGTH:
readTimeline(input, timelines, new PhysicsConstraintStrengthTimeline(frameCount, bezierCount, index), 1);
break;
case PHYSICS_DAMPING:
readTimeline(input, timelines, new PhysicsConstraintDampingTimeline(frameCount, bezierCount, index), 1);
break;
case PHYSICS_MASS:
readTimeline(input, timelines, new PhysicsConstraintMassTimeline(frameCount, bezierCount, index), 1);
break;
case PHYSICS_WIND:
readTimeline(input, timelines, new PhysicsConstraintWindTimeline(frameCount, bezierCount, index), 1);
break;
case PHYSICS_GRAVITY:
readTimeline(input, timelines, new PhysicsConstraintGravityTimeline(frameCount, bezierCount, index), 1);
break;
case PHYSICS_MIX:
readTimeline(input, timelines, new PhysicsConstraintMixTimeline(frameCount, bezierCount, index), 1);
}
}
}
// Attachment timelines.
for (int i = 0, n = readVarint(input, true); i < n; ++i) {
Skin *skin = skeletonData->_skins[readVarint(input, true)];
for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
@ -1379,10 +1466,13 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
event->_intValue = readVarint(input, false);
event->_floatValue = readFloat(input);
bool freeString = readBoolean(input);
const char *event_stringValue = freeString ? readString(input) : eventData->_stringValue.buffer();
event->_stringValue = String(event_stringValue);
if (freeString) SpineExtension::free(event_stringValue, __FILE__, __LINE__);
const char *event_stringValue = readString(input);
if (event_stringValue == nullptr) {
event->_stringValue = eventData->_stringValue;
} else {
event->_stringValue = String(event_stringValue);
SpineExtension::free(event_stringValue, __FILE__, __LINE__);
}
if (!eventData->_audioPath.isEmpty()) {
event->_volume = readFloat(input);

View File

@ -58,6 +58,7 @@
#include <spine/PathConstraintMixTimeline.h>
#include <spine/PathConstraintPositionTimeline.h>
#include <spine/PathConstraintSpacingTimeline.h>
#include <spine/PhysicsConstraintData.h>
#include <spine/PointAttachment.h>
#include <spine/RegionAttachment.h>
#include <spine/RotateTimeline.h>
@ -133,7 +134,7 @@ SkeletonData *SkeletonJson::readSkeletonDataFile(const String &path) {
SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
int i, ii;
SkeletonData *skeletonData;
Json *root, *skeleton, *bones, *boneMap, *ik, *transform, *path, *slots, *skins, *animations, *events;
Json *root, *skeleton, *bones, *boneMap, *ik, *transform, *path, *physics, *slots, *skins, *animations, *events;
_error = "";
_linkedMeshes.clear();
@ -455,6 +456,54 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
}
}
/* Physics constraints */
physics = Json::getItem(root, "physics");
if (physics) {
Json *constraintMap;
skeletonData->_physicsConstraints.ensureCapacity(physics->_size);
skeletonData->_physicsConstraints.setSize(physics->_size, 0);
for (constraintMap = physics->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) {
const char *name;
PhysicsConstraintData *data = new (__FILE__, __LINE__) PhysicsConstraintData(
Json::getString(constraintMap, "name", 0));
data->setOrder(Json::getInt(constraintMap, "order", 0));
data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false));
name = Json::getString(constraintMap, "bone", 0);
data->_bone = skeletonData->findBone(name);
if (!data->_bone) {
delete skeletonData;
setError(root, "Physics bone not found: ", name);
return NULL;
}
data->_x = Json::getFloat(constraintMap, "x", 0);
data->_y = Json::getFloat(constraintMap, "y", 0);
data->_rotate = Json::getFloat(constraintMap, "rotate", 0);
data->_scaleX = Json::getFloat(constraintMap, "scaleX", 0);
data->_shearX = Json::getFloat(constraintMap, "shearX", 0);
data->_limit = Json::getFloat(constraintMap, "limit", 5000) * _scale;
data->_step = 1.0f / Json::getInt(constraintMap, "fps", 60);
data->_inertia = Json::getFloat(constraintMap, "inertia", 1);
data->_strength = Json::getFloat(constraintMap, "strength", 100);
data->_damping = Json::getFloat(constraintMap, "damping", 1);
data->_massInverse = 1 / Json::getFloat(constraintMap, "mass", 1);
data->_wind = Json::getFloat(constraintMap, "wind", 0);
data->_gravity = Json::getFloat(constraintMap, "gravity", 0);
data->_mix = Json::getFloat(constraintMap, "mix", 1);
data->_inertiaGlobal = Json::getBoolean(constraintMap, "inertiaGlobal", false);
data->_strengthGlobal = Json::getBoolean(constraintMap, "strengthGlobal", false);
data->_dampingGlobal = Json::getBoolean(constraintMap, "dampingGlobal", false);
data->_massGlobal = Json::getBoolean(constraintMap, "massGlobal", false);
data->_windGlobal = Json::getBoolean(constraintMap, "windGlobal", false);
data->_gravityGlobal = Json::getBoolean(constraintMap, "gravityGlobal", false);
data->_mixGlobal = Json::getBoolean(constraintMap, "mixGlobal", false);
skeletonData->_physicsConstraints[i] = data;
}
}
/* Skins. */
skins = Json::getItem(root, "skins");
if (skins) {
@ -520,6 +569,19 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
}
}
item = Json::getItem(skinMap, "physics");
if (item) {
for (item = item->_child; item; item = item->_next) {
PhysicsConstraintData *data = skeletonData->findPhysicsConstraint(item->_valueString);
if (!data) {
delete skeletonData;
setError(root, String("Skin physics constraint not found: "), item->_valueString);
return NULL;
}
skin->getConstraints().add(data);
}
}
skeletonData->_skins[skinsIndex++] = skin;
if (strcmp(Json::getString(skinMap, "name", ""), "default") == 0) {
skeletonData->_defaultSkin = skin;

View File

@ -611,6 +611,40 @@ void mixAndMatch(SkeletonData *skeletonData, Atlas *atlas) {
}
}
void celestialCircus(SkeletonData *skeletonData, Atlas *atlas) {
SP_UNUSED(atlas);
SkeletonDrawable drawable(skeletonData);
drawable.timeScale = 1;
drawable.setUsePremultipliedAlpha(true);
Skeleton *skeleton = drawable.skeleton;
skeleton->setPosition(320, 480);
skeleton->updateWorldTransform(Physics_Update);
drawable.state->setAnimation(0, "swing", true);
sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - celestial circus");
window.setFramerateLimit(60);
sf::Event event;
sf::Clock deltaClock;
while (window.isOpen()) {
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) window.close();
}
float delta = deltaClock.getElapsedTime().asSeconds();
deltaClock.restart();
drawable.update(delta);
window.clear();
window.draw(drawable);
window.display();
}
}
/**
* Used for debugging purposes during runtime development
*/
@ -636,11 +670,11 @@ DebugExtension dbgExtension(SpineExtension::getInstance());
int main() {
SpineExtension::setInstance(&dbgExtension);
testcase(ikDemo, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.6f);
testcase(celestialCircus, "data/celestial-circus-pro.json", "data/celestial-circus-pro.skel", "data/celestial-circus-pma.atlas", 0.2f);
testcase(vine, "data/vine-pro.json", "data/vine-pro.skel", "data/vine-pma.atlas", 0.5f);
testcase(dragon, "data/dragon-ess.json", "data/dragon-ess.skel", "data/dragon-pma.atlas", 0.6f);
testcase(vine, "data/vine-pro.json", "data/vine-pro.skel", "data/vine-pma.atlas", 0.5f);
testcase(owl, "data/owl-pro.json", "data/owl-pro.skel", "data/owl-pma.atlas", 0.5f);
testcase(ikDemo, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.6f);
testcase(mixAndMatch, "data/mix-and-match-pro.json", "data/mix-and-match-pro.skel", "data/mix-and-match-pma.atlas", 0.5f);
testcase(coin, "data/coin-pro.json", "data/coin-pro.skel", "data/coin-pma.atlas", 0.5f);
testcase(spineboy, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.6f);

View File

@ -79,6 +79,7 @@ namespace spine {
void SkeletonDrawable::update(float deltaTime, Physics physics) {
state->update(deltaTime * timeScale);
state->apply(*skeleton);
skeleton->update(deltaTime * timeScale);
skeleton->updateWorldTransform(physics);
}