/****************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) #define strdup _strdup #endif using namespace spine; SkeletonJson::SkeletonJson(Atlas *atlas) : _attachmentLoader(new(__FILE__, __LINE__) AtlasAttachmentLoader(atlas)), _scale(1), _ownsLoader(true) {} SkeletonJson::SkeletonJson(AttachmentLoader *attachmentLoader) : _attachmentLoader(attachmentLoader), _scale(1), _ownsLoader(false) { assert(_attachmentLoader != NULL); } SkeletonJson::~SkeletonJson() { ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes); if (_ownsLoader) delete _attachmentLoader; } SkeletonData *SkeletonJson::readSkeletonDataFile(const String &path) { int length; SkeletonData *skeletonData; const char *json = SpineExtension::readFile(path, &length); if (length == 0 || !json) { setError(NULL, "Unable to read skeleton file: ", path); return NULL; } skeletonData = readSkeletonData(json); SpineExtension::free(json, __FILE__, __LINE__); return skeletonData; } SkeletonData *SkeletonJson::readSkeletonData(const char *json) { int i, ii; SkeletonData *skeletonData; Json *root, *skeleton, *bones, *boneMap, *ik, *transform, *path, *slots, *skins, *animations, *events; _error = ""; _linkedMeshes.clear(); root = new Json(json); if (!root) { setError(NULL, "Invalid skeleton JSON: ", Json::getError()); return NULL; } skeletonData = new(__FILE__, __LINE__) SkeletonData(); skeleton = Json::getItem(root, "skeleton"); if (skeleton) { skeletonData->_hash = Json::getString(skeleton, "hash", 0); skeletonData->_version = Json::getString(skeleton, "spine", 0); skeletonData->_x = Json::getFloat(skeleton, "x", 0); skeletonData->_y = Json::getFloat(skeleton, "y", 0); skeletonData->_width = Json::getFloat(skeleton, "width", 0); skeletonData->_height = Json::getFloat(skeleton, "height", 0); skeletonData->_fps = Json::getFloat(skeleton, "fps", 30); skeletonData->_audioPath = Json::getString(skeleton, "audio", 0); skeletonData->_imagesPath = Json::getString(skeleton, "images", 0); } /* Bones. */ bones = Json::getItem(root, "bones"); skeletonData->_bones.setSize(bones->_size, 0); int bonesCount = 0; for (boneMap = bones->_child, i = 0; boneMap; boneMap = boneMap->_next, ++i) { BoneData *data; const char *transformMode; BoneData *parent = 0; const char *parentName = Json::getString(boneMap, "parent", 0); if (parentName) { parent = skeletonData->findBone(parentName); if (!parent) { delete skeletonData; setError(root, "Parent bone not found: ", parentName); return NULL; } } data = new(__FILE__, __LINE__) BoneData(bonesCount, Json::getString(boneMap, "name", 0), parent); data->_length = Json::getFloat(boneMap, "length", 0) * _scale; data->_x = Json::getFloat(boneMap, "x", 0) * _scale; data->_y = Json::getFloat(boneMap, "y", 0) * _scale; data->_rotation = Json::getFloat(boneMap, "rotation", 0); data->_scaleX = Json::getFloat(boneMap, "scaleX", 1); data->_scaleY = Json::getFloat(boneMap, "scaleY", 1); data->_shearX = Json::getFloat(boneMap, "shearX", 0); data->_shearY = Json::getFloat(boneMap, "shearY", 0); transformMode = Json::getString(boneMap, "transform", "normal"); data->_transformMode = TransformMode_Normal; if (strcmp(transformMode, "normal") == 0) data->_transformMode = TransformMode_Normal; else if (strcmp(transformMode, "onlyTranslation") == 0) data->_transformMode = TransformMode_OnlyTranslation; else if (strcmp(transformMode, "noRotationOrReflection") == 0) data->_transformMode = TransformMode_NoRotationOrReflection; else if (strcmp(transformMode, "noScale") == 0) data->_transformMode = TransformMode_NoScale; else if (strcmp(transformMode, "noScaleOrReflection") == 0) data->_transformMode = TransformMode_NoScaleOrReflection; data->_skinRequired = Json::getBoolean(boneMap, "skin", false); skeletonData->_bones[i] = data; bonesCount++; } /* Slots. */ slots = Json::getItem(root, "slots"); if (slots) { Json *slotMap; skeletonData->_slots.ensureCapacity(slots->_size); skeletonData->_slots.setSize(slots->_size, 0); for (slotMap = slots->_child, i = 0; slotMap; slotMap = slotMap->_next, ++i) { SlotData *data; const char *color; const char *dark; Json *item; const char *boneName = Json::getString(slotMap, "bone", 0); BoneData *boneData = skeletonData->findBone(boneName); if (!boneData) { delete skeletonData; setError(root, "Slot bone not found: ", boneName); return NULL; } data = new(__FILE__, __LINE__) SlotData(i, Json::getString(slotMap, "name", 0), *boneData); color = Json::getString(slotMap, "color", 0); if (color) { Color &c = data->getColor(); c.r = toColor(color, 0); c.g = toColor(color, 1); c.b = toColor(color, 2); c.a = toColor(color, 3); } dark = Json::getString(slotMap, "dark", 0); if (dark) { Color &darkColor = data->getDarkColor(); darkColor.r = toColor(dark, 0); darkColor.g = toColor(dark, 1); darkColor.b = toColor(dark, 2); darkColor.a = 1; data->setHasDarkColor(true); } item = Json::getItem(slotMap, "attachment"); if (item) data->setAttachmentName(item->_valueString); item = Json::getItem(slotMap, "blend"); if (item) { if (strcmp(item->_valueString, "additive") == 0) data->_blendMode = BlendMode_Additive; else if (strcmp(item->_valueString, "multiply") == 0) data->_blendMode = BlendMode_Multiply; else if (strcmp(item->_valueString, "screen") == 0) data->_blendMode = BlendMode_Screen; } skeletonData->_slots[i] = data; } } /* IK constraints. */ ik = Json::getItem(root, "ik"); if (ik) { Json *constraintMap; skeletonData->_ikConstraints.ensureCapacity(ik->_size); skeletonData->_ikConstraints.setSize(ik->_size, 0); for (constraintMap = ik->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) { const char *targetName; IkConstraintData *data = new(__FILE__, __LINE__) IkConstraintData(Json::getString(constraintMap, "name", 0)); data->setOrder(Json::getInt(constraintMap, "order", 0)); data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false)); boneMap = Json::getItem(constraintMap, "bones"); data->_bones.ensureCapacity(boneMap->_size); data->_bones.setSize(boneMap->_size, 0); for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) { data->_bones[ii] = skeletonData->findBone(boneMap->_valueString); if (!data->_bones[ii]) { delete skeletonData; setError(root, "IK bone not found: ", boneMap->_valueString); return NULL; } } targetName = Json::getString(constraintMap, "target", 0); data->_target = skeletonData->findBone(targetName); if (!data->_target) { delete skeletonData; setError(root, "Target bone not found: ", targetName); return NULL; } data->_mix = Json::getFloat(constraintMap, "mix", 1); data->_softness = Json::getFloat(constraintMap, "softness", 0) * _scale; data->_bendDirection = Json::getInt(constraintMap, "bendPositive", 1) ? 1 : -1; data->_compress = Json::getInt(constraintMap, "compress", 0) ? true: false; data->_stretch = Json::getInt(constraintMap, "stretch", 0) ? true: false; data->_uniform = Json::getInt(constraintMap, "uniform", 0) ? true: false; skeletonData->_ikConstraints[i] = data; } } /* Transform constraints. */ transform = Json::getItem(root, "transform"); if (transform) { Json *constraintMap; skeletonData->_transformConstraints.ensureCapacity(transform->_size); skeletonData->_transformConstraints.setSize(transform->_size, 0); for (constraintMap = transform->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) { const char *name; TransformConstraintData *data = new(__FILE__, __LINE__) TransformConstraintData(Json::getString(constraintMap, "name", 0)); data->setOrder(Json::getInt(constraintMap, "order", 0)); data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false)); boneMap = Json::getItem(constraintMap, "bones"); data->_bones.ensureCapacity(boneMap->_size); data->_bones.setSize(boneMap->_size, 0); for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) { data->_bones[ii] = skeletonData->findBone(boneMap->_valueString); if (!data->_bones[ii]) { delete skeletonData; setError(root, "Transform bone not found: ", boneMap->_valueString); return NULL; } } name = Json::getString(constraintMap, "target", 0); data->_target = skeletonData->findBone(name); if (!data->_target) { delete skeletonData; setError(root, "Target bone not found: ", name); return NULL; } data->_local = Json::getInt(constraintMap, "local", 0) ? true : false; data->_relative = Json::getInt(constraintMap, "relative", 0) ? true : false; data->_offsetRotation = Json::getFloat(constraintMap, "rotation", 0); data->_offsetX = Json::getFloat(constraintMap, "x", 0) * _scale; data->_offsetY = Json::getFloat(constraintMap, "y", 0) * _scale; data->_offsetScaleX = Json::getFloat(constraintMap, "scaleX", 0); data->_offsetScaleY = Json::getFloat(constraintMap, "scaleY", 0); data->_offsetShearY = Json::getFloat(constraintMap, "shearY", 0); data->_rotateMix = Json::getFloat(constraintMap, "rotateMix", 1); data->_translateMix = Json::getFloat(constraintMap, "translateMix", 1); data->_scaleMix = Json::getFloat(constraintMap, "scaleMix", 1); data->_shearMix = Json::getFloat(constraintMap, "shearMix", 1); skeletonData->_transformConstraints[i] = data; } } /* Path constraints */ path = Json::getItem(root, "path"); if (path) { Json *constraintMap; skeletonData->_pathConstraints.ensureCapacity(path->_size); skeletonData->_pathConstraints.setSize(path->_size, 0); for (constraintMap = path->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) { const char *name; const char *item; PathConstraintData *data = new(__FILE__, __LINE__) PathConstraintData(Json::getString(constraintMap, "name", 0)); data->setOrder(Json::getInt(constraintMap, "order", 0)); data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false)); boneMap = Json::getItem(constraintMap, "bones"); data->_bones.ensureCapacity(boneMap->_size); data->_bones.setSize(boneMap->_size, 0); for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) { data->_bones[ii] = skeletonData->findBone(boneMap->_valueString); if (!data->_bones[ii]) { delete skeletonData; setError(root, "Path bone not found: ", boneMap->_valueString); return NULL; } } name = Json::getString(constraintMap, "target", 0); data->_target = skeletonData->findSlot(name); if (!data->_target) { delete skeletonData; setError(root, "Target slot not found: ", name); return NULL; } item = Json::getString(constraintMap, "positionMode", "percent"); if (strcmp(item, "fixed") == 0) { data->_positionMode = PositionMode_Fixed; } else if (strcmp(item, "percent") == 0) { data->_positionMode = PositionMode_Percent; } item = Json::getString(constraintMap, "spacingMode", "length"); if (strcmp(item, "length") == 0) data->_spacingMode = SpacingMode_Length; else if (strcmp(item, "fixed") == 0) data->_spacingMode = SpacingMode_Fixed; else if (strcmp(item, "percent") == 0) data->_spacingMode = SpacingMode_Percent; item = Json::getString(constraintMap, "rotateMode", "tangent"); if (strcmp(item, "tangent") == 0) data->_rotateMode = RotateMode_Tangent; else if (strcmp(item, "chain") == 0) data->_rotateMode = RotateMode_Chain; else if (strcmp(item, "chainScale") == 0) data->_rotateMode = RotateMode_ChainScale; data->_offsetRotation = Json::getFloat(constraintMap, "rotation", 0); data->_position = Json::getFloat(constraintMap, "position", 0); if (data->_positionMode == PositionMode_Fixed) data->_position *= _scale; data->_spacing = Json::getFloat(constraintMap, "spacing", 0); if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) data->_spacing *= _scale; data->_rotateMix = Json::getFloat(constraintMap, "rotateMix", 1); data->_translateMix = Json::getFloat(constraintMap, "translateMix", 1); skeletonData->_pathConstraints[i] = data; } } /* Skins. */ skins = Json::getItem(root, "skins"); if (skins) { Json *skinMap; skeletonData->_skins.ensureCapacity(skins->_size); skeletonData->_skins.setSize(skins->_size, 0); int skinsIndex = 0; for (skinMap = skins->_child, i = 0; skinMap; skinMap = skinMap->_next, ++i) { Json *attachmentsMap; Json *curves; Skin *skin = nullptr; auto skinName = Json::getString(skinMap, "name", ""); if (strlen(skinName) == 0) { skinName = skinMap->_name; } skin = new(__FILE__, __LINE__) Skin(skinName); Json *item = Json::getItem(skinMap, "bones"); if (item) { for (item = item->_child; item; item = item->_next) { BoneData* data = skeletonData->findBone(item->_valueString); if (!data) { delete skeletonData; setError(root, String("Skin bone not found: "), item->_valueString); return NULL; } skin->getBones().add(data); } } item = Json::getItem(skinMap, "ik"); if (item) { for (item = item->_child; item; item = item->_next) { IkConstraintData* data = skeletonData->findIkConstraint(item->_valueString); if (!data) { delete skeletonData; setError(root, String("Skin IK constraint not found: "), item->_valueString); return NULL; } skin->getConstraints().add(data); } } item = Json::getItem(skinMap, "transform"); if (item) { for (item = item->_child; item; item = item->_next) { TransformConstraintData* data = skeletonData->findTransformConstraint(item->_valueString); if (!data) { delete skeletonData; setError(root, String("Skin transform constraint not found: "), item->_valueString); return NULL; } skin->getConstraints().add(data); } } item = Json::getItem(skinMap, "path"); if (item) { for (item = item->_child; item; item = item->_next) { PathConstraintData* data = skeletonData->findPathConstraint(item->_valueString); if (!data) { delete skeletonData; setError(root, String("Skin path constraint not found: "), item->_valueString); return NULL; } skin->getConstraints().add(data); } } skeletonData->_skins[skinsIndex++] = skin; if (strcmp(skinName, "default") == 0) { skeletonData->_defaultSkin = skin; } auto attachmentsData = Json::getItem(skinMap, "attachments"); if (!attachmentsData) { attachmentsData = skinMap; } for (attachmentsMap = attachmentsData->_child; attachmentsMap; attachmentsMap = attachmentsMap->_next) { SlotData* slot = skeletonData->findSlot(attachmentsMap->_name); Json *attachmentMap; for (attachmentMap = attachmentsMap->_child; attachmentMap; attachmentMap = attachmentMap->_next) { Attachment *attachment = NULL; const char *skinAttachmentName = attachmentMap->_name; const char *attachmentName = Json::getString(attachmentMap, "name", skinAttachmentName); const char *attachmentPath = Json::getString(attachmentMap, "path", attachmentName); const char *color; Json *entry; const char *typeString = Json::getString(attachmentMap, "type", "region"); AttachmentType type; if (strcmp(typeString, "region") == 0) type = AttachmentType_Region; else if (strcmp(typeString, "mesh") == 0) type = AttachmentType_Mesh; else if (strcmp(typeString, "linkedmesh") == 0) type = AttachmentType_Linkedmesh; else if (strcmp(typeString, "boundingbox") == 0) type = AttachmentType_Boundingbox; else if (strcmp(typeString, "path") == 0) type = AttachmentType_Path; else if (strcmp(typeString, "clipping") == 0) type = AttachmentType_Clipping; else if (strcmp(typeString, "point") == 0) type = AttachmentType_Point; else { delete skeletonData; setError(root, "Unknown attachment type: ", typeString); return NULL; } switch (type) { case AttachmentType_Region: { attachment = _attachmentLoader->newRegionAttachment(*skin, attachmentName, attachmentPath); if (!attachment) { // delete skeletonData; // setError(root, "Error reading attachment: ", skinAttachmentName); // return NULL; continue; } RegionAttachment *region = static_cast(attachment); region->_path = attachmentPath; region->_x = Json::getFloat(attachmentMap, "x", 0) * _scale; region->_y = Json::getFloat(attachmentMap, "y", 0) * _scale; region->_scaleX = Json::getFloat(attachmentMap, "scaleX", 1); region->_scaleY = Json::getFloat(attachmentMap, "scaleY", 1); region->_rotation = Json::getFloat(attachmentMap, "rotation", 0); region->_width = Json::getFloat(attachmentMap, "width", 32) * _scale; region->_height = Json::getFloat(attachmentMap, "height", 32) * _scale; color = Json::getString(attachmentMap, "color", 0); if (color) { region->getColor().r = toColor(color, 0); region->getColor().g = toColor(color, 1); region->getColor().b = toColor(color, 2); region->getColor().a = toColor(color, 3); } region->updateOffset(); _attachmentLoader->configureAttachment(region); break; } case AttachmentType_Mesh: case AttachmentType_Linkedmesh: { attachment = _attachmentLoader->newMeshAttachment(*skin, attachmentName, attachmentPath); if (!attachment) { // delete skeletonData; // setError(root, "Error reading attachment: ", skinAttachmentName); // return NULL; continue; } MeshAttachment *mesh = static_cast(attachment); mesh->_path = attachmentPath; color = Json::getString(attachmentMap, "color", 0); if (color) { mesh->getColor().r = toColor(color, 0); mesh->getColor().g = toColor(color, 1); mesh->getColor().b = toColor(color, 2); mesh->getColor().a = toColor(color, 3); } mesh->_width = Json::getFloat(attachmentMap, "width", 32) * _scale; mesh->_height = Json::getFloat(attachmentMap, "height", 32) * _scale; entry = Json::getItem(attachmentMap, "parent"); if (!entry) { int verticesLength; entry = Json::getItem(attachmentMap, "triangles"); mesh->_triangles.ensureCapacity(entry->_size); mesh->_triangles.setSize(entry->_size, 0); for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii) mesh->_triangles[ii] = (unsigned short) entry->_valueInt; entry = Json::getItem(attachmentMap, "uvs"); verticesLength = entry->_size; mesh->_regionUVs.ensureCapacity(verticesLength); mesh->_regionUVs.setSize(verticesLength, 0); for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii) mesh->_regionUVs[ii] = entry->_valueFloat; readVertices(attachmentMap, mesh, verticesLength); mesh->updateUVs(); mesh->_hullLength = Json::getInt(attachmentMap, "hull", 0); entry = Json::getItem(attachmentMap, "edges"); if (entry) { mesh->_edges.ensureCapacity(entry->_size); mesh->_edges.setSize(entry->_size, 0); for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii) mesh->_edges[ii] = entry->_valueInt; } _attachmentLoader->configureAttachment(mesh); } else { bool inheritDeform = Json::getInt(attachmentMap, "deform", 1) ? true : false; LinkedMesh *linkedMesh = new(__FILE__, __LINE__) LinkedMesh(mesh, String(Json::getString(attachmentMap, "skin", 0)), slot->getIndex(), String(entry->_valueString), inheritDeform); _linkedMeshes.add(linkedMesh); } break; } case AttachmentType_Boundingbox: { attachment = _attachmentLoader->newBoundingBoxAttachment(*skin, attachmentName); BoundingBoxAttachment *box = static_cast(attachment); int vertexCount = Json::getInt(attachmentMap, "vertexCount", 0) << 1; readVertices(attachmentMap, box, vertexCount); _attachmentLoader->configureAttachment(attachment); break; } case AttachmentType_Path: { attachment = _attachmentLoader->newPathAttachment(*skin, attachmentName); PathAttachment *pathAttatchment = static_cast(attachment); int vertexCount = 0; pathAttatchment->_closed = Json::getInt(attachmentMap, "closed", 0) ? true : false; pathAttatchment->_constantSpeed = Json::getInt(attachmentMap, "constantSpeed", 1) ? true : false; vertexCount = Json::getInt(attachmentMap, "vertexCount", 0); readVertices(attachmentMap, pathAttatchment, vertexCount << 1); pathAttatchment->_lengths.ensureCapacity(vertexCount / 3); pathAttatchment->_lengths.setSize(vertexCount / 3, 0); curves = Json::getItem(attachmentMap, "lengths"); for (curves = curves->_child, ii = 0; curves; curves = curves->_next, ++ii) pathAttatchment->_lengths[ii] = curves->_valueFloat * _scale; _attachmentLoader->configureAttachment(attachment); break; } case AttachmentType_Point: { attachment = _attachmentLoader->newPointAttachment(*skin, attachmentName); PointAttachment *point = static_cast(attachment); point->_x = Json::getFloat(attachmentMap, "x", 0) * _scale; point->_y = Json::getFloat(attachmentMap, "y", 0) * _scale; point->_rotation = Json::getFloat(attachmentMap, "rotation", 0); _attachmentLoader->configureAttachment(attachment); break; } case AttachmentType_Clipping: { attachment = _attachmentLoader->newClippingAttachment(*skin, attachmentName); ClippingAttachment *clip = static_cast(attachment); int vertexCount = 0; const char *end = Json::getString(attachmentMap, "end", 0); if (end) clip->_endSlot = skeletonData->findSlot(end); vertexCount = Json::getInt(attachmentMap, "vertexCount", 0) << 1; readVertices(attachmentMap, clip, vertexCount); _attachmentLoader->configureAttachment(attachment); break; } } skin->setAttachment(slot->getIndex(), skinAttachmentName, attachment); } } } } /* Linked meshes. */ int n = _linkedMeshes.size(); for (i = 0; i < n; ++i) { LinkedMesh *linkedMesh = _linkedMeshes[i]; Skin *skin = linkedMesh->_skin.length() == 0 ? skeletonData->getDefaultSkin() : skeletonData->findSkin(linkedMesh->_skin); if (skin == NULL) { // delete skeletonData; // setError(root, "Skin not found: ", linkedMesh->_skin.buffer()); // return NULL; continue; } Attachment *parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent); if (parent == NULL) { // delete skeletonData; // setError(root, "Parent mesh not found: ", linkedMesh->_parent.buffer()); // return NULL; continue; } linkedMesh->_mesh->_deformAttachment = linkedMesh->_inheritDeform ? static_cast(parent) : linkedMesh->_mesh; linkedMesh->_mesh->setParentMesh(static_cast(parent)); linkedMesh->_mesh->updateUVs(); _attachmentLoader->configureAttachment(linkedMesh->_mesh); } ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes); _linkedMeshes.clear(); /* Events. */ events = Json::getItem(root, "events"); if (events) { Json *eventMap; skeletonData->_events.ensureCapacity(events->_size); skeletonData->_events.setSize(events->_size, 0); for (eventMap = events->_child, i = 0; eventMap; eventMap = eventMap->_next, ++i) { EventData *eventData = new(__FILE__, __LINE__) EventData(String(eventMap->_name)); eventData->_intValue = Json::getInt(eventMap, "int", 0); eventData->_floatValue = Json::getFloat(eventMap, "float", 0); const char *stringValue = Json::getString(eventMap, "string", 0); eventData->_stringValue = stringValue; const char *audioPath = Json::getString(eventMap, "audio", 0); eventData->_audioPath = audioPath; if (audioPath) { eventData->_volume = Json::getFloat(eventMap, "volume", 1); eventData->_balance = Json::getFloat(eventMap, "balance", 0); } skeletonData->_events[i] = eventData; } } /* Animations. */ animations = Json::getItem(root, "animations"); if (animations) { Json *animationMap; skeletonData->_animations.ensureCapacity(animations->_size); skeletonData->_animations.setSize(animations->_size, 0); int animationsIndex = 0; for (animationMap = animations->_child; animationMap; animationMap = animationMap->_next) { Animation *animation = readAnimation(animationMap, skeletonData); if (!animation) { // delete skeletonData; // delete root; // return NULL; continue; } skeletonData->_animations[animationsIndex++] = animation; } } delete root; return skeletonData; } float SkeletonJson::toColor(const char *value, size_t index) { char digits[3]; char *error; int color; if (index >= strlen(value) / 2) return -1; value += index * 2; digits[0] = *value; digits[1] = *(value + 1); digits[2] = '\0'; color = (int) strtoul(digits, &error, 16); if (*error != 0) return -1; return color / (float) 255; } void SkeletonJson::readCurve(Json *frame, CurveTimeline *timeline, size_t frameIndex) { Json *curve = Json::getItem(frame, "curve"); if (!curve) return; if (curve->_type == Json::JSON_STRING && strcmp(curve->_valueString, "stepped") == 0) timeline->setStepped(frameIndex); else { float c1 = Json::getFloat(frame, "curve", 0); float c2 = Json::getFloat(frame, "c2", 0); float c3 = Json::getFloat(frame, "c3", 1); float c4 = Json::getFloat(frame, "c4", 1); timeline->setCurve(frameIndex, c1, c2, c3, c4); } } Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { Vector timelines; float duration = 0; size_t frameIndex; Json *valueMap; int timelinesCount = 0; Json *bones = Json::getItem(root, "bones"); Json *slots = Json::getItem(root, "slots"); Json *ik = Json::getItem(root, "ik"); Json *transform = Json::getItem(root, "transform"); Json *paths = Json::getItem(root, "path"); if (paths == NULL) { paths = Json::getItem(root, "paths"); } Json *deform = Json::getItem(root, "deform"); Json *drawOrder = Json::getItem(root, "drawOrder"); Json *events = Json::getItem(root, "events"); Json *boneMap, *slotMap, *constraintMap; if (!drawOrder) drawOrder = Json::getItem(root, "draworder"); for (boneMap = bones ? bones->_child : NULL; boneMap; boneMap = boneMap->_next) timelinesCount += boneMap->_size; for (slotMap = slots ? slots->_child : NULL; slotMap; slotMap = slotMap->_next) timelinesCount += slotMap->_size; timelinesCount += ik ? ik->_size : 0; timelinesCount += transform ? transform->_size : 0; for (constraintMap = paths ? paths->_child : NULL; constraintMap; constraintMap = constraintMap->_next) timelinesCount += constraintMap->_size; for (constraintMap = deform ? deform->_child : NULL; constraintMap; constraintMap = constraintMap->_next) for (slotMap = constraintMap->_child; slotMap; slotMap = slotMap->_next) timelinesCount += slotMap->_size; if (drawOrder) ++timelinesCount; if (events) ++timelinesCount; /** Slot timelines. */ for (slotMap = slots ? slots->_child : 0; slotMap; slotMap = slotMap->_next) { Json *timelineMap; int slotIndex = skeletonData->findSlotIndex(slotMap->_name); if (slotIndex == -1) { ContainerUtil::cleanUpVectorOfPointers(timelines); setError(NULL, "Slot not found: ", slotMap->_name); return NULL; } for (timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) { if (strcmp(timelineMap->_name, "attachment") == 0) { AttachmentTimeline *timeline = new(__FILE__, __LINE__) AttachmentTimeline(timelineMap->_size); timeline->_slotIndex = slotIndex; for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { Json *name = Json::getItem(valueMap, "name"); String attachmentName = name->_type == Json::JSON_NULL ? "" : name->_valueString; timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), attachmentName); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[timelineMap->_size - 1]); } else if (strcmp(timelineMap->_name, "color") == 0) { ColorTimeline *timeline = new(__FILE__, __LINE__) ColorTimeline(timelineMap->_size); timeline->_slotIndex = slotIndex; for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { const char *s = Json::getString(valueMap, "color", 0); timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2), toColor(s, 3)); readCurve(valueMap, timeline, frameIndex); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * ColorTimeline::ENTRIES]); } else if (strcmp(timelineMap->_name, "twoColor") == 0) { TwoColorTimeline *timeline = new(__FILE__, __LINE__) TwoColorTimeline(timelineMap->_size); timeline->_slotIndex = slotIndex; for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { const char *s = Json::getString(valueMap, "light", 0); const char *ds = Json::getString(valueMap, "dark", 0); timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2), toColor(s, 3), toColor(ds, 0), toColor(ds, 1), toColor(ds, 2)); readCurve(valueMap, timeline, frameIndex); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * TwoColorTimeline::ENTRIES]); } else { ContainerUtil::cleanUpVectorOfPointers(timelines); setError(NULL, "Invalid timeline type for a slot: ", timelineMap->_name); return NULL; } } } /** Bone timelines. */ for (boneMap = bones ? bones->_child : 0; boneMap; boneMap = boneMap->_next) { Json *timelineMap; int boneIndex = skeletonData->findBoneIndex(boneMap->_name); if (boneIndex == -1) { ContainerUtil::cleanUpVectorOfPointers(timelines); setError(NULL, "Bone not found: ", boneMap->_name); return NULL; } for (timelineMap = boneMap->_child; timelineMap; timelineMap = timelineMap->_next) { if (strcmp(timelineMap->_name, "rotate") == 0) { RotateTimeline *timeline = new(__FILE__, __LINE__) RotateTimeline(timelineMap->_size); timeline->_boneIndex = boneIndex; for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "angle", 0)); readCurve(valueMap, timeline, frameIndex); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * RotateTimeline::ENTRIES]); } else { int isScale = strcmp(timelineMap->_name, "scale") == 0; int isTranslate = strcmp(timelineMap->_name, "translate") == 0; int isShear = strcmp(timelineMap->_name, "shear") == 0; if (isScale || isTranslate || isShear) { float timelineScale = isTranslate ? _scale : 1; float defaultValue = 0; TranslateTimeline *timeline = 0; if (isScale) { timeline = new(__FILE__, __LINE__) ScaleTimeline(timelineMap->_size); defaultValue = 1; } else if (isTranslate) { timeline = new(__FILE__, __LINE__) TranslateTimeline(timelineMap->_size); } else if (isShear) { timeline = new(__FILE__, __LINE__) ShearTimeline(timelineMap->_size); } timeline->_boneIndex = boneIndex; for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "x", defaultValue) * timelineScale, Json::getFloat(valueMap, "y", defaultValue) * timelineScale); readCurve(valueMap, timeline, frameIndex); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * TranslateTimeline::ENTRIES]); } else { ContainerUtil::cleanUpVectorOfPointers(timelines); setError(NULL, "Invalid timeline type for a bone: ", timelineMap->_name); return NULL; } } } } /** IK constraint timelines. */ for (constraintMap = ik ? ik->_child : 0; constraintMap; constraintMap = constraintMap->_next) { IkConstraintData *constraint = skeletonData->findIkConstraint(constraintMap->_name); IkConstraintTimeline *timeline = new(__FILE__, __LINE__) IkConstraintTimeline(constraintMap->_size); for (frameIndex = 0; frameIndex < skeletonData->_ikConstraints.size(); ++frameIndex) { if (constraint == skeletonData->_ikConstraints[frameIndex]) { timeline->_ikConstraintIndex = frameIndex; break; } } for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "mix", 1), Json::getFloat(valueMap, "softness", 0) * _scale, Json::getInt(valueMap, "bendPositive", 1) ? 1 : -1, Json::getInt(valueMap, "compress", 0) ? true : false, Json::getInt(valueMap, "stretch", 0) ? true : false); readCurve(valueMap, timeline, frameIndex); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[(constraintMap->_size - 1) * IkConstraintTimeline::ENTRIES]); } /** Transform constraint timelines. */ for (constraintMap = transform ? transform->_child : 0; constraintMap; constraintMap = constraintMap->_next) { TransformConstraintData *constraint = skeletonData->findTransformConstraint(constraintMap->_name); TransformConstraintTimeline *timeline = new(__FILE__, __LINE__) TransformConstraintTimeline(constraintMap->_size); for (frameIndex = 0; frameIndex < skeletonData->_transformConstraints.size(); ++frameIndex) { if (constraint == skeletonData->_transformConstraints[frameIndex]) { timeline->_transformConstraintIndex = frameIndex; break; } } for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "rotateMix", 1), Json::getFloat(valueMap, "translateMix", 1), Json::getFloat(valueMap, "scaleMix", 1), Json::getFloat(valueMap, "shearMix", 1)); readCurve(valueMap, timeline, frameIndex); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[(constraintMap->_size - 1) * TransformConstraintTimeline::ENTRIES]); } /** Path constraint timelines. */ for (constraintMap = paths ? paths->_child : 0; constraintMap; constraintMap = constraintMap->_next) { size_t constraintIndex = 0, i; Json *timelineMap; PathConstraintData *data = skeletonData->findPathConstraint(constraintMap->_name); if (!data) { ContainerUtil::cleanUpVectorOfPointers(timelines); setError(NULL, "Path constraint not found: ", constraintMap->_name); return NULL; } for (i = 0; i < skeletonData->_pathConstraints.size(); i++) { if (skeletonData->_pathConstraints[i] == data) { constraintIndex = i; break; } } for (timelineMap = constraintMap->_child; timelineMap; timelineMap = timelineMap->_next) { const char *timelineName = timelineMap->_name; if (strcmp(timelineName, "position") == 0 || strcmp(timelineName, "spacing") == 0) { PathConstraintPositionTimeline *timeline; float timelineScale = 1; if (strcmp(timelineName, "spacing") == 0) { timeline = new(__FILE__, __LINE__) PathConstraintSpacingTimeline(timelineMap->_size); if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) { timelineScale = _scale; } } else { timeline = new(__FILE__, __LINE__) PathConstraintPositionTimeline(timelineMap->_size); if (data->_positionMode == PositionMode_Fixed) { timelineScale = _scale; } } timeline->_pathConstraintIndex = constraintIndex; for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, timelineName, 0) * timelineScale); readCurve(valueMap, timeline, frameIndex); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * PathConstraintPositionTimeline::ENTRIES]); } else if (strcmp(timelineName, "mix") == 0) { PathConstraintMixTimeline *timeline = new(__FILE__, __LINE__) PathConstraintMixTimeline(timelineMap->_size); timeline->_pathConstraintIndex = constraintIndex; for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "rotateMix", 1), Json::getFloat(valueMap, "translateMix", 1)); readCurve(valueMap, timeline, frameIndex); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * PathConstraintMixTimeline::ENTRIES]); } } } /** Deform timelines. */ for (constraintMap = deform ? deform->_child : NULL; constraintMap; constraintMap = constraintMap->_next) { Skin *skin = skeletonData->findSkin(constraintMap->_name); for (slotMap = constraintMap->_child; slotMap; slotMap = slotMap->_next) { int slotIndex = skeletonData->findSlotIndex(slotMap->_name); Json *timelineMap; for (timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) { DeformTimeline *timeline; int weighted, deformLength; Attachment *baseAttachment = skin->getAttachment(slotIndex, timelineMap->_name); if (!baseAttachment) { ContainerUtil::cleanUpVectorOfPointers(timelines); // setError(NULL, "Attachment not found: ", timelineMap->_name); // return NULL; continue; } VertexAttachment *attachment = static_cast(baseAttachment); weighted = attachment->_bones.size() != 0; Vector &verts = attachment->_vertices; deformLength = weighted ? verts.size() / 3 * 2 : verts.size(); timeline = new(__FILE__, __LINE__) DeformTimeline(timelineMap->_size); timeline->_slotIndex = slotIndex; timeline->_attachment = attachment; for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { Json *vertices = Json::getItem(valueMap, "vertices"); Vector deformed; if (!vertices) { if (weighted) { deformed.setSize(deformLength, 0); } else { deformed.clearAndAddAll(attachment->_vertices); } } else { int v, start = Json::getInt(valueMap, "offset", 0); Json *vertex; deformed.setSize(deformLength, 0); if (_scale == 1) { for (vertex = vertices->_child, v = start; vertex; vertex = vertex->_next, ++v) { deformed[v] = vertex->_valueFloat; } } else { for (vertex = vertices->_child, v = start; vertex; vertex = vertex->_next, ++v) { deformed[v] = vertex->_valueFloat * _scale; } } if (!weighted) { Vector &verticesAttachment = attachment->_vertices; for (v = 0; v < deformLength; ++v) { deformed[v] += verticesAttachment[v]; } } } timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), deformed); readCurve(valueMap, timeline, frameIndex); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[timelineMap->_size - 1]); } } } /** Draw order timeline. */ if (drawOrder) { DrawOrderTimeline *timeline = new(__FILE__, __LINE__) DrawOrderTimeline(drawOrder->_size); for (valueMap = drawOrder->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { int ii; Vector drawOrder2; Json *offsets = Json::getItem(valueMap, "offsets"); if (offsets) { Json *offsetMap; Vector unchanged; unchanged.ensureCapacity(skeletonData->_slots.size() - offsets->_size); unchanged.setSize(skeletonData->_slots.size() - offsets->_size, 0); size_t originalIndex = 0, unchangedIndex = 0; drawOrder2.ensureCapacity(skeletonData->_slots.size()); drawOrder2.setSize(skeletonData->_slots.size(), 0); for (ii = (int)skeletonData->_slots.size() - 1; ii >= 0; --ii) drawOrder2[ii] = -1; for (offsetMap = offsets->_child; offsetMap; offsetMap = offsetMap->_next) { int slotIndex = skeletonData->findSlotIndex(Json::getString(offsetMap, "slot", 0)); if (slotIndex == -1) { ContainerUtil::cleanUpVectorOfPointers(timelines); setError(NULL, "Slot not found: ", Json::getString(offsetMap, "slot", 0)); return NULL; } /* Collect unchanged items. */ while (originalIndex != (size_t)slotIndex) unchanged[unchangedIndex++] = originalIndex++; /* Set changed items. */ drawOrder2[originalIndex + Json::getInt(offsetMap, "offset", 0)] = originalIndex; originalIndex++; } /* Collect remaining unchanged items. */ while (originalIndex < skeletonData->_slots.size()) unchanged[unchangedIndex++] = originalIndex++; /* Fill in unchanged items. */ for (ii = (int)skeletonData->_slots.size() - 1; ii >= 0; ii--) if (drawOrder2[ii] == -1) drawOrder2[ii] = unchanged[--unchangedIndex]; } timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), drawOrder2); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[drawOrder->_size - 1]); } /** Event timeline. */ if (events) { EventTimeline *timeline = new(__FILE__, __LINE__) EventTimeline(events->_size); for (valueMap = events->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { Event *event; EventData *eventData = skeletonData->findEvent(Json::getString(valueMap, "name", 0)); if (!eventData) { ContainerUtil::cleanUpVectorOfPointers(timelines); setError(NULL, "Event not found: ", Json::getString(valueMap, "name", 0)); return NULL; } event = new(__FILE__, __LINE__) Event(Json::getFloat(valueMap, "time", 0), *eventData); event->_intValue = Json::getInt(valueMap, "int", eventData->_intValue); event->_floatValue = Json::getFloat(valueMap, "float", eventData->_floatValue); event->_stringValue = Json::getString(valueMap, "string", eventData->_stringValue.buffer()); if (!eventData->_audioPath.isEmpty()) { event->_volume = Json::getFloat(valueMap, "volume", 1); event->_balance = Json::getFloat(valueMap, "balance", 0); } timeline->setFrame(frameIndex, event); } timelines.add(timeline); timelinesCount++; duration = MathUtil::max(duration, timeline->_frames[events->_size - 1]); } return new(__FILE__, __LINE__) Animation(String(root->_name), timelines, duration); } void SkeletonJson::readVertices(Json *attachmentMap, VertexAttachment *attachment, size_t verticesLength) { Json *entry; size_t i, n, nn, entrySize; Vector vertices; attachment->setWorldVerticesLength(verticesLength); entry = Json::getItem(attachmentMap, "vertices"); entrySize = entry->_size; vertices.ensureCapacity(entrySize); vertices.setSize(entrySize, 0); for (entry = entry->_child, i = 0; entry; entry = entry->_next, ++i) vertices[i] = entry->_valueFloat; if (verticesLength == entrySize) { if (_scale != 1) { for (i = 0; i < entrySize; ++i) vertices[i] *= _scale; } attachment->getVertices().clearAndAddAll(vertices); return; } Vertices bonesAndWeights; bonesAndWeights._bones.ensureCapacity(verticesLength * 3); bonesAndWeights._vertices.ensureCapacity(verticesLength * 3 * 3); for (i = 0, n = entrySize; i < n;) { int boneCount = (int) vertices[i++]; bonesAndWeights._bones.add(boneCount); for (nn = i + boneCount * 4; i < nn; i += 4) { bonesAndWeights._bones.add((int) vertices[i]); bonesAndWeights._vertices.add(vertices[i + 1] * _scale); bonesAndWeights._vertices.add(vertices[i + 2] * _scale); bonesAndWeights._vertices.add(vertices[i + 3]); } } attachment->getVertices().clearAndAddAll(bonesAndWeights._vertices); attachment->getBones().clearAndAddAll(bonesAndWeights._bones); } void SkeletonJson::setError(Json *root, const String &value1, const String &value2) { _error = String(value1).append(value2); delete root; }