diff --git a/spine-as3/README.md b/spine-as3/README.md index 11b8494fa..377c0b98c 100644 --- a/spine-as3/README.md +++ b/spine-as3/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-as3 works with data exported from the latest version of Spine, except linked meshes are [not yet supported](https://trello.com/c/bERJAFEq/73-update-runtimes-to-support-v3-1-linked-meshes). +spine-as3 works with data exported from the latest version of Spine. spine-as3 supports all Spine features, including meshes. If using the `spine.flash` classes for rendering, meshes are not supported. diff --git a/spine-as3/spine-as3-example/lib/spine-as3.swc b/spine-as3/spine-as3-example/lib/spine-as3.swc index 873bc9063..3f90b6481 100644 Binary files a/spine-as3/spine-as3-example/lib/spine-as3.swc and b/spine-as3/spine-as3-example/lib/spine-as3.swc differ diff --git a/spine-as3/spine-as3/src/spine/Bone.as b/spine-as3/spine-as3/src/spine/Bone.as index fa1497e96..9e70baa93 100644 --- a/spine-as3/spine-as3/src/spine/Bone.as +++ b/spine-as3/spine-as3/src/spine/Bone.as @@ -119,85 +119,79 @@ public class Bone implements Updatable { _b = pa * lb + pb * ld; _c = pc * la + pd * lc; _d = pc * lb + pd * ld; - } else if (data.inheritRotation) { // No scale inheritance. - pa = 1; - pb = 0; - pc = 0; - pd = 1; - do { - radians = parent.appliedRotation * MathUtils.degRad; - cos = Math.cos(radians); - sin = Math.sin(radians); - var temp1:Number = pa * cos + pb * sin; - pb = pa * -sin + pb * cos; - pa = temp1; - temp1 = pc * cos + pd * sin; - pd = pc * -sin + pd * cos; - pc = temp1; - - if (!parent.data.inheritRotation) break; - parent = parent.parent; - } while (parent != null); - _a = pa * la + pb * lc; - _b = pa * lb + pb * ld; - _c = pc * la + pd * lc; - _d = pc * lb + pd * ld; - if (_skeleton.flipX) { - _a = -_a; - _b = -_b; - } - if (_skeleton.flipY != yDown) { - _c = -_c; - _d = -_d; - } - } else if (data.inheritScale) { // No rotation inheritance. - pa = 1; - pb = 0; - pc = 0; - pd = 1; - do { - radians = parent.rotation * MathUtils.degRad; - cos = Math.cos(radians); - sin = Math.sin(radians); - var psx:Number = parent.appliedScaleX, psy:Number = parent.appliedScaleY; - var za:Number = cos * psx, zb:Number = -sin * psy, zc:Number = sin * psx, zd:Number = cos * psy; - var temp2:Number = pa * za + pb * zc; - pb = pa * zb + pb * zd; - pa = temp2; - temp2 = pc * za + pd * zc; - pd = pc * zb + pd * zd; - pc = temp2; - - if (psx < 0) radians = -radians; - cos = Math.cos(-radians); - sin = Math.sin(-radians); - temp2 = pa * cos + pb * sin; - pb = pa * -sin + pb * cos; - pa = temp2; - temp2 = pc * cos + pd * sin; - pd = pc * -sin + pd * cos; - pc = temp2; - - if (!parent.data.inheritScale) break; - parent = parent.parent; - } while (parent != null); - _a = pa * la + pb * lc; - _b = pa * lb + pb * ld; - _c = pc * la + pd * lc; - _d = pc * lb + pd * ld; - if (_skeleton.flipX) { - _a = -_a; - _b = -_b; - } - if (_skeleton.flipY != yDown) { - _c = -_c; - _d = -_d; - } } else { - _a = la; - _b = lb; - _c = lc; - _d = ld; + if (data.inheritRotation) { // No scale inheritance. + pa = 1; + pb = 0; + pc = 0; + pd = 1; + do { + radians = parent.appliedRotation * MathUtils.degRad; + cos = Math.cos(radians); + sin = Math.sin(radians); + var temp1:Number = pa * cos + pb * sin; + pb = pa * -sin + pb * cos; + pa = temp1; + temp1 = pc * cos + pd * sin; + pd = pc * -sin + pd * cos; + pc = temp1; + + if (!parent.data.inheritRotation) break; + parent = parent.parent; + } while (parent != null); + _a = pa * la + pb * lc; + _b = pa * lb + pb * ld; + _c = pc * la + pd * lc; + _d = pc * lb + pd * ld; + } else if (data.inheritScale) { // No rotation inheritance. + pa = 1; + pb = 0; + pc = 0; + pd = 1; + do { + radians = parent.rotation * MathUtils.degRad; + cos = Math.cos(radians); + sin = Math.sin(radians); + var psx:Number = parent.appliedScaleX, psy:Number = parent.appliedScaleY; + var za:Number = cos * psx, zb:Number = -sin * psy, zc:Number = sin * psx, zd:Number = cos * psy; + var temp2:Number = pa * za + pb * zc; + pb = pa * zb + pb * zd; + pa = temp2; + temp2 = pc * za + pd * zc; + pd = pc * zb + pd * zd; + pc = temp2; + + if (psx < 0) radians = -radians; + cos = Math.cos(-radians); + sin = Math.sin(-radians); + temp2 = pa * cos + pb * sin; + pb = pa * -sin + pb * cos; + pa = temp2; + temp2 = pc * cos + pd * sin; + pd = pc * -sin + pd * cos; + pc = temp2; + + if (!parent.data.inheritScale) break; + parent = parent.parent; + } while (parent != null); + _a = pa * la + pb * lc; + _b = pa * lb + pb * ld; + _c = pc * la + pd * lc; + _d = pc * lb + pd * ld; + } else { + _a = la; + _b = lb; + _c = lc; + _d = ld; + } + if (_skeleton.flipX) { + _a = -_a; + _b = -_b; + } + if (_skeleton.flipY != yDown) { + _c = -_c; + _d = -_d; + } } } diff --git a/spine-as3/spine-as3/src/spine/IkConstraint.as b/spine-as3/spine-as3/src/spine/IkConstraint.as index f0ad1dbc1..03621ae54 100644 --- a/spine-as3/spine-as3/src/spine/IkConstraint.as +++ b/spine-as3/spine-as3/src/spine/IkConstraint.as @@ -84,7 +84,7 @@ public class IkConstraint implements Updatable { rotationIK = 360 - rotationIK; if (rotationIK > 180) rotationIK -= 360; else if (rotationIK < -180) rotationIK += 360; - bone.updateWorldTransformWith(bone.x, bone.y, rotation + (rotationIK - rotation) * alpha, bone.scaleX, bone.scaleY); + bone.updateWorldTransformWith(bone.x, bone.y, rotation + (rotationIK - rotation) * alpha, bone.appliedScaleX, bone.appliedScaleY); } /** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The @@ -92,26 +92,32 @@ public class IkConstraint implements Updatable { * @param child Any descendant bone of the parent. */ static public function apply2 (parent:Bone, child:Bone, targetX:Number, targetY:Number, bendDir:int, alpha:Number) : void { if (alpha == 0) return; - var px:Number = parent.x, py:Number = parent.y, psx:Number = parent.scaleX, psy:Number = parent.scaleY; - var csx:Number = child.scaleX, cy:Number = child.y; - var offset1:int, offset2:int, sign2:int; + var px:Number = parent.x, py:Number = parent.y, psx:Number = parent.appliedScaleX, psy:Number = parent.appliedScaleY; + var o1:int, o2:int, s2:int; if (psx < 0) { psx = -psx; - offset1 = 180; - sign2 = -1; + o1 = 180; + s2 = -1; } else { - offset1 = 0; - sign2 = 1; + o1 = 0; + s2 = 1; } if (psy < 0) { psy = -psy; - sign2 = -sign2; + s2 = -s2; + } + var cx:Number = child.x, cy:Number = child.y, csx:Number = child.appliedScaleX; + var u:Boolean = Math.abs(psx - psy) <= 0.0001; + if (!u && cy != 0) { + child._worldX = parent.a * cx + parent.worldX; + child._worldY = parent.c * cx + parent.worldY; + cy = 0; } if (csx < 0) { csx = -csx; - offset2 = 180; + o2 = 180; } else - offset2 = 0; + o2 = 0; var pp:Bone = parent.parent; var tx:Number, ty:Number, dx:Number, dy:Number; if (!pp) { @@ -132,7 +138,7 @@ public class IkConstraint implements Updatable { } var l1:Number = Math.sqrt(dx * dx + dy * dy), l2:Number = child.data.length * csx, a1:Number, a2:Number; outer: - if (Math.abs(psx - psy) <= 0.0001) { + if (u) { l2 *= psx; var cos:Number = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2); if (cos < -1) cos = -1; @@ -141,7 +147,6 @@ public class IkConstraint implements Updatable { var ad:Number = l1 + l2 * cos, o:Number = l2 * Math.sin(a2); a1 = Math.atan2(ty * ad - tx * o, tx * ad + ty * o); } else { - cy = 0; var a:Number = psx * l2, b:Number = psy * l2, ta:Number = Math.atan2(ty, tx); var aa:Number = a * a, bb:Number = b * b, ll:Number = l1 * l1, dd:Number = tx * tx + ty * ty; var c0:Number = bb * ll + aa * dd - aa * bb, c1:Number = -2 * bb * l1, c2:Number = bb - aa; @@ -198,17 +203,17 @@ public class IkConstraint implements Updatable { a2 = maxAngle * bendDir; } } - var offset:Number = Math.atan2(cy, child.x) * sign2; - a1 = (a1 - offset) * MathUtils.radDeg + offset1; - a2 = (a2 + offset) * MathUtils.radDeg * sign2 + offset2; + var os:Number = Math.atan2(cy, cx) * s2; + a1 = (a1 - os) * MathUtils.radDeg + o1; + a2 = (a2 + os) * MathUtils.radDeg * s2 + o2; if (a1 > 180) a1 -= 360; else if (a1 < -180) a1 += 360; if (a2 > 180) a2 -= 360; else if (a2 < -180) a2 += 360; var rotation:Number = parent.rotation; - parent.updateWorldTransformWith(parent.x, parent.y, rotation + (a1 - rotation) * alpha, parent.scaleX, parent.scaleY); + parent.updateWorldTransformWith(px, py, rotation + (a1 - rotation) * alpha, parent.appliedScaleX, parent.appliedScaleY); rotation = child.rotation; - child.updateWorldTransformWith(child.x, cy, rotation + (a2 - rotation) * alpha, child.scaleX, child.scaleY); + child.updateWorldTransformWith(cx, cy, rotation + (a2 - rotation) * alpha, child.appliedScaleX, child.appliedScaleY); } } diff --git a/spine-as3/spine-as3/src/spine/SkeletonJson.as b/spine-as3/spine-as3/src/spine/SkeletonJson.as index 09708e338..24294e038 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonJson.as +++ b/spine-as3/spine-as3/src/spine/SkeletonJson.as @@ -29,235 +29,279 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ package spine { - import flash.utils.ByteArray; +import flash.utils.ByteArray; - import spine.animation.Animation; - import spine.animation.AttachmentTimeline; - import spine.animation.ColorTimeline; - import spine.animation.CurveTimeline; - import spine.animation.DrawOrderTimeline; - import spine.animation.EventTimeline; - import spine.animation.FfdTimeline; - import spine.animation.IkConstraintTimeline; - import spine.animation.RotateTimeline; - import spine.animation.ScaleTimeline; - import spine.animation.Timeline; - import spine.animation.TranslateTimeline; - import spine.attachments.Attachment; - import spine.attachments.AttachmentLoader; - import spine.attachments.AttachmentType; - import spine.attachments.BoundingBoxAttachment; - import spine.attachments.MeshAttachment; - import spine.attachments.RegionAttachment; - import spine.attachments.WeightedMeshAttachment; +import spine.animation.Animation; +import spine.animation.AttachmentTimeline; +import spine.animation.ColorTimeline; +import spine.animation.CurveTimeline; +import spine.animation.DrawOrderTimeline; +import spine.animation.EventTimeline; +import spine.animation.FfdTimeline; +import spine.animation.IkConstraintTimeline; +import spine.animation.RotateTimeline; +import spine.animation.ScaleTimeline; +import spine.animation.Timeline; +import spine.animation.TranslateTimeline; +import spine.attachments.Attachment; +import spine.attachments.AttachmentLoader; +import spine.attachments.AttachmentType; +import spine.attachments.BoundingBoxAttachment; +import spine.attachments.MeshAttachment; +import spine.attachments.RegionAttachment; +import spine.attachments.WeightedMeshAttachment; - public class SkeletonJson { - public var attachmentLoader:AttachmentLoader; - public var scale:Number = 1; +public class SkeletonJson { + public var attachmentLoader:AttachmentLoader; + public var scale:Number = 1; + private var linkedMeshes:Vector. = new Vector.(); - public function SkeletonJson(attachmentLoader:AttachmentLoader = null) { - this.attachmentLoader = attachmentLoader; + public function SkeletonJson (attachmentLoader:AttachmentLoader = null) { + this.attachmentLoader = attachmentLoader; + } + + /** @param object A String or ByteArray. */ + public function readSkeletonData (object:*, name:String = null) : SkeletonData { + if (object == null) throw new ArgumentError("object cannot be null."); + + var root:Object; + if (object is String) + root = JSON.parse(String(object)); + else if (object is ByteArray) + root = JSON.parse(ByteArray(object).readUTFBytes(ByteArray(object).length)); + else if (object is Object) + root = object; + else + throw new ArgumentError("object must be a String, ByteArray or Object."); + + var skeletonData:SkeletonData = new SkeletonData(); + skeletonData.name = name; + + // Skeleton. + var skeletonMap:Object = root["skeleton"]; + if (skeletonMap) { + skeletonData.hash = skeletonMap["hash"]; + skeletonData.version = skeletonMap["spine"]; + skeletonData.width = skeletonMap["width"] || 0; + skeletonData.height = skeletonMap["height"] || 0; } - /** @param object A String or ByteArray. */ - public function readSkeletonData(object:*, name:String = null) : SkeletonData { - if (object == null) throw new ArgumentError("object cannot be null."); + // Bones. + var boneData:BoneData; + for each (var boneMap:Object in root["bones"]) { + var parent:BoneData = null; + var parentName:String = boneMap["parent"]; + if (parentName) { + parent = skeletonData.findBone(parentName); + if (!parent) throw new Error("Parent bone not found: " + parentName); + } + boneData = new BoneData(boneMap["name"], parent); + boneData.length = Number(boneMap["length"] || 0) * scale; + boneData.x = Number(boneMap["x"] || 0) * scale; + boneData.y = Number(boneMap["y"] || 0) * scale; + boneData.rotation = (boneMap["rotation"] || 0); + boneData.scaleX = boneMap.hasOwnProperty("scaleX") ? boneMap["scaleX"] : 1; + boneData.scaleY = boneMap.hasOwnProperty("scaleY") ? boneMap["scaleY"] : 1; + boneData.inheritScale = boneMap.hasOwnProperty("inheritScale") ? boneMap["inheritScale"] : true; + boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true; + skeletonData.bones[skeletonData.bones.length] = boneData; + } - var root:Object; - if (object is String) - root = JSON.parse(String(object)); - else if (object is ByteArray) - root = JSON.parse(ByteArray(object).readUTFBytes(ByteArray(object).length)); - else if (object is Object) - root = object; - else - throw new ArgumentError("object must be a String, ByteArray or Object."); + // IK constraints. + for each (var ikMap:Object in root["ik"]) { + var ikConstraintData:IkConstraintData = new IkConstraintData(ikMap["name"]); - var skeletonData:SkeletonData = new SkeletonData(); - skeletonData.name = name; - - // Skeleton. - var skeletonMap:Object = root["skeleton"]; - if (skeletonMap) { - skeletonData.hash = skeletonMap["hash"]; - skeletonData.version = skeletonMap["spine"]; - skeletonData.width = skeletonMap["width"] || 0; - skeletonData.height = skeletonMap["height"] || 0; + for each (var boneName:String in ikMap["bones"]) { + var bone:BoneData = skeletonData.findBone(boneName); + if (!bone) throw new Error("IK bone not found: " + boneName); + ikConstraintData.bones[ikConstraintData.bones.length] = bone; } - // Bones. - var boneData:BoneData; - for each (var boneMap:Object in root["bones"]) { - var parent:BoneData = null; - var parentName:String = boneMap["parent"]; - if (parentName) { - parent = skeletonData.findBone(parentName); - if (!parent) throw new Error("Parent bone not found: " + parentName); + ikConstraintData.target = skeletonData.findBone(ikMap["target"]); + if (!ikConstraintData.target) throw new Error("Target bone not found: " + ikMap["target"]); + + ikConstraintData.bendDirection = (!ikMap.hasOwnProperty("bendPositive") || ikMap["bendPositive"]) ? 1 : -1; + ikConstraintData.mix = ikMap.hasOwnProperty("mix") ? ikMap["mix"] : 1; + + skeletonData.ikConstraints[skeletonData.ikConstraints.length] = ikConstraintData; + } + + // Transform constraints. + for each (var transformMap:Object in root["transform"]) { + var transformConstraintData:TransformConstraintData = new TransformConstraintData(transformMap["name"]); + + transformConstraintData.bone = skeletonData.findBone(transformMap["bone"]); + if (!transformConstraintData.bone) throw new Error("Bone not found: " + transformMap["bone"]); + + transformConstraintData.target = skeletonData.findBone(transformMap["target"]); + if (!transformConstraintData.target) throw new Error("Target bone not found: " + transformMap["target"]); + + transformConstraintData.translateMix = transformMap.hasOwnProperty("translateMix") ? transformMap["translateMix"] : 1; + transformConstraintData.x = Number(boneMap["x"] || 0) * scale; + transformConstraintData.y = Number(boneMap["y"] || 0) * scale; + + skeletonData.transformConstraints[skeletonData.transformConstraints.length] = transformConstraintData; + } + + // Slots. + for each (var slotMap:Object in root["slots"]) { + boneName = slotMap["bone"]; + boneData = skeletonData.findBone(boneName); + if (!boneData) throw new Error("Slot bone not found: " + boneName); + var slotData:SlotData = new SlotData(slotMap["name"], boneData); + + var color:String = slotMap["color"]; + if (color) { + slotData.r = toColor(color, 0); + slotData.g = toColor(color, 1); + slotData.b = toColor(color, 2); + slotData.a = toColor(color, 3); + } + + slotData.attachmentName = slotMap["attachment"]; + slotData.blendMode = BlendMode[slotMap["blend"] || "normal"]; + + skeletonData.slots[skeletonData.slots.length] = slotData; + } + + // Skins. + var skins:Object = root["skins"]; + for (var skinName:String in skins) { + var skinMap:Object = skins[skinName]; + var skin:Skin = new Skin(skinName); + for (var slotName:String in skinMap) { + var slotIndex:int = skeletonData.findSlotIndex(slotName); + var slotEntry:Object = skinMap[slotName]; + for (var attachmentName:String in slotEntry) { + var attachment:Attachment = readAttachment(skin, slotIndex, attachmentName, slotEntry[attachmentName]); + if (attachment != null) + skin.addAttachment(slotIndex, attachmentName, attachment); } - boneData = new BoneData(boneMap["name"], parent); - boneData.length = Number(boneMap["length"] || 0) * scale; - boneData.x = Number(boneMap["x"] || 0) * scale; - boneData.y = Number(boneMap["y"] || 0) * scale; - boneData.rotation = (boneMap["rotation"] || 0); - boneData.scaleX = boneMap.hasOwnProperty("scaleX") ? boneMap["scaleX"] : 1; - boneData.scaleY = boneMap.hasOwnProperty("scaleY") ? boneMap["scaleY"] : 1; - boneData.inheritScale = boneMap.hasOwnProperty("inheritScale") ? boneMap["inheritScale"] : true; - boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true; - skeletonData.bones[skeletonData.bones.length] = boneData; } + skeletonData.skins[skeletonData.skins.length] = skin; + if (skin.name == "default") + skeletonData.defaultSkin = skin; + } - // IK constraints. - for each (var ikMap:Object in root["ik"]) { - var ikConstraintData:IkConstraintData = new IkConstraintData(ikMap["name"]); - - for each (var boneName:String in ikMap["bones"]) { - var bone:BoneData = skeletonData.findBone(boneName); - if (!bone) throw new Error("IK bone not found: " + boneName); - ikConstraintData.bones[ikConstraintData.bones.length] = bone; - } - - ikConstraintData.target = skeletonData.findBone(ikMap["target"]); - if (!ikConstraintData.target) throw new Error("Target bone not found: " + ikMap["target"]); - - ikConstraintData.bendDirection = (!ikMap.hasOwnProperty("bendPositive") || ikMap["bendPositive"]) ? 1 : -1; - ikConstraintData.mix = ikMap.hasOwnProperty("mix") ? ikMap["mix"] : 1; - - skeletonData.ikConstraints[skeletonData.ikConstraints.length] = ikConstraintData; + // Linked meshes. + for each (var linkedMesh:LinkedMesh in linkedMeshes) { + var parentSkin:Skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); + if (!parentSkin) throw new Error("Skin not found: " + linkedMesh.skin); + var parentMesh:Attachment = parentSkin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + if (!parentMesh) throw new Error("Parent mesh not found: " + linkedMesh.parent); + if (linkedMesh.mesh is MeshAttachment) { + var mesh:MeshAttachment = MeshAttachment(linkedMesh.mesh); + mesh.parentMesh = MeshAttachment(parentMesh); + mesh.updateUVs(); + } else { + var weightedMesh:WeightedMeshAttachment = WeightedMeshAttachment(linkedMesh.mesh); + weightedMesh.parentMesh = WeightedMeshAttachment(parentMesh); + weightedMesh.updateUVs(); } + } + linkedMeshes.length = 0; - // Transform constraints. - for each (var transformMap:Object in root["transform"]) { - var transformConstraintData:TransformConstraintData = new TransformConstraintData(transformMap["name"]); - - transformConstraintData.bone = skeletonData.findBone(transformMap["bone"]); - if (!transformConstraintData.bone) throw new Error("Bone not found: " + transformMap["bone"]); - - transformConstraintData.target = skeletonData.findBone(transformMap["target"]); - if (!transformConstraintData.target) throw new Error("Target bone not found: " + transformMap["target"]); - - transformConstraintData.translateMix = transformMap.hasOwnProperty("translateMix") ? transformMap["translateMix"] : 1; - transformConstraintData.x = Number(boneMap["x"] || 0) * scale; - transformConstraintData.y = Number(boneMap["y"] || 0) * scale; - - skeletonData.transformConstraints[skeletonData.transformConstraints.length] = transformConstraintData; + // Events. + var events:Object = root["events"]; + if (events) { + for (var eventName:String in events) { + var eventMap:Object = events[eventName]; + var eventData:EventData = new EventData(eventName); + eventData.intValue = eventMap["int"] || 0; + eventData.floatValue = eventMap["float"] || 0; + eventData.stringValue = eventMap["string"] || null; + skeletonData.events[skeletonData.events.length] = eventData; } + } - // Slots. - for each (var slotMap:Object in root["slots"]) { - boneName = slotMap["bone"]; - boneData = skeletonData.findBone(boneName); - if (!boneData) throw new Error("Slot bone not found: " + boneName); - var slotData:SlotData = new SlotData(slotMap["name"], boneData); + // Animations. + var animations:Object = root["animations"]; + for (var animationName:String in animations) + readAnimation(animationName, animations[animationName], skeletonData); - var color:String = slotMap["color"]; + return skeletonData; + } + + private function readAttachment (skin:Skin, slotIndex:int, name:String, map:Object) : Attachment { + name = map["name"] || name; + + var typeName:String = map["type"] || "region"; + if (typeName == "skinnedmesh") typeName = "weightedmesh"; + var type:AttachmentType = AttachmentType[typeName]; + var path:String = map["path"] || name; + + var scale:Number = this.scale; + var color:String, vertices:Vector.; + switch (type) { + case AttachmentType.region: + var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, path); + if (!region) return null; + region.path = path; + region.x = Number(map["x"] || 0) * scale; + region.y = Number(map["y"] || 0) * scale; + region.scaleX = map.hasOwnProperty("scaleX") ? map["scaleX"] : 1; + region.scaleY = map.hasOwnProperty("scaleY") ? map["scaleY"] : 1; + region.rotation = map["rotation"] || 0; + region.width = Number(map["width"] || 0) * scale; + region.height = Number(map["height"] || 0) * scale; + color = map["color"]; if (color) { - slotData.r = toColor(color, 0); - slotData.g = toColor(color, 1); - slotData.b = toColor(color, 2); - slotData.a = toColor(color, 3); + region.r = toColor(color, 0); + region.g = toColor(color, 1); + region.b = toColor(color, 2); + region.a = toColor(color, 3); + } + region.updateOffset(); + return region; + case AttachmentType.mesh: + case AttachmentType.linkedmesh: + var mesh:MeshAttachment = attachmentLoader.newMeshAttachment(skin, name, path); + if (!mesh) return null; + mesh.path = path; + + color = map["color"]; + if (color) { + mesh.r = toColor(color, 0); + mesh.g = toColor(color, 1); + mesh.b = toColor(color, 2); + mesh.a = toColor(color, 3); } - slotData.attachmentName = slotMap["attachment"]; - slotData.blendMode = BlendMode[slotMap["blend"] || "normal"]; + mesh.width = Number(map["width"] || 0) * scale; + mesh.height = Number(map["height"] || 0) * scale; - skeletonData.slots[skeletonData.slots.length] = slotData; - } - - // Skins. - var skins:Object = root["skins"]; - for (var skinName:String in skins) { - var skinMap:Object = skins[skinName]; - var skin:Skin = new Skin(skinName); - for (var slotName:String in skinMap) { - var slotIndex:int = skeletonData.findSlotIndex(slotName); - var slotEntry:Object = skinMap[slotName]; - for (var attachmentName:String in slotEntry) { - var attachment:Attachment = readAttachment(skin, attachmentName, slotEntry[attachmentName]); - if (attachment != null) - skin.addAttachment(slotIndex, attachmentName, attachment); - } - } - skeletonData.skins[skeletonData.skins.length] = skin; - if (skin.name == "default") - skeletonData.defaultSkin = skin; - } - - // Events. - var events:Object = root["events"]; - if (events) { - for (var eventName:String in events) { - var eventMap:Object = events[eventName]; - var eventData:EventData = new EventData(eventName); - eventData.intValue = eventMap["int"] || 0; - eventData.floatValue = eventMap["float"] || 0; - eventData.stringValue = eventMap["string"] || null; - skeletonData.events[skeletonData.events.length] = eventData; - } - } - - // Animations. - var animations:Object = root["animations"]; - for (var animationName:String in animations) - readAnimation(animationName, animations[animationName], skeletonData); - - return skeletonData; - } - - private function readAttachment(skin:Skin, name:String, map:Object) : Attachment { - name = map["name"] || name; - - var typeName:String = map["type"] || "region"; - if (typeName == "skinnedmesh") typeName = "weightedmesh"; - var type:AttachmentType = AttachmentType[typeName]; - var path:String = map["path"] || name; - - var scale:Number = this.scale; - var color:String, vertices:Vector.; - switch (type) { - case AttachmentType.region: - var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, path); - if (!region) return null; - region.path = path; - region.x = Number(map["x"] || 0) * scale; - region.y = Number(map["y"] || 0) * scale; - region.scaleX = map.hasOwnProperty("scaleX") ? map["scaleX"] : 1; - region.scaleY = map.hasOwnProperty("scaleY") ? map["scaleY"] : 1; - region.rotation = map["rotation"] || 0; - region.width = Number(map["width"] || 0) * scale; - region.height = Number(map["height"] || 0) * scale; - color = map["color"]; - if (color) { - region.r = toColor(color, 0); - region.g = toColor(color, 1); - region.b = toColor(color, 2); - region.a = toColor(color, 3); - } - region.updateOffset(); - return region; - case AttachmentType.mesh: - var mesh:MeshAttachment = attachmentLoader.newMeshAttachment(skin, name, path); - if (!mesh) return null; - mesh.path = path; + if (!map["parent"]) { mesh.vertices = getFloatArray(map, "vertices", scale); mesh.triangles = getUintArray(map, "triangles"); mesh.regionUVs = getFloatArray(map, "uvs", 1); mesh.updateUVs(); - color = map["color"]; - if (color) { - mesh.r = toColor(color, 0); - mesh.g = toColor(color, 1); - mesh.b = toColor(color, 2); - mesh.a = toColor(color, 3); - } + mesh.hullLength = int(map["hull"] || 0) * 2; if (map["edges"]) mesh.edges = getIntArray(map, "edges"); - mesh.width = Number(map["width"] || 0) * scale; - mesh.height = Number(map["height"] || 0) * scale; - return mesh; - case AttachmentType.weightedmesh: - var weightedMesh:WeightedMeshAttachment = attachmentLoader.newWeightedMeshAttachment(skin, name, path); - if (!weightedMesh) return null; - weightedMesh.path = path; + } else { + mesh.inheritFFD = map.hasOwnProperty("ffd") ? map["ffd"] : true; + linkedMeshes[linkedMeshes.length] = new LinkedMesh(mesh, map["skin"], slotIndex, map["parent"]); + } + return mesh; + case AttachmentType.weightedmesh: + case AttachmentType.weightedlinkedmesh: + var weightedMesh:WeightedMeshAttachment = attachmentLoader.newWeightedMeshAttachment(skin, name, path); + if (!weightedMesh) return null; + + weightedMesh.path = path; + + color = map["color"]; + if (color) { + weightedMesh.r = toColor(color, 0); + weightedMesh.g = toColor(color, 1); + weightedMesh.b = toColor(color, 2); + weightedMesh.a = toColor(color, 3); + } + + weightedMesh.width = Number(map["width"] || 0) * scale; + weightedMesh.height = Number(map["height"] || 0) * scale; + + if (!map["parent"]) { var uvs:Vector. = getFloatArray(map, "uvs", 1); vertices = getFloatArray(map, "vertices", 1); var weights:Vector. = new Vector.(); @@ -278,297 +322,309 @@ package spine { weightedMesh.triangles = getUintArray(map, "triangles"); weightedMesh.regionUVs = uvs; weightedMesh.updateUVs(); - color = map["color"]; - if (color) { - weightedMesh.r = toColor(color, 0); - weightedMesh.g = toColor(color, 1); - weightedMesh.b = toColor(color, 2); - weightedMesh.a = toColor(color, 3); - } + weightedMesh.hullLength = int(map["hull"] || 0) * 2; if (map["edges"]) weightedMesh.edges = getIntArray(map, "edges"); - weightedMesh.width = Number(map["width"] || 0) * scale; - weightedMesh.height = Number(map["height"] || 0) * scale; - return weightedMesh; - case AttachmentType.boundingbox: - var box:BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name); - vertices = box.vertices; - for each (var point:Number in map["vertices"]) - vertices[vertices.length] = point * scale; - return box; - } - - return null; + } else { + weightedMesh.inheritFFD = map.hasOwnProperty("ffd") ? map["ffd"] : true; + linkedMeshes[linkedMeshes.length] = new LinkedMesh(weightedMesh, map["skin"], slotIndex, map["parent"]); + } + return weightedMesh; + case AttachmentType.boundingbox: + var box:BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name); + vertices = box.vertices; + for each (var point:Number in map["vertices"]) + vertices[vertices.length] = point * scale; + return box; } - private function readAnimation(name:String, map:Object, skeletonData:SkeletonData) : void { - var timelines:Vector. = new Vector.(); - var duration:Number = 0; + return null; + } - var slotMap:Object, slotIndex:int, slotName:String; - var values:Array, valueMap:Object, frameIndex:int; - var i:int; - var timelineName:String; + private function readAnimation (name:String, map:Object, skeletonData:SkeletonData) : void { + var timelines:Vector. = new Vector.(); + var duration:Number = 0; - var slots:Object = map["slots"]; - for (slotName in slots) { - slotMap = slots[slotName]; + var slotMap:Object, slotIndex:int, slotName:String; + var values:Array, valueMap:Object, frameIndex:int; + var i:int; + var timelineName:String; + + var slots:Object = map["slots"]; + for (slotName in slots) { + slotMap = slots[slotName]; + slotIndex = skeletonData.findSlotIndex(slotName); + + for (timelineName in slotMap) { + values = slotMap[timelineName]; + if (timelineName == "color") { + var colorTimeline:ColorTimeline = new ColorTimeline(values.length); + colorTimeline.slotIndex = slotIndex; + + frameIndex = 0; + for each (valueMap in values) { + var color:String = valueMap["color"]; + var r:Number = toColor(color, 0); + var g:Number = toColor(color, 1); + var b:Number = toColor(color, 2); + var a:Number = toColor(color, 3); + colorTimeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); + readCurve(colorTimeline, frameIndex, valueMap); + frameIndex++; + } + timelines[timelines.length] = colorTimeline; + duration = Math.max(duration, colorTimeline.frames[colorTimeline.frameCount * 5 - 5]); + } else if (timelineName == "attachment") { + var attachmentTimeline:AttachmentTimeline = new AttachmentTimeline(values.length); + attachmentTimeline.slotIndex = slotIndex; + + frameIndex = 0; + for each (valueMap in values) + attachmentTimeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); + timelines[timelines.length] = attachmentTimeline; + duration = Math.max(duration, attachmentTimeline.frames[attachmentTimeline.frameCount - 1]); + } else + throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); + } + } + + var bones:Object = map["bones"]; + for (var boneName:String in bones) { + var boneIndex:int = skeletonData.findBoneIndex(boneName); + if (boneIndex == -1) throw new Error("Bone not found: " + boneName); + var boneMap:Object = bones[boneName]; + + for (timelineName in boneMap) { + values = boneMap[timelineName]; + if (timelineName == "rotate") { + var rotateTimeline:RotateTimeline = new RotateTimeline(values.length); + rotateTimeline.boneIndex = boneIndex; + + frameIndex = 0; + for each (valueMap in values) { + rotateTimeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]); + readCurve(rotateTimeline, frameIndex, valueMap); + frameIndex++; + } + timelines[timelines.length] = rotateTimeline; + duration = Math.max(duration, rotateTimeline.frames[rotateTimeline.frameCount * 2 - 2]); + } else if (timelineName == "translate" || timelineName == "scale") { + var timeline:TranslateTimeline; + var timelineScale:Number = 1; + if (timelineName == "scale") + timeline = new ScaleTimeline(values.length); + else { + timeline = new TranslateTimeline(values.length); + timelineScale = scale; + } + timeline.boneIndex = boneIndex; + + frameIndex = 0; + for each (valueMap in values) { + var x:Number = Number(valueMap["x"] || 0) * timelineScale; + var y:Number = Number(valueMap["y"] || 0) * timelineScale; + timeline.setFrame(frameIndex, valueMap["time"], x, y); + readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines[timelines.length] = timeline; + duration = Math.max(duration, timeline.frames[timeline.frameCount * 3 - 3]); + } else + throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); + } + } + + var ikMap:Object = map["ik"]; + for (var ikConstraintName:String in ikMap) { + var ikConstraint:IkConstraintData = skeletonData.findIkConstraint(ikConstraintName); + values = ikMap[ikConstraintName]; + var ikTimeline:IkConstraintTimeline = new IkConstraintTimeline(values.length); + ikTimeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint); + frameIndex = 0; + for each (valueMap in values) { + var mix:Number = valueMap.hasOwnProperty("mix") ? valueMap["mix"] : 1; + var bendDirection:int = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1; + ikTimeline.setFrame(frameIndex, valueMap["time"], mix, bendDirection); + readCurve(ikTimeline, frameIndex, valueMap); + frameIndex++; + } + timelines[timelines.length] = ikTimeline; + duration = Math.max(duration, ikTimeline.frames[ikTimeline.frameCount * 3 - 3]); + } + + var ffd:Object = map["ffd"]; + for (var skinName:String in ffd) { + var skin:Skin = skeletonData.findSkin(skinName); + slotMap = ffd[skinName]; + for (slotName in slotMap) { slotIndex = skeletonData.findSlotIndex(slotName); + var meshMap:Object = slotMap[slotName]; + for (var meshName:String in meshMap) { + values = meshMap[meshName]; + var ffdTimeline:FfdTimeline = new FfdTimeline(values.length); + var attachment:Attachment = skin.getAttachment(slotIndex, meshName); + if (!attachment) throw new Error("FFD attachment not found: " + meshName); + ffdTimeline.slotIndex = slotIndex; + ffdTimeline.attachment = attachment; - for (timelineName in slotMap) { - values = slotMap[timelineName]; - if (timelineName == "color") { - var colorTimeline:ColorTimeline = new ColorTimeline(values.length); - colorTimeline.slotIndex = slotIndex; + var vertexCount:int; + if (attachment is MeshAttachment) + vertexCount = (attachment as MeshAttachment).vertices.length; + else + vertexCount = (attachment as WeightedMeshAttachment).weights.length / 3 * 2; - frameIndex = 0; - for each (valueMap in values) { - var color:String = valueMap["color"]; - var r:Number = toColor(color, 0); - var g:Number = toColor(color, 1); - var b:Number = toColor(color, 2); - var a:Number = toColor(color, 3); - colorTimeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); - readCurve(colorTimeline, frameIndex, valueMap); - frameIndex++; - } - timelines[timelines.length] = colorTimeline; - duration = Math.max(duration, colorTimeline.frames[colorTimeline.frameCount * 5 - 5]); - } else if (timelineName == "attachment") { - var attachmentTimeline:AttachmentTimeline = new AttachmentTimeline(values.length); - attachmentTimeline.slotIndex = slotIndex; - - frameIndex = 0; - for each (valueMap in values) - attachmentTimeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); - timelines[timelines.length] = attachmentTimeline; - duration = Math.max(duration, attachmentTimeline.frames[attachmentTimeline.frameCount - 1]); - } else - throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); - } - } - - var bones:Object = map["bones"]; - for (var boneName:String in bones) { - var boneIndex:int = skeletonData.findBoneIndex(boneName); - if (boneIndex == -1) throw new Error("Bone not found: " + boneName); - var boneMap:Object = bones[boneName]; - - for (timelineName in boneMap) { - values = boneMap[timelineName]; - if (timelineName == "rotate") { - var rotateTimeline:RotateTimeline = new RotateTimeline(values.length); - rotateTimeline.boneIndex = boneIndex; - - frameIndex = 0; - for each (valueMap in values) { - rotateTimeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]); - readCurve(rotateTimeline, frameIndex, valueMap); - frameIndex++; - } - timelines[timelines.length] = rotateTimeline; - duration = Math.max(duration, rotateTimeline.frames[rotateTimeline.frameCount * 2 - 2]); - } else if (timelineName == "translate" || timelineName == "scale") { - var timeline:TranslateTimeline; - var timelineScale:Number = 1; - if (timelineName == "scale") - timeline = new ScaleTimeline(values.length); - else { - timeline = new TranslateTimeline(values.length); - timelineScale = scale; - } - timeline.boneIndex = boneIndex; - - frameIndex = 0; - for each (valueMap in values) { - var x:Number = Number(valueMap["x"] || 0) * timelineScale; - var y:Number = Number(valueMap["y"] || 0) * timelineScale; - timeline.setFrame(frameIndex, valueMap["time"], x, y); - readCurve(timeline, frameIndex, valueMap); - frameIndex++; - } - timelines[timelines.length] = timeline; - duration = Math.max(duration, timeline.frames[timeline.frameCount * 3 - 3]); - } else - throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); - } - } - - var ikMap:Object = map["ik"]; - for (var ikConstraintName:String in ikMap) { - var ikConstraint:IkConstraintData = skeletonData.findIkConstraint(ikConstraintName); - values = ikMap[ikConstraintName]; - var ikTimeline:IkConstraintTimeline = new IkConstraintTimeline(values.length); - ikTimeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint); - frameIndex = 0; - for each (valueMap in values) { - var mix:Number = valueMap.hasOwnProperty("mix") ? valueMap["mix"] : 1; - var bendDirection:int = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1; - ikTimeline.setFrame(frameIndex, valueMap["time"], mix, bendDirection); - readCurve(ikTimeline, frameIndex, valueMap); - frameIndex++; - } - timelines[timelines.length] = ikTimeline; - duration = Math.max(duration, ikTimeline.frames[ikTimeline.frameCount * 3 - 3]); - } - - var ffd:Object = map["ffd"]; - for (var skinName:String in ffd) { - var skin:Skin = skeletonData.findSkin(skinName); - slotMap = ffd[skinName]; - for (slotName in slotMap) { - slotIndex = skeletonData.findSlotIndex(slotName); - var meshMap:Object = slotMap[slotName]; - for (var meshName:String in meshMap) { - values = meshMap[meshName]; - var ffdTimeline:FfdTimeline = new FfdTimeline(values.length); - var attachment:Attachment = skin.getAttachment(slotIndex, meshName); - if (!attachment) throw new Error("FFD attachment not found: " + meshName); - ffdTimeline.slotIndex = slotIndex; - ffdTimeline.attachment = attachment; - - var vertexCount:int; - if (attachment is MeshAttachment) - vertexCount = (attachment as MeshAttachment).vertices.length; - else - vertexCount = (attachment as WeightedMeshAttachment).weights.length / 3 * 2; - - frameIndex = 0; - for each (valueMap in values) { - var vertices:Vector.; - if (!valueMap["vertices"]) { - if (attachment is MeshAttachment) - vertices = (attachment as MeshAttachment).vertices; - else - vertices = new Vector.(vertexCount, true); - } else { - var verticesValue:Array = valueMap["vertices"]; + frameIndex = 0; + for each (valueMap in values) { + var vertices:Vector.; + if (!valueMap["vertices"]) { + if (attachment is MeshAttachment) + vertices = (attachment as MeshAttachment).vertices; + else vertices = new Vector.(vertexCount, true); - var start:int = valueMap["offset"] || 0; - var n:int = verticesValue.length; - if (scale == 1) { - for (i = 0; i < n; i++) - vertices[i + start] = verticesValue[i]; - } else { - for (i = 0; i < n; i++) - vertices[i + start] = verticesValue[i] * scale; - } - if (attachment is MeshAttachment) { - var meshVertices:Vector. = (attachment as MeshAttachment).vertices; - for (i = 0; i < vertexCount; i++) - vertices[i] += meshVertices[i]; - } + } else { + var verticesValue:Array = valueMap["vertices"]; + vertices = new Vector.(vertexCount, true); + var start:int = valueMap["offset"] || 0; + var n:int = verticesValue.length; + if (scale == 1) { + for (i = 0; i < n; i++) + vertices[i + start] = verticesValue[i]; + } else { + for (i = 0; i < n; i++) + vertices[i + start] = verticesValue[i] * scale; + } + if (attachment is MeshAttachment) { + var meshVertices:Vector. = (attachment as MeshAttachment).vertices; + for (i = 0; i < vertexCount; i++) + vertices[i] += meshVertices[i]; } - - ffdTimeline.setFrame(frameIndex, valueMap["time"], vertices); - readCurve(ffdTimeline, frameIndex, valueMap); - frameIndex++; } - timelines[timelines.length] = ffdTimeline; - duration = Math.max(duration, ffdTimeline.frames[ffdTimeline.frameCount - 1]); + + ffdTimeline.setFrame(frameIndex, valueMap["time"], vertices); + readCurve(ffdTimeline, frameIndex, valueMap); + frameIndex++; } + timelines[timelines.length] = ffdTimeline; + duration = Math.max(duration, ffdTimeline.frames[ffdTimeline.frameCount - 1]); } } + } - var drawOrderValues:Array = map["drawOrder"]; - if (!drawOrderValues) drawOrderValues = map["draworder"]; - if (drawOrderValues) { - var drawOrderTimeline:DrawOrderTimeline = new DrawOrderTimeline(drawOrderValues.length); - var slotCount:int = skeletonData.slots.length; - frameIndex = 0; - for each (var drawOrderMap:Object in drawOrderValues) { - var drawOrder:Vector. = null; - if (drawOrderMap["offsets"]) { - drawOrder = new Vector.(slotCount); - for (i = slotCount - 1; i >= 0; i--) - drawOrder[i] = -1; - var offsets:Array = drawOrderMap["offsets"]; - var unchanged:Vector. = new Vector.(slotCount - offsets.length); - var originalIndex:int = 0, unchangedIndex:int = 0; - for each (var offsetMap:Object in offsets) { - slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]); - if (slotIndex == -1) throw new Error("Slot not found: " + offsetMap["slot"]); - // Collect unchanged items. - while (originalIndex != slotIndex) - unchanged[unchangedIndex++] = originalIndex++; - // Set changed items. - drawOrder[originalIndex + offsetMap["offset"]] = originalIndex++; - } - // Collect remaining unchanged items. - while (originalIndex < slotCount) + var drawOrderValues:Array = map["drawOrder"]; + if (!drawOrderValues) drawOrderValues = map["draworder"]; + if (drawOrderValues) { + var drawOrderTimeline:DrawOrderTimeline = new DrawOrderTimeline(drawOrderValues.length); + var slotCount:int = skeletonData.slots.length; + frameIndex = 0; + for each (var drawOrderMap:Object in drawOrderValues) { + var drawOrder:Vector. = null; + if (drawOrderMap["offsets"]) { + drawOrder = new Vector.(slotCount); + for (i = slotCount - 1; i >= 0; i--) + drawOrder[i] = -1; + var offsets:Array = drawOrderMap["offsets"]; + var unchanged:Vector. = new Vector.(slotCount - offsets.length); + var originalIndex:int = 0, unchangedIndex:int = 0; + for each (var offsetMap:Object in offsets) { + slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]); + if (slotIndex == -1) throw new Error("Slot not found: " + offsetMap["slot"]); + // Collect unchanged items. + while (originalIndex != slotIndex) unchanged[unchangedIndex++] = originalIndex++; - // Fill in unchanged items. - for (i = slotCount - 1; i >= 0; i--) - if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; + // Set changed items. + drawOrder[originalIndex + offsetMap["offset"]] = originalIndex++; } - drawOrderTimeline.setFrame(frameIndex++, drawOrderMap["time"], drawOrder); + // Collect remaining unchanged items. + while (originalIndex < slotCount) + unchanged[unchangedIndex++] = originalIndex++; + // Fill in unchanged items. + for (i = slotCount - 1; i >= 0; i--) + if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; } - timelines[timelines.length] = drawOrderTimeline; - duration = Math.max(duration, drawOrderTimeline.frames[drawOrderTimeline.frameCount - 1]); + drawOrderTimeline.setFrame(frameIndex++, drawOrderMap["time"], drawOrder); } + timelines[timelines.length] = drawOrderTimeline; + duration = Math.max(duration, drawOrderTimeline.frames[drawOrderTimeline.frameCount - 1]); + } - var eventsMap:Array = map["events"]; - if (eventsMap) { - var eventTimeline:EventTimeline = new EventTimeline(eventsMap.length); - frameIndex = 0; - for each (var eventMap:Object in eventsMap) { - var eventData:EventData = skeletonData.findEvent(eventMap["name"]); - if (!eventData) throw new Error("Event not found: " + eventMap["name"]); - var event:Event = new Event(eventMap["time"], eventData); - event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue; - event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue; - event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue; - eventTimeline.setFrame(frameIndex++, event); - } - timelines[timelines.length] = eventTimeline; - duration = Math.max(duration, eventTimeline.frames[eventTimeline.frameCount - 1]); + var eventsMap:Array = map["events"]; + if (eventsMap) { + var eventTimeline:EventTimeline = new EventTimeline(eventsMap.length); + frameIndex = 0; + for each (var eventMap:Object in eventsMap) { + var eventData:EventData = skeletonData.findEvent(eventMap["name"]); + if (!eventData) throw new Error("Event not found: " + eventMap["name"]); + var event:Event = new Event(eventMap["time"], eventData); + event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue; + event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue; + event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue; + eventTimeline.setFrame(frameIndex++, event); } - - skeletonData.animations[skeletonData.animations.length] = new Animation(name, timelines, duration); + timelines[timelines.length] = eventTimeline; + duration = Math.max(duration, eventTimeline.frames[eventTimeline.frameCount - 1]); } - static private function readCurve(timeline:CurveTimeline, frameIndex:int, valueMap:Object) : void { - var curve:Object = valueMap["curve"]; - if (!curve) return; - if (curve == "stepped") - timeline.setStepped(frameIndex); - else if (curve is Array) - timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); - } + skeletonData.animations[skeletonData.animations.length] = new Animation(name, timelines, duration); + } - static private function toColor(hexString:String, colorIndex:int) : Number { - if (hexString.length != 8) throw new ArgumentError("Color hexidecimal length must be 8, recieved: " + hexString); - return parseInt(hexString.substring(colorIndex * 2, colorIndex * 2 + 2), 16) / 255; - } + static private function readCurve (timeline:CurveTimeline, frameIndex:int, valueMap:Object) : void { + var curve:Object = valueMap["curve"]; + if (!curve) return; + if (curve == "stepped") + timeline.setStepped(frameIndex); + else if (curve is Array) + timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); + } - static private function getFloatArray(map:Object, name:String, scale:Number) : Vector. { - var list:Array = map[name]; - var values:Vector. = new Vector.(list.length, true); - var i:int = 0, n:int = list.length; - if (scale == 1) { - for (; i < n; i++) - values[i] = list[i]; - } else { - for (; i < n; i++) - values[i] = list[i] * scale; - } - return values; - } + static private function toColor (hexString:String, colorIndex:int) : Number { + if (hexString.length != 8) throw new ArgumentError("Color hexidecimal length must be 8, recieved: " + hexString); + return parseInt(hexString.substring(colorIndex * 2, colorIndex * 2 + 2), 16) / 255; + } - static private function getIntArray(map:Object, name:String) : Vector. { - var list:Array = map[name]; - var values:Vector. = new Vector.(list.length, true); - for (var i:int = 0, n:int = list.length; i < n; i++) - values[i] = int(list[i]); - return values; + static private function getFloatArray (map:Object, name:String, scale:Number) : Vector. { + var list:Array = map[name]; + var values:Vector. = new Vector.(list.length, true); + var i:int = 0, n:int = list.length; + if (scale == 1) { + for (; i < n; i++) + values[i] = list[i]; + } else { + for (; i < n; i++) + values[i] = list[i] * scale; } + return values; + } - static private function getUintArray(map:Object, name:String) : Vector. { - var list:Array = map[name]; - var values:Vector. = new Vector.(list.length, true); - for (var i:int = 0, n:int = list.length; i < n; i++) - values[i] = int(list[i]); - return values; - } + static private function getIntArray (map:Object, name:String) : Vector. { + var list:Array = map[name]; + var values:Vector. = new Vector.(list.length, true); + for (var i:int = 0, n:int = list.length; i < n; i++) + values[i] = int(list[i]); + return values; + } + + static private function getUintArray (map:Object, name:String) : Vector. { + var list:Array = map[name]; + var values:Vector. = new Vector.(list.length, true); + for (var i:int = 0, n:int = list.length; i < n; i++) + values[i] = int(list[i]); + return values; + } +} + +} + +import spine.attachments.Attachment; + +internal class LinkedMesh { + internal var parent:String, skin:String; + internal var slotIndex:int; + internal var mesh:Attachment; + + public function LinkedMesh (mesh:Attachment, skin:String, slotIndex:int, parent:String) { + this.mesh = mesh; + this.skin = skin; + this.slotIndex = slotIndex; + this.parent = parent; } } diff --git a/spine-as3/spine-as3/src/spine/animation/FfdTimeline.as b/spine-as3/spine-as3/src/spine/animation/FfdTimeline.as index 062e41bbd..8f87cd07b 100644 --- a/spine-as3/spine-as3/src/spine/animation/FfdTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/FfdTimeline.as @@ -30,6 +30,7 @@ *****************************************************************************/ package spine.animation { +import spine.attachments.FfdAttachment; import spine.Event; import spine.Skeleton; import spine.Slot; @@ -55,7 +56,8 @@ public class FfdTimeline extends CurveTimeline { override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { var slot:Slot = skeleton.slots[slotIndex]; - if (slot.attachment != attachment) return; + var slotAttachment:FfdAttachment = slot.attachment as FfdAttachment; + if (!slotAttachment || !slotAttachment.applyFFD(attachment)) return; var frames:Vector. = this.frames; if (time < frames[0]) return; // Time is before first frame. diff --git a/spine-as3/spine-as3/src/spine/attachments/AttachmentType.as b/spine-as3/spine-as3/src/spine/attachments/AttachmentType.as index 6106e6217..786afb086 100644 --- a/spine-as3/spine-as3/src/spine/attachments/AttachmentType.as +++ b/spine-as3/spine-as3/src/spine/attachments/AttachmentType.as @@ -37,6 +37,8 @@ public class AttachmentType { public static const boundingbox:AttachmentType = new AttachmentType(2, "boundingbox"); public static const mesh:AttachmentType = new AttachmentType(3, "mesh"); public static const weightedmesh:AttachmentType = new AttachmentType(4, "weightedmesh"); + public static const linkedmesh:AttachmentType = new AttachmentType(3, "linkedmesh"); + public static const weightedlinkedmesh:AttachmentType = new AttachmentType(4, "weightedlinkedmesh"); public var ordinal:int; public var name:String; diff --git a/spine-as3/spine-as3/src/spine/attachments/FfdAttachment.as b/spine-as3/spine-as3/src/spine/attachments/FfdAttachment.as new file mode 100644 index 000000000..5edab5adf --- /dev/null +++ b/spine-as3/spine-as3/src/spine/attachments/FfdAttachment.as @@ -0,0 +1,38 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.3 + * + * Copyright (c) 2013-2015, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to use, install, execute and perform the Spine + * Runtimes Software (the "Software") and derivative works solely for personal + * or internal use. Without the written permission of Esoteric Software (see + * Section 2 of the Spine Software License Agreement), you may not (a) modify, + * translate, adapt or otherwise create derivative works, improvements of the + * Software or develop new applications using the Software or (b) remove, + * delete, alter or obscure any trademarks or any copyright, trademark, patent + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) 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 THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package spine.attachments { + +public interface FfdAttachment { + function applyFFD (sourceAttachment:Attachment) : Boolean; +} + +} diff --git a/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as b/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as index 032ee77ff..e0f61b199 100644 --- a/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as +++ b/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as @@ -33,7 +33,7 @@ package spine.attachments { import spine.Slot; import spine.Bone; -public dynamic class MeshAttachment extends Attachment { +public dynamic class MeshAttachment extends Attachment implements FfdAttachment { public var vertices:Vector.; public var uvs:Vector.; public var regionUVs:Vector.; @@ -43,6 +43,8 @@ public dynamic class MeshAttachment extends Attachment { public var g:Number = 1; public var b:Number = 1; public var a:Number = 1; + private var _parentMesh:MeshAttachment; + public var inheritFFD:Boolean; public var path:String; public var rendererObject:Object; @@ -102,6 +104,27 @@ public dynamic class MeshAttachment extends Attachment { worldVertices[int(ii + 1)] = vx * m10 + vy * m11 + y; } } + + public function applyFFD (sourceAttachment:Attachment) : Boolean { + return this == sourceAttachment || (inheritFFD && _parentMesh == sourceAttachment); + } + + public function get parentMesh () : MeshAttachment { + return _parentMesh; + } + + public function set parentMesh (parentMesh:MeshAttachment) : void { + _parentMesh = parentMesh; + if (parentMesh != null) { + vertices = parentMesh.vertices; + regionUVs = parentMesh.regionUVs; + triangles = parentMesh.triangles; + hullLength = parentMesh.hullLength; + edges = parentMesh.edges; + width = parentMesh.width; + height = parentMesh.height; + } + } } } diff --git a/spine-as3/spine-as3/src/spine/attachments/WeightedMeshAttachment.as b/spine-as3/spine-as3/src/spine/attachments/WeightedMeshAttachment.as index cdf3af231..b1862a582 100644 --- a/spine-as3/spine-as3/src/spine/attachments/WeightedMeshAttachment.as +++ b/spine-as3/spine-as3/src/spine/attachments/WeightedMeshAttachment.as @@ -33,7 +33,7 @@ package spine.attachments { import spine.Slot; import spine.Bone; -public dynamic class WeightedMeshAttachment extends Attachment { +public dynamic class WeightedMeshAttachment extends Attachment implements FfdAttachment { public var bones:Vector.; public var weights:Vector.; public var uvs:Vector.; @@ -44,6 +44,8 @@ public dynamic class WeightedMeshAttachment extends Attachment { public var g:Number = 1; public var b:Number = 1; public var a:Number = 1; + private var _parentMesh:WeightedMeshAttachment; + public var inheritFFD:Boolean; public var path:String; public var rendererObject:Object; @@ -127,6 +129,28 @@ public dynamic class WeightedMeshAttachment extends Attachment { } } } + + public function applyFFD (sourceAttachment:Attachment) : Boolean { + return this == sourceAttachment || (inheritFFD && _parentMesh == sourceAttachment); + } + + public function get parentMesh () : WeightedMeshAttachment { + return _parentMesh; + } + + public function set parentMesh (parentMesh:WeightedMeshAttachment) : void { + _parentMesh = parentMesh; + if (parentMesh != null) { + bones = parentMesh.bones; + weights = parentMesh.weights; + regionUVs = parentMesh.regionUVs; + triangles = parentMesh.triangles; + hullLength = parentMesh.hullLength; + edges = parentMesh.edges; + width = parentMesh.width; + height = parentMesh.height; + } + } } } diff --git a/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as b/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as index d7be0f31b..e11d92be5 100644 --- a/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as +++ b/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as @@ -78,7 +78,7 @@ public class SkeletonSprite extends Sprite { for (var i:int = 0, n:int = drawOrder.length; i < n; i++) { var slot:Slot = drawOrder[i]; var regionAttachment:RegionAttachment = slot.attachment as RegionAttachment; - if (regionAttachment != null) { + if (regionAttachment) { var wrapper:Sprite = regionAttachment["wrapper"]; var region:AtlasRegion = AtlasRegion(regionAttachment.rendererObject); if (!wrapper) { diff --git a/spine-starling/README.md b/spine-starling/README.md index 701fbee72..fccbc8469 100644 --- a/spine-starling/README.md +++ b/spine-starling/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-starling works with data exported from the latest version of Spine, except linked meshes are [not yet supported](https://trello.com/c/bERJAFEq/73-update-runtimes-to-support-v3-1-linked-meshes). +spine-starling works with data exported from the latest version of Spine. spine-starling supports all Spine features. diff --git a/spine-starling/spine-starling-example/lib/spine-as3.swc b/spine-starling/spine-starling-example/lib/spine-as3.swc index 873bc9063..3f90b6481 100644 Binary files a/spine-starling/spine-starling-example/lib/spine-as3.swc and b/spine-starling/spine-starling-example/lib/spine-as3.swc differ diff --git a/spine-starling/spine-starling-example/lib/spine-starling.swc b/spine-starling/spine-starling-example/lib/spine-starling.swc index 8b592e3b1..11ea13962 100644 Binary files a/spine-starling/spine-starling-example/lib/spine-starling.swc and b/spine-starling/spine-starling-example/lib/spine-starling.swc differ diff --git a/spine-starling/spine-starling/lib/spine-as3.swc b/spine-starling/spine-starling/lib/spine-as3.swc index 873bc9063..3f90b6481 100644 Binary files a/spine-starling/spine-starling/lib/spine-as3.swc and b/spine-starling/spine-starling/lib/spine-as3.swc differ