123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035 |
- /******************************************************************************
- * 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));
- 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;
- }
- }
- }
|