From 4b5322d5d6a7be42c23b0dbda80e41b9fb641c0c Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Wed, 17 Feb 2016 03:54:04 +0100 Subject: [PATCH] Updated spine-as3 to Spine v3. --- spine-as3/spine-as3/src/spine/Bone.as | 250 ++-- spine-as3/spine-as3/src/spine/BoneData.as | 2 - spine-as3/spine-as3/src/spine/Event.as | 6 +- spine-as3/spine-as3/src/spine/IkConstraint.as | 188 ++- .../FlipYTimeline.as => MathUtils.as} | 14 +- spine-as3/spine-as3/src/spine/Skeleton.as | 95 +- spine-as3/spine-as3/src/spine/SkeletonData.as | 17 +- spine-as3/spine-as3/src/spine/SkeletonJson.as | 1025 ++++++++--------- ...lipXTimeline.as => TransformConstraint.as} | 63 +- .../src/spine/TransformConstraintData.as | 56 + spine-as3/spine-as3/src/spine/Updatable.as | 38 + .../src/spine/animation/Animation.as | 4 +- .../src/spine/animation/EventTimeline.as | 4 +- .../attachments/AtlasAttachmentLoader.as | 6 +- .../src/spine/attachments/AttachmentLoader.as | 2 +- .../src/spine/attachments/AttachmentType.as | 2 +- .../attachments/BoundingBoxAttachment.as | 8 +- .../src/spine/attachments/MeshAttachment.as | 8 +- .../src/spine/attachments/RegionAttachment.as | 8 +- ...ttachment.as => WeightedMeshAttachment.as} | 12 +- .../src/spine/flash/SkeletonSprite.as | 4 +- 21 files changed, 1021 insertions(+), 791 deletions(-) rename spine-as3/spine-as3/src/spine/{animation/FlipYTimeline.as => MathUtils.as} (87%) rename spine-as3/spine-as3/src/spine/{animation/FlipXTimeline.as => TransformConstraint.as} (60%) create mode 100644 spine-as3/spine-as3/src/spine/TransformConstraintData.as create mode 100644 spine-as3/spine-as3/src/spine/Updatable.as rename spine-as3/spine-as3/src/spine/attachments/{SkinnedMeshAttachment.as => WeightedMeshAttachment.as} (92%) diff --git a/spine-as3/spine-as3/src/spine/Bone.as b/spine-as3/spine-as3/src/spine/Bone.as index ce8eed0af..ef44e0573 100644 --- a/spine-as3/spine-as3/src/spine/Bone.as +++ b/spine-as3/spine-as3/src/spine/Bone.as @@ -31,7 +31,7 @@ package spine { -public class Bone { +public class Bone implements Updatable { static public var yDown:Boolean; internal var _data:BoneData; @@ -40,23 +40,20 @@ public class Bone { public var x:Number; public var y:Number; public var rotation:Number; - public var rotationIK:Number; public var scaleX:Number; public var scaleY:Number; - public var flipX:Boolean; - public var flipY:Boolean; + public var appliedRotation:Number; + public var appliedScaleX:Number; + public var appliedScaleY:Number; - internal var _m00:Number; - internal var _m01:Number; - internal var _m10:Number; - internal var _m11:Number; + internal var _a:Number; + internal var _b:Number; + internal var _c:Number; + internal var _d:Number; internal var _worldX:Number; internal var _worldY:Number; - internal var _worldRotation:Number; - internal var _worldScaleX:Number; - internal var _worldScaleY:Number; - internal var _worldFlipX:Boolean; - internal var _worldFlipY:Boolean; + internal var _worldSignX:Number; + internal var _worldSignY:Number; /** @param parent May be null. */ public function Bone (data:BoneData, skeleton:Skeleton, parent:Bone) { @@ -68,48 +65,136 @@ public class Bone { setToSetupPose(); } - /** Computes the world SRT using the parent bone and the local SRT. */ + /** Computes the world SRT using the parent bone and this bone's local SRT. */ public function updateWorldTransform () : void { + updateWorldTransformWith(x, y, rotation, scaleX, scaleY); + } + + /** Same as updateWorldTransform(). This method exists for Bone to implement Updatable. */ + public function update () : void { + updateWorldTransformWith(x, y, rotation, scaleX, scaleY); + } + + /** Computes the world SRT using the parent bone and the specified local SRT. */ + public function updateWorldTransformWith (x:Number, y:Number, rotation:Number, scaleX:Number, scaleY:Number) : void { + appliedRotation = rotation; + appliedScaleX = scaleX; + appliedScaleY = scaleY; + + var radians:Number = rotation * MathUtils.degRad; + var cos:Number = Math.cos(radians), sin:Number = Math.sin(radians); + var la:Number = cos * scaleX, lb:Number = -sin * scaleY, lc:Number = sin * scaleX, ld:Number = cos * scaleY; var parent:Bone = _parent; - if (parent) { - _worldX = x * parent._m00 + y * parent._m01 + parent._worldX; - _worldY = x * parent._m10 + y * parent._m11 + parent._worldY; - if (_data.inheritScale) { - _worldScaleX = parent._worldScaleX * scaleX; - _worldScaleY = parent._worldScaleY * scaleY; - } else { - _worldScaleX = scaleX; - _worldScaleY = scaleY; + if (!parent) { // Root bone. + var skeleton:Skeleton = this.skeleton; + if (skeleton.flipX) { + x = -x; + la = -la; + lb = -lb; } - _worldRotation = _data.inheritRotation ? parent._worldRotation + rotationIK : rotationIK; - _worldFlipX = parent._worldFlipX != flipX; - _worldFlipY = parent._worldFlipY != flipY; - } else { - var skeletonFlipX:Boolean = _skeleton.flipX, skeletonFlipY:Boolean = _skeleton.flipY; - _worldX = skeletonFlipX ? -x : x; - _worldY = skeletonFlipY != yDown ? -y : y; - _worldScaleX = scaleX; - _worldScaleY = scaleY; - _worldRotation = rotationIK; - _worldFlipX = skeletonFlipX != flipX; - _worldFlipY = skeletonFlipY != flipY; + if (skeleton.flipY != yDown) { + y = -y; + lc = -lc; + ld = -ld; + } + _a = la; + _b = lb; + _c = lc; + _d = ld; + _worldX = x; + _worldY = y; + _worldSignX = scaleX < 0 ? -1 : 1; + _worldSignY = scaleY < 0 ? -1 : 1; + return; } - var radians:Number = _worldRotation * (Math.PI / 180); - var cos:Number = Math.cos(radians); - var sin:Number = Math.sin(radians); - if (_worldFlipX) { - _m00 = -cos * _worldScaleX; - _m01 = sin * _worldScaleY; + + var pa:Number = parent._a, pb:Number = parent._b, pc:Number = parent._c, pd:Number = parent._d; + _worldX = pa * x + pb * y + parent._worldX; + _worldY = pc * x + pd * y + parent._worldY; + _worldSignX = parent._worldSignX * (scaleX < 0 ? -1 : 1); + _worldSignY = parent._worldSignY * (scaleY < 0 ? -1 : 1); + + if (data.inheritRotation && data.inheritScale) { + _a = pa * la + pb * lc; + _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; + while (parent != null) { + 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; + parent = parent.parent; + } + _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; + while (parent) { + 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; + + parent = parent.parent; + } + _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 { - _m00 = cos * _worldScaleX; - _m01 = -sin * _worldScaleY; - } - if (_worldFlipY != yDown) { - _m10 = -sin * _worldScaleX; - _m11 = -cos * _worldScaleY; - } else { - _m10 = sin * _worldScaleX; - _m11 = cos * _worldScaleY; + _a = la; + _b = lb; + _c = lc; + _d = ld; } } @@ -117,11 +202,8 @@ public class Bone { x = _data.x; y = _data.y; rotation = _data.rotation; - rotationIK = rotation; scaleX = _data.scaleX; scaleY = _data.scaleY; - flipX = _data.flipX; - flipY = _data.flipY; } public function get data () : BoneData { @@ -136,20 +218,20 @@ public class Bone { return _skeleton; } - public function get m00 () : Number { - return _m00; + public function get a () : Number { + return _a; } - public function get m01 () : Number { - return _m01; + public function get b () : Number { + return _b; } - public function get m10 () : Number { - return _m10; + public function get c () : Number { + return _c; } - public function get m11 () : Number { - return _m11; + public function get d () : Number { + return _d; } public function get worldX () : Number { @@ -160,42 +242,42 @@ public class Bone { return _worldY; } - public function get worldRotation () : Number { - return _worldRotation; + public function get worldSignX () : Number { + return _worldSignX; + } + + public function get worldSignY () : Number { + return _worldSignY; + } + + public function get worldRotationX () : Number { + return Math.atan2(_c, _a) * MathUtils.radDeg; + } + + public function get worldRotationY () : Number { + return Math.atan2(_d, _b) * MathUtils.radDeg; } public function get worldScaleX () : Number { - return _worldScaleX; + return Math.sqrt(_a * _a + _b * _b) * _worldSignX; } public function get worldScaleY () : Number { - return _worldScaleY; - } - - public function get worldFlipX () : Boolean { - return _worldFlipX; - } - - public function get worldFlipY () : Boolean { - return _worldFlipY; + return Math.sqrt(_c * _c + _d * _d) * _worldSignY; } public function worldToLocal (world:Vector.) : void { - var dx:Number = world[0] - _worldX, dy:Number = world[1] - _worldY; - var m00:Number = _m00, m10:Number = _m10, m01:Number = _m01, m11:Number = _m11; - if (_worldFlipX != (_worldFlipY != yDown)) { - m00 = -m00; - m11 = -m11; - } - var invDet:Number = 1 / (m00 * m11 - m01 * m10); - world[0] = (dx * m00 * invDet - dy * m01 * invDet); - world[1] = (dy * m11 * invDet - dx * m10 * invDet); + var x:Number = world[0] - _worldX, y:Number = world[1] - _worldY; + var a:Number = _a, b:Number = _b, c:Number = _c, d:Number = _d; + var invDet:Number = 1 / (a * d - b * c); + world[0] = (x * a * invDet - y * b * invDet); + world[1] = (y * d * invDet - x * c * invDet); } public function localToWorld (local:Vector.) : void { var localX:Number = local[0], localY:Number = local[1]; - local[0] = localX * _m00 + localY * _m01 + _worldX; - local[1] = localX * _m10 + localY * _m11 + _worldY; + local[0] = localX * _a + localY * _b + _worldX; + local[1] = localX * _c + localY * _d + _worldY; } public function toString () : String { diff --git a/spine-as3/spine-as3/src/spine/BoneData.as b/spine-as3/spine-as3/src/spine/BoneData.as index 418c5926d..be24ce2e2 100644 --- a/spine-as3/spine-as3/src/spine/BoneData.as +++ b/spine-as3/spine-as3/src/spine/BoneData.as @@ -42,8 +42,6 @@ public class BoneData { public var scaleY:Number = 1; public var inheritScale:Boolean = true; public var inheritRotation:Boolean = true; - public var flipX:Boolean; - public var flipY:Boolean; /** @param parent May be null. */ public function BoneData (name:String, parent:BoneData) { diff --git a/spine-as3/spine-as3/src/spine/Event.as b/spine-as3/spine-as3/src/spine/Event.as index 2199ae3d6..cd2ecabaa 100644 --- a/spine-as3/spine-as3/src/spine/Event.as +++ b/spine-as3/spine-as3/src/spine/Event.as @@ -33,12 +33,14 @@ package spine { public class Event { internal var _data:EventData; - public var intValue:int;; + public var time:Number; + public var intValue:int; public var floatValue:Number; public var stringValue:String; - public function Event (data:EventData) { + public function Event (time:Number, data:EventData) { if (data == null) throw new ArgumentError("data cannot be null."); + this.time = time; _data = data; } diff --git a/spine-as3/spine-as3/src/spine/IkConstraint.as b/spine-as3/spine-as3/src/spine/IkConstraint.as index 6b055657b..e3b0dac32 100644 --- a/spine-as3/spine-as3/src/spine/IkConstraint.as +++ b/spine-as3/spine-as3/src/spine/IkConstraint.as @@ -31,10 +31,7 @@ package spine { -public class IkConstraint { - static private const tempPosition:Vector. = new Vector.(2, true); - static private const radDeg:Number = 180 / Math.PI; - +public class IkConstraint implements Updatable { internal var _data:IkConstraintData; public var bones:Vector.; public var target:Bone; @@ -55,6 +52,10 @@ public class IkConstraint { } public function apply () : void { + update(); + } + + public function update () : void { switch (bones.length) { case 1: apply1(bones[0], target._worldX, target._worldY, mix); @@ -76,76 +77,137 @@ public class IkConstraint { /** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world * coordinate system. */ static public function apply1 (bone:Bone, targetX:Number, targetY:Number, alpha:Number) : void { - var parentRotation:Number = (!bone._data.inheritRotation || bone._parent == null) ? 0 : bone._parent._worldRotation; + var parentRotation:Number = bone.parent == null ? 0 : bone.parent.worldRotationX; var rotation:Number = bone.rotation; - var rotationIK:Number = Math.atan2(targetY - bone._worldY, targetX - bone._worldX) * radDeg; - if (bone._worldFlipX != (bone._worldFlipY != Bone.yDown)) rotationIK = -rotationIK; - rotationIK -= parentRotation; - bone.rotationIK = rotation + (rotationIK - rotation) * alpha; + var rotationIK:Number = Math.atan2(targetY - bone.worldY, targetX - bone.worldX) * MathUtils.radDeg - parentRotation; + if (bone.worldSignX != bone.worldSignY) 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); } /** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The * target is specified in the world coordinate system. * @param child Any descendant bone of the parent. */ - static public function apply2 (parent:Bone, child:Bone, targetX:Number, targetY:Number, bendDirection:int, alpha:Number) : void { - var childRotation:Number = child.rotation, parentRotation:Number = parent.rotation; - if (alpha == 0) { - child.rotationIK = childRotation; - parent.rotationIK = parentRotation; - return; - } - var positionX:Number, positionY:Number; - var parentParent:Bone = parent._parent; - if (parentParent) { - tempPosition[0] = targetX; - tempPosition[1] = targetY; - parentParent.worldToLocal(tempPosition); - targetX = (tempPosition[0] - parent.x) * parentParent._worldScaleX; - targetY = (tempPosition[1] - parent.y) * parentParent._worldScaleY; + 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; + if (psx < 0) { + psx = -psx; + offset1 = 180; + sign2 = -1; } else { - targetX -= parent.x; - targetY -= parent.y; + offset1 = 0; + sign2 = 1; } - if (child._parent == parent) { - positionX = child.x; - positionY = child.y; + if (psy < 0) { + psy = -psy; + sign2 = -sign2; + } + if (csx < 0) { + csx = -csx; + offset2 = 180; + } else + offset2 = 0; + var pp:Bone = parent.parent; + var tx:Number, ty:Number, dx:Number, dy:Number; + if (!pp) { + tx = targetX - px; + ty = targetY - py; + dx = child.worldX - px; + dy = child.worldY - py; } else { - tempPosition[0] = child.x; - tempPosition[1] = child.y; - child._parent.localToWorld(tempPosition); - parent.worldToLocal(tempPosition); - positionX = tempPosition[0]; - positionY = tempPosition[1]; + var ppa:Number = pp.a, ppb:Number = pp.b, ppc:Number = pp.c, ppd:Number = pp.d; + var invDet:Number = 1 / (ppa * ppd - ppb * ppc); + var wx:Number = pp.worldX, wy:Number = pp.worldY, twx:Number = targetX - wx, twy:Number = targetY - wy; + tx = (twx * ppd - twy * ppb) * invDet - px; + ty = (twy * ppa - twx * ppc) * invDet - py; + twx = child.worldX - wx; + twy = child.worldY - wy; + dx = (twx * ppd - twy * ppb) * invDet - px; + dy = (twy * ppa - twx * ppc) * invDet - py; } - var childX:Number = positionX * parent._worldScaleX, childY:Number = positionY * parent._worldScaleY; - var offset:Number = Math.atan2(childY, childX); - var len1:Number = Math.sqrt(childX * childX + childY * childY), len2:Number = child.data.length * child._worldScaleX; - // Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/ - var cosDenom:Number = 2 * len1 * len2; - if (cosDenom < 0.0001) { - child.rotationIK = childRotation + (Math.atan2(targetY, targetX) * radDeg - parentRotation - childRotation) * alpha; - return; + 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) { + l2 *= psx; + var cos:Number = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2); + if (cos < -1) cos = -1; + else if (cos > 1) cos = 1; + a2 = Math.acos(cos) * bendDir; + 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; + var d:Number = c1 * c1 - 4 * c2 * c0; + if (d >= 0) { + var q:Number = Math.sqrt(d); + if (c1 < 0) q = -q; + q = -(c1 + q) / 2; + var r0:Number = q / c2, r1:Number = c0 / q; + var r:Number = Math.abs(r0) < Math.abs(r1) ? r0 : r1; + if (r * r <= dd) { + var y1:Number = Math.sqrt(dd - r * r) * bendDir; + a1 = ta - Math.atan2(y1, r); + a2 = Math.atan2(y1 / psy, (r - l1) / psx); + break outer; + } + } + var minAngle:Number = 0, minDist:Number = Number.MAX_VALUE, minX:Number = 0, minY:Number = 0; + var maxAngle:Number = 0, maxDist:Number = 0, maxX:Number = 0, maxY:Number = 0; + var x:Number = l1 + a, dist:Number = x * x; + if (dist > maxDist) { + maxAngle = 0; + maxDist = dist; + maxX = x; + } + x = l1 - a; + dist = x * x; + if (dist < minDist) { + minAngle = Math.PI; + minDist = dist; + minX = x; + } + var angle:Number = Math.acos(-a * l1 / (aa - bb)); + x = a * Math.cos(angle) + l1; + var y:Number = b * Math.sin(angle); + dist = x * x + y * y; + if (dist < minDist) { + minAngle = angle; + minDist = dist; + minX = x; + minY = y; + } + if (dist > maxDist) { + maxAngle = angle; + maxDist = dist; + maxX = x; + maxY = y; + } + if (dd <= (minDist + maxDist) / 2) { + a1 = ta - Math.atan2(minY * bendDir, minX); + a2 = minAngle * bendDir; + } else { + a1 = ta - Math.atan2(maxY * bendDir, maxX); + a2 = maxAngle * bendDir; + } } - var cos:Number = (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom; - if (cos < -1) - cos = -1; - else if (cos > 1) - cos = 1; - var childAngle:Number = Math.acos(cos) * bendDirection; - var adjacent:Number = len1 + len2 * cos, opposite:Number = len2 * Math.sin(childAngle); - var parentAngle:Number = Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite); - var rotation:Number = (parentAngle - offset) * radDeg - parentRotation; - if (rotation > 180) - rotation -= 360; - else if (rotation < -180) // - rotation += 360; - parent.rotationIK = parentRotation + rotation * alpha; - rotation = (childAngle + offset) * radDeg - childRotation; - if (rotation > 180) - rotation -= 360; - else if (rotation < -180) // - rotation += 360; - child.rotationIK = childRotation + (rotation + parent._worldRotation - child._parent._worldRotation) * alpha; + var offset:Number = Math.atan2(cy, child.x) * sign2; + a1 = (a1 - offset) * MathUtils.radDeg + offset1; + a2 = (a2 + offset) * MathUtils.radDeg * sign2 + offset2; + 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); + rotation = child.rotation; + child.updateWorldTransformWith(child.x, cy, rotation + (a2 - rotation) * alpha, child.scaleX, child.scaleY); } } diff --git a/spine-as3/spine-as3/src/spine/animation/FlipYTimeline.as b/spine-as3/spine-as3/src/spine/MathUtils.as similarity index 87% rename from spine-as3/spine-as3/src/spine/animation/FlipYTimeline.as rename to spine-as3/spine-as3/src/spine/MathUtils.as index f5dd7ac09..ee0eef7d7 100644 --- a/spine-as3/spine-as3/src/spine/animation/FlipYTimeline.as +++ b/spine-as3/spine-as3/src/spine/MathUtils.as @@ -29,17 +29,11 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -package spine.animation { -import spine.Bone; +package spine { -public class FlipYTimeline extends FlipXTimeline { - public function FlipYTimeline (frameCount:int) { - super(frameCount); - } - - override protected function setFlip (bone:Bone, flip:Boolean) : void { - bone.flipY = flip; - } +public class MathUtils { + static public var radDeg:Number = 180 / Math.PI; + static public var degRad:Number = Math.PI / 180; } } diff --git a/spine-as3/spine-as3/src/spine/Skeleton.as b/spine-as3/spine-as3/src/spine/Skeleton.as index be7cef806..39e99cfbf 100644 --- a/spine-as3/spine-as3/src/spine/Skeleton.as +++ b/spine-as3/spine-as3/src/spine/Skeleton.as @@ -38,7 +38,8 @@ public class Skeleton { public var slots:Vector.; public var drawOrder:Vector.; public var ikConstraints:Vector.; - private var _boneCache:Vector.> = new Vector.>(); + public var transformConstraints:Vector.; + private var _updateCache:Vector. = new Vector.(); private var _skin:Skin; public var r:Number = 1, g:Number = 1, b:Number = 1, a:Number = 1; public var time:Number = 0; @@ -68,69 +69,51 @@ public class Skeleton { ikConstraints = new Vector.(); for each (var ikConstraintData:IkConstraintData in data.ikConstraints) ikConstraints[ikConstraints.length] = new IkConstraint(ikConstraintData, this); + + transformConstraints = new Vector.(); + for each (var transformConstraintData:TransformConstraintData in data.transformConstraints) + transformConstraints[transformConstraints.length] = new TransformConstraint(transformConstraintData, this); updateCache(); } - /** Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed. */ + /** Caches information about bones and constraints. Must be called if bones or constraints are added or removed. */ public function updateCache () : void { - var ikConstraintsCount:int = ikConstraints.length; - - var arrayCount:int = ikConstraintsCount + 1; - if (_boneCache.length > arrayCount) _boneCache.splice(arrayCount, _boneCache.length - arrayCount); - for each (var cachedBones:Vector. in _boneCache) - cachedBones.length = 0; - while (_boneCache.length < arrayCount) - _boneCache[_boneCache.length] = new Vector.(); - - var nonIkBones:Vector. = _boneCache[0]; - - outer: + var updateCache:Vector. = _updateCache; + var ikConstraints:Vector. = this.ikConstraints; + var transformConstraints:Vector. = this.transformConstraints; + updateCache.length = bones.length + ikConstraints.length + transformConstraints.length; + var i:int = 0; for each (var bone:Bone in bones) { - var current:Bone = bone; - do { - var ii:int = 0; - for each (var ikConstraint:IkConstraint in ikConstraints) { - var parent:Bone = ikConstraint.bones[0]; - var child:Bone = ikConstraint.bones[int(ikConstraint.bones.length - 1)]; - while (true) { - if (current == child) { - _boneCache[ii].push(bone); - _boneCache[int(ii + 1)].push(bone); - continue outer; - } - if (child == parent) break; - child = child.parent; - } - ii++; + updateCache[i++] = bone; + for each (var transformConstraint:TransformConstraint in transformConstraints) { + if (bone == transformConstraint.bone) { + updateCache[i++] = transformConstraint; + break; } - current = current.parent; - } while (current != null); - nonIkBones[nonIkBones.length] = bone; + } + for each (var ikConstraint:IkConstraint in ikConstraints) { + if (bone == ikConstraint.bones[ikConstraint.bones.length - 1]) { + updateCache[i++] = ikConstraint; + break; + } + } } } - /** Updates the world transform for each bone and applies IK constraints. */ + /** Updates the world transform for each bone and applies constraints. */ public function updateWorldTransform () : void { - var bone:Bone; - for each (bone in bones) - bone.rotationIK = bone.rotation; - var i:int = 0, last:int = _boneCache.length - 1; - while (true) { - for each (bone in _boneCache[i]) - bone.updateWorldTransform(); - if (i == last) break; - ikConstraints[i].apply(); - i++; - } + for each (var updatable:Updatable in _updateCache) + updatable.update(); } - /** Sets the bones and slots to their setup pose values. */ + /** Sets the bones, constraints, and slots to their setup pose values. */ public function setToSetupPose () : void { setBonesToSetupPose(); setSlotsToSetupPose(); } + /** Sets the bones and constraints to their setup pose values. */ public function setBonesToSetupPose () : void { for each (var bone:Bone in bones) bone.setToSetupPose(); @@ -139,6 +122,12 @@ public class Skeleton { ikConstraint.bendDirection = ikConstraint._data.bendDirection; ikConstraint.mix = ikConstraint._data.mix; } + + for each (var transformConstraint:TransformConstraint in transformConstraints) { + transformConstraint.translateMix = transformConstraint._data.translateMix; + transformConstraint.x = transformConstraint._data.x; + transformConstraint.y = transformConstraint._data.y; + } } public function setSlotsToSetupPose () : void { @@ -275,10 +264,18 @@ public class Skeleton { } /** @return May be null. */ - public function findIkConstraint (ikConstraintName:String) : IkConstraint { - if (ikConstraintName == null) throw new ArgumentError("ikConstraintName cannot be null."); + public function findIkConstraint (constraintName:String) : IkConstraint { + if (constraintName == null) throw new ArgumentError("constraintName cannot be null."); for each (var ikConstraint:IkConstraint in ikConstraints) - if (ikConstraint._data._name == ikConstraintName) return ikConstraint; + if (ikConstraint._data._name == constraintName) return ikConstraint; + return null; + } + + /** @return May be null. */ + public function findTransformConstraint (constraintName:String) : TransformConstraint { + if (constraintName == null) throw new ArgumentError("constraintName cannot be null."); + for each (var transformConstraint:TransformConstraint in transformConstraints) + if (transformConstraint._data._name == constraintName) return transformConstraint; return null; } diff --git a/spine-as3/spine-as3/src/spine/SkeletonData.as b/spine-as3/spine-as3/src/spine/SkeletonData.as index e9ea809b8..1a0802ccd 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonData.as +++ b/spine-as3/spine-as3/src/spine/SkeletonData.as @@ -42,6 +42,7 @@ public class SkeletonData { public var events:Vector. = new Vector.(); public var animations:Vector. = new Vector.(); public var ikConstraints:Vector. = new Vector.(); + public var transformConstraints:Vector. = new Vector.(); public var width:Number, height:Number; public var version:String, hash:String; @@ -118,10 +119,20 @@ public class SkeletonData { // --- IK constraints. /** @return May be null. */ - public function findIkConstraint (ikConstraintName:String) : IkConstraintData { - if (ikConstraintName == null) throw new ArgumentError("ikConstraintName cannot be null."); + public function findIkConstraint (constraintName:String) : IkConstraintData { + if (constraintName == null) throw new ArgumentError("constraintName cannot be null."); for each (var ikConstraintData:IkConstraintData in ikConstraints) - if (ikConstraintData._name == ikConstraintName) return ikConstraintData; + if (ikConstraintData._name == constraintName) return ikConstraintData; + return null; + } + + // --- Transform constraints. + + /** @return May be null. */ + public function findTransformConstraint (constraintName:String) : TransformConstraintData { + if (constraintName == null) throw new ArgumentError("constraintName cannot be null."); + for each (var transformConstraintData:TransformConstraintData in transformConstraints) + if (transformConstraintData._name == constraintName) return transformConstraintData; return null; } diff --git a/spine-as3/spine-as3/src/spine/SkeletonJson.as b/spine-as3/spine-as3/src/spine/SkeletonJson.as index 7b5c63a48..7b8d99457 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonJson.as +++ b/spine-as3/spine-as3/src/spine/SkeletonJson.as @@ -28,560 +28,547 @@ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * 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.FlipXTimeline; -import spine.animation.FlipYTimeline; -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.SkinnedMeshAttachment; + 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; - 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; + public function SkeletonJson(attachmentLoader : AttachmentLoader = null) { + this.attachmentLoader = attachmentLoader; } - // 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.flipX = boneMap["flipX"] || false; - boneData.flipY = boneMap["flipY"] || false; - boneData.inheritScale = boneMap.hasOwnProperty("inheritScale") ? boneMap["inheritScale"] : true; - boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true; - skeletonData.bones[skeletonData.bones.length] = boneData; - } + /** @param object A String or ByteArray. */ + public function readSkeletonData(object : *, name : String = null) : SkeletonData { + if (object == null) throw new ArgumentError("object cannot be null."); - // IK constraints. - for each (var ikMap:Object in root["ik"]) { - var ikConstraintData:IkConstraintData = new IkConstraintData(ikMap["name"]); + 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."); - 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; + 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; } - 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; - } - - // 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); + // 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; } - slotData.attachmentName = slotMap["attachment"]; - slotData.blendMode = BlendMode[slotMap["blend"] || "normal"]; + // IK constraints. + for each (var ikMap : Object in root["ik"]) { + var ikConstraintData : IkConstraintData = new IkConstraintData(ikMap["name"]); - skeletonData.slots[skeletonData.slots.length] = slotData; - } + 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; + } - // 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); + 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, 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; } } - skeletonData.skins[skeletonData.skins.length] = skin; - if (skin.name == "default") - skeletonData.defaultSkin = skin; + + // Animations. + var animations : Object = root["animations"]; + for (var animationName : String in animations) + readAnimation(animationName, animations[animationName], skeletonData); + + return skeletonData; } - // 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; - } - } + private function readAttachment(skin : Skin, name : String, map : Object) : Attachment { + name = map["name"] || name; - // Animations. - var animations:Object = root["animations"]; - for (var animationName:String in animations) - readAnimation(animationName, animations[animationName], skeletonData); + var typeName : String = map["type"] || "region"; + if (typeName == "skinnedmesh") typeName = "weightedmesh"; + var type : AttachmentType = AttachmentType[typeName]; + var path : String = map["path"] || name; - return skeletonData; - } - - private function readAttachment (skin:Skin, name:String, map:Object) : Attachment { - name = map["name"] || name; - - var type:AttachmentType = AttachmentType[map["type"] || "region"]; - 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; - 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.skinnedmesh: - var weightedMesh:SkinnedMeshAttachment = attachmentLoader.newSkinnedMeshAttachment(skin, name, path); - if (!weightedMesh) return null; - weightedMesh.path = path; - - var uvs:Vector. = getFloatArray(map, "uvs", 1); - vertices = getFloatArray(map, "vertices", 1); - var weights:Vector. = new Vector.(); - var bones:Vector. = new Vector.(); - for (var i:int = 0, n:int = vertices.length; i < n; ) { - var boneCount:int = int(vertices[i++]); - bones[bones.length] = boneCount; - for (var nn:int = i + boneCount * 4; i < nn; ) { - bones[bones.length] = vertices[i]; - weights[weights.length] = vertices[i + 1] * scale; - weights[weights.length] = vertices[i + 2] * scale; - weights[weights.length] = vertices[i + 3]; - i += 4; - } - } - weightedMesh.bones = bones; - weightedMesh.weights = weights; - 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; - } - - private function readAnimation (name:String, map:Object, skeletonData:SkeletonData) : void { - var timelines:Vector. = new Vector.(); - var duration:Number = 0; - - 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++; + 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); } - 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++; + region.updateOffset(); + return region; + case AttachmentType.mesh: + var mesh : MeshAttachment = attachmentLoader.newMeshAttachment(skin, name, path); + if (!mesh) return null; + mesh.path = path; + 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); } - 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 if (timelineName == "flipX" || timelineName == "flipY") { - var flipX:Boolean = timelineName == "flipX"; - var flipTimeline:FlipXTimeline = flipX ? new FlipXTimeline(values.length) : new FlipYTimeline(values.length); - flipTimeline.boneIndex = boneIndex; - - var field:String = flipX ? "x" : "y"; - frameIndex = 0; - for each (valueMap in values) { - flipTimeline.setFrame(frameIndex, valueMap["time"], valueMap[field] || false); - frameIndex++; - } - timelines[timelines.length] = flipTimeline; - duration = Math.max(duration, flipTimeline.frames[flipTimeline.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 SkinnedMeshAttachment).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"]; - 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]; - } + 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; + var uvs : Vector. = getFloatArray(map, "uvs", 1); + vertices = getFloatArray(map, "vertices", 1); + var weights : Vector. = new Vector.(); + var bones : Vector. = new Vector.(); + for (var i : int = 0, n : int = vertices.length; i < n;) { + var boneCount : int = int(vertices[i++]); + bones[bones.length] = boneCount; + for (var nn : int = i + boneCount * 4; i < nn;) { + bones[bones.length] = vertices[i]; + weights[weights.length] = vertices[i + 1] * scale; + weights[weights.length] = vertices[i + 2] * scale; + weights[weights.length] = vertices[i + 3]; + i += 4; } - - ffdTimeline.setFrame(frameIndex, valueMap["time"], vertices); - readCurve(ffdTimeline, frameIndex, valueMap); - frameIndex++; } - timelines[timelines.length] = ffdTimeline; - duration = Math.max(duration, ffdTimeline.frames[ffdTimeline.frameCount - 1]); - } + weightedMesh.bones = bones; + weightedMesh.weights = weights; + 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; } - 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) + private function readAnimation(name : String, map : Object, skeletonData : SkeletonData) : void { + var timelines : Vector. = new Vector.(); + var duration : Number = 0; + + 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; + + 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"]; + 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]); + } + } + } + + 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) unchanged[unchangedIndex++] = originalIndex++; - // Set changed items. - drawOrder[originalIndex + offsetMap["offset"]] = originalIndex++; + // Fill in unchanged items. + for (i = slotCount - 1; i >= 0; i--) + if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; } - // 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]; + drawOrderTimeline.setFrame(frameIndex++, drawOrderMap["time"], drawOrder); } - drawOrderTimeline.setFrame(frameIndex++, drawOrderMap["time"], drawOrder); + timelines[timelines.length] = drawOrderTimeline; + duration = Math.max(duration, drawOrderTimeline.frames[drawOrderTimeline.frameCount - 1]); } - 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(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++, eventMap["time"], event); + 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]); } - timelines[timelines.length] = eventTimeline; - duration = Math.max(duration, eventTimeline.frames[eventTimeline.frameCount - 1]); + + skeletonData.animations[skeletonData.animations.length] = new Animation(name, timelines, duration); } - skeletonData.animations[skeletonData.animations.length] = new Animation(name, timelines, duration); - } - - 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 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 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; + 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 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 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 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; } - 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; } } - -} diff --git a/spine-as3/spine-as3/src/spine/animation/FlipXTimeline.as b/spine-as3/spine-as3/src/spine/TransformConstraint.as similarity index 60% rename from spine-as3/spine-as3/src/spine/animation/FlipXTimeline.as rename to spine-as3/spine-as3/src/spine/TransformConstraint.as index f6dfeeddc..a6db2bb75 100644 --- a/spine-as3/spine-as3/src/spine/animation/FlipXTimeline.as +++ b/spine-as3/spine-as3/src/spine/TransformConstraint.as @@ -29,45 +29,50 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -package spine.animation { -import spine.Bone; -import spine.Event; -import spine.Skeleton; +package spine { -public class FlipXTimeline implements Timeline { - public var boneIndex:int; - public var frames:Vector.; // time, flip, ... +public class TransformConstraint implements Updatable { + internal var _data:TransformConstraintData; + public var bone:Bone; + public var target:Bone; + public var translateMix:Number; + public var x:Number; + public var y:Number; - public function FlipXTimeline (frameCount:int) { - frames = new Vector.(frameCount * 2, true); + public function TransformConstraint (data:TransformConstraintData, skeleton:Skeleton) { + if (data == null) throw new ArgumentError("data cannot be null."); + if (skeleton == null) throw new ArgumentError("skeleton cannot be null."); + _data = data; + translateMix = data.translateMix; + x = data.x; + y = data.y; + + bone = skeleton.findBone(data.bone._name); + target = skeleton.findBone(data.target._name); } - public function get frameCount () : int { - return frames.length / 2; + public function apply () : void { + update(); } - /** Sets the time and angle of the specified keyframe. */ - public function setFrame (frameIndex:int, time:Number, flip:Boolean) : void { - frameIndex *= 2; - frames[frameIndex] = time; - frames[int(frameIndex + 1)] = flip ? 1 : 0; + public function update () : void { + var translateMix:Number = translateMix; + if (translateMix > 0) { + var local:Vector. = new Vector.(2, true); + local[0] = x; + local[1] = y; + target.localToWorld(local); + bone._worldX += (local[0] - bone._worldX) * translateMix; + bone._worldY += (local[1] - bone._worldY) * translateMix; + } } - public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { - if (time < frames[0]) { - if (lastTime > time) apply(skeleton, lastTime, int.MAX_VALUE, null, 0); - return; - } else if (lastTime > time) // - lastTime = -1; - - var frameIndex:int = (time >= frames[frames.length - 2] ? frames.length : Animation.binarySearch(frames, time, 2)) - 2; - if (frames[frameIndex] < lastTime) return; - - setFlip(skeleton.bones[boneIndex], frames[frameIndex + 1] != 0); + public function get data () : TransformConstraintData { + return _data; } - protected function setFlip (bone:Bone, flip:Boolean) : void { - bone.flipX = flip; + public function toString () : String { + return _data._name; } } diff --git a/spine-as3/spine-as3/src/spine/TransformConstraintData.as b/spine-as3/spine-as3/src/spine/TransformConstraintData.as new file mode 100644 index 000000000..6e5ee6fef --- /dev/null +++ b/spine-as3/spine-as3/src/spine/TransformConstraintData.as @@ -0,0 +1,56 @@ +/****************************************************************************** + * 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 { + +public class TransformConstraintData { + internal var _name:String; + public var bone:BoneData; + public var target:BoneData; + public var translateMix:Number; + public var x:Number; + public var y:Number; + + public function TransformConstraintData (name:String) { + if (name == null) throw new ArgumentError("name cannot be null."); + _name = name; + } + + public function get name () : String { + return _name; + } + + public function toString () : String { + return _name; + } +} + +} diff --git a/spine-as3/spine-as3/src/spine/Updatable.as b/spine-as3/spine-as3/src/spine/Updatable.as new file mode 100644 index 000000000..f5d49dbe2 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/Updatable.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 { + +public interface Updatable { + function update () : void; +} + +} diff --git a/spine-as3/spine-as3/src/spine/animation/Animation.as b/spine-as3/spine-as3/src/spine/animation/Animation.as index fbf3734bd..0dcf026f3 100644 --- a/spine-as3/spine-as3/src/spine/animation/Animation.as +++ b/spine-as3/spine-as3/src/spine/animation/Animation.as @@ -56,7 +56,7 @@ public class Animation { if (loop && duration != 0) { time %= duration; - lastTime %= duration; + if (lastTime > 0) lastTime %= duration; } for (var i:int = 0, n:int = timelines.length; i < n; i++) @@ -70,7 +70,7 @@ public class Animation { if (loop && duration != 0) { time %= duration; - lastTime %= duration; + if (lastTime > 0) lastTime %= duration; } for (var i:int = 0, n:int = timelines.length; i < n; i++) diff --git a/spine-as3/spine-as3/src/spine/animation/EventTimeline.as b/spine-as3/spine-as3/src/spine/animation/EventTimeline.as index d515933a8..832d34c74 100644 --- a/spine-as3/spine-as3/src/spine/animation/EventTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/EventTimeline.as @@ -47,8 +47,8 @@ public class EventTimeline implements Timeline { } /** Sets the time and value of the specified keyframe. */ - public function setFrame (frameIndex:int, time:Number, event:Event) : void { - frames[frameIndex] = time; + public function setFrame (frameIndex:int, event:Event) : void { + frames[frameIndex] = event.time; events[frameIndex] = event; } diff --git a/spine-as3/spine-as3/src/spine/attachments/AtlasAttachmentLoader.as b/spine-as3/spine-as3/src/spine/attachments/AtlasAttachmentLoader.as index 6ed219cfd..82ff1a52a 100644 --- a/spine-as3/spine-as3/src/spine/attachments/AtlasAttachmentLoader.as +++ b/spine-as3/spine-as3/src/spine/attachments/AtlasAttachmentLoader.as @@ -83,11 +83,11 @@ public class AtlasAttachmentLoader implements AttachmentLoader { return attachment; } - public function newSkinnedMeshAttachment (skin:Skin, name:String, path:String) : SkinnedMeshAttachment { + public function newWeightedMeshAttachment (skin:Skin, name:String, path:String) : WeightedMeshAttachment { var region:AtlasRegion = atlas.findRegion(path); if (region == null) - throw new Error("Region not found in atlas: " + path + " (skinned mesh attachment: " + name + ")"); - var attachment:SkinnedMeshAttachment = new SkinnedMeshAttachment(name); + throw new Error("Region not found in atlas: " + path + " (weighted mesh attachment: " + name + ")"); + var attachment:WeightedMeshAttachment = new WeightedMeshAttachment(name); attachment.rendererObject = region; var scaleX:Number = region.page.width / nextPOT(region.page.width); var scaleY:Number = region.page.height / nextPOT(region.page.height); diff --git a/spine-as3/spine-as3/src/spine/attachments/AttachmentLoader.as b/spine-as3/spine-as3/src/spine/attachments/AttachmentLoader.as index 4ce03c429..c5bec787b 100644 --- a/spine-as3/spine-as3/src/spine/attachments/AttachmentLoader.as +++ b/spine-as3/spine-as3/src/spine/attachments/AttachmentLoader.as @@ -40,7 +40,7 @@ public interface AttachmentLoader { function newMeshAttachment (skin:Skin, name:String, path:String) : MeshAttachment; /** @return May be null to not load an attachment. */ - function newSkinnedMeshAttachment (skin:Skin, name:String, path:String) : SkinnedMeshAttachment; + function newWeightedMeshAttachment (skin:Skin, name:String, path:String) : WeightedMeshAttachment; /** @return May be null to not load an attachment. */ function newBoundingBoxAttachment (skin:Skin, name:String) : BoundingBoxAttachment; diff --git a/spine-as3/spine-as3/src/spine/attachments/AttachmentType.as b/spine-as3/spine-as3/src/spine/attachments/AttachmentType.as index 903e04c05..6106e6217 100644 --- a/spine-as3/spine-as3/src/spine/attachments/AttachmentType.as +++ b/spine-as3/spine-as3/src/spine/attachments/AttachmentType.as @@ -36,7 +36,7 @@ public class AttachmentType { public static const regionsequence:AttachmentType = new AttachmentType(1, "regionsequence"); public static const boundingbox:AttachmentType = new AttachmentType(2, "boundingbox"); public static const mesh:AttachmentType = new AttachmentType(3, "mesh"); - public static const skinnedmesh:AttachmentType = new AttachmentType(4, "skinnedmesh"); + public static const weightedmesh:AttachmentType = new AttachmentType(4, "weightedmesh"); public var ordinal:int; public var name:String; diff --git a/spine-as3/spine-as3/src/spine/attachments/BoundingBoxAttachment.as b/spine-as3/spine-as3/src/spine/attachments/BoundingBoxAttachment.as index 78feb3f9b..916ae9e9c 100644 --- a/spine-as3/spine-as3/src/spine/attachments/BoundingBoxAttachment.as +++ b/spine-as3/spine-as3/src/spine/attachments/BoundingBoxAttachment.as @@ -42,10 +42,10 @@ public dynamic class BoundingBoxAttachment extends Attachment { public function computeWorldVertices (x:Number, y:Number, bone:Bone, worldVertices:Vector.) : void { x += bone.worldX; y += bone.worldY; - var m00:Number = bone.m00; - var m01:Number = bone.m01; - var m10:Number = bone.m10; - var m11:Number = bone.m11; + var m00:Number = bone.a; + var m01:Number = bone.b; + var m10:Number = bone.c; + var m11:Number = bone.d; var vertices:Vector. = this.vertices; for (var i:int = 0, n:int = vertices.length; i < n; i += 2) { var ii:int = i + 1; diff --git a/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as b/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as index 30ff417db..032ee77ff 100644 --- a/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as +++ b/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as @@ -88,10 +88,10 @@ public dynamic class MeshAttachment extends Attachment { var bone:Bone = slot.bone; x += bone.worldX; y += bone.worldY; - var m00:Number = bone.m00; - var m01:Number = bone.m01; - var m10:Number = bone.m10; - var m11:Number = bone.m11; + var m00:Number = bone.a; + var m01:Number = bone.b; + var m10:Number = bone.c; + var m11:Number = bone.d; var vertices:Vector. = this.vertices; var verticesCount:int = vertices.length; if (slot.attachmentVertices.length == verticesCount) vertices = slot.attachmentVertices; diff --git a/spine-as3/spine-as3/src/spine/attachments/RegionAttachment.as b/spine-as3/spine-as3/src/spine/attachments/RegionAttachment.as index 673035ed8..971e349c1 100644 --- a/spine-as3/spine-as3/src/spine/attachments/RegionAttachment.as +++ b/spine-as3/spine-as3/src/spine/attachments/RegionAttachment.as @@ -125,10 +125,10 @@ public dynamic class RegionAttachment extends Attachment { public function computeWorldVertices (x:Number, y:Number, bone:Bone, worldVertices:Vector.) : void { x += bone.worldX; y += bone.worldY; - var m00:Number = bone.m00; - var m01:Number = bone.m01; - var m10:Number = bone.m10; - var m11:Number = bone.m11; + var m00:Number = bone.a; + var m01:Number = bone.b; + var m10:Number = bone.c; + var m11:Number = bone.d; var x1:Number = offset[X1]; var y1:Number = offset[Y1]; var x2:Number = offset[X2]; diff --git a/spine-as3/spine-as3/src/spine/attachments/SkinnedMeshAttachment.as b/spine-as3/spine-as3/src/spine/attachments/WeightedMeshAttachment.as similarity index 92% rename from spine-as3/spine-as3/src/spine/attachments/SkinnedMeshAttachment.as rename to spine-as3/spine-as3/src/spine/attachments/WeightedMeshAttachment.as index 9eabcee0c..cdf3af231 100644 --- a/spine-as3/spine-as3/src/spine/attachments/SkinnedMeshAttachment.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 SkinnedMeshAttachment extends Attachment { +public dynamic class WeightedMeshAttachment extends Attachment { public var bones:Vector.; public var weights:Vector.; public var uvs:Vector.; @@ -64,7 +64,7 @@ public dynamic class SkinnedMeshAttachment extends Attachment { public var width:Number; public var height:Number; - public function SkinnedMeshAttachment (name:String) { + public function WeightedMeshAttachment (name:String) { super(name); } @@ -102,8 +102,8 @@ public dynamic class SkinnedMeshAttachment extends Attachment { vx = weights[b]; vy = weights[int(b + 1)]; weight = weights[int(b + 2)]; - wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; - wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; + wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight; + wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight; } worldVertices[w] = wx + x; worldVertices[int(w + 1)] = wy + y; @@ -119,8 +119,8 @@ public dynamic class SkinnedMeshAttachment extends Attachment { vx = weights[b] + ffd[f]; vy = weights[int(b + 1)] + ffd[int(f + 1)]; weight = weights[int(b + 2)]; - wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; - wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; + wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight; + wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight; } worldVertices[w] = wx + x; worldVertices[int(w + 1)] = wy + y; diff --git a/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as b/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as index 6c141dac9..fb207e2f1 100644 --- a/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as +++ b/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as @@ -129,12 +129,10 @@ public class SkeletonSprite extends Sprite { var bone:Bone = slot.bone; var flipX:int = skeleton.flipX ? -1 : 1; var flipY:int = skeleton.flipY ? -1 : 1; - if (bone.worldFlipX) flipX = -flipX; - if (bone.worldFlipY) flipY = -flipY; wrapper.x = bone.worldX; wrapper.y = bone.worldY; - wrapper.rotation = -bone.worldRotation * flipX * flipY; + wrapper.rotation = -bone.worldRotationX * flipX * flipY; wrapper.scaleX = bone.worldScaleX * flipX; wrapper.scaleY = bone.worldScaleY * flipY; addChild(wrapper);