From c04f89e8ee49e58da9143849a5c4b48e7cd59c1a Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sat, 5 Jul 2025 02:10:45 +0200 Subject: [PATCH] [cpp] 4.3 porting WIP --- .../spine-cpp/include/spine/AttachmentType.h | 13 + spine-cpp/spine-cpp/include/spine/BlendMode.h | 10 + spine-cpp/spine-cpp/include/spine/Color.h | 28 + spine-cpp/spine-cpp/include/spine/Inherit.h | 11 + spine-cpp/spine-cpp/include/spine/Json.h | 8 + .../spine-cpp/include/spine/PositionMode.h | 8 + .../spine-cpp/include/spine/RotateMode.h | 9 + .../spine-cpp/include/spine/SkeletonJson.h | 12 +- .../spine-cpp/include/spine/SpacingMode.h | 10 + spine-cpp/spine-cpp/include/spine/Vector.h | 6 +- spine-cpp/spine-cpp/include/spine/Vertices.h | 2 +- spine-cpp/spine-cpp/src/spine/Json.cpp | 1 + .../spine-cpp/src/spine/SkeletonJson.cpp | 1306 ++++++++--------- 13 files changed, 709 insertions(+), 715 deletions(-) diff --git a/spine-cpp/spine-cpp/include/spine/AttachmentType.h b/spine-cpp/spine-cpp/include/spine/AttachmentType.h index 8b1a7ed25..480c1b23b 100644 --- a/spine-cpp/spine-cpp/include/spine/AttachmentType.h +++ b/spine-cpp/spine-cpp/include/spine/AttachmentType.h @@ -30,6 +30,8 @@ #ifndef Spine_AttachmentType_h #define Spine_AttachmentType_h +#include + namespace spine { enum AttachmentType { AttachmentType_Region, @@ -40,6 +42,17 @@ namespace spine { AttachmentType_Point, AttachmentType_Clipping }; + + inline AttachmentType AttachmentType_valueOf(const char* value) { + if (strcmp(value, "region") == 0) return AttachmentType_Region; + else if (strcmp(value, "mesh") == 0) return AttachmentType_Mesh; + else if (strcmp(value, "linkedmesh") == 0) return AttachmentType_Linkedmesh; + else if (strcmp(value, "boundingbox") == 0) return AttachmentType_Boundingbox; + else if (strcmp(value, "path") == 0) return AttachmentType_Path; + else if (strcmp(value, "clipping") == 0) return AttachmentType_Clipping; + else if (strcmp(value, "point") == 0) return AttachmentType_Point; + else return AttachmentType_Region; // default + } } #endif /* Spine_AttachmentType_h */ diff --git a/spine-cpp/spine-cpp/include/spine/BlendMode.h b/spine-cpp/spine-cpp/include/spine/BlendMode.h index f34e95b14..083686716 100644 --- a/spine-cpp/spine-cpp/include/spine/BlendMode.h +++ b/spine-cpp/spine-cpp/include/spine/BlendMode.h @@ -30,6 +30,8 @@ #ifndef Spine_BlendMode_h #define Spine_BlendMode_h +#include + namespace spine { enum BlendMode { BlendMode_Normal = 0, @@ -37,6 +39,14 @@ namespace spine { BlendMode_Multiply, BlendMode_Screen }; + + inline BlendMode BlendMode_valueOf(const char* value) { + if (strcmp(value, "normal") == 0) return BlendMode_Normal; + else if (strcmp(value, "additive") == 0) return BlendMode_Additive; + else if (strcmp(value, "multiply") == 0) return BlendMode_Multiply; + else if (strcmp(value, "screen") == 0) return BlendMode_Screen; + else return BlendMode_Normal; // default + } } #endif /* Spine_BlendMode_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Color.h b/spine-cpp/spine-cpp/include/spine/Color.h index 9a8cdeb09..119bc78d1 100644 --- a/spine-cpp/spine-cpp/include/spine/Color.h +++ b/spine-cpp/spine-cpp/include/spine/Color.h @@ -31,6 +31,8 @@ #define SPINE_COLOR_H #include +#include +#include namespace spine { class SP_API Color : public SpineObject { @@ -102,6 +104,32 @@ namespace spine { return *this; } + // Parse hex color string like "ff0000ff" (RRGGBBAA) or "ff0000" (RRGGBB) + static Color valueOf(const char* hexString) { + Color color; + valueOf(hexString, color); + return color; + } + + // Parse hex color string into existing Color object + static void valueOf(const char* hexString, Color& color) { + size_t len = strlen(hexString); + if (len >= 6) { + color.r = parseHex(hexString, 0); + color.g = parseHex(hexString, 1); + color.b = parseHex(hexString, 2); + color.a = len >= 8 ? parseHex(hexString, 3) : 1.0f; + } + } + + static float parseHex(const char* value, size_t index) { + char digits[3]; + digits[0] = value[index * 2]; + digits[1] = value[index * 2 + 1]; + digits[2] = '\0'; + return strtoul(digits, NULL, 16) / 255.0f; + } + float r, g, b, a; }; } diff --git a/spine-cpp/spine-cpp/include/spine/Inherit.h b/spine-cpp/spine-cpp/include/spine/Inherit.h index d6408fa1f..02ec9e3c3 100644 --- a/spine-cpp/spine-cpp/include/spine/Inherit.h +++ b/spine-cpp/spine-cpp/include/spine/Inherit.h @@ -30,6 +30,8 @@ #ifndef Spine_TransformMode_h #define Spine_TransformMode_h +#include + namespace spine { /// Determines how a bone inherits world transforms from parent bones. enum Inherit { @@ -39,6 +41,15 @@ namespace spine { Inherit_NoScale, Inherit_NoScaleOrReflection }; + + inline Inherit Inherit_valueOf(const char* value) { + if (strcmp(value, "normal") == 0) return Inherit_Normal; + else if (strcmp(value, "onlyTranslation") == 0) return Inherit_OnlyTranslation; + else if (strcmp(value, "noRotationOrReflection") == 0) return Inherit_NoRotationOrReflection; + else if (strcmp(value, "noScale") == 0) return Inherit_NoScale; + else if (strcmp(value, "noScaleOrReflection") == 0) return Inherit_NoScaleOrReflection; + else return Inherit_Normal; // default + } } #endif /* Spine_TransformMode_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Json.h b/spine-cpp/spine-cpp/include/spine/Json.h index 03809a129..9b3f6cf87 100644 --- a/spine-cpp/spine-cpp/include/spine/Json.h +++ b/spine-cpp/spine-cpp/include/spine/Json.h @@ -51,6 +51,14 @@ namespace spine { static const int JSON_ARRAY; static const int JSON_OBJECT; + template + static bool asArray(Json *value, Vector &array) { + array.setSize(value->_size, 0); + Json *vertex = value->_child; + for (int i = 0; vertex; vertex = vertex->_next, i++) + array[i] = vertex->_valueInt; + } + /* Get item "string" from object. Case insensitive. */ static Json *getItem(Json *object, const char *string); diff --git a/spine-cpp/spine-cpp/include/spine/PositionMode.h b/spine-cpp/spine-cpp/include/spine/PositionMode.h index 8226b37b6..c1761a0b2 100644 --- a/spine-cpp/spine-cpp/include/spine/PositionMode.h +++ b/spine-cpp/spine-cpp/include/spine/PositionMode.h @@ -30,6 +30,8 @@ #ifndef Spine_PositionMode_h #define Spine_PositionMode_h +#include + namespace spine { /// Controls how the first bone is positioned along the path. /// @@ -38,6 +40,12 @@ namespace spine { PositionMode_Fixed = 0, PositionMode_Percent }; + + inline PositionMode PositionMode_valueOf(const char* value) { + if (strcmp(value, "fixed") == 0) return PositionMode_Fixed; + else if (strcmp(value, "percent") == 0) return PositionMode_Percent; + else return PositionMode_Percent; // default + } } #endif /* Spine_PositionMode_h */ diff --git a/spine-cpp/spine-cpp/include/spine/RotateMode.h b/spine-cpp/spine-cpp/include/spine/RotateMode.h index 2a1c5142f..f159578df 100644 --- a/spine-cpp/spine-cpp/include/spine/RotateMode.h +++ b/spine-cpp/spine-cpp/include/spine/RotateMode.h @@ -30,6 +30,8 @@ #ifndef Spine_RotateMode_h #define Spine_RotateMode_h +#include + namespace spine { /// Controls how bones are rotated, translated, and scaled to match the path. /// @@ -41,6 +43,13 @@ namespace spine { /// doesn't affect other constrained bones. RotateMode_ChainScale }; + + inline RotateMode RotateMode_valueOf(const char* value) { + if (strcmp(value, "tangent") == 0) return RotateMode_Tangent; + else if (strcmp(value, "chain") == 0) return RotateMode_Chain; + else if (strcmp(value, "chainScale") == 0) return RotateMode_ChainScale; + else return RotateMode_Tangent; // default + } } #endif /* Spine_RotateMode_h */ diff --git a/spine-cpp/spine-cpp/include/spine/SkeletonJson.h b/spine-cpp/spine-cpp/include/spine/SkeletonJson.h index 73ac5a071..8083236ad 100644 --- a/spine-cpp/spine-cpp/include/spine/SkeletonJson.h +++ b/spine-cpp/spine-cpp/include/spine/SkeletonJson.h @@ -61,6 +61,10 @@ namespace spine { class Sequence; + class Skin; + + class Attachment; + class SP_API SkeletonJson : public SpineObject { public: explicit SkeletonJson(Atlas *atlas); @@ -95,14 +99,16 @@ namespace spine { readCurve(Json *curve, CurveTimeline *timeline, int bezier, int frame, int value, float time1, float time2, float value1, float value2, float scale); - static Timeline *readTimeline(Json *keyMap, CurveTimeline1 *timeline, float defaultValue, float scale); + static void readTimeline(Vector &timelines, Json *keyMap, CurveTimeline1 *timeline, float defaultValue, float scale); - static Timeline * - readTimeline(Json *keyMap, CurveTimeline2 *timeline, const char *name1, const char *name2, float defaultValue, + static void + readTimeline(Vector &timelines, Json *keyMap, CurveTimeline2 *timeline, const char *name1, const char *name2, float defaultValue, float scale); Animation *readAnimation(Json *root, SkeletonData *skeletonData); + Attachment *readAttachment(Json *map, Skin *skin, int slotIndex, const char *name, SkeletonData *skeletonData); + void readVertices(Json *attachmentMap, VertexAttachment *attachment, size_t verticesLength); void setError(Json *root, const String &value1, const String &value2); diff --git a/spine-cpp/spine-cpp/include/spine/SpacingMode.h b/spine-cpp/spine-cpp/include/spine/SpacingMode.h index 85b45681e..546ff3cc3 100644 --- a/spine-cpp/spine-cpp/include/spine/SpacingMode.h +++ b/spine-cpp/spine-cpp/include/spine/SpacingMode.h @@ -30,6 +30,8 @@ #ifndef Spine_SpacingMode_h #define Spine_SpacingMode_h +#include + namespace spine { /// Controls how bones after the first bone are positioned along the path. /// @@ -40,6 +42,14 @@ namespace spine { SpacingMode_Percent, SpacingMode_Proportional }; + + inline SpacingMode SpacingMode_valueOf(const char* value) { + if (strcmp(value, "length") == 0) return SpacingMode_Length; + else if (strcmp(value, "fixed") == 0) return SpacingMode_Fixed; + else if (strcmp(value, "percent") == 0) return SpacingMode_Percent; + else if (strcmp(value, "proportional") == 0) return SpacingMode_Proportional; + else return SpacingMode_Length; // default + } } #endif /* Spine_SpacingMode_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Vector.h b/spine-cpp/spine-cpp/include/spine/Vector.h index 8ad9196e3..a6d11c86c 100644 --- a/spine-cpp/spine-cpp/include/spine/Vector.h +++ b/spine-cpp/spine-cpp/include/spine/Vector.h @@ -129,8 +129,10 @@ namespace spine { } inline void clearAndAddAll(const Vector &inValue) { - this->clear(); - this->addAll(inValue); + ensureCapacity(inValue.size()); + for (size_t i = 0; i < inValue.size(); i++) + _buffer[i] = inValue[i]; + _size = inValue.size(); } inline void removeAt(size_t inIndex) { diff --git a/spine-cpp/spine-cpp/include/spine/Vertices.h b/spine-cpp/spine-cpp/include/spine/Vertices.h index 070d823c7..1f3bd86b7 100644 --- a/spine-cpp/spine-cpp/include/spine/Vertices.h +++ b/spine-cpp/spine-cpp/include/spine/Vertices.h @@ -36,7 +36,7 @@ namespace spine { class SP_API Vertices : public SpineObject { public: Vector _bones; - Vector _vertices; + Vector _weights; }; } diff --git a/spine-cpp/spine-cpp/src/spine/Json.cpp b/spine-cpp/spine-cpp/src/spine/Json.cpp index 89d1cc9d5..305dc68ab 100644 --- a/spine-cpp/spine-cpp/src/spine/Json.cpp +++ b/spine-cpp/spine-cpp/src/spine/Json.cpp @@ -37,6 +37,7 @@ THE SOFTWARE. #include #include #include +#include #include #include diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp index 025c454bc..a8ff1c9a6 100644 --- a/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp +++ b/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include @@ -73,32 +75,33 @@ #include #include #include +#include +#include +#include +#include using namespace spine; -static float toColor(const char *value, size_t index) { - char digits[3]; - char *error; - int color; +#define SKELETON_JSON_ERROR(root, message, value) \ + do { \ + delete skeletonData; \ + setError(root, message, value); \ + return NULL; \ + } while (0) - 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; +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 void toColor(Color &color, const char *value, bool hasAlpha) { - color.r = toColor(value, 0); - color.g = toColor(value, 1); - color.b = toColor(value, 2); - if (hasAlpha) color.a = toColor(value, 3); +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)), @@ -126,16 +129,31 @@ SkeletonData *SkeletonJson::readSkeletonDataFile(const String &path) { } 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) { - int i, ii; + int ii; SkeletonData *skeletonData; - Json *root, *skeleton, *bones, *boneMap, *ik, *transform, *path, *physics, *slots, *skins, *animations, *events; + Json *root, *skeleton, *bones, *constraints, *slots, *skins, *animations, *events; _error = ""; _linkedMeshes.clear(); @@ -156,9 +174,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) { 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); - delete skeletonData; - setError(NULL, errorMsg, ""); - return NULL; + SKELETON_JSON_ERROR(NULL, errorMsg, ""); } skeletonData->_x = Json::getFloat(skeleton, "x", 0); skeletonData->_y = Json::getFloat(skeleton, "y", 0); @@ -172,632 +188,365 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) { /* 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 *inherit; - - 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; + 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; } - - 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); - inherit = Json::getString(boneMap, "inherit", "normal"); - data->_inherit = Inherit_Normal; - if (strcmp(inherit, "normal") == 0) data->_inherit = Inherit_Normal; - else if (strcmp(inherit, "onlyTranslation") == 0) - data->_inherit = Inherit_OnlyTranslation; - else if (strcmp(inherit, "noRotationOrReflection") == 0) - data->_inherit = Inherit_NoRotationOrReflection; - else if (strcmp(inherit, "noScale") == 0) - data->_inherit = Inherit_NoScale; - else if (strcmp(inherit, "noScaleOrReflection") == 0) - data->_inherit = Inherit_NoScaleOrReflection; - data->_skinRequired = Json::getBoolean(boneMap, "skin", false); - - const char *color = Json::getString(boneMap, "color", NULL); - if (color) toColor(data->getColor(), color, true); - - data->_icon = Json::getString(boneMap, "icon", ""); - data->_visible = Json::getBoolean(boneMap, "visible", true); - - 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; - + 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) { - delete skeletonData; - setError(root, "Slot bone not found: ", boneName); - return NULL; - } + if (!boneData) SKELETON_JSON_ERROR(root, "Slot bone not found: ", boneName); - String slotName = String(Json::getString(slotMap, "name", 0)); - data = new (__FILE__, __LINE__) SlotData(i, slotName, *boneData); + SlotData *data = new (__FILE__, __LINE__) SlotData(slotCount, slotName, *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); - } + const char *color = Json::getString(slotMap, "color", 0); + if (color) Color::valueOf(color, data->_setup.getColor()); - dark = Json::getString(slotMap, "dark", 0); + const char *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); + data->_setup._darkColor = Color::valueOf(dark); + data->_setup._hasDarkColor = 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; - } + 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[i] = data; + skeletonData->_slots[slotCount] = 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; + /* 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); - 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; + Json *entry = Json::getItem(constraintMap, "bones"); + data->_bones.setSize(entry->_size, 0); + 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->_ikConstraints.add(data); + skeletonData->_constraints.add(data); } + else if (strcmp(type, "transform") == 0) { + TransformConstraintData *data = new (__FILE__, __LINE__) TransformConstraintData(name); + data->setSkinRequired(skinRequired); - 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; + Json *entry = Json::getItem(constraintMap, "bones"); + data->_bones.setSize(entry->_size, 0); + 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); } - } - 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; - } + 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->_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->_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); - data->_mixRotate = Json::getFloat(constraintMap, "mixRotate", 1); - data->_mixX = Json::getFloat(constraintMap, "mixX", 1); - data->_mixY = Json::getFloat(constraintMap, "mixY", data->_mixX); - data->_mixScaleX = Json::getFloat(constraintMap, "mixScaleX", 1); - data->_mixScaleY = Json::getFloat(constraintMap, "mixScaleY", data->_mixScaleX); - data->_mixShearY = Json::getFloat(constraintMap, "mixShearY", 1); + 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 (Json *toEntry = toEntry->_child; toEntry; toEntry = toEntry->_next) { + ToProperty *to = NULL; + float toScale = 1; - skeletonData->_transformConstraints[i] = data; - } - } + 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); + } - /* 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; + to->offset = Json::getFloat(toEntry, "offset", 0) * toScale; + to->max = Json::getFloat(toEntry, "max", 1) * toScale; + to->scale = Json::getFloat(toEntry, "scale", 0) * toScale / fromScale; + from->to.add(to); + } + } - 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; + 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->_transformConstraints.add(data); + skeletonData->_constraints.add(data); } + else if (strcmp(type, "path") == 0) { + PathConstraintData *data = new (__FILE__, __LINE__) PathConstraintData(name); + data->setSkinRequired(skinRequired); - 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; + Json *entry = Json::getItem(constraintMap, "bones"); + data->_bones.setSize(entry->_size, 0); + 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->_pathConstraints.add(data); + skeletonData->_constraints.add(data); } + else if (strcmp(type, "physics") == 0) { + PhysicsConstraintData *data = new (__FILE__, __LINE__) PhysicsConstraintData(name); + data->setSkinRequired(skinRequired); - 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; + 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->_physicsConstraints.add(data); + 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); - 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; - else - data->_spacingMode = SpacingMode_Proportional; + 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); + } + } - 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->_mixRotate = Json::getFloat(constraintMap, "mixRotate", 1); - data->_mixX = Json::getFloat(constraintMap, "mixX", 1); - data->_mixY = Json::getFloat(constraintMap, "mixY", data->_mixX); - - skeletonData->_pathConstraints[i] = data; - } - } - - /* Physics constraints */ - physics = Json::getItem(root, "physics"); - if (physics) { - Json *constraintMap; - skeletonData->_physicsConstraints.ensureCapacity(physics->_size); - skeletonData->_physicsConstraints.setSize(physics->_size, 0); - for (constraintMap = physics->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) { - const char *name; - - PhysicsConstraintData *data = new (__FILE__, __LINE__) PhysicsConstraintData( - Json::getString(constraintMap, "name", 0)); - data->setOrder(Json::getInt(constraintMap, "order", 0)); - data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false)); - - name = Json::getString(constraintMap, "bone", 0); - data->_bone = skeletonData->findBone(name); - if (!data->_bone) { - delete skeletonData; - setError(root, "Physics bone not found: ", name); - return NULL; + skeletonData->_constraints.add(data); } - - data->_x = Json::getFloat(constraintMap, "x", 0); - data->_y = Json::getFloat(constraintMap, "y", 0); - data->_rotate = Json::getFloat(constraintMap, "rotate", 0); - data->_scaleX = Json::getFloat(constraintMap, "scaleX", 0); - data->_shearX = Json::getFloat(constraintMap, "shearX", 0); - data->_limit = Json::getFloat(constraintMap, "limit", 5000) * _scale; - data->_step = 1.0f / Json::getInt(constraintMap, "fps", 60); - data->_inertia = Json::getFloat(constraintMap, "inertia", 1); - data->_strength = Json::getFloat(constraintMap, "strength", 100); - data->_damping = Json::getFloat(constraintMap, "damping", 1); - data->_massInverse = 1.0f / Json::getFloat(constraintMap, "mass", 1); - data->_wind = Json::getFloat(constraintMap, "wind", 0); - data->_gravity = Json::getFloat(constraintMap, "gravity", 0); - data->_mix = Json::getFloat(constraintMap, "mix", 1); - data->_inertiaGlobal = Json::getBoolean(constraintMap, "inertiaGlobal", false); - data->_strengthGlobal = Json::getBoolean(constraintMap, "strengthGlobal", false); - data->_dampingGlobal = Json::getBoolean(constraintMap, "dampingGlobal", false); - data->_massGlobal = Json::getBoolean(constraintMap, "massGlobal", false); - data->_windGlobal = Json::getBoolean(constraintMap, "windGlobal", false); - data->_gravityGlobal = Json::getBoolean(constraintMap, "gravityGlobal", false); - data->_mixGlobal = Json::getBoolean(constraintMap, "mixGlobal", false); - - skeletonData->_physicsConstraints[i] = data; } } /* Skins. */ skins = Json::getItem(root, "skins"); if (skins) { - 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; - + 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 (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; - } + 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 (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; - } + for (Json *entry = item->_child; entry; entry = entry->_next) { + IkConstraintData *data = skeletonData->findConstraint(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 (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; - } + for (Json *entry = item->_child; entry; entry = entry->_next) { + TransformConstraintData *data = skeletonData->findConstraint(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 (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; - } + for (Json *entry = item->_child; entry; entry = entry->_next) { + PathConstraintData *data = skeletonData->findConstraint(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 (item = item->_child; item; item = item->_next) { - PhysicsConstraintData *data = skeletonData->findPhysicsConstraint(item->_valueString); - if (!data) { - delete skeletonData; - setError(root, String("Skin physics constraint not found: "), item->_valueString); - return NULL; - } + for (Json *entry = item->_child; entry; entry = entry->_next) { + PhysicsConstraintData *data = skeletonData->findConstraint(entry->_valueString); + if (!data) SKELETON_JSON_ERROR(root, "Skin physics constraint not found: ", entry->_valueString); skin->getConstraints().add(data); } } - - skeletonData->_skins[skinsIndex++] = skin; - if (strcmp(Json::getString(skinMap, "name", ""), "default") == 0) { - skeletonData->_defaultSkin = skin; + item = Json::getItem(skinMap, "slider"); + if (item) { + for (Json *entry = item->_child; entry; entry = entry->_next) { + SliderData *data = skeletonData->findConstraint(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 (attachmentsMap = attachments->_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: { - Sequence *sequence = readSequence(Json::getItem(attachmentMap, "sequence")); - attachment = _attachmentLoader->newRegionAttachment(*skin, attachmentName, attachmentPath, sequence); - if (!attachment) { - delete skeletonData; - setError(root, "Error reading attachment: ", skinAttachmentName); - return NULL; - } - - 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; - region->_sequence = sequence; - - color = Json::getString(attachmentMap, "color", 0); - if (color) toColor(region->getColor(), color, true); - - if (region->_region != NULL) region->updateRegion(); - _attachmentLoader->configureAttachment(region); - break; - } - case AttachmentType_Mesh: - case AttachmentType_Linkedmesh: { - Sequence *sequence = readSequence(Json::getItem(attachmentMap, "sequence")); - attachment = _attachmentLoader->newMeshAttachment(*skin, attachmentName, attachmentPath, sequence); - - if (!attachment) { - delete skeletonData; - setError(root, "Error reading attachment: ", skinAttachmentName); - return NULL; - } - - MeshAttachment *mesh = static_cast(attachment); - mesh->_path = attachmentPath; - - color = Json::getString(attachmentMap, "color", 0); - if (color) toColor(mesh->getColor(), color, true); - - mesh->_width = Json::getFloat(attachmentMap, "width", 32) * _scale; - mesh->_height = Json::getFloat(attachmentMap, "height", 32) * _scale; - mesh->_sequence = sequence; - - 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); - - if (mesh->_region != NULL) mesh->updateRegion(); - - 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 inheritTimelines = Json::getInt(attachmentMap, "timelines", 1) ? true : false; - LinkedMesh *linkedMesh = new (__FILE__, __LINE__) LinkedMesh(mesh, - String(Json::getString( - attachmentMap, - "skin", 0)), - slot->getIndex(), - String(entry->_valueString), - inheritTimelines); - _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); - color = Json::getString(attachmentMap, "color", NULL); - if (color) toColor(box->getColor(), color, true); - _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; - color = Json::getString(attachmentMap, "color", NULL); - if (color) toColor(pathAttatchment->getColor(), color, true); - _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); - color = Json::getString(attachmentMap, "color", NULL); - if (color) toColor(point->getColor(), color, true); - _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); - color = Json::getString(attachmentMap, "color", NULL); - if (color) toColor(clip->getColor(), color, true); - _attachmentLoader->configureAttachment(attachment); - break; - } - } - - skin->setAttachment(slot->getIndex(), skinAttachmentName, attachment); + 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 (i = 0; i < n; ++i) { + 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) { - delete skeletonData; - setError(root, "Skin not found: ", linkedMesh->_skin.buffer()); - return NULL; - } + if (skin == NULL) SKELETON_JSON_ERROR(root, "Skin not found: ", linkedMesh->_skin.buffer()); Attachment *parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent); - if (parent == NULL) { - delete skeletonData; - setError(root, "Parent mesh not found: ", linkedMesh->_parent.buffer()); - return NULL; - } + if (parent == NULL) SKELETON_JSON_ERROR(root, "Parent mesh not found: ", linkedMesh->_parent.buffer()); linkedMesh->_mesh->_timelineAttachment = linkedMesh->_inheritTimelines ? static_cast(parent) : linkedMesh->_mesh; linkedMesh->_mesh->setParentMesh(static_cast(parent)); @@ -810,40 +559,30 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) { /* 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) { + 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); - 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->_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[i] = eventData; + skeletonData->_events[eventIndex] = 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) { + for (Json *animationMap = animations->_child; animationMap; animationMap = animationMap->_next) { Animation *animation = readAnimation(animationMap, skeletonData); - if (!animation) { - delete skeletonData; - delete root; - return NULL; - } + if (!animation) SKELETON_JSON_ERROR(root, "Error reading animation: ", animationMap->_name); skeletonData->_animations[animationsIndex++] = animation; } } @@ -853,94 +592,168 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) { 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; + } + + Vector uvs; + if (!Json::asArray(Json::getItem(map, "uvs"), uvs)) return NULL; + readVertices(map, mesh, uvs.size()); + Vector triangles; + if (!Json::asArray(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); + Vector edges; + if (!Json::asArray(Json::getItem(map, "edges"), edges)) return NULL; + 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::asArray(Json::getItem(map, "lengths"), path->_lengths)) return NULL; + for (int i = 0; i < 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, "setupIndex", 0); + sequence->_setupIndex = Json::getInt(item, "setup", 0); return sequence; } -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::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 && 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; -} - -Timeline *SkeletonJson::readTimeline(Json *keyMap, CurveTimeline1 *timeline, float defaultValue, float scale) { - float time = Json::getFloat(keyMap, "time", 0); - float value = Json::getFloat(keyMap, "value", defaultValue) * scale; - int bezier = 0; - for (int frame = 0;; frame++) { - timeline->setFrame(frame, time, value); - Json *nextMap = keyMap->_next; - if (!nextMap) break; - 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; - } - // timeline.shrink(); // BOZO - return timeline; -} - -Timeline *SkeletonJson::readTimeline(Json *keyMap, CurveTimeline2 *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; - float value2 = Json::getFloat(keyMap, name2, defaultValue) * scale; - int bezier = 0; - for (int frame = 0;; frame++) { - timeline->setFrame(frame, time, value1, value2); - Json *nextMap = keyMap->_next; - if (!nextMap) break; - float time2 = Json::getFloat(nextMap, "time", 0); - float nvalue1 = Json::getFloat(nextMap, name1, defaultValue) * scale; - float 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); +void SkeletonJson::readVertices(Json *map, VertexAttachment *attachment, size_t verticesLength) { + attachment->setWorldVerticesLength(verticesLength); + Vector vertices; + if (!Json::asArray(Json::getItem(map, "vertices"), vertices)) return; + if (verticesLength == vertices.size()) { + if (_scale != 1) { + for (int i = 0; i < vertices.size(); ++i) + vertices[i] *= _scale; } - time = time2; - value1 = nvalue1; - value2 = nvalue2; - keyMap = nextMap; + attachment->getVertices().clearAndAddAll(vertices); + return; } - // timeline.shrink(); // BOZO - return timeline; -} -int SkeletonJson::findSlotIndex(SkeletonData *skeletonData, const String &slotName, Vector timelines) { - int slotIndex = ContainerUtil::findIndexWithName(skeletonData->getSlots(), slotName); - if (slotIndex == -1) { - ContainerUtil::cleanUpVectorOfPointers(timelines); - setError(NULL, "Slot not found: ", slotName); + Vertices bonesAndWeights; + bonesAndWeights._weights.ensureCapacity(verticesLength * 3 * 3); + bonesAndWeights._bones.ensureCapacity(verticesLength * 3); + for (int i = 0, n = 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]); + } } - return slotIndex; + attachment->getBones().clearAndAddAll(bonesAndWeights._bones); + attachment->getVertices().clearAndAddAll(bonesAndWeights._weights); } Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { @@ -977,7 +790,7 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { RGBATimeline *timeline = new (__FILE__, __LINE__) RGBATimeline(frames, frames << 2, slotIndex); keyMap = timelineMap->_child; float time = Json::getFloat(keyMap, "time", 0); - toColor(color, Json::getString(keyMap, "color", 0), true); + Color::valueOf(Json::getString(keyMap, "color", 0), color); for (frame = 0, bezier = 0;; ++frame) { timeline->setFrame(frame, time, color.r, color.g, color.b, color.a); @@ -987,7 +800,7 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { break; } float time2 = Json::getFloat(nextMap, "time", 0); - toColor(newColor, Json::getString(nextMap, "color", 0), true); + Color::valueOf(Json::getString(nextMap, "color", 0), newColor); curve = Json::getItem(keyMap, "curve"); if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); @@ -1004,7 +817,12 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { RGBTimeline *timeline = new (__FILE__, __LINE__) RGBTimeline(frames, frames * 3, slotIndex); keyMap = timelineMap->_child; float time = Json::getFloat(keyMap, "time", 0); - toColor(color, Json::getString(keyMap, "color", 0), false); + const char* colorStr = Json::getString(keyMap, "color", 0); + if (colorStr && strlen(colorStr) >= 6) { + color.r = Color::parseHex(colorStr, 0); + color.g = Color::parseHex(colorStr, 1); + color.b = Color::parseHex(colorStr, 2); + } for (frame = 0, bezier = 0;; ++frame) { timeline->setFrame(frame, time, color.r, color.g, color.b); @@ -1014,7 +832,12 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { break; } float time2 = Json::getFloat(nextMap, "time", 0); - toColor(newColor, Json::getString(nextMap, "color", 0), false); + const char* colorStr = Json::getString(nextMap, "color", 0); + if (colorStr && strlen(colorStr) >= 6) { + newColor.r = Color::parseHex(colorStr, 0); + newColor.g = Color::parseHex(colorStr, 1); + newColor.b = Color::parseHex(colorStr, 2); + } curve = Json::getItem(keyMap, "curve"); if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); @@ -1034,8 +857,13 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { RGBA2Timeline *timeline = new (__FILE__, __LINE__) RGBA2Timeline(frames, frames * 7, slotIndex); keyMap = timelineMap->_child; float time = Json::getFloat(keyMap, "time", 0); - toColor(color, Json::getString(keyMap, "light", 0), true); - toColor(color2, Json::getString(keyMap, "dark", 0), false); + 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 (frame = 0, bezier = 0;; ++frame) { timeline->setFrame(frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b); @@ -1045,8 +873,13 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { break; } float time2 = Json::getFloat(nextMap, "time", 0); - toColor(newColor, Json::getString(nextMap, "light", 0), true); - toColor(newColor2, Json::getString(nextMap, "dark", 0), false); + Color::valueOf(Json::getString(nextMap, "light", 0), newColor); + const char* darkStr = Json::getString(nextMap, "dark", 0); + if (darkStr && strlen(darkStr) >= 6) { + newColor2.r = Color::parseHex(darkStr, 0); + newColor2.g = Color::parseHex(darkStr, 1); + newColor2.b = Color::parseHex(darkStr, 2); + } curve = Json::getItem(keyMap, "curve"); if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); @@ -1067,8 +900,18 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { RGBA2Timeline *timeline = new (__FILE__, __LINE__) RGBA2Timeline(frames, frames * 6, slotIndex); keyMap = timelineMap->_child; float time = Json::getFloat(keyMap, "time", 0); - toColor(color, Json::getString(keyMap, "light", 0), false); - toColor(color2, Json::getString(keyMap, "dark", 0), false); + const char* lightStr = Json::getString(keyMap, "light", 0); + 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 (frame = 0, bezier = 0;; ++frame) { timeline->setFrame(frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b); @@ -1078,8 +921,18 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { break; } float time2 = Json::getFloat(nextMap, "time", 0); - toColor(newColor, Json::getString(nextMap, "light", 0), false); - toColor(newColor2, Json::getString(nextMap, "dark", 0), false); + const char* lightStr = Json::getString(nextMap, "light", 0); + if (lightStr && strlen(lightStr) >= 6) { + newColor.r = Color::parseHex(lightStr, 0); + newColor.g = Color::parseHex(lightStr, 1); + newColor.b = Color::parseHex(lightStr, 2); + } + const char* darkStr = Json::getString(nextMap, "dark", 0); + if (darkStr && strlen(darkStr) >= 6) { + newColor2.r = Color::parseHex(darkStr, 0); + newColor2.g = Color::parseHex(darkStr, 1); + newColor2.b = Color::parseHex(darkStr, 2); + } curve = Json::getItem(keyMap, "curve"); if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); @@ -1351,7 +1204,7 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { for (Json *constraintMap = physics ? physics->_child : 0; constraintMap; constraintMap = constraintMap->_next) { int index = -1; if (constraintMap->_name && strlen(constraintMap->_name) > 0) { - PhysicsConstraintData *constraint = skeletonData->findPhysicsConstraint(constraintMap->_name); + PhysicsConstraintData *constraint = skeletonData->findConstraint(constraintMap->_name); if (!constraint) { ContainerUtil::cleanUpVectorOfPointers(timelines); setError(NULL, "Physics constraint not found: ", constraintMap->_name); @@ -1531,7 +1384,7 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { for (ii = (int) skeletonData->_slots.size() - 1; ii >= 0; ii--) if (drawOrder2[ii] == -1) drawOrder2[ii] = unchanged[--unchangedIndex]; } - timeline->setFrame(frame, Json::getFloat(keyMap, "time", 0), drawOrder2); + timeline->setFrame(frame, Json::getFloat(keyMap, "time", 0), &drawOrder2); } timelines.add(timeline); } @@ -1568,47 +1421,82 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { 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; +void SkeletonJson::readTimeline(Vector &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; } - - attachment->getVertices().clearAndAddAll(vertices); - 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; } +} - 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]); +void SkeletonJson::readTimeline(Vector &timelines, Json *keyMap, CurveTimeline2 *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; } +} - attachment->getVertices().clearAndAddAll(bonesAndWeights._vertices); - attachment->getBones().clearAndAddAll(bonesAndWeights._bones); +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, Vector timelines) { + int slotIndex = ContainerUtil::findIndexWithName(skeletonData->getSlots(), slotName); + if (slotIndex == -1) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Slot not found: ", slotName); + } + return slotIndex; } void SkeletonJson::setError(Json *root, const String &value1, const String &value2) {