mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 22:34:53 +08:00
1470 lines
64 KiB
C++
1470 lines
64 KiB
C++
/******************************************************************************
|
|
* Spine Runtimes License Agreement
|
|
* Last updated April 5, 2025. Replaces all prior versions.
|
|
*
|
|
* Copyright (c) 2013-2025, 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.
|
|
*****************************************************************************/
|
|
|
|
#include <spine/SkeletonJson.h>
|
|
|
|
#include <spine/Atlas.h>
|
|
#include <spine/AtlasAttachmentLoader.h>
|
|
#include <spine/CurveTimeline.h>
|
|
#include <spine/Json.h>
|
|
#include <spine/LinkedMesh.h>
|
|
#include <spine/SkeletonData.h>
|
|
#include <spine/Attachment.h>
|
|
#include <spine/Skin.h>
|
|
#include <spine/VertexAttachment.h>
|
|
|
|
#include <spine/AttachmentTimeline.h>
|
|
#include <spine/AttachmentType.h>
|
|
#include <spine/BoneData.h>
|
|
#include <spine/BoundingBoxAttachment.h>
|
|
#include <spine/ClippingAttachment.h>
|
|
#include <spine/ColorTimeline.h>
|
|
#include <spine/ArrayUtils.h>
|
|
#include <spine/DeformTimeline.h>
|
|
#include <spine/DrawOrderTimeline.h>
|
|
#include <spine/Event.h>
|
|
#include <spine/EventData.h>
|
|
#include <spine/EventTimeline.h>
|
|
#include <spine/IkConstraintData.h>
|
|
#include <spine/IkConstraintTimeline.h>
|
|
#include <spine/Inherit.h>
|
|
#include <spine/InheritTimeline.h>
|
|
#include <spine/MeshAttachment.h>
|
|
#include <spine/PathAttachment.h>
|
|
#include <spine/PathConstraintData.h>
|
|
#include <spine/PathConstraintMixTimeline.h>
|
|
#include <spine/PathConstraintPositionTimeline.h>
|
|
#include <spine/PathConstraintSpacingTimeline.h>
|
|
#include <spine/PhysicsConstraintData.h>
|
|
#include <spine/PhysicsConstraintTimeline.h>
|
|
#include <spine/PointAttachment.h>
|
|
#include <spine/RegionAttachment.h>
|
|
#include <spine/RotateTimeline.h>
|
|
#include <spine/ScaleTimeline.h>
|
|
#include <spine/ShearTimeline.h>
|
|
#include <spine/Skin.h>
|
|
#include <spine/SlotData.h>
|
|
#include <spine/TransformConstraintData.h>
|
|
#include <spine/TransformConstraintTimeline.h>
|
|
#include <spine/TranslateTimeline.h>
|
|
#include <spine/SequenceTimeline.h>
|
|
#include <spine/Version.h>
|
|
#include <spine/SliderData.h>
|
|
#include <spine/SliderPose.h>
|
|
#include <spine/SliderTimeline.h>
|
|
#include <spine/BoneLocal.h>
|
|
#include <spine/PathConstraintPose.h>
|
|
#include <spine/SliderMixTimeline.h>
|
|
|
|
using namespace spine;
|
|
|
|
class SP_API Vertices : public SpineObject {
|
|
public:
|
|
Array<int> _bones;
|
|
Array<float> _weights;
|
|
};
|
|
|
|
#define SKELETON_JSON_ERROR(root, message, value) \
|
|
do { \
|
|
delete skeletonData; \
|
|
setError(root, message, value); \
|
|
return NULL; \
|
|
} while (0)
|
|
|
|
static FromProperty *fromProperty(const char *type) {
|
|
if (strcmp(type, "rotate") == 0)
|
|
return new FromRotate();
|
|
else if (strcmp(type, "x") == 0)
|
|
return new FromX();
|
|
else if (strcmp(type, "y") == 0)
|
|
return new FromY();
|
|
else if (strcmp(type, "scaleX") == 0)
|
|
return new FromScaleX();
|
|
else if (strcmp(type, "scaleY") == 0)
|
|
return new FromScaleY();
|
|
else if (strcmp(type, "shearY") == 0)
|
|
return new FromShearY();
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static float propertyScale(const char *type, float scale) {
|
|
if (strcmp(type, "x") == 0 || strcmp(type, "y") == 0)
|
|
return scale;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
SkeletonJson::SkeletonJson(Atlas &atlas) : _attachmentLoader(new(__FILE__, __LINE__) AtlasAttachmentLoader(atlas)), _scale(1), _ownsLoader(true) {
|
|
}
|
|
|
|
SkeletonJson::SkeletonJson(AttachmentLoader &attachmentLoader, bool ownsLoader)
|
|
: _attachmentLoader(&attachmentLoader), _scale(1), _ownsLoader(ownsLoader) {
|
|
}
|
|
|
|
SkeletonJson::~SkeletonJson() {
|
|
ArrayUtils::deleteElements(_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__);
|
|
|
|
if (skeletonData) {
|
|
// Extract filename without extension from path
|
|
int lastSlash = path.lastIndexOf('/');
|
|
int lastBackslash = path.lastIndexOf('\\');
|
|
int start = 0;
|
|
if (lastSlash != -1) start = lastSlash + 1;
|
|
if (lastBackslash != -1 && lastBackslash > start) start = lastBackslash + 1;
|
|
|
|
int lastDot = path.lastIndexOf('.');
|
|
if (lastDot != -1 && lastDot > start) {
|
|
skeletonData->_name = path.substring(start, lastDot - start);
|
|
} else {
|
|
skeletonData->_name = path.substring(start);
|
|
}
|
|
}
|
|
|
|
return skeletonData;
|
|
}
|
|
|
|
SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
|
|
SkeletonData *skeletonData;
|
|
Json *root, *skeleton, *bones, *constraints, *slots, *skins, *animations, *events;
|
|
|
|
_error = "";
|
|
_linkedMeshes.clear();
|
|
|
|
root = new (__FILE__, __LINE__) 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);
|
|
if (!skeletonData->_version.startsWith(SPINE_VERSION_STRING)) {
|
|
char errorMsg[255];
|
|
snprintf(errorMsg, 255, "Skeleton version %s does not match runtime version %s", skeletonData->_version.buffer(), SPINE_VERSION_STRING);
|
|
SKELETON_JSON_ERROR(NULL, errorMsg, "");
|
|
}
|
|
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->_referenceScale = Json::getFloat(skeleton, "referenceScale", 100) * _scale;
|
|
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");
|
|
if (bones) {
|
|
skeletonData->_bones.setSize(bones->_size, 0);
|
|
Json *boneMap = bones->_child;
|
|
for (int bonesCount = 0; boneMap; boneMap = boneMap->_next, ++bonesCount) {
|
|
BoneData *parent = 0;
|
|
const char *parentName = Json::getString(boneMap, "parent", 0);
|
|
if (parentName) {
|
|
parent = skeletonData->findBone(parentName);
|
|
if (!parent) SKELETON_JSON_ERROR(root, "Parent bone not found: ", parentName);
|
|
}
|
|
BoneData *data = new (__FILE__, __LINE__) BoneData(bonesCount, Json::getString(boneMap, "name", 0), parent);
|
|
data->_length = Json::getFloat(boneMap, "length", 0) * _scale;
|
|
BoneLocal &setup = data->_setup;
|
|
setup._x = Json::getFloat(boneMap, "x", 0) * _scale;
|
|
setup._y = Json::getFloat(boneMap, "y", 0) * _scale;
|
|
setup._rotation = Json::getFloat(boneMap, "rotation", 0);
|
|
setup._scaleX = Json::getFloat(boneMap, "scaleX", 1);
|
|
setup._scaleY = Json::getFloat(boneMap, "scaleY", 1);
|
|
setup._shearX = Json::getFloat(boneMap, "shearX", 0);
|
|
setup._shearY = Json::getFloat(boneMap, "shearY", 0);
|
|
setup._inherit = Inherit_valueOf(Json::getString(boneMap, "inherit", "normal"));
|
|
data->_skinRequired = Json::getBoolean(boneMap, "skin", false);
|
|
|
|
const char *color = Json::getString(boneMap, "color", NULL);
|
|
if (color) Color::valueOf(color, data->getColor());
|
|
|
|
data->_icon = Json::getString(boneMap, "icon", "");
|
|
data->_visible = Json::getBoolean(boneMap, "visible", true);
|
|
|
|
skeletonData->_bones[bonesCount] = data;
|
|
}
|
|
}
|
|
|
|
/* Slots. */
|
|
slots = Json::getItem(root, "slots");
|
|
if (slots) {
|
|
skeletonData->_slots.setSize(slots->_size, 0);
|
|
Json *slotMap = slots->_child;
|
|
for (int slotCount = 0; slotMap; slotMap = slotMap->_next, ++slotCount) {
|
|
String slotName = String(Json::getString(slotMap, "name", 0));
|
|
const char *boneName = Json::getString(slotMap, "bone", 0);
|
|
BoneData *boneData = skeletonData->findBone(boneName);
|
|
if (!boneData) SKELETON_JSON_ERROR(root, "Slot bone not found: ", boneName);
|
|
|
|
SlotData *data = new (__FILE__, __LINE__) SlotData(slotCount, slotName, *boneData);
|
|
|
|
const char *color = Json::getString(slotMap, "color", 0);
|
|
if (color) Color::valueOf(color, data->_setup.getColor());
|
|
|
|
const char *dark = Json::getString(slotMap, "dark", 0);
|
|
if (dark) {
|
|
data->_setup._darkColor = Color::valueOf(dark);
|
|
data->_setup._hasDarkColor = true;
|
|
}
|
|
|
|
data->setAttachmentName(Json::getString(slotMap, "attachment", NULL));
|
|
data->_blendMode = BlendMode_valueOf(Json::getString(slotMap, "blend", "normal"));
|
|
data->_visible = Json::getBoolean(slotMap, "visible", true);
|
|
skeletonData->_slots[slotCount] = data;
|
|
}
|
|
}
|
|
|
|
/* Constraints. */
|
|
constraints = Json::getItem(root, "constraints");
|
|
if (constraints) {
|
|
for (Json *constraintMap = constraints->_child; constraintMap; constraintMap = constraintMap->_next) {
|
|
const char *name = Json::getString(constraintMap, "name", 0);
|
|
bool skinRequired = Json::getBoolean(constraintMap, "skin", false);
|
|
const char *type = Json::getString(constraintMap, "type", 0);
|
|
if (strcmp(type, "ik") == 0) {
|
|
IkConstraintData *data = new (__FILE__, __LINE__) IkConstraintData(name);
|
|
data->setSkinRequired(skinRequired);
|
|
|
|
Json *entry = Json::getItem(constraintMap, "bones");
|
|
data->_bones.setSize(entry->_size, 0);
|
|
entry = entry->_child;
|
|
for (int boneCount = 0; entry; entry = entry->_next, ++boneCount) {
|
|
data->_bones[boneCount] = skeletonData->findBone(entry->_valueString);
|
|
if (!data->_bones[boneCount]) SKELETON_JSON_ERROR(root, "IK bone not found: ", entry->_valueString);
|
|
}
|
|
|
|
const char *targetName = Json::getString(constraintMap, "target", 0);
|
|
data->_target = skeletonData->findBone(targetName);
|
|
if (!data->_target) SKELETON_JSON_ERROR(root, "IK target bone not found: ", targetName);
|
|
|
|
data->_uniform = Json::getBoolean(constraintMap, "uniform", false);
|
|
IkConstraintPose &setup = data->_setup;
|
|
setup._mix = Json::getFloat(constraintMap, "mix", 1);
|
|
setup._softness = Json::getFloat(constraintMap, "softness", 0) * _scale;
|
|
setup._bendDirection = Json::getBoolean(constraintMap, "bendPositive", true) ? 1 : -1;
|
|
setup._compress = Json::getBoolean(constraintMap, "compress", false);
|
|
setup._stretch = Json::getBoolean(constraintMap, "stretch", false);
|
|
|
|
skeletonData->_constraints.add(data);
|
|
} else if (strcmp(type, "transform") == 0) {
|
|
TransformConstraintData *data = new (__FILE__, __LINE__) TransformConstraintData(name);
|
|
data->setSkinRequired(skinRequired);
|
|
|
|
Json *entry = Json::getItem(constraintMap, "bones");
|
|
data->_bones.setSize(entry->_size, 0);
|
|
entry = entry->_child;
|
|
for (int boneCount = 0; entry; entry = entry->_next, ++boneCount) {
|
|
data->_bones[boneCount] = skeletonData->findBone(entry->_valueString);
|
|
if (!data->_bones[boneCount]) SKELETON_JSON_ERROR(root, "Transform constraint bone not found: ", entry->_valueString);
|
|
}
|
|
|
|
const char *sourceName = Json::getString(constraintMap, "source", 0);
|
|
data->_source = skeletonData->findBone(sourceName);
|
|
if (!data->_source) SKELETON_JSON_ERROR(root, "Transform constraint source bone not found: ", sourceName);
|
|
|
|
data->_localSource = Json::getBoolean(constraintMap, "localSource", false);
|
|
data->_localTarget = Json::getBoolean(constraintMap, "localTarget", false);
|
|
data->_additive = Json::getBoolean(constraintMap, "additive", false);
|
|
data->_clamp = Json::getBoolean(constraintMap, "clamp", false);
|
|
|
|
bool rotate = false, x = false, y = false, scaleX = false, scaleY = false, shearY = false;
|
|
Json *properties = Json::getItem(constraintMap, "properties");
|
|
if (properties) {
|
|
for (Json *fromEntry = properties->_child; fromEntry; fromEntry = fromEntry->_next) {
|
|
FromProperty *from = fromProperty(fromEntry->_name);
|
|
if (!from) SKELETON_JSON_ERROR(root, "Invalid transform constraint from property: ", fromEntry->_name);
|
|
float fromScale = propertyScale(fromEntry->_name, _scale);
|
|
from->_offset = Json::getFloat(fromEntry, "offset", 0) * fromScale;
|
|
Json *toEntry = Json::getItem(fromEntry, "to");
|
|
if (toEntry) {
|
|
for (toEntry = toEntry->_child; toEntry; toEntry = toEntry->_next) {
|
|
ToProperty *to = NULL;
|
|
float toScale = 1;
|
|
|
|
if (strcmp(toEntry->_name, "rotate") == 0) {
|
|
rotate = true;
|
|
to = new ToRotate();
|
|
} else if (strcmp(toEntry->_name, "x") == 0) {
|
|
x = true;
|
|
to = new ToX();
|
|
toScale = _scale;
|
|
} else if (strcmp(toEntry->_name, "y") == 0) {
|
|
y = true;
|
|
to = new ToY();
|
|
toScale = _scale;
|
|
} else if (strcmp(toEntry->_name, "scaleX") == 0) {
|
|
scaleX = true;
|
|
to = new ToScaleX();
|
|
} else if (strcmp(toEntry->_name, "scaleY") == 0) {
|
|
scaleY = true;
|
|
to = new ToScaleY();
|
|
} else if (strcmp(toEntry->_name, "shearY") == 0) {
|
|
shearY = true;
|
|
to = new ToShearY();
|
|
} else {
|
|
SKELETON_JSON_ERROR(root, "Invalid transform constraint to property: ", toEntry->_name);
|
|
}
|
|
|
|
to->_offset = Json::getFloat(toEntry, "offset", 0) * toScale;
|
|
to->_max = Json::getFloat(toEntry, "max", 1) * toScale;
|
|
to->_scale = Json::getFloat(toEntry, "scale", 1) * toScale / fromScale;
|
|
from->_to.add(to);
|
|
}
|
|
}
|
|
|
|
if (from->_to.size() > 0)
|
|
data->_properties.add(from);
|
|
else
|
|
delete from;
|
|
}
|
|
}
|
|
|
|
data->_offsets[TransformConstraintData::ROTATION] = Json::getFloat(constraintMap, "rotation", 0);
|
|
data->_offsets[TransformConstraintData::X] = Json::getFloat(constraintMap, "x", 0) * _scale;
|
|
data->_offsets[TransformConstraintData::Y] = Json::getFloat(constraintMap, "y", 0) * _scale;
|
|
data->_offsets[TransformConstraintData::SCALEX] = Json::getFloat(constraintMap, "scaleX", 0);
|
|
data->_offsets[TransformConstraintData::SCALEY] = Json::getFloat(constraintMap, "scaleY", 0);
|
|
data->_offsets[TransformConstraintData::SHEARY] = Json::getFloat(constraintMap, "shearY", 0);
|
|
|
|
TransformConstraintPose &setup = data->_setup;
|
|
if (rotate) setup._mixRotate = Json::getFloat(constraintMap, "mixRotate", 1);
|
|
if (x) setup._mixX = Json::getFloat(constraintMap, "mixX", 1);
|
|
if (y) setup._mixY = Json::getFloat(constraintMap, "mixY", setup._mixX);
|
|
if (scaleX) setup._mixScaleX = Json::getFloat(constraintMap, "mixScaleX", 1);
|
|
if (scaleY) setup._mixScaleY = Json::getFloat(constraintMap, "mixScaleY", setup._mixScaleX);
|
|
if (shearY) setup._mixShearY = Json::getFloat(constraintMap, "mixShearY", 1);
|
|
|
|
skeletonData->_constraints.add(data);
|
|
} else if (strcmp(type, "path") == 0) {
|
|
PathConstraintData *data = new (__FILE__, __LINE__) PathConstraintData(name);
|
|
data->setSkinRequired(skinRequired);
|
|
|
|
Json *entry = Json::getItem(constraintMap, "bones");
|
|
data->_bones.setSize(entry->_size, 0);
|
|
entry = entry->_child;
|
|
for (int boneCount = 0; entry; entry = entry->_next, ++boneCount) {
|
|
data->_bones[boneCount] = skeletonData->findBone(entry->_valueString);
|
|
if (!data->_bones[boneCount]) SKELETON_JSON_ERROR(root, "Path bone not found: ", entry->_valueString);
|
|
}
|
|
|
|
const char *slotName = Json::getString(constraintMap, "slot", 0);
|
|
data->_slot = skeletonData->findSlot(slotName);
|
|
if (!data->_slot) SKELETON_JSON_ERROR(root, "Path slot not found: ", slotName);
|
|
|
|
data->_positionMode = PositionMode_valueOf(Json::getString(constraintMap, "positionMode", "percent"));
|
|
data->_spacingMode = SpacingMode_valueOf(Json::getString(constraintMap, "spacingMode", "length"));
|
|
data->_rotateMode = RotateMode_valueOf(Json::getString(constraintMap, "rotateMode", "tangent"));
|
|
data->_offsetRotation = Json::getFloat(constraintMap, "rotation", 0);
|
|
PathConstraintPose &setup = data->_setup;
|
|
setup._position = Json::getFloat(constraintMap, "position", 0);
|
|
if (data->_positionMode == PositionMode_Fixed) setup._position *= _scale;
|
|
setup._spacing = Json::getFloat(constraintMap, "spacing", 0);
|
|
if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) setup._spacing *= _scale;
|
|
setup._mixRotate = Json::getFloat(constraintMap, "mixRotate", 1);
|
|
setup._mixX = Json::getFloat(constraintMap, "mixX", 1);
|
|
setup._mixY = Json::getFloat(constraintMap, "mixY", setup._mixX);
|
|
|
|
skeletonData->_constraints.add(data);
|
|
} else if (strcmp(type, "physics") == 0) {
|
|
PhysicsConstraintData *data = new (__FILE__, __LINE__) PhysicsConstraintData(name);
|
|
data->setSkinRequired(skinRequired);
|
|
|
|
const char *boneName = Json::getString(constraintMap, "bone", 0);
|
|
data->_bone = skeletonData->findBone(boneName);
|
|
if (!data->_bone) SKELETON_JSON_ERROR(root, "Physics bone not found: ", boneName);
|
|
|
|
data->_x = Json::getFloat(constraintMap, "x", 0);
|
|
data->_y = Json::getFloat(constraintMap, "y", 0);
|
|
data->_rotate = Json::getFloat(constraintMap, "rotate", 0);
|
|
data->_scaleX = Json::getFloat(constraintMap, "scaleX", 0);
|
|
data->_shearX = Json::getFloat(constraintMap, "shearX", 0);
|
|
data->_limit = Json::getFloat(constraintMap, "limit", 5000) * _scale;
|
|
data->_step = 1.0f / Json::getInt(constraintMap, "fps", 60);
|
|
PhysicsConstraintPose &setup = data->_setup;
|
|
setup._inertia = Json::getFloat(constraintMap, "inertia", 0.5f);
|
|
setup._strength = Json::getFloat(constraintMap, "strength", 100);
|
|
setup._damping = Json::getFloat(constraintMap, "damping", 0.85f);
|
|
setup._massInverse = 1.0f / Json::getFloat(constraintMap, "mass", 1);
|
|
setup._wind = Json::getFloat(constraintMap, "wind", 0);
|
|
setup._gravity = Json::getFloat(constraintMap, "gravity", 0);
|
|
setup._mix = Json::getFloat(constraintMap, "mix", 1);
|
|
data->_inertiaGlobal = Json::getBoolean(constraintMap, "inertiaGlobal", false);
|
|
data->_strengthGlobal = Json::getBoolean(constraintMap, "strengthGlobal", false);
|
|
data->_dampingGlobal = Json::getBoolean(constraintMap, "dampingGlobal", false);
|
|
data->_massGlobal = Json::getBoolean(constraintMap, "massGlobal", false);
|
|
data->_windGlobal = Json::getBoolean(constraintMap, "windGlobal", false);
|
|
data->_gravityGlobal = Json::getBoolean(constraintMap, "gravityGlobal", false);
|
|
data->_mixGlobal = Json::getBoolean(constraintMap, "mixGlobal", false);
|
|
|
|
skeletonData->_constraints.add(data);
|
|
} else if (strcmp(type, "slider") == 0) {
|
|
SliderData *data = new (__FILE__, __LINE__) SliderData(name);
|
|
data->setSkinRequired(skinRequired);
|
|
data->_additive = Json::getBoolean(constraintMap, "additive", false);
|
|
data->_loop = Json::getBoolean(constraintMap, "loop", false);
|
|
data->_setup._time = Json::getFloat(constraintMap, "time", 0);
|
|
data->_setup._mix = Json::getFloat(constraintMap, "mix", 1);
|
|
|
|
const char *boneName = Json::getString(constraintMap, "bone", NULL);
|
|
if (boneName != NULL) {
|
|
data->_bone = skeletonData->findBone(boneName);
|
|
if (!data->_bone) SKELETON_JSON_ERROR(root, "Slider bone not found: ", boneName);
|
|
const char *property = Json::getString(constraintMap, "property", 0);
|
|
data->_property = fromProperty(property);
|
|
if (data->_property) {
|
|
float propertyScaleValue = propertyScale(property, _scale);
|
|
data->_property->_offset = Json::getFloat(constraintMap, "from", 0) * propertyScaleValue;
|
|
data->_offset = Json::getFloat(constraintMap, "to", 0);
|
|
data->_scale = Json::getFloat(constraintMap, "scale", 1) / propertyScaleValue;
|
|
data->_local = Json::getBoolean(constraintMap, "local", false);
|
|
}
|
|
}
|
|
|
|
skeletonData->_constraints.add(data);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Skins. */
|
|
skins = Json::getItem(root, "skins");
|
|
if (skins) {
|
|
for (Json *skinMap = skins->_child; skinMap; skinMap = skinMap->_next) {
|
|
Skin *skin = new (__FILE__, __LINE__) Skin(Json::getString(skinMap, "name", ""));
|
|
Json *item = Json::getItem(skinMap, "bones");
|
|
if (item) {
|
|
for (Json *entry = item->_child; entry; entry = entry->_next) {
|
|
BoneData *data = skeletonData->findBone(entry->_valueString);
|
|
if (!data) SKELETON_JSON_ERROR(root, "Skin bone not found: ", entry->_valueString);
|
|
skin->getBones().add(data);
|
|
}
|
|
}
|
|
item = Json::getItem(skinMap, "ik");
|
|
if (item) {
|
|
for (Json *entry = item->_child; entry; entry = entry->_next) {
|
|
IkConstraintData *data = skeletonData->findConstraint<IkConstraintData>(entry->_valueString);
|
|
if (!data) SKELETON_JSON_ERROR(root, "Skin IK constraint not found: ", entry->_valueString);
|
|
skin->getConstraints().add(data);
|
|
}
|
|
}
|
|
item = Json::getItem(skinMap, "transform");
|
|
if (item) {
|
|
for (Json *entry = item->_child; entry; entry = entry->_next) {
|
|
TransformConstraintData *data = skeletonData->findConstraint<TransformConstraintData>(entry->_valueString);
|
|
if (!data) SKELETON_JSON_ERROR(root, "Skin transform constraint not found: ", entry->_valueString);
|
|
skin->getConstraints().add(data);
|
|
}
|
|
}
|
|
item = Json::getItem(skinMap, "path");
|
|
if (item) {
|
|
for (Json *entry = item->_child; entry; entry = entry->_next) {
|
|
PathConstraintData *data = skeletonData->findConstraint<PathConstraintData>(entry->_valueString);
|
|
if (!data) SKELETON_JSON_ERROR(root, "Skin path constraint not found: ", entry->_valueString);
|
|
skin->getConstraints().add(data);
|
|
}
|
|
}
|
|
item = Json::getItem(skinMap, "physics");
|
|
if (item) {
|
|
for (Json *entry = item->_child; entry; entry = entry->_next) {
|
|
PhysicsConstraintData *data = skeletonData->findConstraint<PhysicsConstraintData>(entry->_valueString);
|
|
if (!data) SKELETON_JSON_ERROR(root, "Skin physics constraint not found: ", entry->_valueString);
|
|
skin->getConstraints().add(data);
|
|
}
|
|
}
|
|
item = Json::getItem(skinMap, "slider");
|
|
if (item) {
|
|
for (Json *entry = item->_child; entry; entry = entry->_next) {
|
|
SliderData *data = skeletonData->findConstraint<SliderData>(entry->_valueString);
|
|
if (!data) SKELETON_JSON_ERROR(root, "Skin slider not found: ", entry->_valueString);
|
|
skin->getConstraints().add(data);
|
|
}
|
|
}
|
|
|
|
Json *attachments = Json::getItem(skinMap, "attachments");
|
|
if (attachments)
|
|
for (Json *slotEntry = attachments->_child; slotEntry; slotEntry = slotEntry->_next) {
|
|
SlotData *slot = skeletonData->findSlot(slotEntry->_name);
|
|
if (!slot) SKELETON_JSON_ERROR(root, "Skin slot not found: ", slotEntry->_name);
|
|
for (Json *entry = slotEntry->_child; entry; entry = entry->_next) {
|
|
Attachment *attachment = readAttachment(entry, skin, slot->getIndex(), entry->_name, skeletonData);
|
|
if (attachment)
|
|
skin->setAttachment(slot->getIndex(), entry->_name, *attachment);
|
|
else
|
|
SKELETON_JSON_ERROR(root, "Error reading attachment: ", entry->_name);
|
|
}
|
|
}
|
|
|
|
const char *color = Json::getString(skinMap, "color", NULL);
|
|
if (color) Color::valueOf(color, skin->getColor());
|
|
|
|
skeletonData->_skins.add(skin);
|
|
if (strcmp(skin->getName().buffer(), "default") == 0) skeletonData->_defaultSkin = skin;
|
|
}
|
|
}
|
|
|
|
/* Linked meshes. */
|
|
int n = (int) _linkedMeshes.size();
|
|
for (int i = 0; i < n; ++i) {
|
|
LinkedMesh *linkedMesh = _linkedMeshes[i];
|
|
Skin *skin = linkedMesh->_skin.length() == 0 ? skeletonData->getDefaultSkin() : skeletonData->findSkin(linkedMesh->_skin);
|
|
if (skin == NULL) SKELETON_JSON_ERROR(root, "Skin not found: ", linkedMesh->_skin.buffer());
|
|
Attachment *parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent);
|
|
if (parent == NULL) SKELETON_JSON_ERROR(root, "Parent mesh not found: ", linkedMesh->_parent.buffer());
|
|
linkedMesh->_mesh->_timelineAttachment = linkedMesh->_inheritTimelines ? static_cast<VertexAttachment *>(parent) : linkedMesh->_mesh;
|
|
linkedMesh->_mesh->setParentMesh(static_cast<MeshAttachment *>(parent));
|
|
if (linkedMesh->_mesh->_region != NULL) linkedMesh->_mesh->updateRegion();
|
|
}
|
|
ArrayUtils::deleteElements(_linkedMeshes);
|
|
_linkedMeshes.clear();
|
|
|
|
/* Events. */
|
|
events = Json::getItem(root, "events");
|
|
if (events) {
|
|
skeletonData->_events.setSize(events->_size, 0);
|
|
int eventIndex = 0;
|
|
for (Json *eventMap = events->_child; eventMap; eventMap = eventMap->_next) {
|
|
EventData *eventData = new (__FILE__, __LINE__) EventData(String(eventMap->_name));
|
|
eventData->_intValue = Json::getInt(eventMap, "int", 0);
|
|
eventData->_floatValue = Json::getFloat(eventMap, "float", 0);
|
|
eventData->_stringValue = Json::getString(eventMap, "string", 0);
|
|
eventData->_audioPath = Json::getString(eventMap, "audio", 0);
|
|
if (eventData->_audioPath != NULL) {
|
|
eventData->_volume = Json::getFloat(eventMap, "volume", 1);
|
|
eventData->_balance = Json::getFloat(eventMap, "balance", 0);
|
|
}
|
|
skeletonData->_events[eventIndex] = eventData;
|
|
eventIndex++;
|
|
}
|
|
}
|
|
|
|
/* Animations. */
|
|
animations = Json::getItem(root, "animations");
|
|
if (animations) {
|
|
skeletonData->_animations.setSize(animations->_size, 0);
|
|
int animationsIndex = 0;
|
|
for (Json *animationMap = animations->_child; animationMap; animationMap = animationMap->_next) {
|
|
Animation *animation = readAnimation(animationMap, skeletonData);
|
|
if (!animation) SKELETON_JSON_ERROR(root, "Error reading animation: ", animationMap->_name);
|
|
skeletonData->_animations[animationsIndex++] = animation;
|
|
}
|
|
}
|
|
|
|
/* Slider animations. */
|
|
if (constraints) {
|
|
for (Json *constraintMap = constraints->_child; constraintMap; constraintMap = constraintMap->_next) {
|
|
const char *type = Json::getString(constraintMap, "type", 0);
|
|
if (strcmp(type, "slider") == 0) {
|
|
SliderData *data = skeletonData->findConstraint<SliderData>(Json::getString(constraintMap, "name", NULL));
|
|
const char *animationName = Json::getString(constraintMap, "animation", NULL);
|
|
if (animationName) {
|
|
data->_animation = skeletonData->findAnimation(animationName);
|
|
if (!data->_animation) SKELETON_JSON_ERROR(root, "Slider animation not found: ", animationName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delete root;
|
|
|
|
return skeletonData;
|
|
}
|
|
|
|
Attachment *SkeletonJson::readAttachment(Json *map, Skin *skin, int slotIndex, const char *name, SkeletonData *skeletonData) {
|
|
float scale = _scale;
|
|
const char *nameStr = Json::getString(map, "name", name);
|
|
|
|
const char *typeStr = Json::getString(map, "type", "region");
|
|
AttachmentType type = AttachmentType_valueOf(typeStr);
|
|
switch (type) {
|
|
case AttachmentType_Region: {
|
|
const char *path = Json::getString(map, "path", nameStr);
|
|
Json *sequenceJson = Json::getItem(map, "sequence");
|
|
Sequence *sequence = readSequence(sequenceJson);
|
|
RegionAttachment *region = _attachmentLoader->newRegionAttachment(*skin, nameStr, path, sequence);
|
|
if (!region) return NULL;
|
|
region->_path = path;
|
|
region->setX(Json::getFloat(map, "x", 0) * scale);
|
|
region->setY(Json::getFloat(map, "y", 0) * scale);
|
|
region->setScaleX(Json::getFloat(map, "scaleX", 1));
|
|
region->setScaleY(Json::getFloat(map, "scaleY", 1));
|
|
region->setRotation(Json::getFloat(map, "rotation", 0));
|
|
region->setWidth(Json::getFloat(map, "width", 0) * scale);
|
|
region->setHeight(Json::getFloat(map, "height", 0) * scale);
|
|
region->setSequence(sequence);
|
|
|
|
const char *color = Json::getString(map, "color", NULL);
|
|
if (color) Color::valueOf(color, region->getColor());
|
|
|
|
if (region->_region != NULL) region->updateRegion();
|
|
return region;
|
|
}
|
|
case AttachmentType_Boundingbox: {
|
|
BoundingBoxAttachment *box = _attachmentLoader->newBoundingBoxAttachment(*skin, nameStr);
|
|
if (!box) return NULL;
|
|
readVertices(map, box, Json::getInt(map, "vertexCount", 0) << 1);
|
|
|
|
const char *color = Json::getString(map, "color", NULL);
|
|
if (color) Color::valueOf(color, box->getColor());
|
|
return box;
|
|
}
|
|
case AttachmentType_Mesh:
|
|
case AttachmentType_Linkedmesh: {
|
|
const char *path = Json::getString(map, "path", nameStr);
|
|
Sequence *sequence = readSequence(Json::getItem(map, "sequence"));
|
|
MeshAttachment *mesh = _attachmentLoader->newMeshAttachment(*skin, nameStr, path, sequence);
|
|
if (!mesh) return NULL;
|
|
mesh->_path = path;
|
|
|
|
const char *color = Json::getString(map, "color", NULL);
|
|
if (color) Color::valueOf(color, mesh->getColor());
|
|
|
|
mesh->setWidth(Json::getFloat(map, "width", 0) * scale);
|
|
mesh->setHeight(Json::getFloat(map, "height", 0) * scale);
|
|
mesh->setSequence(sequence);
|
|
|
|
const char *parent = Json::getString(map, "parent", NULL);
|
|
if (parent) {
|
|
LinkedMesh *linkedMesh = new (__FILE__, __LINE__)
|
|
LinkedMesh(*mesh, Json::getString(map, "skin", NULL), slotIndex, parent, Json::getBoolean(map, "timelines", true));
|
|
_linkedMeshes.add(linkedMesh);
|
|
return mesh;
|
|
}
|
|
|
|
Array<float> uvs;
|
|
if (!Json::asFloatArray(Json::getItem(map, "uvs"), uvs)) return NULL;
|
|
readVertices(map, mesh, uvs.size());
|
|
Array<unsigned short> triangles;
|
|
if (!Json::asUnsignedShortArray(Json::getItem(map, "triangles"), triangles)) return NULL;
|
|
mesh->_triangles.clearAndAddAll(triangles);
|
|
mesh->_regionUVs.clearAndAddAll(uvs);
|
|
if (mesh->_region != NULL) mesh->updateRegion();
|
|
|
|
if (Json::getInt(map, "hull", 0)) mesh->setHullLength(Json::getInt(map, "hull", 0) << 1);
|
|
Array<unsigned short> edges;
|
|
Json::asUnsignedShortArray(Json::getItem(map, "edges"), edges);
|
|
if (edges.size() > 0) mesh->_edges.clearAndAddAll(edges);
|
|
return mesh;
|
|
}
|
|
case AttachmentType_Path: {
|
|
PathAttachment *path = _attachmentLoader->newPathAttachment(*skin, nameStr);
|
|
if (!path) return NULL;
|
|
path->setClosed(Json::getBoolean(map, "closed", false));
|
|
path->setConstantSpeed(Json::getBoolean(map, "constantSpeed", true));
|
|
|
|
int vertexCount = Json::getInt(map, "vertexCount", 0);
|
|
readVertices(map, path, vertexCount << 1);
|
|
|
|
if (!Json::asFloatArray(Json::getItem(map, "lengths"), path->_lengths)) return NULL;
|
|
for (int i = 0; i < (int) path->_lengths.size(); i++) path->_lengths[i] *= scale;
|
|
|
|
const char *color = Json::getString(map, "color", NULL);
|
|
if (color) Color::valueOf(color, path->getColor());
|
|
return path;
|
|
}
|
|
case AttachmentType_Point: {
|
|
PointAttachment *point = _attachmentLoader->newPointAttachment(*skin, nameStr);
|
|
if (!point) return NULL;
|
|
point->setX(Json::getFloat(map, "x", 0) * scale);
|
|
point->setY(Json::getFloat(map, "y", 0) * scale);
|
|
point->setRotation(Json::getFloat(map, "rotation", 0));
|
|
|
|
const char *color = Json::getString(map, "color", NULL);
|
|
if (color) Color::valueOf(color, point->getColor());
|
|
return point;
|
|
}
|
|
case AttachmentType_Clipping: {
|
|
ClippingAttachment *clip = _attachmentLoader->newClippingAttachment(*skin, nameStr);
|
|
if (!clip) return NULL;
|
|
|
|
const char *end = Json::getString(map, "end", NULL);
|
|
if (end) {
|
|
SlotData *slot = skeletonData->findSlot(end);
|
|
if (!slot) return NULL;
|
|
clip->setEndSlot(slot);
|
|
}
|
|
|
|
readVertices(map, clip, Json::getInt(map, "vertexCount", 0) << 1);
|
|
|
|
const char *color = Json::getString(map, "color", NULL);
|
|
if (color) Color::valueOf(color, clip->getColor());
|
|
return clip;
|
|
}
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Sequence *SkeletonJson::readSequence(Json *item) {
|
|
if (item == NULL) return NULL;
|
|
Sequence *sequence = new Sequence(Json::getInt(item, "count", 0));
|
|
sequence->_start = Json::getInt(item, "start", 1);
|
|
sequence->_digits = Json::getInt(item, "digits", 0);
|
|
sequence->_setupIndex = Json::getInt(item, "setup", 0);
|
|
return sequence;
|
|
}
|
|
|
|
void SkeletonJson::readVertices(Json *map, VertexAttachment *attachment, size_t verticesLength) {
|
|
attachment->setWorldVerticesLength(verticesLength);
|
|
Array<float> vertices;
|
|
if (!Json::asFloatArray(Json::getItem(map, "vertices"), vertices)) {
|
|
return;
|
|
}
|
|
if (verticesLength == vertices.size()) {
|
|
if (_scale != 1) {
|
|
for (int i = 0; i < (int) vertices.size(); ++i) vertices[i] *= _scale;
|
|
}
|
|
attachment->getVertices().clearAndAddAll(vertices);
|
|
return;
|
|
}
|
|
|
|
Vertices bonesAndWeights;
|
|
bonesAndWeights._weights.ensureCapacity(verticesLength * 3 * 3);
|
|
bonesAndWeights._bones.ensureCapacity(verticesLength * 3);
|
|
for (int i = 0, n = (int) vertices.size(); i < n;) {
|
|
int boneCount = (int) vertices[i++];
|
|
bonesAndWeights._bones.add(boneCount);
|
|
for (int nn = i + (boneCount << 2); i < nn; i += 4) {
|
|
bonesAndWeights._bones.add((int) vertices[i]);
|
|
bonesAndWeights._weights.add(vertices[i + 1] * _scale);
|
|
bonesAndWeights._weights.add(vertices[i + 2] * _scale);
|
|
bonesAndWeights._weights.add(vertices[i + 3]);
|
|
}
|
|
}
|
|
attachment->getBones().clearAndAddAll(bonesAndWeights._bones);
|
|
attachment->getVertices().clearAndAddAll(bonesAndWeights._weights);
|
|
}
|
|
|
|
Animation *SkeletonJson::readAnimation(Json *map, SkeletonData *skeletonData) {
|
|
Array<Timeline *> timelines;
|
|
|
|
// Slot timelines.
|
|
for (Json *slotMap = Json::getItem(map, "slots") ? Json::getItem(map, "slots")->_child : NULL; slotMap; slotMap = slotMap->_next) {
|
|
int slotIndex = findSlotIndex(skeletonData, slotMap->_name, timelines);
|
|
if (slotIndex == -1) return NULL;
|
|
|
|
for (Json *timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) {
|
|
Json *keyMap = timelineMap->_child;
|
|
if (keyMap == NULL) continue;
|
|
|
|
int frames = timelineMap->_size;
|
|
if (strcmp(timelineMap->_name, "attachment") == 0) {
|
|
AttachmentTimeline *timeline = new (__FILE__, __LINE__) AttachmentTimeline(frames, slotIndex);
|
|
for (int frame = 0; keyMap; keyMap = keyMap->_next, ++frame) {
|
|
timeline->setFrame(frame, Json::getFloat(keyMap, "time", 0),
|
|
Json::getItem(keyMap, "name") ? Json::getItem(keyMap, "name")->_valueString : NULL);
|
|
}
|
|
timelines.add(timeline);
|
|
|
|
} else if (strcmp(timelineMap->_name, "rgba") == 0) {
|
|
RGBATimeline *timeline = new (__FILE__, __LINE__) RGBATimeline(frames, frames << 2, slotIndex);
|
|
float time = Json::getFloat(keyMap, "time", 0);
|
|
Color color;
|
|
Color::valueOf(Json::getString(keyMap, "color", 0), color);
|
|
|
|
for (int frame = 0, bezier = 0;; ++frame) {
|
|
timeline->setFrame(frame, time, color.r, color.g, color.b, color.a);
|
|
Json *nextMap = keyMap->_next;
|
|
if (!nextMap) {
|
|
break;
|
|
}
|
|
float time2 = Json::getFloat(nextMap, "time", 0);
|
|
Color newColor;
|
|
Color::valueOf(Json::getString(nextMap, "color", 0), newColor);
|
|
Json *curve = Json::getItem(keyMap, "curve");
|
|
if (curve) {
|
|
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color.a, newColor.a, 1);
|
|
}
|
|
time = time2;
|
|
color = newColor;
|
|
keyMap = nextMap;
|
|
}
|
|
timelines.add(timeline);
|
|
} else if (strcmp(timelineMap->_name, "rgb") == 0) {
|
|
RGBTimeline *timeline = new (__FILE__, __LINE__) RGBTimeline(frames, frames * 3, slotIndex);
|
|
float time = Json::getFloat(keyMap, "time", 0);
|
|
const char *colorStr = Json::getString(keyMap, "color", 0);
|
|
Color color;
|
|
if (colorStr && strlen(colorStr) >= 6) {
|
|
color.r = Color::parseHex(colorStr, 0);
|
|
color.g = Color::parseHex(colorStr, 1);
|
|
color.b = Color::parseHex(colorStr, 2);
|
|
}
|
|
|
|
for (int frame = 0, bezier = 0;; ++frame) {
|
|
timeline->setFrame(frame, time, color.r, color.g, color.b);
|
|
Json *nextMap = keyMap->_next;
|
|
if (!nextMap) {
|
|
break;
|
|
}
|
|
float time2 = Json::getFloat(nextMap, "time", 0);
|
|
const char *colorStr2 = Json::getString(nextMap, "color", 0);
|
|
Color newColor;
|
|
if (colorStr2 && strlen(colorStr2) >= 6) {
|
|
newColor.r = Color::parseHex(colorStr2, 0);
|
|
newColor.g = Color::parseHex(colorStr2, 1);
|
|
newColor.b = Color::parseHex(colorStr2, 2);
|
|
}
|
|
Json *curve = Json::getItem(keyMap, "curve");
|
|
if (curve) {
|
|
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
|
|
}
|
|
time = time2;
|
|
color = newColor;
|
|
keyMap = nextMap;
|
|
}
|
|
timelines.add(timeline);
|
|
} else if (strcmp(timelineMap->_name, "alpha") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) AlphaTimeline(frames, frames, slotIndex), 0, 1);
|
|
} else if (strcmp(timelineMap->_name, "rgba2") == 0) {
|
|
RGBA2Timeline *timeline = new (__FILE__, __LINE__) RGBA2Timeline(frames, frames * 7, slotIndex);
|
|
float time = Json::getFloat(keyMap, "time", 0);
|
|
Color color, color2;
|
|
Color::valueOf(Json::getString(keyMap, "light", 0), color);
|
|
const char *darkStr = Json::getString(keyMap, "dark", 0);
|
|
if (darkStr && strlen(darkStr) >= 6) {
|
|
color2.r = Color::parseHex(darkStr, 0);
|
|
color2.g = Color::parseHex(darkStr, 1);
|
|
color2.b = Color::parseHex(darkStr, 2);
|
|
}
|
|
|
|
for (int frame = 0, bezier = 0;; ++frame) {
|
|
timeline->setFrame(frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b);
|
|
Json *nextMap = keyMap->_next;
|
|
if (!nextMap) {
|
|
break;
|
|
}
|
|
float time2 = Json::getFloat(nextMap, "time", 0);
|
|
Color newColor, newColor2;
|
|
Color::valueOf(Json::getString(nextMap, "light", 0), newColor);
|
|
const char *darkStr2 = Json::getString(nextMap, "dark", 0);
|
|
if (darkStr2 && strlen(darkStr2) >= 6) {
|
|
newColor2.r = Color::parseHex(darkStr2, 0);
|
|
newColor2.g = Color::parseHex(darkStr2, 1);
|
|
newColor2.b = Color::parseHex(darkStr2, 2);
|
|
}
|
|
Json *curve = Json::getItem(keyMap, "curve");
|
|
if (curve) {
|
|
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color.a, newColor.a, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, color2.r, newColor2.r, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, color2.g, newColor2.g, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 6, time, time2, color2.b, newColor2.b, 1);
|
|
}
|
|
time = time2;
|
|
color = newColor;
|
|
color2 = newColor2;
|
|
keyMap = nextMap;
|
|
}
|
|
timelines.add(timeline);
|
|
} else if (strcmp(timelineMap->_name, "rgb2") == 0) {
|
|
RGB2Timeline *timeline = new (__FILE__, __LINE__) RGB2Timeline(frames, frames * 6, slotIndex);
|
|
float time = Json::getFloat(keyMap, "time", 0);
|
|
const char *lightStr = Json::getString(keyMap, "light", 0);
|
|
Color color, color2;
|
|
if (lightStr && strlen(lightStr) >= 6) {
|
|
color.r = Color::parseHex(lightStr, 0);
|
|
color.g = Color::parseHex(lightStr, 1);
|
|
color.b = Color::parseHex(lightStr, 2);
|
|
}
|
|
const char *darkStr = Json::getString(keyMap, "dark", 0);
|
|
if (darkStr && strlen(darkStr) >= 6) {
|
|
color2.r = Color::parseHex(darkStr, 0);
|
|
color2.g = Color::parseHex(darkStr, 1);
|
|
color2.b = Color::parseHex(darkStr, 2);
|
|
}
|
|
|
|
for (int frame = 0, bezier = 0;; ++frame) {
|
|
timeline->setFrame(frame, time, color.r, color.g, color.b, color2.r, color2.g, color2.b);
|
|
Json *nextMap = keyMap->_next;
|
|
if (!nextMap) {
|
|
break;
|
|
}
|
|
float time2 = Json::getFloat(nextMap, "time", 0);
|
|
const char *lightStr2 = Json::getString(nextMap, "light", 0);
|
|
Color newColor, newColor2;
|
|
if (lightStr2 && strlen(lightStr2) >= 6) {
|
|
newColor.r = Color::parseHex(lightStr2, 0);
|
|
newColor.g = Color::parseHex(lightStr2, 1);
|
|
newColor.b = Color::parseHex(lightStr2, 2);
|
|
}
|
|
const char *darkStr2 = Json::getString(nextMap, "dark", 0);
|
|
if (darkStr2 && strlen(darkStr2) >= 6) {
|
|
newColor2.r = Color::parseHex(darkStr2, 0);
|
|
newColor2.g = Color::parseHex(darkStr2, 1);
|
|
newColor2.b = Color::parseHex(darkStr2, 2);
|
|
}
|
|
Json *curve = Json::getItem(keyMap, "curve");
|
|
if (curve) {
|
|
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color2.r, newColor2.r, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, color2.g, newColor2.g, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, color2.b, newColor2.b, 1);
|
|
}
|
|
time = time2;
|
|
color = newColor;
|
|
color2 = newColor2;
|
|
keyMap = nextMap;
|
|
}
|
|
timelines.add(timeline);
|
|
} else {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bone timelines.
|
|
for (Json *boneMap = Json::getItem(map, "bones") ? Json::getItem(map, "bones")->_child : NULL; boneMap; boneMap = boneMap->_next) {
|
|
int boneIndex = ArrayUtils::findIndexWithName(skeletonData->_bones, boneMap->_name);
|
|
if (boneIndex == -1) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
|
|
for (Json *timelineMap = boneMap->_child; timelineMap; timelineMap = timelineMap->_next) {
|
|
Json *keyMap = timelineMap->_child;
|
|
if (keyMap == NULL) continue;
|
|
|
|
int frames = timelineMap->_size;
|
|
if (strcmp(timelineMap->_name, "rotate") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) RotateTimeline(frames, frames, boneIndex), 0, 1);
|
|
} else if (strcmp(timelineMap->_name, "translate") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) TranslateTimeline(frames, frames << 1, boneIndex), "x", "y", 0, _scale);
|
|
} else if (strcmp(timelineMap->_name, "translatex") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) TranslateXTimeline(frames, frames, boneIndex), 0, _scale);
|
|
} else if (strcmp(timelineMap->_name, "translatey") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) TranslateYTimeline(frames, frames, boneIndex), 0, _scale);
|
|
} else if (strcmp(timelineMap->_name, "scale") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) ScaleTimeline(frames, frames << 1, boneIndex), "x", "y", 1, 1);
|
|
} else if (strcmp(timelineMap->_name, "scalex") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) ScaleXTimeline(frames, frames, boneIndex), 1, 1);
|
|
} else if (strcmp(timelineMap->_name, "scaley") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) ScaleYTimeline(frames, frames, boneIndex), 1, 1);
|
|
} else if (strcmp(timelineMap->_name, "shear") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) ShearTimeline(frames, frames << 1, boneIndex), "x", "y", 0, 1);
|
|
} else if (strcmp(timelineMap->_name, "shearx") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) ShearXTimeline(frames, frames, boneIndex), 0, 1);
|
|
} else if (strcmp(timelineMap->_name, "sheary") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) ShearYTimeline(frames, frames, boneIndex), 0, 1);
|
|
} else if (strcmp(timelineMap->_name, "inherit") == 0) {
|
|
InheritTimeline *timeline = new (__FILE__, __LINE__) InheritTimeline(frames, boneIndex);
|
|
for (int frame = 0; keyMap; keyMap = keyMap->_next, frame++) {
|
|
float time = Json::getFloat(keyMap, "time", 0);
|
|
Inherit inherit = Inherit_valueOf(Json::getString(keyMap, "inherit", "normal"));
|
|
timeline->setFrame(frame, time, inherit);
|
|
}
|
|
timelines.add(timeline);
|
|
} else {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// IK constraint timelines.
|
|
for (Json *timelineMap = Json::getItem(map, "ik") ? Json::getItem(map, "ik")->_child : NULL; timelineMap; timelineMap = timelineMap->_next) {
|
|
Json *keyMap = timelineMap->_child;
|
|
if (keyMap == NULL) continue;
|
|
IkConstraintData *constraint = skeletonData->findConstraint<IkConstraintData>(timelineMap->_name);
|
|
if (!constraint) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
int constraintIndex = skeletonData->_constraints.indexOf(constraint);
|
|
IkConstraintTimeline *timeline = new (__FILE__, __LINE__) IkConstraintTimeline(timelineMap->_size, timelineMap->_size << 1, constraintIndex);
|
|
float time = Json::getFloat(keyMap, "time", 0);
|
|
float mix = Json::getFloat(keyMap, "mix", 1), softness = Json::getFloat(keyMap, "softness", 0) * _scale;
|
|
for (int frame = 0, bezier = 0;; frame++) {
|
|
timeline->setFrame(frame, time, mix, softness, Json::getBoolean(keyMap, "bendPositive", true) ? 1 : -1,
|
|
Json::getBoolean(keyMap, "compress", false), Json::getBoolean(keyMap, "stretch", false));
|
|
Json *nextMap = keyMap->_next;
|
|
if (!nextMap) {
|
|
break;
|
|
}
|
|
|
|
float time2 = Json::getFloat(nextMap, "time", 0);
|
|
float mix2 = Json::getFloat(nextMap, "mix", 1), softness2 = Json::getFloat(nextMap, "softness", 0) * _scale;
|
|
Json *curve = Json::getItem(keyMap, "curve");
|
|
if (curve) {
|
|
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2, _scale);
|
|
}
|
|
|
|
time = time2;
|
|
mix = mix2;
|
|
softness = softness2;
|
|
keyMap = nextMap;
|
|
}
|
|
|
|
timelines.add(timeline);
|
|
}
|
|
|
|
// Transform constraint timelines.
|
|
for (Json *timelineMap = Json::getItem(map, "transform") ? Json::getItem(map, "transform")->_child : NULL; timelineMap;
|
|
timelineMap = timelineMap->_next) {
|
|
Json *keyMap = timelineMap->_child;
|
|
if (keyMap == NULL) continue;
|
|
TransformConstraintData *constraint = skeletonData->findConstraint<TransformConstraintData>(timelineMap->_name);
|
|
if (!constraint) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
int constraintIndex = skeletonData->_constraints.indexOf(constraint);
|
|
TransformConstraintTimeline *timeline = new (__FILE__, __LINE__)
|
|
TransformConstraintTimeline(timelineMap->_size, timelineMap->_size * 6, constraintIndex);
|
|
float time = Json::getFloat(keyMap, "time", 0);
|
|
float mixRotate = Json::getFloat(keyMap, "mixRotate", 1);
|
|
float mixX = Json::getFloat(keyMap, "mixX", 1), mixY = Json::getFloat(keyMap, "mixY", mixX);
|
|
float mixScaleX = Json::getFloat(keyMap, "mixScaleX", 1), mixScaleY = Json::getFloat(keyMap, "mixScaleY", 1);
|
|
float mixShearY = Json::getFloat(keyMap, "mixShearY", 1);
|
|
for (int frame = 0, bezier = 0;; frame++) {
|
|
timeline->setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
|
|
Json *nextMap = keyMap->_next;
|
|
if (!nextMap) {
|
|
break;
|
|
}
|
|
float time2 = Json::getFloat(nextMap, "time", 0);
|
|
float mixRotate2 = Json::getFloat(nextMap, "mixRotate", 1);
|
|
float mixX2 = Json::getFloat(nextMap, "mixX", 1), mixY2 = Json::getFloat(nextMap, "mixY", mixX2);
|
|
float mixScaleX2 = Json::getFloat(nextMap, "mixScaleX", 1), mixScaleY2 = Json::getFloat(nextMap, "mixScaleY", 1);
|
|
float mixShearY2 = Json::getFloat(nextMap, "mixShearY", 1);
|
|
Json *curve = Json::getItem(keyMap, "curve");
|
|
if (curve) {
|
|
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, mixShearY, mixShearY2, 1);
|
|
}
|
|
|
|
time = time2;
|
|
mixRotate = mixRotate2;
|
|
mixX = mixX2;
|
|
mixY = mixY2;
|
|
mixScaleX = mixScaleX2;
|
|
mixScaleY = mixScaleY2;
|
|
mixShearY = mixShearY2;
|
|
keyMap = nextMap;
|
|
}
|
|
|
|
timelines.add(timeline);
|
|
}
|
|
|
|
// Path constraint timelines.
|
|
for (Json *constraintMap = Json::getItem(map, "path") ? Json::getItem(map, "path")->_child : NULL; constraintMap;
|
|
constraintMap = constraintMap->_next) {
|
|
PathConstraintData *constraint = skeletonData->findConstraint<PathConstraintData>(constraintMap->_name);
|
|
if (!constraint) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
int index = skeletonData->_constraints.indexOf(constraint);
|
|
for (Json *timelineMap = constraintMap->_child; timelineMap; timelineMap = timelineMap->_next) {
|
|
Json *keyMap = timelineMap->_child;
|
|
if (keyMap == NULL) continue;
|
|
|
|
int frames = timelineMap->_size;
|
|
const char *timelineName = timelineMap->_name;
|
|
if (strcmp(timelineName, "position") == 0) {
|
|
PathConstraintPositionTimeline *timeline = new (__FILE__, __LINE__) PathConstraintPositionTimeline(frames, frames, index);
|
|
readTimeline(timelines, keyMap, timeline, 0, constraint->_positionMode == PositionMode_Fixed ? _scale : 1);
|
|
} else if (strcmp(timelineName, "spacing") == 0) {
|
|
CurveTimeline1 *timeline = new (__FILE__, __LINE__) PathConstraintSpacingTimeline(frames, frames, index);
|
|
readTimeline(timelines, keyMap, timeline, 0,
|
|
constraint->_spacingMode == SpacingMode_Length || constraint->_spacingMode == SpacingMode_Fixed ? _scale : 1);
|
|
} else if (strcmp(timelineName, "mix") == 0) {
|
|
PathConstraintMixTimeline *timeline = new (__FILE__, __LINE__) PathConstraintMixTimeline(frames, frames * 3, index);
|
|
float time = Json::getFloat(keyMap, "time", 0);
|
|
float mixRotate = Json::getFloat(keyMap, "mixRotate", 1);
|
|
float mixX = Json::getFloat(keyMap, "mixX", 1);
|
|
float mixY = Json::getFloat(keyMap, "mixY", mixX);
|
|
for (int frame = 0, bezier = 0;; frame++) {
|
|
timeline->setFrame(frame, time, mixRotate, mixX, mixY);
|
|
Json *nextMap = keyMap->_next;
|
|
if (!nextMap) {
|
|
break;
|
|
}
|
|
float time2 = Json::getFloat(nextMap, "time", 0);
|
|
float mixRotate2 = Json::getFloat(nextMap, "mixRotate", 1);
|
|
float mixX2 = Json::getFloat(nextMap, "mixX", 1), mixY2 = Json::getFloat(nextMap, "mixY", mixX2);
|
|
Json *curve = Json::getItem(keyMap, "curve");
|
|
if (curve != NULL) {
|
|
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
|
|
}
|
|
time = time2;
|
|
mixRotate = mixRotate2;
|
|
mixX = mixX2;
|
|
mixY = mixY2;
|
|
keyMap = nextMap;
|
|
}
|
|
timelines.add(timeline);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Physics constraint timelines.
|
|
for (Json *constraintMap = Json::getItem(map, "physics") ? Json::getItem(map, "physics")->_child : NULL; constraintMap;
|
|
constraintMap = constraintMap->_next) {
|
|
int index = -1;
|
|
if (constraintMap->_name && strlen(constraintMap->_name) > 0) {
|
|
PhysicsConstraintData *constraint = skeletonData->findConstraint<PhysicsConstraintData>(constraintMap->_name);
|
|
if (!constraint) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
index = skeletonData->_constraints.indexOf(constraint);
|
|
}
|
|
for (Json *timelineMap = constraintMap->_child; timelineMap; timelineMap = timelineMap->_next) {
|
|
Json *keyMap = timelineMap->_child;
|
|
if (keyMap == NULL) continue;
|
|
|
|
int frames = timelineMap->_size;
|
|
const char *timelineName = timelineMap->_name;
|
|
if (strcmp(timelineName, "reset") == 0) {
|
|
PhysicsConstraintResetTimeline *timeline = new (__FILE__, __LINE__) PhysicsConstraintResetTimeline(frames, index);
|
|
for (int frame = 0; keyMap != NULL; keyMap = keyMap->_next, frame++) timeline->setFrame(frame, Json::getFloat(keyMap, "time", 0));
|
|
timelines.add(timeline);
|
|
continue;
|
|
}
|
|
|
|
CurveTimeline1 *timeline = NULL;
|
|
float defaultValue = 0;
|
|
if (strcmp(timelineName, "inertia") == 0) {
|
|
timeline = new (__FILE__, __LINE__) PhysicsConstraintInertiaTimeline(frames, frames, index);
|
|
} else if (strcmp(timelineName, "strength") == 0) {
|
|
timeline = new (__FILE__, __LINE__) PhysicsConstraintStrengthTimeline(frames, frames, index);
|
|
} else if (strcmp(timelineName, "damping") == 0) {
|
|
timeline = new (__FILE__, __LINE__) PhysicsConstraintDampingTimeline(frames, frames, index);
|
|
} else if (strcmp(timelineName, "mass") == 0) {
|
|
timeline = new (__FILE__, __LINE__) PhysicsConstraintMassTimeline(frames, frames, index);
|
|
} else if (strcmp(timelineName, "wind") == 0) {
|
|
timeline = new (__FILE__, __LINE__) PhysicsConstraintWindTimeline(frames, frames, index);
|
|
} else if (strcmp(timelineName, "gravity") == 0) {
|
|
timeline = new (__FILE__, __LINE__) PhysicsConstraintGravityTimeline(frames, frames, index);
|
|
} else if (strcmp(timelineName, "mix") == 0) {
|
|
defaultValue = 1;
|
|
timeline = new (__FILE__, __LINE__) PhysicsConstraintMixTimeline(frames, frames, index);
|
|
} else {
|
|
continue;
|
|
}
|
|
readTimeline(timelines, keyMap, timeline, defaultValue, 1);
|
|
}
|
|
}
|
|
|
|
// Slider timelines.
|
|
for (Json *constraintMap = Json::getItem(map, "slider") ? Json::getItem(map, "slider")->_child : NULL; constraintMap;
|
|
constraintMap = constraintMap->_next) {
|
|
SliderData *constraint = skeletonData->findConstraint<SliderData>(constraintMap->_name);
|
|
if (!constraint) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
int index = skeletonData->_constraints.indexOf(constraint);
|
|
for (Json *timelineMap = constraintMap->_child; timelineMap; timelineMap = timelineMap->_next) {
|
|
Json *keyMap = timelineMap->_child;
|
|
if (keyMap == NULL) continue;
|
|
|
|
int frames = timelineMap->_size;
|
|
if (strcmp(timelineMap->_name, "time") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) SliderTimeline(frames, frames, index), 1, 1);
|
|
} else if (strcmp(timelineMap->_name, "mix") == 0) {
|
|
readTimeline(timelines, keyMap, new (__FILE__, __LINE__) SliderMixTimeline(frames, frames, index), 1, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Attachment timelines.
|
|
for (Json *attachmentsMap = Json::getItem(map, "attachments") ? Json::getItem(map, "attachments")->_child : NULL; attachmentsMap;
|
|
attachmentsMap = attachmentsMap->_next) {
|
|
Skin *skin = skeletonData->findSkin(attachmentsMap->_name);
|
|
if (!skin) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
for (Json *slotMap = attachmentsMap->_child; slotMap; slotMap = slotMap->_next) {
|
|
int slotIndex = findSlotIndex(skeletonData, slotMap->_name, timelines);
|
|
if (slotIndex == -1) return NULL;
|
|
for (Json *attachmentMap = slotMap->_child; attachmentMap; attachmentMap = attachmentMap->_next) {
|
|
Attachment *attachment = skin->getAttachment(slotIndex, attachmentMap->_name);
|
|
if (!attachment) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
for (Json *timelineMap = attachmentMap->_child; timelineMap; timelineMap = timelineMap->_next) {
|
|
Json *keyMap = timelineMap->_child;
|
|
int frames = timelineMap->_size;
|
|
String timelineName = timelineMap->_name;
|
|
if (timelineName == "deform") {
|
|
VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(attachment);
|
|
bool weighted = vertexAttachment->_bones.size() != 0;
|
|
Array<float> &vertices = vertexAttachment->_vertices;
|
|
int deformLength = weighted ? (int) vertices.size() / 3 * 2 : (int) vertices.size();
|
|
|
|
DeformTimeline *timeline = new (__FILE__, __LINE__) DeformTimeline(frames, frames, slotIndex, *vertexAttachment);
|
|
float time = Json::getFloat(keyMap, "time", 0);
|
|
for (int frame = 0, bezier = 0;; frame++) {
|
|
Array<float> deform;
|
|
Json *verticesValue = Json::getItem(keyMap, "vertices");
|
|
if (!verticesValue) {
|
|
if (weighted) {
|
|
deform.setSize(deformLength, 0);
|
|
} else {
|
|
deform.clearAndAddAll(vertexAttachment->_vertices);
|
|
}
|
|
} else {
|
|
deform.setSize(deformLength, 0);
|
|
int i, start = Json::getInt(keyMap, "offset", 0);
|
|
Json *vertex;
|
|
for (vertex = verticesValue->_child, i = start; vertex; vertex = vertex->_next, ++i) {
|
|
deform[i] = vertex->_valueFloat;
|
|
}
|
|
if (_scale != 1) {
|
|
for (vertex = verticesValue->_child, i = start; vertex; vertex = vertex->_next, ++i) {
|
|
deform[i] *= _scale;
|
|
}
|
|
}
|
|
if (!weighted) {
|
|
for (i = 0; i < deformLength; ++i) {
|
|
deform[i] += vertices[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
timeline->setFrame(frame, time, deform);
|
|
Json *nextMap = keyMap->_next;
|
|
if (!nextMap) {
|
|
break;
|
|
}
|
|
float time2 = Json::getFloat(nextMap, "time", 0);
|
|
Json *curve = Json::getItem(keyMap, "curve");
|
|
if (curve) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1);
|
|
time = time2;
|
|
keyMap = nextMap;
|
|
}
|
|
timelines.add(timeline);
|
|
} else if (timelineName == "sequence") {
|
|
SequenceTimeline *timeline = new (__FILE__, __LINE__) SequenceTimeline(frames, slotIndex, *attachment);
|
|
float lastDelay = 0;
|
|
for (int frame = 0; keyMap != NULL; keyMap = keyMap->_next, frame++) {
|
|
float delay = Json::getFloat(keyMap, "delay", lastDelay);
|
|
timeline->setFrame(frame, Json::getFloat(keyMap, "time", 0),
|
|
SequenceMode_valueOf(Json::getString(keyMap, "mode", "hold")), Json::getInt(keyMap, "index", 0),
|
|
delay);
|
|
lastDelay = delay;
|
|
}
|
|
timelines.add(timeline);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw order timeline.
|
|
Json *drawOrder = Json::getItem(map, "drawOrder");
|
|
if (drawOrder) {
|
|
DrawOrderTimeline *timeline = new (__FILE__, __LINE__) DrawOrderTimeline(drawOrder->_size);
|
|
int slotCount = (int) skeletonData->_slots.size();
|
|
int frame = 0;
|
|
for (Json *keyMap = drawOrder->_child; keyMap; keyMap = keyMap->_next, ++frame) {
|
|
Array<int> drawOrder2;
|
|
Json *offsets = Json::getItem(keyMap, "offsets");
|
|
if (offsets) {
|
|
drawOrder2.setSize(slotCount, 0);
|
|
for (int i = slotCount - 1; i >= 0; i--) drawOrder2[i] = -1;
|
|
Array<int> unchanged;
|
|
unchanged.setSize(slotCount - offsets->_size, 0);
|
|
int originalIndex = 0, unchangedIndex = 0;
|
|
for (Json *offsetMap = offsets->_child; offsetMap; offsetMap = offsetMap->_next) {
|
|
SlotData *slot = skeletonData->findSlot(Json::getString(offsetMap, "slot", 0));
|
|
if (!slot) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
/* Collect unchanged items. */
|
|
while (originalIndex != slot->_index) unchanged[unchangedIndex++] = originalIndex++;
|
|
/* Set changed items. */
|
|
int index = originalIndex;
|
|
drawOrder2[index + Json::getInt(offsetMap, "offset", 0)] = originalIndex++;
|
|
}
|
|
/* Collect remaining unchanged items. */
|
|
while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++;
|
|
/* Fill in unchanged items. */
|
|
for (int i = slotCount - 1; i >= 0; i--)
|
|
if (drawOrder2[i] == -1) drawOrder2[i] = unchanged[--unchangedIndex];
|
|
}
|
|
timeline->setFrame(frame, Json::getFloat(keyMap, "time", 0), &drawOrder2);
|
|
}
|
|
timelines.add(timeline);
|
|
}
|
|
|
|
// Event timeline.
|
|
Json *events = Json::getItem(map, "events");
|
|
if (events) {
|
|
EventTimeline *timeline = new (__FILE__, __LINE__) EventTimeline(events->_size);
|
|
int frame = 0;
|
|
for (Json *keyMap = events->_child; keyMap; keyMap = keyMap->_next, ++frame) {
|
|
EventData *eventData = skeletonData->findEvent(Json::getString(keyMap, "name", 0));
|
|
if (!eventData) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
return NULL;
|
|
}
|
|
Event *event = new (__FILE__, __LINE__) Event(Json::getFloat(keyMap, "time", 0), *eventData);
|
|
event->_intValue = Json::getInt(keyMap, "int", eventData->_intValue);
|
|
event->_floatValue = Json::getFloat(keyMap, "float", eventData->_floatValue);
|
|
event->_stringValue = Json::getString(keyMap, "string", eventData->_stringValue.buffer());
|
|
if (!eventData->_audioPath.isEmpty()) {
|
|
event->_volume = Json::getFloat(keyMap, "volume", eventData->_volume);
|
|
event->_balance = Json::getFloat(keyMap, "balance", eventData->_balance);
|
|
}
|
|
timeline->setFrame(frame, *event);
|
|
}
|
|
timelines.add(timeline);
|
|
}
|
|
|
|
float duration = 0;
|
|
for (size_t i = 0; i < timelines.size(); i++) duration = MathUtil::max(duration, timelines[i]->getDuration());
|
|
return new (__FILE__, __LINE__) Animation(String(map->_name), timelines, duration);
|
|
}
|
|
|
|
void SkeletonJson::readTimeline(Array<Timeline *> &timelines, Json *keyMap, CurveTimeline1 *timeline, float defaultValue, float scale) {
|
|
float time = Json::getFloat(keyMap, "time", 0), value = Json::getFloat(keyMap, "value", defaultValue) * scale;
|
|
for (int frame = 0, bezier = 0;; frame++) {
|
|
timeline->setFrame(frame, time, value);
|
|
Json *nextMap = keyMap->_next;
|
|
if (!nextMap) {
|
|
timelines.add(timeline);
|
|
return;
|
|
}
|
|
float time2 = Json::getFloat(nextMap, "time", 0);
|
|
float value2 = Json::getFloat(nextMap, "value", defaultValue) * scale;
|
|
Json *curve = Json::getItem(keyMap, "curve");
|
|
if (curve != NULL) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value, value2, scale);
|
|
time = time2;
|
|
value = value2;
|
|
keyMap = nextMap;
|
|
}
|
|
}
|
|
|
|
void SkeletonJson::readTimeline(Array<Timeline *> &timelines, Json *keyMap, BoneTimeline2 *timeline, const char *name1, const char *name2,
|
|
float defaultValue, float scale) {
|
|
float time = Json::getFloat(keyMap, "time", 0);
|
|
float value1 = Json::getFloat(keyMap, name1, defaultValue) * scale, value2 = Json::getFloat(keyMap, name2, defaultValue) * scale;
|
|
for (int frame = 0, bezier = 0;; frame++) {
|
|
timeline->setFrame(frame, time, value1, value2);
|
|
Json *nextMap = keyMap->_next;
|
|
if (!nextMap) {
|
|
timelines.add(timeline);
|
|
return;
|
|
}
|
|
float time2 = Json::getFloat(nextMap, "time", 0);
|
|
float nvalue1 = Json::getFloat(nextMap, name1, defaultValue) * scale, nvalue2 = Json::getFloat(nextMap, name2, defaultValue) * scale;
|
|
Json *curve = Json::getItem(keyMap, "curve");
|
|
if (curve != NULL) {
|
|
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale);
|
|
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale);
|
|
}
|
|
time = time2;
|
|
value1 = nvalue1;
|
|
value2 = nvalue2;
|
|
keyMap = nextMap;
|
|
}
|
|
}
|
|
|
|
int SkeletonJson::readCurve(Json *curve, CurveTimeline *timeline, int bezier, int frame, int value, float time1, float time2, float value1,
|
|
float value2, float scale) {
|
|
if (curve->_type == Json::JSON_STRING) {
|
|
if (strcmp(curve->_valueString, "stepped") == 0) timeline->setStepped(frame);
|
|
return bezier;
|
|
}
|
|
curve = Json::getItem(curve, value << 2);
|
|
float cx1 = curve->_valueFloat;
|
|
curve = curve->_next;
|
|
float cy1 = curve->_valueFloat * scale;
|
|
curve = curve->_next;
|
|
float cx2 = curve->_valueFloat;
|
|
curve = curve->_next;
|
|
float cy2 = curve->_valueFloat * scale;
|
|
setBezier(timeline, frame, value, bezier, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
|
|
return bezier + 1;
|
|
}
|
|
|
|
void SkeletonJson::setBezier(CurveTimeline *timeline, int frame, int value, int bezier, float time1, float value1, float cx1, float cy1, float cx2,
|
|
float cy2, float time2, float value2) {
|
|
timeline->setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
|
|
}
|
|
|
|
int SkeletonJson::findSlotIndex(SkeletonData *skeletonData, const String &slotName, Array<Timeline *> timelines) {
|
|
int slotIndex = ArrayUtils::findIndexWithName(skeletonData->getSlots(), slotName);
|
|
if (slotIndex == -1) {
|
|
ArrayUtils::deleteElements(timelines);
|
|
setError(NULL, "Slot not found: ", slotName);
|
|
}
|
|
return slotIndex;
|
|
}
|
|
|
|
void SkeletonJson::setError(Json *root, const String &value1, const String &value2) {
|
|
_error = String(value1).append(value2);
|
|
delete root;
|
|
}
|