2022-06-25 00:23:03 +08:00

1039 lines
37 KiB
C++

/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, 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.
*****************************************************************************/
#ifdef SPINE_UE4
#include "SpinePluginPrivatePCH.h"
#endif
#include <spine/SkeletonBinary.h>
#include <spine/SkeletonData.h>
#include <spine/Atlas.h>
#include <spine/AtlasAttachmentLoader.h>
#include <spine/LinkedMesh.h>
#include <spine/Skin.h>
#include <spine/Attachment.h>
#include <spine/VertexAttachment.h>
#include <spine/Animation.h>
#include <spine/CurveTimeline.h>
#include <spine/ContainerUtil.h>
#include <spine/BoneData.h>
#include <spine/SlotData.h>
#include <spine/IkConstraintData.h>
#include <spine/TransformConstraintData.h>
#include <spine/PathConstraintData.h>
#include <spine/AttachmentType.h>
#include <spine/RegionAttachment.h>
#include <spine/BoundingBoxAttachment.h>
#include <spine/MeshAttachment.h>
#include <spine/PathAttachment.h>
#include <spine/PointAttachment.h>
#include <spine/ClippingAttachment.h>
#include <spine/EventData.h>
#include <spine/AttachmentTimeline.h>
#include <spine/ColorTimeline.h>
#include <spine/TwoColorTimeline.h>
#include <spine/RotateTimeline.h>
#include <spine/TranslateTimeline.h>
#include <spine/ScaleTimeline.h>
#include <spine/ShearTimeline.h>
#include <spine/IkConstraintTimeline.h>
#include <spine/TransformConstraintTimeline.h>
#include <spine/PathConstraintPositionTimeline.h>
#include <spine/PathConstraintSpacingTimeline.h>
#include <spine/PathConstraintMixTimeline.h>
#include <spine/DeformTimeline.h>
#include <spine/DrawOrderTimeline.h>
#include <spine/EventTimeline.h>
#include <spine/Event.h>
using namespace spine;
const int SkeletonBinary::BONE_ROTATE = 0;
const int SkeletonBinary::BONE_TRANSLATE = 1;
const int SkeletonBinary::BONE_SCALE = 2;
const int SkeletonBinary::BONE_SHEAR = 3;
const int SkeletonBinary::SLOT_ATTACHMENT = 0;
const int SkeletonBinary::SLOT_COLOR = 1;
const int SkeletonBinary::SLOT_TWO_COLOR = 2;
const int SkeletonBinary::PATH_POSITION = 0;
const int SkeletonBinary::PATH_SPACING = 1;
const int SkeletonBinary::PATH_MIX = 2;
const int SkeletonBinary::CURVE_LINEAR = 0;
const int SkeletonBinary::CURVE_STEPPED = 1;
const int SkeletonBinary::CURVE_BEZIER = 2;
SkeletonBinary::SkeletonBinary(Atlas *atlasArray) : _attachmentLoader(
new(__FILE__, __LINE__) AtlasAttachmentLoader(atlasArray)), _error(), _scale(1), _ownsLoader(true) {
}
SkeletonBinary::SkeletonBinary(AttachmentLoader *attachmentLoader) : _attachmentLoader(attachmentLoader), _error(),
_scale(1), _ownsLoader(false)
{
assert(_attachmentLoader != NULL);
}
SkeletonBinary::~SkeletonBinary() {
ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes);
_linkedMeshes.clear();
if (_ownsLoader) delete _attachmentLoader;
}
SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, const int length) {
bool nonessential;
SkeletonData *skeletonData;
DataInput *input = new(__FILE__, __LINE__) DataInput();
input->cursor = binary;
input->end = binary + length;
_linkedMeshes.clear();
skeletonData = new(__FILE__, __LINE__) SkeletonData();
char *skeletonData_hash = readString(input);
skeletonData->_hash.own(skeletonData_hash);
char *skeletonData_version = readString(input);
skeletonData->_version.own(skeletonData_version);
if ("3.8.75" == skeletonData->_version) {
delete input;
delete skeletonData;
setError("Unsupported skeleton data, please export with a newer version of Spine.", "");
return NULL;
}
skeletonData->_x = readFloat(input);
skeletonData->_y = readFloat(input);
skeletonData->_width = readFloat(input);
skeletonData->_height = readFloat(input);
nonessential = readBoolean(input);
if (nonessential) {
/* Skip images path, audio path & fps */
skeletonData->_fps = readFloat(input);
skeletonData->_imagesPath.own(readString(input));
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);
for (int i = 0; i < numBones; ++i) {
const char *name = readString(input);
BoneData *parent = i == 0 ? 0 : skeletonData->_bones[readVarint(input, true)];
BoneData *data = new(__FILE__, __LINE__) BoneData(i, String(name, true), parent);
data->_rotation = readFloat(input);
data->_x = readFloat(input) * _scale;
data->_y = readFloat(input) * _scale;
data->_scaleX = readFloat(input);
data->_scaleY = readFloat(input);
data->_shearX = readFloat(input);
data->_shearY = readFloat(input);
data->_length = readFloat(input) * _scale;
data->_transformMode = static_cast<TransformMode>(readVarint(input, true));
data->_skinRequired = readBoolean(input);
if (nonessential) readInt(input); /* Skip bone color. */
skeletonData->_bones[i] = data;
}
/* Slots. */
int slotsCount = readVarint(input, true);
skeletonData->_slots.setSize(slotsCount, 0);
for (int i = 0; i < slotsCount; ++i) {
const char *slotName = readString(input);
BoneData *boneData = skeletonData->_bones[readVarint(input, true)];
SlotData *slotData = new(__FILE__, __LINE__) SlotData(i, String(slotName, true), *boneData);
readColor(input, slotData->getColor());
unsigned char r = readByte(input);
unsigned char g = readByte(input);
unsigned char b = readByte(input);
unsigned char a = readByte(input);
if (!(r == 0xff && g == 0xff && b == 0xff && a == 0xff)) {
slotData->getDarkColor().set(r / 255.0f, g / 255.0f, b / 255.0f, 1);
slotData->setHasDarkColor(true);
}
slotData->_attachmentName = readStringRef(input, skeletonData);
slotData->_blendMode = static_cast<BlendMode>(readVarint(input, true));
skeletonData->_slots[i] = slotData;
}
/* IK constraints. */
int ikConstraintsCount = readVarint(input, true);
skeletonData->_ikConstraints.setSize(ikConstraintsCount, 0);
for (int i = 0; i < ikConstraintsCount; ++i) {
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);
skeletonData->_ikConstraints[i] = data;
}
/* Transform constraints. */
int transformConstraintsCount = readVarint(input, true);
skeletonData->_transformConstraints.setSize(transformConstraintsCount, 0);
for (int i = 0; i < transformConstraintsCount; ++i) {
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->_rotateMix = readFloat(input);
data->_translateMix = readFloat(input);
data->_scaleMix = readFloat(input);
data->_shearMix = readFloat(input);
skeletonData->_transformConstraints[i] = data;
}
/* Path constraints */
int pathConstraintsCount = readVarint(input, true);
skeletonData->_pathConstraints.setSize(pathConstraintsCount, 0);
for (int i = 0; i < pathConstraintsCount; ++i) {
const char *name = readString(input);
PathConstraintData *data = new(__FILE__, __LINE__) PathConstraintData(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->_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);
data->_position = readFloat(input);
if (data->_positionMode == PositionMode_Fixed) data->_position *= _scale;
data->_spacing = readFloat(input);
if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed)
data->_spacing *= _scale;
data->_rotateMix = readFloat(input);
data->_translateMix = readFloat(input);
skeletonData->_pathConstraints[i] = data;
}
/* Default skin. */
Skin* defaultSkin = readSkin(input, true, skeletonData, nonessential);
if (defaultSkin) {
skeletonData->_defaultSkin = defaultSkin;
skeletonData->_skins.add(defaultSkin);
}
/* Skins. */
for (size_t i = 0, n = (size_t)readVarint(input, true); i < n; ++i)
skeletonData->_skins.add(readSkin(input, false, skeletonData, nonessential));
/* Linked meshes. */
for (int i = 0, n = _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;
}
Attachment *parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent);
if (parent == NULL) {
delete input;
delete skeletonData;
setError("Parent mesh not found: ", linkedMesh->_parent.buffer());
return NULL;
}
linkedMesh->_mesh->_deformAttachment = linkedMesh->_inheritDeform ? static_cast<VertexAttachment*>(parent) : linkedMesh->_mesh;
linkedMesh->_mesh->setParentMesh(static_cast<MeshAttachment *>(parent));
linkedMesh->_mesh->updateUVs();
_attachmentLoader->configureAttachment(linkedMesh->_mesh);
}
ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes);
_linkedMeshes.clear();
/* Events. */
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));
eventData->_intValue = readVarint(input, false);
eventData->_floatValue = readFloat(input);
eventData->_stringValue.own(readString(input));
eventData->_audioPath.own(readString(input)); // skip audio path
if (!eventData->_audioPath.isEmpty()) {
eventData->_volume = readFloat(input);
eventData->_balance = readFloat(input);
}
skeletonData->_events[i] = eventData;
}
/* Animations. */
int animationsCount = readVarint(input, true);
skeletonData->_animations.setSize(animationsCount, 0);
for (int i = 0; i < animationsCount; ++i) {
String name(readString(input), true);
Animation *animation = readAnimation(name, input, skeletonData);
if (!animation) {
delete input;
delete skeletonData;
return NULL;
}
skeletonData->_animations[i] = animation;
}
delete input;
return skeletonData;
}
SkeletonData *SkeletonBinary::readSkeletonDataFile(const String &path) {
int length;
SkeletonData *skeletonData;
const char *binary = SpineExtension::readFile(path.buffer(), &length);
if (length == 0 || !binary) {
setError("Unable to read skeleton file: ", path.buffer());
return NULL;
}
skeletonData = readSkeletonData((unsigned char *) binary, length);
SpineExtension::free(binary, __FILE__, __LINE__);
return skeletonData;
}
void SkeletonBinary::setError(const char *value1, const char *value2) {
char message[256];
int length;
strcpy(message, value1);
length = (int) strlen(value1);
if (value2) strncat(message + length, value2, 255 - length);
_error = String(message);
}
char *SkeletonBinary::readString(DataInput *input) {
int length = readVarint(input, true);
char *string;
if (length == 0) return NULL;
string = SpineExtension::alloc<char>(length, __FILE__, __LINE__);
memcpy(string, input->cursor, length - 1);
input->cursor += length - 1;
string[length - 1] = '\0';
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;
float floatValue;
} intToFloat;
intToFloat.intValue = readInt(input);
return intToFloat.floatValue;
}
unsigned char SkeletonBinary::readByte(DataInput *input) {
return *input->cursor++;
}
signed char SkeletonBinary::readSByte(DataInput *input) {
return (signed char) readByte(input);
}
bool SkeletonBinary::readBoolean(DataInput *input) {
return readByte(input) != 0;
}
int SkeletonBinary::readInt(DataInput *input) {
int result = readByte(input);
result <<= 8;
result |= readByte(input);
result <<= 8;
result |= readByte(input);
result <<= 8;
result |= readByte(input);
return result;
}
void SkeletonBinary::readColor(DataInput *input, Color &color) {
color.r = readByte(input) / 255.0f;
color.g = readByte(input) / 255.0f;
color.b = readByte(input) / 255.0f;
color.a = readByte(input) / 255.0f;
}
int SkeletonBinary::readVarint(DataInput *input, bool optimizePositive) {
unsigned char b = readByte(input);
int value = b & 0x7F;
if (b & 0x80) {
b = readByte(input);
value |= (b & 0x7F) << 7;
if (b & 0x80) {
b = readByte(input);
value |= (b & 0x7F) << 14;
if (b & 0x80) {
b = readByte(input);
value |= (b & 0x7F) << 21;
if (b & 0x80) value |= (readByte(input) & 0x7F) << 28;
}
}
}
if (!optimizePositive) value = (((unsigned int) value >> 1) ^ -(value & 1));
return value;
}
Skin *SkeletonBinary::readSkin(DataInput *input, bool defaultSkin, SkeletonData *skeletonData, bool nonessential) {
Skin *skin;
int slotCount = 0;
if (defaultSkin) {
slotCount = readVarint(input, true);
if (slotCount == 0) return NULL;
skin = new(__FILE__, __LINE__) Skin("default");
} else {
skin = new(__FILE__, __LINE__) Skin(readStringRef(input, skeletonData));
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)]);
slotCount = readVarint(input, true);
}
for (int i = 0; i < slotCount; ++i) {
int slotIndex = readVarint(input, true);
for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
String name(readStringRef(input, skeletonData));
Attachment *attachment = readAttachment(input, skin, slotIndex, name, skeletonData, nonessential);
if (attachment) skin->setAttachment(slotIndex, String(name), attachment);
}
}
return skin;
}
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));
switch (type) {
case AttachmentType_Region: {
String path(readStringRef(input, skeletonData));
if (path.isEmpty()) path = name;
RegionAttachment *region = _attachmentLoader->newRegionAttachment(*skin, String(name), String(path));
if (region == NULL){
return NULL;
}
region->_path = path;
region->_rotation = readFloat(input);
region->_x = readFloat(input) * _scale;
region->_y = readFloat(input) * _scale;
region->_scaleX = readFloat(input);
region->_scaleY = readFloat(input);
region->_width = readFloat(input) * _scale;
region->_height = readFloat(input) * _scale;
readColor(input, region->getColor());
region->updateOffset();
_attachmentLoader->configureAttachment(region);
return region;
}
case AttachmentType_Boundingbox: {
int vertexCount = readVarint(input, true);
BoundingBoxAttachment *box = _attachmentLoader->newBoundingBoxAttachment(*skin, String(name));
readVertices(input, static_cast<VertexAttachment *>(box), vertexCount);
if (nonessential) {
/* Skip color. */
readInt(input);
}
_attachmentLoader->configureAttachment(box);
return box;
}
case AttachmentType_Mesh: {
int vertexCount;
MeshAttachment *mesh;
String path(readStringRef(input, skeletonData));
if (path.isEmpty()) path = name;
mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path));
mesh->_path = path;
readColor(input, mesh->getColor());
vertexCount = readVarint(input, true);
readFloatArray(input, vertexCount << 1, 1, mesh->getRegionUVs());
readShortArray(input, mesh->getTriangles());
readVertices(input, static_cast<VertexAttachment *>(mesh), vertexCount);
mesh->updateUVs();
mesh->_hullLength = readVarint(input, true) << 1;
if (nonessential) {
readShortArray(input, mesh->getEdges());
mesh->_width = readFloat(input) * _scale;
mesh->_height = readFloat(input) * _scale;
} else {
mesh->_width = 0;
mesh->_height = 0;
}
_attachmentLoader->configureAttachment(mesh);
return mesh;
}
case AttachmentType_Linkedmesh: {
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(readStringRef(input, skeletonData));
String parent(readStringRef(input, skeletonData));
bool inheritDeform = readBoolean(input);
if (nonessential) {
mesh->_width = readFloat(input) * _scale;
mesh->_height = readFloat(input) * _scale;
}
LinkedMesh *linkedMesh = new(__FILE__, __LINE__) LinkedMesh(mesh, String(skinName), slotIndex,
String(parent), inheritDeform);
_linkedMeshes.add(linkedMesh);
return mesh;
}
case AttachmentType_Path: {
PathAttachment *path = _attachmentLoader->newPathAttachment(*skin, String(name));
path->_closed = readBoolean(input);
path->_constantSpeed = readBoolean(input);
int vertexCount = readVarint(input, true);
readVertices(input, static_cast<VertexAttachment *>(path), vertexCount);
int lengthsLength = vertexCount / 3;
path->_lengths.setSize(lengthsLength, 0);
for (int i = 0; i < lengthsLength; ++i) {
path->_lengths[i] = readFloat(input) * _scale;
}
if (nonessential) {
/* Skip color. */
readInt(input);
}
_attachmentLoader->configureAttachment(path);
return path;
}
case AttachmentType_Point: {
PointAttachment *point = _attachmentLoader->newPointAttachment(*skin, String(name));
point->_rotation = readFloat(input);
point->_x = readFloat(input) * _scale;
point->_y = readFloat(input) * _scale;
if (nonessential) {
/* Skip color. */
readInt(input);
}
_attachmentLoader->configureAttachment(point);
return point;
}
case AttachmentType_Clipping: {
int endSlotIndex = readVarint(input, true);
int vertexCount = readVarint(input, true);
ClippingAttachment *clip = _attachmentLoader->newClippingAttachment(*skin, name);
readVertices(input, static_cast<VertexAttachment *>(clip), vertexCount);
clip->_endSlot = skeletonData->_slots[endSlotIndex];
if (nonessential) {
/* Skip color. */
readInt(input);
}
_attachmentLoader->configureAttachment(clip);
return clip;
}
}
return NULL;
}
void SkeletonBinary::readVertices(DataInput *input, VertexAttachment *attachment, int vertexCount) {
float scale = _scale;
int verticesLength = vertexCount << 1;
attachment->setWorldVerticesLength(vertexCount << 1);
if (!readBoolean(input)) {
readFloatArray(input, verticesLength, scale, attachment->getVertices());
return;
}
Vector<float> &vertices = attachment->getVertices();
Vector<size_t> &bones = attachment->getBones();
vertices.ensureCapacity(verticesLength * 3 * 3);
bones.ensureCapacity(verticesLength * 3);
for (int i = 0; i < vertexCount; ++i) {
int boneCount = readVarint(input, true);
bones.add(boneCount);
for (int ii = 0; ii < boneCount; ++ii) {
bones.add(readVarint(input, true));
vertices.add(readFloat(input) * scale);
vertices.add(readFloat(input) * scale);
vertices.add(readFloat(input));
}
}
}
void SkeletonBinary::readFloatArray(DataInput *input, int n, float scale, Vector<float> &array) {
array.setSize(n, 0);
int i;
if (scale == 1) {
for (i = 0; i < n; ++i) {
array[i] = readFloat(input);
}
} else {
for (i = 0; i < n; ++i) {
array[i] = readFloat(input) * scale;
}
}
}
void SkeletonBinary::readShortArray(DataInput *input, Vector<unsigned short> &array) {
int n = readVarint(input, true);
array.setSize(n, 0);
int i;
for (i = 0; i < n; ++i) {
array[i] = readByte(input) << 8;
array[i] |= readByte(input);
}
}
Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, SkeletonData *skeletonData) {
Vector<Timeline *> timelines;
float scale = _scale;
float duration = 0;
// Slot timelines.
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) {
unsigned char timelineType = readByte(input);
int frameCount = readVarint(input, true);
switch (timelineType) {
case SLOT_ATTACHMENT: {
AttachmentTimeline *timeline = new(__FILE__, __LINE__) AttachmentTimeline(frameCount);
timeline->_slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
String attachmentName(readStringRef(input, skeletonData));
timeline->setFrame(frameIndex, time, attachmentName);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[frameCount - 1]);
break;
}
case SLOT_COLOR: {
ColorTimeline *timeline = new(__FILE__, __LINE__) ColorTimeline(frameCount);
timeline->_slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
int color = readInt(input);
float r = ((color & 0xff000000) >> 24) / 255.0f;
float g = ((color & 0x00ff0000) >> 16) / 255.0f;
float b = ((color & 0x0000ff00) >> 8) / 255.0f;
float a = ((color & 0x000000ff)) / 255.0f;
timeline->setFrame(frameIndex, time, r, g, b, a);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * ColorTimeline::ENTRIES]);
break;
}
case SLOT_TWO_COLOR: {
TwoColorTimeline *timeline = new(__FILE__, __LINE__) TwoColorTimeline(frameCount);
timeline->_slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
int color = readInt(input);
float r = ((color & 0xff000000) >> 24) / 255.0f;
float g = ((color & 0x00ff0000) >> 16) / 255.0f;
float b = ((color & 0x0000ff00) >> 8) / 255.0f;
float a = ((color & 0x000000ff)) / 255.0f;
int color2 = readInt(input); // 0x00rrggbb
float r2 = ((color2 & 0x00ff0000) >> 16) / 255.0f;
float g2 = ((color2 & 0x0000ff00) >> 8) / 255.0f;
float b2 = ((color2 & 0x000000ff)) / 255.0f;
timeline->setFrame(frameIndex, time, r, g, b, a, r2, g2, b2);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * TwoColorTimeline::ENTRIES]);
break;
}
default: {
ContainerUtil::cleanUpVectorOfPointers(timelines);
setError("Invalid timeline type for a slot: ", skeletonData->_slots[slotIndex]->_name.buffer());
return NULL;
}
}
}
}
// Bone timelines.
for (int i = 0, n = readVarint(input, true); i < n; ++i) {
int boneIndex = readVarint(input, true);
for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
unsigned char timelineType = readByte(input);
int frameCount = readVarint(input, true);
switch (timelineType) {
case BONE_ROTATE: {
RotateTimeline *timeline = new(__FILE__, __LINE__) RotateTimeline(frameCount);
timeline->_boneIndex = boneIndex;
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
float degrees = readFloat(input);
timeline->setFrame(frameIndex, time, degrees);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * RotateTimeline::ENTRIES]);
break;
}
case BONE_TRANSLATE:
case BONE_SCALE:
case BONE_SHEAR: {
TranslateTimeline *timeline;
float timelineScale = 1;
if (timelineType == BONE_SCALE) {
timeline = new(__FILE__, __LINE__) ScaleTimeline(frameCount);
} else if (timelineType == BONE_SHEAR) {
timeline = new(__FILE__, __LINE__) ShearTimeline(frameCount);
} else {
timeline = new(__FILE__, __LINE__) TranslateTimeline(frameCount);
timelineScale = scale;
}
timeline->_boneIndex = boneIndex;
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
float x = readFloat(input) * timelineScale;
float y = readFloat(input) * timelineScale;
timeline->setFrame(frameIndex, time, x, y);
if (frameIndex < frameCount - 1) {
readCurve(input, frameIndex, timeline);
}
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * TranslateTimeline::ENTRIES]);
break;
}
default: {
ContainerUtil::cleanUpVectorOfPointers(timelines);
setError("Invalid timeline type for a bone: ", skeletonData->_bones[boneIndex]->_name.buffer());
return NULL;
}
}
}
}
// IK timelines.
for (int i = 0, n = readVarint(input, true); i < n; ++i) {
int index = readVarint(input, true);
int frameCount = readVarint(input, true);
IkConstraintTimeline *timeline = new(__FILE__, __LINE__) IkConstraintTimeline(frameCount);
timeline->_ikConstraintIndex = index;
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
float mix = readFloat(input);
float softness = readFloat(input) * _scale;
signed char bendDirection = readSByte(input);
bool compress = readBoolean(input);
bool stretch = readBoolean(input);
timeline->setFrame(frameIndex, time, mix, softness, bendDirection, compress, stretch);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * IkConstraintTimeline::ENTRIES]);
}
// Transform constraint timelines.
for (int i = 0, n = readVarint(input, true); i < n; ++i) {
int index = readVarint(input, true);
int frameCount = readVarint(input, true);
TransformConstraintTimeline *timeline = new(__FILE__, __LINE__) TransformConstraintTimeline(frameCount);
timeline->_transformConstraintIndex = index;
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
float rotateMix = readFloat(input);
float translateMix = readFloat(input);
float scaleMix = readFloat(input);
float shearMix = readFloat(input);
timeline->setFrame(frameIndex, time, rotateMix, translateMix, scaleMix, shearMix);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * TransformConstraintTimeline::ENTRIES]);
}
// Path constraint timelines.
for (int i = 0, n = readVarint(input, true); i < n; ++i) {
int index = readVarint(input, true);
PathConstraintData *data = skeletonData->_pathConstraints[index];
for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
int timelineType = readSByte(input);
int frameCount = readVarint(input, true);
switch (timelineType) {
case PATH_POSITION:
case PATH_SPACING: {
PathConstraintPositionTimeline *timeline;
float timelineScale = 1;
if (timelineType == PATH_SPACING) {
timeline = new(__FILE__, __LINE__) PathConstraintSpacingTimeline(frameCount);
if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) timelineScale = scale;
} else {
timeline = new(__FILE__, __LINE__) PathConstraintPositionTimeline(frameCount);
if (data->_positionMode == PositionMode_Fixed) timelineScale = scale;
}
timeline->_pathConstraintIndex = index;
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
float value = readFloat(input) * timelineScale;
timeline->setFrame(frameIndex, time, value);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * PathConstraintPositionTimeline::ENTRIES]);
break;
}
case PATH_MIX: {
PathConstraintMixTimeline *timeline = new(__FILE__, __LINE__) PathConstraintMixTimeline(frameCount);
timeline->_pathConstraintIndex = index;
for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
float rotateMix = readFloat(input);
float translateMix = readFloat(input);
timeline->setFrame(frameIndex, time, rotateMix, translateMix);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[(frameCount - 1) * PathConstraintMixTimeline::ENTRIES]);
break;
}
}
}
}
// Deform 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) {
int slotIndex = readVarint(input, true);
for (int iii = 0, nnn = readVarint(input, true); iii < nnn; iii++) {
const char *attachmentName = readStringRef(input, skeletonData);
Attachment *baseAttachment = skin->getAttachment(slotIndex, String(attachmentName));
if (!baseAttachment) {
ContainerUtil::cleanUpVectorOfPointers(timelines);
setError("Attachment not found: ", attachmentName);
return NULL;
}
VertexAttachment *attachment = static_cast<VertexAttachment *>(baseAttachment);
bool weighted = attachment->_bones.size() > 0;
Vector<float> &vertices = attachment->_vertices;
size_t deformLength = weighted ? vertices.size() / 3 * 2 : vertices.size();
size_t frameCount = (size_t)readVarint(input, true);
DeformTimeline *timeline = new(__FILE__, __LINE__) DeformTimeline(frameCount);
timeline->_slotIndex = slotIndex;
timeline->_attachment = attachment;
for (size_t frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
Vector<float> deform;
size_t end = (size_t)readVarint(input, true);
if (end == 0) {
if (weighted) {
deform.setSize(deformLength, 0);
for (size_t iiii = 0; iiii < deformLength; ++iiii)
deform[iiii] = 0;
} else {
deform.clearAndAddAll(vertices);
}
} else {
deform.setSize(deformLength, 0);
size_t start = (size_t)readVarint(input, true);
end += start;
if (scale == 1) {
for (size_t v = start; v < end; ++v)
deform[v] = readFloat(input);
} else {
for (size_t v = start; v < end; ++v)
deform[v] = readFloat(input) * scale;
}
if (!weighted) {
for (size_t v = 0, vn = deform.size(); v < vn; ++v)
deform[v] += vertices[v];
}
}
timeline->setFrame(frameIndex, time, deform);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[frameCount - 1]);
}
}
}
// Draw order timeline.
size_t drawOrderCount = (size_t)readVarint(input, true);
if (drawOrderCount > 0) {
DrawOrderTimeline *timeline = new(__FILE__, __LINE__) DrawOrderTimeline(drawOrderCount);
size_t slotCount = skeletonData->_slots.size();
for (size_t i = 0; i < drawOrderCount; ++i) {
float time = readFloat(input);
size_t offsetCount = (size_t)readVarint(input, true);
Vector<int> drawOrder;
drawOrder.setSize(slotCount, 0);
for (int ii = (int)slotCount - 1; ii >= 0; --ii)
drawOrder[ii] = -1;
Vector<int> unchanged;
unchanged.setSize(slotCount - offsetCount, 0);
size_t originalIndex = 0, unchangedIndex = 0;
for (size_t ii = 0; ii < offsetCount; ++ii) {
size_t slotIndex = (size_t)readVarint(input, true);
// Collect unchanged items.
while (originalIndex != slotIndex)
unchanged[unchangedIndex++] = originalIndex++;
// Set changed items.
size_t index = originalIndex;
drawOrder[index + (size_t)readVarint(input, true)] = originalIndex++;
}
// Collect remaining unchanged items.
while (originalIndex < slotCount) {
unchanged[unchangedIndex++] = originalIndex++;
}
// Fill in unchanged items.
for (int ii = (int)slotCount - 1; ii >= 0; --ii)
if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
timeline->setFrame(i, time, drawOrder);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[drawOrderCount - 1]);
}
// Event timeline.
int eventCount = readVarint(input, true);
if (eventCount > 0) {
EventTimeline *timeline = new(__FILE__, __LINE__) EventTimeline(eventCount);
for (int i = 0; i < eventCount; ++i) {
float time = readFloat(input);
EventData *eventData = skeletonData->_events[readVarint(input, true)];
Event *event = new(__FILE__, __LINE__) Event(time, *eventData);
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__);
if (!eventData->_audioPath.isEmpty()) {
event->_volume = readFloat(input);
event->_balance = readFloat(input);
}
timeline->setFrame(i, event);
}
timelines.add(timeline);
duration = MathUtil::max(duration, timeline->_frames[eventCount - 1]);
}
return new(__FILE__, __LINE__) Animation(String(name), timelines, duration);
}
void SkeletonBinary::readCurve(DataInput *input, int frameIndex, CurveTimeline *timeline) {
switch (readByte(input)) {
case CURVE_STEPPED: {
timeline->setStepped(frameIndex);
break;
}
case CURVE_BEZIER: {
float cx1 = readFloat(input);
float cy1 = readFloat(input);
float cx2 = readFloat(input);
float cy2 = readFloat(input);
timeline->setCurve(frameIndex, cx1, cy1, cx2, cy2);
break;
}
}
}