Updated spine-as3 to Spine v3.

This commit is contained in:
NathanSweet 2016-02-17 03:54:04 +01:00
parent 0f723b9fa7
commit 4b5322d5d6
21 changed files with 1021 additions and 791 deletions

View File

@ -31,7 +31,7 @@
package spine { package spine {
public class Bone { public class Bone implements Updatable {
static public var yDown:Boolean; static public var yDown:Boolean;
internal var _data:BoneData; internal var _data:BoneData;
@ -40,23 +40,20 @@ public class Bone {
public var x:Number; public var x:Number;
public var y:Number; public var y:Number;
public var rotation:Number; public var rotation:Number;
public var rotationIK:Number;
public var scaleX:Number; public var scaleX:Number;
public var scaleY:Number; public var scaleY:Number;
public var flipX:Boolean; public var appliedRotation:Number;
public var flipY:Boolean; public var appliedScaleX:Number;
public var appliedScaleY:Number;
internal var _m00:Number; internal var _a:Number;
internal var _m01:Number; internal var _b:Number;
internal var _m10:Number; internal var _c:Number;
internal var _m11:Number; internal var _d:Number;
internal var _worldX:Number; internal var _worldX:Number;
internal var _worldY:Number; internal var _worldY:Number;
internal var _worldRotation:Number; internal var _worldSignX:Number;
internal var _worldScaleX:Number; internal var _worldSignY:Number;
internal var _worldScaleY:Number;
internal var _worldFlipX:Boolean;
internal var _worldFlipY:Boolean;
/** @param parent May be null. */ /** @param parent May be null. */
public function Bone (data:BoneData, skeleton:Skeleton, parent:Bone) { public function Bone (data:BoneData, skeleton:Skeleton, parent:Bone) {
@ -68,48 +65,136 @@ public class Bone {
setToSetupPose(); 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 { 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; var parent:Bone = _parent;
if (parent) { if (!parent) { // Root bone.
_worldX = x * parent._m00 + y * parent._m01 + parent._worldX; var skeleton:Skeleton = this.skeleton;
_worldY = x * parent._m10 + y * parent._m11 + parent._worldY; if (skeleton.flipX) {
if (_data.inheritScale) { x = -x;
_worldScaleX = parent._worldScaleX * scaleX; la = -la;
_worldScaleY = parent._worldScaleY * scaleY; lb = -lb;
} else {
_worldScaleX = scaleX;
_worldScaleY = scaleY;
} }
_worldRotation = _data.inheritRotation ? parent._worldRotation + rotationIK : rotationIK; if (skeleton.flipY != yDown) {
_worldFlipX = parent._worldFlipX != flipX; y = -y;
_worldFlipY = parent._worldFlipY != flipY; lc = -lc;
} else { ld = -ld;
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;
} }
var radians:Number = _worldRotation * (Math.PI / 180); _a = la;
var cos:Number = Math.cos(radians); _b = lb;
var sin:Number = Math.sin(radians); _c = lc;
if (_worldFlipX) { _d = ld;
_m00 = -cos * _worldScaleX; _worldX = x;
_m01 = sin * _worldScaleY; _worldY = y;
} else { _worldSignX = scaleX < 0 ? -1 : 1;
_m00 = cos * _worldScaleX; _worldSignY = scaleY < 0 ? -1 : 1;
_m01 = -sin * _worldScaleY; return;
}
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;
} }
if (_worldFlipY != yDown) {
_m10 = -sin * _worldScaleX;
_m11 = -cos * _worldScaleY;
} else { } else {
_m10 = sin * _worldScaleX; _a = la;
_m11 = cos * _worldScaleY; _b = lb;
_c = lc;
_d = ld;
} }
} }
@ -117,11 +202,8 @@ public class Bone {
x = _data.x; x = _data.x;
y = _data.y; y = _data.y;
rotation = _data.rotation; rotation = _data.rotation;
rotationIK = rotation;
scaleX = _data.scaleX; scaleX = _data.scaleX;
scaleY = _data.scaleY; scaleY = _data.scaleY;
flipX = _data.flipX;
flipY = _data.flipY;
} }
public function get data () : BoneData { public function get data () : BoneData {
@ -136,20 +218,20 @@ public class Bone {
return _skeleton; return _skeleton;
} }
public function get m00 () : Number { public function get a () : Number {
return _m00; return _a;
} }
public function get m01 () : Number { public function get b () : Number {
return _m01; return _b;
} }
public function get m10 () : Number { public function get c () : Number {
return _m10; return _c;
} }
public function get m11 () : Number { public function get d () : Number {
return _m11; return _d;
} }
public function get worldX () : Number { public function get worldX () : Number {
@ -160,42 +242,42 @@ public class Bone {
return _worldY; return _worldY;
} }
public function get worldRotation () : Number { public function get worldSignX () : Number {
return _worldRotation; 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 { public function get worldScaleX () : Number {
return _worldScaleX; return Math.sqrt(_a * _a + _b * _b) * _worldSignX;
} }
public function get worldScaleY () : Number { public function get worldScaleY () : Number {
return _worldScaleY; return Math.sqrt(_c * _c + _d * _d) * _worldSignY;
}
public function get worldFlipX () : Boolean {
return _worldFlipX;
}
public function get worldFlipY () : Boolean {
return _worldFlipY;
} }
public function worldToLocal (world:Vector.<Number>) : void { public function worldToLocal (world:Vector.<Number>) : void {
var dx:Number = world[0] - _worldX, dy:Number = world[1] - _worldY; var x:Number = world[0] - _worldX, y:Number = world[1] - _worldY;
var m00:Number = _m00, m10:Number = _m10, m01:Number = _m01, m11:Number = _m11; var a:Number = _a, b:Number = _b, c:Number = _c, d:Number = _d;
if (_worldFlipX != (_worldFlipY != yDown)) { var invDet:Number = 1 / (a * d - b * c);
m00 = -m00; world[0] = (x * a * invDet - y * b * invDet);
m11 = -m11; world[1] = (y * d * invDet - x * c * invDet);
}
var invDet:Number = 1 / (m00 * m11 - m01 * m10);
world[0] = (dx * m00 * invDet - dy * m01 * invDet);
world[1] = (dy * m11 * invDet - dx * m10 * invDet);
} }
public function localToWorld (local:Vector.<Number>) : void { public function localToWorld (local:Vector.<Number>) : void {
var localX:Number = local[0], localY:Number = local[1]; var localX:Number = local[0], localY:Number = local[1];
local[0] = localX * _m00 + localY * _m01 + _worldX; local[0] = localX * _a + localY * _b + _worldX;
local[1] = localX * _m10 + localY * _m11 + _worldY; local[1] = localX * _c + localY * _d + _worldY;
} }
public function toString () : String { public function toString () : String {

View File

@ -42,8 +42,6 @@ public class BoneData {
public var scaleY:Number = 1; public var scaleY:Number = 1;
public var inheritScale:Boolean = true; public var inheritScale:Boolean = true;
public var inheritRotation:Boolean = true; public var inheritRotation:Boolean = true;
public var flipX:Boolean;
public var flipY:Boolean;
/** @param parent May be null. */ /** @param parent May be null. */
public function BoneData (name:String, parent:BoneData) { public function BoneData (name:String, parent:BoneData) {

View File

@ -33,12 +33,14 @@ package spine {
public class Event { public class Event {
internal var _data:EventData; internal var _data:EventData;
public var intValue:int;; public var time:Number;
public var intValue:int;
public var floatValue:Number; public var floatValue:Number;
public var stringValue:String; 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."); if (data == null) throw new ArgumentError("data cannot be null.");
this.time = time;
_data = data; _data = data;
} }

View File

@ -31,10 +31,7 @@
package spine { package spine {
public class IkConstraint { public class IkConstraint implements Updatable {
static private const tempPosition:Vector.<Number> = new Vector.<Number>(2, true);
static private const radDeg:Number = 180 / Math.PI;
internal var _data:IkConstraintData; internal var _data:IkConstraintData;
public var bones:Vector.<Bone>; public var bones:Vector.<Bone>;
public var target:Bone; public var target:Bone;
@ -55,6 +52,10 @@ public class IkConstraint {
} }
public function apply () : void { public function apply () : void {
update();
}
public function update () : void {
switch (bones.length) { switch (bones.length) {
case 1: case 1:
apply1(bones[0], target._worldX, target._worldY, mix); 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 /** 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. */ * coordinate system. */
static public function apply1 (bone:Bone, targetX:Number, targetY:Number, alpha:Number) : void { 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 rotation:Number = bone.rotation;
var rotationIK:Number = Math.atan2(targetY - bone._worldY, targetX - bone._worldX) * radDeg; var rotationIK:Number = Math.atan2(targetY - bone.worldY, targetX - bone.worldX) * MathUtils.radDeg - parentRotation;
if (bone._worldFlipX != (bone._worldFlipY != Bone.yDown)) rotationIK = -rotationIK; if (bone.worldSignX != bone.worldSignY) rotationIK = 360 - rotationIK;
rotationIK -= parentRotation; if (rotationIK > 180) rotationIK -= 360;
bone.rotationIK = rotation + (rotationIK - rotation) * alpha; 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 /** 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. * target is specified in the world coordinate system.
* @param child Any descendant bone of the parent. */ * @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 { static public function apply2 (parent:Bone, child:Bone, targetX:Number, targetY:Number, bendDir:int, alpha:Number) : void {
var childRotation:Number = child.rotation, parentRotation:Number = parent.rotation; if (alpha == 0) return;
if (alpha == 0) { var px:Number = parent.x, py:Number = parent.y, psx:Number = parent.scaleX, psy:Number = parent.scaleY;
child.rotationIK = childRotation; var csx:Number = child.scaleX, cy:Number = child.y;
parent.rotationIK = parentRotation; var offset1:int, offset2:int, sign2:int;
return; if (psx < 0) {
} psx = -psx;
var positionX:Number, positionY:Number; offset1 = 180;
var parentParent:Bone = parent._parent; sign2 = -1;
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;
} else { } else {
targetX -= parent.x; offset1 = 0;
targetY -= parent.y; sign2 = 1;
} }
if (child._parent == parent) { if (psy < 0) {
positionX = child.x; psy = -psy;
positionY = child.y; 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 { } else {
tempPosition[0] = child.x; var ppa:Number = pp.a, ppb:Number = pp.b, ppc:Number = pp.c, ppd:Number = pp.d;
tempPosition[1] = child.y; var invDet:Number = 1 / (ppa * ppd - ppb * ppc);
child._parent.localToWorld(tempPosition); var wx:Number = pp.worldX, wy:Number = pp.worldY, twx:Number = targetX - wx, twy:Number = targetY - wy;
parent.worldToLocal(tempPosition); tx = (twx * ppd - twy * ppb) * invDet - px;
positionX = tempPosition[0]; ty = (twy * ppa - twx * ppc) * invDet - py;
positionY = tempPosition[1]; 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 l1:Number = Math.sqrt(dx * dx + dy * dy), l2:Number = child.data.length * csx, a1:Number, a2:Number;
var offset:Number = Math.atan2(childY, childX); outer:
var len1:Number = Math.sqrt(childX * childX + childY * childY), len2:Number = child.data.length * child._worldScaleX; if (Math.abs(psx - psy) <= 0.0001) {
// Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/ l2 *= psx;
var cosDenom:Number = 2 * len1 * len2; var cos:Number = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
if (cosDenom < 0.0001) { if (cos < -1) cos = -1;
child.rotationIK = childRotation + (Math.atan2(targetY, targetX) * radDeg - parentRotation - childRotation) * alpha; else if (cos > 1) cos = 1;
return; 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 cos:Number = (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom; }
if (cos < -1) var minAngle:Number = 0, minDist:Number = Number.MAX_VALUE, minX:Number = 0, minY:Number = 0;
cos = -1; var maxAngle:Number = 0, maxDist:Number = 0, maxX:Number = 0, maxY:Number = 0;
else if (cos > 1) var x:Number = l1 + a, dist:Number = x * x;
cos = 1; if (dist > maxDist) {
var childAngle:Number = Math.acos(cos) * bendDirection; maxAngle = 0;
var adjacent:Number = len1 + len2 * cos, opposite:Number = len2 * Math.sin(childAngle); maxDist = dist;
var parentAngle:Number = Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite); maxX = x;
var rotation:Number = (parentAngle - offset) * radDeg - parentRotation; }
if (rotation > 180) x = l1 - a;
rotation -= 360; dist = x * x;
else if (rotation < -180) // if (dist < minDist) {
rotation += 360; minAngle = Math.PI;
parent.rotationIK = parentRotation + rotation * alpha; minDist = dist;
rotation = (childAngle + offset) * radDeg - childRotation; minX = x;
if (rotation > 180) }
rotation -= 360; var angle:Number = Math.acos(-a * l1 / (aa - bb));
else if (rotation < -180) // x = a * Math.cos(angle) + l1;
rotation += 360; var y:Number = b * Math.sin(angle);
child.rotationIK = childRotation + (rotation + parent._worldRotation - child._parent._worldRotation) * alpha; 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 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);
} }
} }

View File

@ -29,17 +29,11 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
package spine.animation { package spine {
import spine.Bone;
public class FlipYTimeline extends FlipXTimeline { public class MathUtils {
public function FlipYTimeline (frameCount:int) { static public var radDeg:Number = 180 / Math.PI;
super(frameCount); static public var degRad:Number = Math.PI / 180;
}
override protected function setFlip (bone:Bone, flip:Boolean) : void {
bone.flipY = flip;
}
} }
} }

View File

@ -38,7 +38,8 @@ public class Skeleton {
public var slots:Vector.<Slot>; public var slots:Vector.<Slot>;
public var drawOrder:Vector.<Slot>; public var drawOrder:Vector.<Slot>;
public var ikConstraints:Vector.<IkConstraint>; public var ikConstraints:Vector.<IkConstraint>;
private var _boneCache:Vector.<Vector.<Bone>> = new Vector.<Vector.<Bone>>(); public var transformConstraints:Vector.<TransformConstraint>;
private var _updateCache:Vector.<Updatable> = new Vector.<Updatable>();
private var _skin:Skin; private var _skin:Skin;
public var r:Number = 1, g:Number = 1, b:Number = 1, a:Number = 1; public var r:Number = 1, g:Number = 1, b:Number = 1, a:Number = 1;
public var time:Number = 0; public var time:Number = 0;
@ -69,68 +70,50 @@ public class Skeleton {
for each (var ikConstraintData:IkConstraintData in data.ikConstraints) for each (var ikConstraintData:IkConstraintData in data.ikConstraints)
ikConstraints[ikConstraints.length] = new IkConstraint(ikConstraintData, this); ikConstraints[ikConstraints.length] = new IkConstraint(ikConstraintData, this);
transformConstraints = new Vector.<TransformConstraint>();
for each (var transformConstraintData:TransformConstraintData in data.transformConstraints)
transformConstraints[transformConstraints.length] = new TransformConstraint(transformConstraintData, this);
updateCache(); 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 { public function updateCache () : void {
var ikConstraintsCount:int = ikConstraints.length; var updateCache:Vector.<Updatable> = _updateCache;
var ikConstraints:Vector.<IkConstraint> = this.ikConstraints;
var arrayCount:int = ikConstraintsCount + 1; var transformConstraints:Vector.<TransformConstraint> = this.transformConstraints;
if (_boneCache.length > arrayCount) _boneCache.splice(arrayCount, _boneCache.length - arrayCount); updateCache.length = bones.length + ikConstraints.length + transformConstraints.length;
for each (var cachedBones:Vector.<Bone> in _boneCache) var i:int = 0;
cachedBones.length = 0;
while (_boneCache.length < arrayCount)
_boneCache[_boneCache.length] = new Vector.<Bone>();
var nonIkBones:Vector.<Bone> = _boneCache[0];
outer:
for each (var bone:Bone in bones) { for each (var bone:Bone in bones) {
var current:Bone = bone; updateCache[i++] = bone;
do { for each (var transformConstraint:TransformConstraint in transformConstraints) {
var ii:int = 0; if (bone == transformConstraint.bone) {
updateCache[i++] = transformConstraint;
break;
}
}
for each (var ikConstraint:IkConstraint in ikConstraints) { for each (var ikConstraint:IkConstraint in ikConstraints) {
var parent:Bone = ikConstraint.bones[0]; if (bone == ikConstraint.bones[ikConstraint.bones.length - 1]) {
var child:Bone = ikConstraint.bones[int(ikConstraint.bones.length - 1)]; updateCache[i++] = ikConstraint;
while (true) { break;
if (current == child) {
_boneCache[ii].push(bone);
_boneCache[int(ii + 1)].push(bone);
continue outer;
} }
if (child == parent) break;
child = child.parent;
} }
ii++;
}
current = current.parent;
} while (current != null);
nonIkBones[nonIkBones.length] = bone;
} }
} }
/** 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 { public function updateWorldTransform () : void {
var bone:Bone; for each (var updatable:Updatable in _updateCache)
for each (bone in bones) updatable.update();
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++;
}
} }
/** 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 { public function setToSetupPose () : void {
setBonesToSetupPose(); setBonesToSetupPose();
setSlotsToSetupPose(); setSlotsToSetupPose();
} }
/** Sets the bones and constraints to their setup pose values. */
public function setBonesToSetupPose () : void { public function setBonesToSetupPose () : void {
for each (var bone:Bone in bones) for each (var bone:Bone in bones)
bone.setToSetupPose(); bone.setToSetupPose();
@ -139,6 +122,12 @@ public class Skeleton {
ikConstraint.bendDirection = ikConstraint._data.bendDirection; ikConstraint.bendDirection = ikConstraint._data.bendDirection;
ikConstraint.mix = ikConstraint._data.mix; 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 { public function setSlotsToSetupPose () : void {
@ -275,10 +264,18 @@ public class Skeleton {
} }
/** @return May be null. */ /** @return May be null. */
public function findIkConstraint (ikConstraintName:String) : IkConstraint { public function findIkConstraint (constraintName:String) : IkConstraint {
if (ikConstraintName == null) throw new ArgumentError("ikConstraintName cannot be null."); if (constraintName == null) throw new ArgumentError("constraintName cannot be null.");
for each (var ikConstraint:IkConstraint in ikConstraints) 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; return null;
} }

View File

@ -42,6 +42,7 @@ public class SkeletonData {
public var events:Vector.<EventData> = new Vector.<EventData>(); public var events:Vector.<EventData> = new Vector.<EventData>();
public var animations:Vector.<Animation> = new Vector.<Animation>(); public var animations:Vector.<Animation> = new Vector.<Animation>();
public var ikConstraints:Vector.<IkConstraintData> = new Vector.<IkConstraintData>(); public var ikConstraints:Vector.<IkConstraintData> = new Vector.<IkConstraintData>();
public var transformConstraints:Vector.<TransformConstraintData> = new Vector.<TransformConstraintData>();
public var width:Number, height:Number; public var width:Number, height:Number;
public var version:String, hash:String; public var version:String, hash:String;
@ -118,10 +119,20 @@ public class SkeletonData {
// --- IK constraints. // --- IK constraints.
/** @return May be null. */ /** @return May be null. */
public function findIkConstraint (ikConstraintName:String) : IkConstraintData { public function findIkConstraint (constraintName:String) : IkConstraintData {
if (ikConstraintName == null) throw new ArgumentError("ikConstraintName cannot be null."); if (constraintName == null) throw new ArgumentError("constraintName cannot be null.");
for each (var ikConstraintData:IkConstraintData in ikConstraints) 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; return null;
} }

View File

@ -28,45 +28,42 @@
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
package spine { package spine {
import flash.utils.ByteArray; import flash.utils.ByteArray;
import spine.animation.Animation; import spine.animation.Animation;
import spine.animation.AttachmentTimeline; import spine.animation.AttachmentTimeline;
import spine.animation.ColorTimeline; import spine.animation.ColorTimeline;
import spine.animation.CurveTimeline; import spine.animation.CurveTimeline;
import spine.animation.DrawOrderTimeline; import spine.animation.DrawOrderTimeline;
import spine.animation.EventTimeline; import spine.animation.EventTimeline;
import spine.animation.FfdTimeline; import spine.animation.FfdTimeline;
import spine.animation.FlipXTimeline; import spine.animation.IkConstraintTimeline;
import spine.animation.FlipYTimeline; import spine.animation.RotateTimeline;
import spine.animation.IkConstraintTimeline; import spine.animation.ScaleTimeline;
import spine.animation.RotateTimeline; import spine.animation.Timeline;
import spine.animation.ScaleTimeline; import spine.animation.TranslateTimeline;
import spine.animation.Timeline; import spine.attachments.Attachment;
import spine.animation.TranslateTimeline; import spine.attachments.AttachmentLoader;
import spine.attachments.Attachment; import spine.attachments.AttachmentType;
import spine.attachments.AttachmentLoader; import spine.attachments.BoundingBoxAttachment;
import spine.attachments.AttachmentType; import spine.attachments.MeshAttachment;
import spine.attachments.BoundingBoxAttachment; import spine.attachments.RegionAttachment;
import spine.attachments.MeshAttachment; import spine.attachments.WeightedMeshAttachment;
import spine.attachments.RegionAttachment;
import spine.attachments.SkinnedMeshAttachment;
public class SkeletonJson { public class SkeletonJson {
public var attachmentLoader:AttachmentLoader; public var attachmentLoader : AttachmentLoader;
public var scale:Number = 1; public var scale : Number = 1;
public function SkeletonJson (attachmentLoader:AttachmentLoader = null) { public function SkeletonJson(attachmentLoader : AttachmentLoader = null) {
this.attachmentLoader = attachmentLoader; this.attachmentLoader = attachmentLoader;
} }
/** @param object A String or ByteArray. */ /** @param object A String or ByteArray. */
public function readSkeletonData (object:*, name:String = null) : SkeletonData { public function readSkeletonData(object : *, name : String = null) : SkeletonData {
if (object == null) throw new ArgumentError("object cannot be null."); if (object == null) throw new ArgumentError("object cannot be null.");
var root:Object; var root : Object;
if (object is String) if (object is String)
root = JSON.parse(String(object)); root = JSON.parse(String(object));
else if (object is ByteArray) else if (object is ByteArray)
@ -76,11 +73,11 @@ public class SkeletonJson {
else else
throw new ArgumentError("object must be a String, ByteArray or Object."); throw new ArgumentError("object must be a String, ByteArray or Object.");
var skeletonData:SkeletonData = new SkeletonData(); var skeletonData : SkeletonData = new SkeletonData();
skeletonData.name = name; skeletonData.name = name;
// Skeleton. // Skeleton.
var skeletonMap:Object = root["skeleton"]; var skeletonMap : Object = root["skeleton"];
if (skeletonMap) { if (skeletonMap) {
skeletonData.hash = skeletonMap["hash"]; skeletonData.hash = skeletonMap["hash"];
skeletonData.version = skeletonMap["spine"]; skeletonData.version = skeletonMap["spine"];
@ -89,10 +86,10 @@ public class SkeletonJson {
} }
// Bones. // Bones.
var boneData:BoneData; var boneData : BoneData;
for each (var boneMap:Object in root["bones"]) { for each (var boneMap : Object in root["bones"]) {
var parent:BoneData = null; var parent : BoneData = null;
var parentName:String = boneMap["parent"]; var parentName : String = boneMap["parent"];
if (parentName) { if (parentName) {
parent = skeletonData.findBone(parentName); parent = skeletonData.findBone(parentName);
if (!parent) throw new Error("Parent bone not found: " + parentName); if (!parent) throw new Error("Parent bone not found: " + parentName);
@ -104,19 +101,17 @@ public class SkeletonJson {
boneData.rotation = (boneMap["rotation"] || 0); boneData.rotation = (boneMap["rotation"] || 0);
boneData.scaleX = boneMap.hasOwnProperty("scaleX") ? boneMap["scaleX"] : 1; boneData.scaleX = boneMap.hasOwnProperty("scaleX") ? boneMap["scaleX"] : 1;
boneData.scaleY = boneMap.hasOwnProperty("scaleY") ? boneMap["scaleY"] : 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.inheritScale = boneMap.hasOwnProperty("inheritScale") ? boneMap["inheritScale"] : true;
boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true; boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true;
skeletonData.bones[skeletonData.bones.length] = boneData; skeletonData.bones[skeletonData.bones.length] = boneData;
} }
// IK constraints. // IK constraints.
for each (var ikMap:Object in root["ik"]) { for each (var ikMap : Object in root["ik"]) {
var ikConstraintData:IkConstraintData = new IkConstraintData(ikMap["name"]); var ikConstraintData : IkConstraintData = new IkConstraintData(ikMap["name"]);
for each (var boneName:String in ikMap["bones"]) { for each (var boneName : String in ikMap["bones"]) {
var bone:BoneData = skeletonData.findBone(boneName); var bone : BoneData = skeletonData.findBone(boneName);
if (!bone) throw new Error("IK bone not found: " + boneName); if (!bone) throw new Error("IK bone not found: " + boneName);
ikConstraintData.bones[ikConstraintData.bones.length] = bone; ikConstraintData.bones[ikConstraintData.bones.length] = bone;
} }
@ -130,14 +125,31 @@ public class SkeletonJson {
skeletonData.ikConstraints[skeletonData.ikConstraints.length] = ikConstraintData; 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. // Slots.
for each (var slotMap:Object in root["slots"]) { for each (var slotMap : Object in root["slots"]) {
boneName = slotMap["bone"]; boneName = slotMap["bone"];
boneData = skeletonData.findBone(boneName); boneData = skeletonData.findBone(boneName);
if (!boneData) throw new Error("Slot bone not found: " + boneName); if (!boneData) throw new Error("Slot bone not found: " + boneName);
var slotData:SlotData = new SlotData(slotMap["name"], boneData); var slotData : SlotData = new SlotData(slotMap["name"], boneData);
var color:String = slotMap["color"]; var color : String = slotMap["color"];
if (color) { if (color) {
slotData.r = toColor(color, 0); slotData.r = toColor(color, 0);
slotData.g = toColor(color, 1); slotData.g = toColor(color, 1);
@ -152,15 +164,15 @@ public class SkeletonJson {
} }
// Skins. // Skins.
var skins:Object = root["skins"]; var skins : Object = root["skins"];
for (var skinName:String in skins) { for (var skinName : String in skins) {
var skinMap:Object = skins[skinName]; var skinMap : Object = skins[skinName];
var skin:Skin = new Skin(skinName); var skin : Skin = new Skin(skinName);
for (var slotName:String in skinMap) { for (var slotName : String in skinMap) {
var slotIndex:int = skeletonData.findSlotIndex(slotName); var slotIndex : int = skeletonData.findSlotIndex(slotName);
var slotEntry:Object = skinMap[slotName]; var slotEntry : Object = skinMap[slotName];
for (var attachmentName:String in slotEntry) { for (var attachmentName : String in slotEntry) {
var attachment:Attachment = readAttachment(skin, attachmentName, slotEntry[attachmentName]); var attachment : Attachment = readAttachment(skin, attachmentName, slotEntry[attachmentName]);
if (attachment != null) if (attachment != null)
skin.addAttachment(slotIndex, attachmentName, attachment); skin.addAttachment(slotIndex, attachmentName, attachment);
} }
@ -171,11 +183,11 @@ public class SkeletonJson {
} }
// Events. // Events.
var events:Object = root["events"]; var events : Object = root["events"];
if (events) { if (events) {
for (var eventName:String in events) { for (var eventName : String in events) {
var eventMap:Object = events[eventName]; var eventMap : Object = events[eventName];
var eventData:EventData = new EventData(eventName); var eventData : EventData = new EventData(eventName);
eventData.intValue = eventMap["int"] || 0; eventData.intValue = eventMap["int"] || 0;
eventData.floatValue = eventMap["float"] || 0; eventData.floatValue = eventMap["float"] || 0;
eventData.stringValue = eventMap["string"] || null; eventData.stringValue = eventMap["string"] || null;
@ -184,24 +196,26 @@ public class SkeletonJson {
} }
// Animations. // Animations.
var animations:Object = root["animations"]; var animations : Object = root["animations"];
for (var animationName:String in animations) for (var animationName : String in animations)
readAnimation(animationName, animations[animationName], skeletonData); readAnimation(animationName, animations[animationName], skeletonData);
return skeletonData; return skeletonData;
} }
private function readAttachment (skin:Skin, name:String, map:Object) : Attachment { private function readAttachment(skin : Skin, name : String, map : Object) : Attachment {
name = map["name"] || name; name = map["name"] || name;
var type:AttachmentType = AttachmentType[map["type"] || "region"]; var typeName : String = map["type"] || "region";
var path:String = map["path"] || name; if (typeName == "skinnedmesh") typeName = "weightedmesh";
var type : AttachmentType = AttachmentType[typeName];
var path : String = map["path"] || name;
var scale:Number = this.scale; var scale : Number = this.scale;
var color:String, vertices:Vector.<Number>; var color : String, vertices : Vector.<Number>;
switch (type) { switch (type) {
case AttachmentType.region: case AttachmentType.region:
var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, path); var region : RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, path);
if (!region) return null; if (!region) return null;
region.path = path; region.path = path;
region.x = Number(map["x"] || 0) * scale; region.x = Number(map["x"] || 0) * scale;
@ -211,7 +225,6 @@ public class SkeletonJson {
region.rotation = map["rotation"] || 0; region.rotation = map["rotation"] || 0;
region.width = Number(map["width"] || 0) * scale; region.width = Number(map["width"] || 0) * scale;
region.height = Number(map["height"] || 0) * scale; region.height = Number(map["height"] || 0) * scale;
color = map["color"]; color = map["color"];
if (color) { if (color) {
region.r = toColor(color, 0); region.r = toColor(color, 0);
@ -219,19 +232,16 @@ public class SkeletonJson {
region.b = toColor(color, 2); region.b = toColor(color, 2);
region.a = toColor(color, 3); region.a = toColor(color, 3);
} }
region.updateOffset(); region.updateOffset();
return region; return region;
case AttachmentType.mesh: case AttachmentType.mesh:
var mesh:MeshAttachment = attachmentLoader.newMeshAttachment(skin, name, path); var mesh : MeshAttachment = attachmentLoader.newMeshAttachment(skin, name, path);
if (!mesh) return null; if (!mesh) return null;
mesh.path = path; mesh.path = path;
mesh.vertices = getFloatArray(map, "vertices", scale); mesh.vertices = getFloatArray(map, "vertices", scale);
mesh.triangles = getUintArray(map, "triangles"); mesh.triangles = getUintArray(map, "triangles");
mesh.regionUVs = getFloatArray(map, "uvs", 1); mesh.regionUVs = getFloatArray(map, "uvs", 1);
mesh.updateUVs(); mesh.updateUVs();
color = map["color"]; color = map["color"];
if (color) { if (color) {
mesh.r = toColor(color, 0); mesh.r = toColor(color, 0);
@ -239,25 +249,23 @@ public class SkeletonJson {
mesh.b = toColor(color, 2); mesh.b = toColor(color, 2);
mesh.a = toColor(color, 3); mesh.a = toColor(color, 3);
} }
mesh.hullLength = int(map["hull"] || 0) * 2; mesh.hullLength = int(map["hull"] || 0) * 2;
if (map["edges"]) mesh.edges = getIntArray(map, "edges"); if (map["edges"]) mesh.edges = getIntArray(map, "edges");
mesh.width = Number(map["width"] || 0) * scale; mesh.width = Number(map["width"] || 0) * scale;
mesh.height = Number(map["height"] || 0) * scale; mesh.height = Number(map["height"] || 0) * scale;
return mesh; return mesh;
case AttachmentType.skinnedmesh: case AttachmentType.weightedmesh:
var weightedMesh:SkinnedMeshAttachment = attachmentLoader.newSkinnedMeshAttachment(skin, name, path); var weightedMesh : WeightedMeshAttachment = attachmentLoader.newWeightedMeshAttachment(skin, name, path);
if (!weightedMesh) return null; if (!weightedMesh) return null;
weightedMesh.path = path; weightedMesh.path = path;
var uvs : Vector.<Number> = getFloatArray(map, "uvs", 1);
var uvs:Vector.<Number> = getFloatArray(map, "uvs", 1);
vertices = getFloatArray(map, "vertices", 1); vertices = getFloatArray(map, "vertices", 1);
var weights:Vector.<Number> = new Vector.<Number>(); var weights : Vector.<Number> = new Vector.<Number>();
var bones:Vector.<int> = new Vector.<int>(); var bones : Vector.<int> = new Vector.<int>();
for (var i:int = 0, n:int = vertices.length; i < n; ) { for (var i : int = 0, n : int = vertices.length; i < n;) {
var boneCount:int = int(vertices[i++]); var boneCount : int = int(vertices[i++]);
bones[bones.length] = boneCount; bones[bones.length] = boneCount;
for (var nn:int = i + boneCount * 4; i < nn; ) { for (var nn : int = i + boneCount * 4; i < nn;) {
bones[bones.length] = vertices[i]; bones[bones.length] = vertices[i];
weights[weights.length] = vertices[i + 1] * scale; weights[weights.length] = vertices[i + 1] * scale;
weights[weights.length] = vertices[i + 2] * scale; weights[weights.length] = vertices[i + 2] * scale;
@ -270,7 +278,6 @@ public class SkeletonJson {
weightedMesh.triangles = getUintArray(map, "triangles"); weightedMesh.triangles = getUintArray(map, "triangles");
weightedMesh.regionUVs = uvs; weightedMesh.regionUVs = uvs;
weightedMesh.updateUVs(); weightedMesh.updateUVs();
color = map["color"]; color = map["color"];
if (color) { if (color) {
weightedMesh.r = toColor(color, 0); weightedMesh.r = toColor(color, 0);
@ -278,16 +285,15 @@ public class SkeletonJson {
weightedMesh.b = toColor(color, 2); weightedMesh.b = toColor(color, 2);
weightedMesh.a = toColor(color, 3); weightedMesh.a = toColor(color, 3);
} }
weightedMesh.hullLength = int(map["hull"] || 0) * 2; weightedMesh.hullLength = int(map["hull"] || 0) * 2;
if (map["edges"]) weightedMesh.edges = getIntArray(map, "edges"); if (map["edges"]) weightedMesh.edges = getIntArray(map, "edges");
weightedMesh.width = Number(map["width"] || 0) * scale; weightedMesh.width = Number(map["width"] || 0) * scale;
weightedMesh.height = Number(map["height"] || 0) * scale; weightedMesh.height = Number(map["height"] || 0) * scale;
return weightedMesh; return weightedMesh;
case AttachmentType.boundingbox: case AttachmentType.boundingbox:
var box:BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name); var box : BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name);
vertices = box.vertices; vertices = box.vertices;
for each (var point:Number in map["vertices"]) for each (var point : Number in map["vertices"])
vertices[vertices.length] = point * scale; vertices[vertices.length] = point * scale;
return box; return box;
} }
@ -295,16 +301,16 @@ public class SkeletonJson {
return null; return null;
} }
private function readAnimation (name:String, map:Object, skeletonData:SkeletonData) : void { private function readAnimation(name : String, map : Object, skeletonData : SkeletonData) : void {
var timelines:Vector.<Timeline> = new Vector.<Timeline>(); var timelines : Vector.<Timeline> = new Vector.<Timeline>();
var duration:Number = 0; var duration : Number = 0;
var slotMap:Object, slotIndex:int, slotName:String; var slotMap : Object, slotIndex : int, slotName : String;
var values:Array, valueMap:Object, frameIndex:int; var values : Array, valueMap : Object, frameIndex : int;
var i:int; var i : int;
var timelineName:String; var timelineName : String;
var slots:Object = map["slots"]; var slots : Object = map["slots"];
for (slotName in slots) { for (slotName in slots) {
slotMap = slots[slotName]; slotMap = slots[slotName];
slotIndex = skeletonData.findSlotIndex(slotName); slotIndex = skeletonData.findSlotIndex(slotName);
@ -312,25 +318,24 @@ public class SkeletonJson {
for (timelineName in slotMap) { for (timelineName in slotMap) {
values = slotMap[timelineName]; values = slotMap[timelineName];
if (timelineName == "color") { if (timelineName == "color") {
var colorTimeline:ColorTimeline = new ColorTimeline(values.length); var colorTimeline : ColorTimeline = new ColorTimeline(values.length);
colorTimeline.slotIndex = slotIndex; colorTimeline.slotIndex = slotIndex;
frameIndex = 0; frameIndex = 0;
for each (valueMap in values) { for each (valueMap in values) {
var color:String = valueMap["color"]; var color : String = valueMap["color"];
var r:Number = toColor(color, 0); var r : Number = toColor(color, 0);
var g:Number = toColor(color, 1); var g : Number = toColor(color, 1);
var b:Number = toColor(color, 2); var b : Number = toColor(color, 2);
var a:Number = toColor(color, 3); var a : Number = toColor(color, 3);
colorTimeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); colorTimeline.setFrame(frameIndex, valueMap["time"], r, g, b, a);
readCurve(colorTimeline, frameIndex, valueMap); readCurve(colorTimeline, frameIndex, valueMap);
frameIndex++; frameIndex++;
} }
timelines[timelines.length] = colorTimeline; timelines[timelines.length] = colorTimeline;
duration = Math.max(duration, colorTimeline.frames[colorTimeline.frameCount * 5 - 5]); duration = Math.max(duration, colorTimeline.frames[colorTimeline.frameCount * 5 - 5]);
} else if (timelineName == "attachment") { } else if (timelineName == "attachment") {
var attachmentTimeline:AttachmentTimeline = new AttachmentTimeline(values.length); var attachmentTimeline : AttachmentTimeline = new AttachmentTimeline(values.length);
attachmentTimeline.slotIndex = slotIndex; attachmentTimeline.slotIndex = slotIndex;
frameIndex = 0; frameIndex = 0;
@ -338,22 +343,21 @@ public class SkeletonJson {
attachmentTimeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); attachmentTimeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]);
timelines[timelines.length] = attachmentTimeline; timelines[timelines.length] = attachmentTimeline;
duration = Math.max(duration, attachmentTimeline.frames[attachmentTimeline.frameCount - 1]); duration = Math.max(duration, attachmentTimeline.frames[attachmentTimeline.frameCount - 1]);
} else } else
throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
} }
} }
var bones:Object = map["bones"]; var bones : Object = map["bones"];
for (var boneName:String in bones) { for (var boneName : String in bones) {
var boneIndex:int = skeletonData.findBoneIndex(boneName); var boneIndex : int = skeletonData.findBoneIndex(boneName);
if (boneIndex == -1) throw new Error("Bone not found: " + boneName); if (boneIndex == -1) throw new Error("Bone not found: " + boneName);
var boneMap:Object = bones[boneName]; var boneMap : Object = bones[boneName];
for (timelineName in boneMap) { for (timelineName in boneMap) {
values = boneMap[timelineName]; values = boneMap[timelineName];
if (timelineName == "rotate") { if (timelineName == "rotate") {
var rotateTimeline:RotateTimeline = new RotateTimeline(values.length); var rotateTimeline : RotateTimeline = new RotateTimeline(values.length);
rotateTimeline.boneIndex = boneIndex; rotateTimeline.boneIndex = boneIndex;
frameIndex = 0; frameIndex = 0;
@ -364,10 +368,9 @@ public class SkeletonJson {
} }
timelines[timelines.length] = rotateTimeline; timelines[timelines.length] = rotateTimeline;
duration = Math.max(duration, rotateTimeline.frames[rotateTimeline.frameCount * 2 - 2]); duration = Math.max(duration, rotateTimeline.frames[rotateTimeline.frameCount * 2 - 2]);
} else if (timelineName == "translate" || timelineName == "scale") { } else if (timelineName == "translate" || timelineName == "scale") {
var timeline:TranslateTimeline; var timeline : TranslateTimeline;
var timelineScale:Number = 1; var timelineScale : Number = 1;
if (timelineName == "scale") if (timelineName == "scale")
timeline = new ScaleTimeline(values.length); timeline = new ScaleTimeline(values.length);
else { else {
@ -378,44 +381,29 @@ public class SkeletonJson {
frameIndex = 0; frameIndex = 0;
for each (valueMap in values) { for each (valueMap in values) {
var x:Number = Number(valueMap["x"] || 0) * timelineScale; var x : Number = Number(valueMap["x"] || 0) * timelineScale;
var y:Number = Number(valueMap["y"] || 0) * timelineScale; var y : Number = Number(valueMap["y"] || 0) * timelineScale;
timeline.setFrame(frameIndex, valueMap["time"], x, y); timeline.setFrame(frameIndex, valueMap["time"], x, y);
readCurve(timeline, frameIndex, valueMap); readCurve(timeline, frameIndex, valueMap);
frameIndex++; frameIndex++;
} }
timelines[timelines.length] = timeline; timelines[timelines.length] = timeline;
duration = Math.max(duration, timeline.frames[timeline.frameCount * 3 - 3]); 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 } else
throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
} }
} }
var ikMap:Object = map["ik"]; var ikMap : Object = map["ik"];
for (var ikConstraintName:String in ikMap) { for (var ikConstraintName : String in ikMap) {
var ikConstraint:IkConstraintData = skeletonData.findIkConstraint(ikConstraintName); var ikConstraint : IkConstraintData = skeletonData.findIkConstraint(ikConstraintName);
values = ikMap[ikConstraintName]; values = ikMap[ikConstraintName];
var ikTimeline:IkConstraintTimeline = new IkConstraintTimeline(values.length); var ikTimeline : IkConstraintTimeline = new IkConstraintTimeline(values.length);
ikTimeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint); ikTimeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint);
frameIndex = 0; frameIndex = 0;
for each (valueMap in values) { for each (valueMap in values) {
var mix:Number = valueMap.hasOwnProperty("mix") ? valueMap["mix"] : 1; var mix : Number = valueMap.hasOwnProperty("mix") ? valueMap["mix"] : 1;
var bendDirection:int = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1; var bendDirection : int = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1;
ikTimeline.setFrame(frameIndex, valueMap["time"], mix, bendDirection); ikTimeline.setFrame(frameIndex, valueMap["time"], mix, bendDirection);
readCurve(ikTimeline, frameIndex, valueMap); readCurve(ikTimeline, frameIndex, valueMap);
frameIndex++; frameIndex++;
@ -424,40 +412,40 @@ public class SkeletonJson {
duration = Math.max(duration, ikTimeline.frames[ikTimeline.frameCount * 3 - 3]); duration = Math.max(duration, ikTimeline.frames[ikTimeline.frameCount * 3 - 3]);
} }
var ffd:Object = map["ffd"]; var ffd : Object = map["ffd"];
for (var skinName:String in ffd) { for (var skinName : String in ffd) {
var skin:Skin = skeletonData.findSkin(skinName); var skin : Skin = skeletonData.findSkin(skinName);
slotMap = ffd[skinName]; slotMap = ffd[skinName];
for (slotName in slotMap) { for (slotName in slotMap) {
slotIndex = skeletonData.findSlotIndex(slotName); slotIndex = skeletonData.findSlotIndex(slotName);
var meshMap:Object = slotMap[slotName]; var meshMap : Object = slotMap[slotName];
for (var meshName:String in meshMap) { for (var meshName : String in meshMap) {
values = meshMap[meshName]; values = meshMap[meshName];
var ffdTimeline:FfdTimeline = new FfdTimeline(values.length); var ffdTimeline : FfdTimeline = new FfdTimeline(values.length);
var attachment:Attachment = skin.getAttachment(slotIndex, meshName); var attachment : Attachment = skin.getAttachment(slotIndex, meshName);
if (!attachment) throw new Error("FFD attachment not found: " + meshName); if (!attachment) throw new Error("FFD attachment not found: " + meshName);
ffdTimeline.slotIndex = slotIndex; ffdTimeline.slotIndex = slotIndex;
ffdTimeline.attachment = attachment; ffdTimeline.attachment = attachment;
var vertexCount:int; var vertexCount : int;
if (attachment is MeshAttachment) if (attachment is MeshAttachment)
vertexCount = (attachment as MeshAttachment).vertices.length; vertexCount = (attachment as MeshAttachment).vertices.length;
else else
vertexCount = (attachment as SkinnedMeshAttachment).weights.length / 3 * 2; vertexCount = (attachment as WeightedMeshAttachment).weights.length / 3 * 2;
frameIndex = 0; frameIndex = 0;
for each (valueMap in values) { for each (valueMap in values) {
var vertices:Vector.<Number>; var vertices : Vector.<Number>;
if (!valueMap["vertices"]) { if (!valueMap["vertices"]) {
if (attachment is MeshAttachment) if (attachment is MeshAttachment)
vertices = (attachment as MeshAttachment).vertices; vertices = (attachment as MeshAttachment).vertices;
else else
vertices = new Vector.<Number>(vertexCount, true); vertices = new Vector.<Number>(vertexCount, true);
} else { } else {
var verticesValue:Array = valueMap["vertices"]; var verticesValue : Array = valueMap["vertices"];
vertices = new Vector.<Number>(vertexCount, true); vertices = new Vector.<Number>(vertexCount, true);
var start:int = valueMap["offset"] || 0; var start : int = valueMap["offset"] || 0;
var n:int = verticesValue.length; var n : int = verticesValue.length;
if (scale == 1) { if (scale == 1) {
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
vertices[i + start] = verticesValue[i]; vertices[i + start] = verticesValue[i];
@ -466,7 +454,7 @@ public class SkeletonJson {
vertices[i + start] = verticesValue[i] * scale; vertices[i + start] = verticesValue[i] * scale;
} }
if (attachment is MeshAttachment) { if (attachment is MeshAttachment) {
var meshVertices:Vector.<Number> = (attachment as MeshAttachment).vertices; var meshVertices : Vector.<Number> = (attachment as MeshAttachment).vertices;
for (i = 0; i < vertexCount; i++) for (i = 0; i < vertexCount; i++)
vertices[i] += meshVertices[i]; vertices[i] += meshVertices[i];
} }
@ -482,22 +470,22 @@ public class SkeletonJson {
} }
} }
var drawOrderValues:Array = map["drawOrder"]; var drawOrderValues : Array = map["drawOrder"];
if (!drawOrderValues) drawOrderValues = map["draworder"]; if (!drawOrderValues) drawOrderValues = map["draworder"];
if (drawOrderValues) { if (drawOrderValues) {
var drawOrderTimeline:DrawOrderTimeline = new DrawOrderTimeline(drawOrderValues.length); var drawOrderTimeline : DrawOrderTimeline = new DrawOrderTimeline(drawOrderValues.length);
var slotCount:int = skeletonData.slots.length; var slotCount : int = skeletonData.slots.length;
frameIndex = 0; frameIndex = 0;
for each (var drawOrderMap:Object in drawOrderValues) { for each (var drawOrderMap : Object in drawOrderValues) {
var drawOrder:Vector.<int> = null; var drawOrder : Vector.<int> = null;
if (drawOrderMap["offsets"]) { if (drawOrderMap["offsets"]) {
drawOrder = new Vector.<int>(slotCount); drawOrder = new Vector.<int>(slotCount);
for (i = slotCount - 1; i >= 0; i--) for (i = slotCount - 1; i >= 0; i--)
drawOrder[i] = -1; drawOrder[i] = -1;
var offsets:Array = drawOrderMap["offsets"]; var offsets : Array = drawOrderMap["offsets"];
var unchanged:Vector.<int> = new Vector.<int>(slotCount - offsets.length); var unchanged : Vector.<int> = new Vector.<int>(slotCount - offsets.length);
var originalIndex:int = 0, unchangedIndex:int = 0; var originalIndex : int = 0, unchangedIndex : int = 0;
for each (var offsetMap:Object in offsets) { for each (var offsetMap : Object in offsets) {
slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]); slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]);
if (slotIndex == -1) throw new Error("Slot not found: " + offsetMap["slot"]); if (slotIndex == -1) throw new Error("Slot not found: " + offsetMap["slot"]);
// Collect unchanged items. // Collect unchanged items.
@ -519,18 +507,18 @@ public class SkeletonJson {
duration = Math.max(duration, drawOrderTimeline.frames[drawOrderTimeline.frameCount - 1]); duration = Math.max(duration, drawOrderTimeline.frames[drawOrderTimeline.frameCount - 1]);
} }
var eventsMap:Array = map["events"]; var eventsMap : Array = map["events"];
if (eventsMap) { if (eventsMap) {
var eventTimeline:EventTimeline = new EventTimeline(eventsMap.length); var eventTimeline : EventTimeline = new EventTimeline(eventsMap.length);
frameIndex = 0; frameIndex = 0;
for each (var eventMap:Object in eventsMap) { for each (var eventMap : Object in eventsMap) {
var eventData:EventData = skeletonData.findEvent(eventMap["name"]); var eventData : EventData = skeletonData.findEvent(eventMap["name"]);
if (!eventData) throw new Error("Event not found: " + eventMap["name"]); if (!eventData) throw new Error("Event not found: " + eventMap["name"]);
var event:Event = new Event(eventData); var event : Event = new Event(eventMap["time"], eventData);
event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue; event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue;
event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue; event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue;
event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue; event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue;
eventTimeline.setFrame(frameIndex++, eventMap["time"], event); eventTimeline.setFrame(frameIndex++, event);
} }
timelines[timelines.length] = eventTimeline; timelines[timelines.length] = eventTimeline;
duration = Math.max(duration, eventTimeline.frames[eventTimeline.frameCount - 1]); duration = Math.max(duration, eventTimeline.frames[eventTimeline.frameCount - 1]);
@ -539,8 +527,8 @@ public class SkeletonJson {
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 { static private function readCurve(timeline : CurveTimeline, frameIndex : int, valueMap : Object) : void {
var curve:Object = valueMap["curve"]; var curve : Object = valueMap["curve"];
if (!curve) return; if (!curve) return;
if (curve == "stepped") if (curve == "stepped")
timeline.setStepped(frameIndex); timeline.setStepped(frameIndex);
@ -548,15 +536,15 @@ public class SkeletonJson {
timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]);
} }
static private function toColor (hexString:String, colorIndex:int) : Number { static private function toColor(hexString : String, colorIndex : int) : Number {
if (hexString.length != 8) throw new ArgumentError("Color hexidecimal length must be 8, recieved: " + hexString); 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; return parseInt(hexString.substring(colorIndex * 2, colorIndex * 2 + 2), 16) / 255;
} }
static private function getFloatArray (map:Object, name:String, scale:Number) : Vector.<Number> { static private function getFloatArray(map : Object, name : String, scale : Number) : Vector.<Number> {
var list:Array = map[name]; var list : Array = map[name];
var values:Vector.<Number> = new Vector.<Number>(list.length, true); var values : Vector.<Number> = new Vector.<Number>(list.length, true);
var i:int = 0, n:int = list.length; var i : int = 0, n : int = list.length;
if (scale == 1) { if (scale == 1) {
for (; i < n; i++) for (; i < n; i++)
values[i] = list[i]; values[i] = list[i];
@ -567,21 +555,20 @@ public class SkeletonJson {
return values; return values;
} }
static private function getIntArray (map:Object, name:String) : Vector.<int> { static private function getIntArray(map : Object, name : String) : Vector.<int> {
var list:Array = map[name]; var list : Array = map[name];
var values:Vector.<int> = new Vector.<int>(list.length, true); var values : Vector.<int> = new Vector.<int>(list.length, true);
for (var i:int = 0, n:int = list.length; i < n; i++) for (var i : int = 0, n : int = list.length; i < n; i++)
values[i] = int(list[i]); values[i] = int(list[i]);
return values; return values;
} }
static private function getUintArray (map:Object, name:String) : Vector.<uint> { static private function getUintArray(map : Object, name : String) : Vector.<uint> {
var list:Array = map[name]; var list : Array = map[name];
var values:Vector.<uint> = new Vector.<uint>(list.length, true); var values : Vector.<uint> = new Vector.<uint>(list.length, true);
for (var i:int = 0, n:int = list.length; i < n; i++) for (var i : int = 0, n : int = list.length; i < n; i++)
values[i] = int(list[i]); values[i] = int(list[i]);
return values; return values;
} }
} }
} }

View File

@ -29,45 +29,50 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
package spine.animation { package spine {
import spine.Bone;
import spine.Event;
import spine.Skeleton;
public class FlipXTimeline implements Timeline { public class TransformConstraint implements Updatable {
public var boneIndex:int; internal var _data:TransformConstraintData;
public var frames:Vector.<Number>; // time, flip, ... 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) { public function TransformConstraint (data:TransformConstraintData, skeleton:Skeleton) {
frames = new Vector.<Number>(frameCount * 2, true); 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 { public function apply () : void {
return frames.length / 2; update();
} }
/** Sets the time and angle of the specified keyframe. */ public function update () : void {
public function setFrame (frameIndex:int, time:Number, flip:Boolean) : void { var translateMix:Number = translateMix;
frameIndex *= 2; if (translateMix > 0) {
frames[frameIndex] = time; var local:Vector.<Number> = new Vector.<Number>(2, true);
frames[int(frameIndex + 1)] = flip ? 1 : 0; 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.<Event>, alpha:Number) : void { public function get data () : TransformConstraintData {
if (time < frames[0]) { return _data;
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);
} }
protected function setFlip (bone:Bone, flip:Boolean) : void { public function toString () : String {
bone.flipX = flip; return _data._name;
} }
} }

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -56,7 +56,7 @@ public class Animation {
if (loop && duration != 0) { if (loop && duration != 0) {
time %= duration; time %= duration;
lastTime %= duration; if (lastTime > 0) lastTime %= duration;
} }
for (var i:int = 0, n:int = timelines.length; i < n; i++) for (var i:int = 0, n:int = timelines.length; i < n; i++)
@ -70,7 +70,7 @@ public class Animation {
if (loop && duration != 0) { if (loop && duration != 0) {
time %= duration; time %= duration;
lastTime %= duration; if (lastTime > 0) lastTime %= duration;
} }
for (var i:int = 0, n:int = timelines.length; i < n; i++) for (var i:int = 0, n:int = timelines.length; i < n; i++)

View File

@ -47,8 +47,8 @@ public class EventTimeline implements Timeline {
} }
/** Sets the time and value of the specified keyframe. */ /** Sets the time and value of the specified keyframe. */
public function setFrame (frameIndex:int, time:Number, event:Event) : void { public function setFrame (frameIndex:int, event:Event) : void {
frames[frameIndex] = time; frames[frameIndex] = event.time;
events[frameIndex] = event; events[frameIndex] = event;
} }

View File

@ -83,11 +83,11 @@ public class AtlasAttachmentLoader implements AttachmentLoader {
return attachment; 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); var region:AtlasRegion = atlas.findRegion(path);
if (region == null) if (region == null)
throw new Error("Region not found in atlas: " + path + " (skinned mesh attachment: " + name + ")"); throw new Error("Region not found in atlas: " + path + " (weighted mesh attachment: " + name + ")");
var attachment:SkinnedMeshAttachment = new SkinnedMeshAttachment(name); var attachment:WeightedMeshAttachment = new WeightedMeshAttachment(name);
attachment.rendererObject = region; attachment.rendererObject = region;
var scaleX:Number = region.page.width / nextPOT(region.page.width); var scaleX:Number = region.page.width / nextPOT(region.page.width);
var scaleY:Number = region.page.height / nextPOT(region.page.height); var scaleY:Number = region.page.height / nextPOT(region.page.height);

View File

@ -40,7 +40,7 @@ public interface AttachmentLoader {
function newMeshAttachment (skin:Skin, name:String, path:String) : MeshAttachment; function newMeshAttachment (skin:Skin, name:String, path:String) : MeshAttachment;
/** @return May be null to not load an attachment. */ /** @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. */ /** @return May be null to not load an attachment. */
function newBoundingBoxAttachment (skin:Skin, name:String) : BoundingBoxAttachment; function newBoundingBoxAttachment (skin:Skin, name:String) : BoundingBoxAttachment;

View File

@ -36,7 +36,7 @@ public class AttachmentType {
public static const regionsequence:AttachmentType = new AttachmentType(1, "regionsequence"); public static const regionsequence:AttachmentType = new AttachmentType(1, "regionsequence");
public static const boundingbox:AttachmentType = new AttachmentType(2, "boundingbox"); public static const boundingbox:AttachmentType = new AttachmentType(2, "boundingbox");
public static const mesh:AttachmentType = new AttachmentType(3, "mesh"); 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 ordinal:int;
public var name:String; public var name:String;

View File

@ -42,10 +42,10 @@ public dynamic class BoundingBoxAttachment extends Attachment {
public function computeWorldVertices (x:Number, y:Number, bone:Bone, worldVertices:Vector.<Number>) : void { public function computeWorldVertices (x:Number, y:Number, bone:Bone, worldVertices:Vector.<Number>) : void {
x += bone.worldX; x += bone.worldX;
y += bone.worldY; y += bone.worldY;
var m00:Number = bone.m00; var m00:Number = bone.a;
var m01:Number = bone.m01; var m01:Number = bone.b;
var m10:Number = bone.m10; var m10:Number = bone.c;
var m11:Number = bone.m11; var m11:Number = bone.d;
var vertices:Vector.<Number> = this.vertices; var vertices:Vector.<Number> = this.vertices;
for (var i:int = 0, n:int = vertices.length; i < n; i += 2) { for (var i:int = 0, n:int = vertices.length; i < n; i += 2) {
var ii:int = i + 1; var ii:int = i + 1;

View File

@ -88,10 +88,10 @@ public dynamic class MeshAttachment extends Attachment {
var bone:Bone = slot.bone; var bone:Bone = slot.bone;
x += bone.worldX; x += bone.worldX;
y += bone.worldY; y += bone.worldY;
var m00:Number = bone.m00; var m00:Number = bone.a;
var m01:Number = bone.m01; var m01:Number = bone.b;
var m10:Number = bone.m10; var m10:Number = bone.c;
var m11:Number = bone.m11; var m11:Number = bone.d;
var vertices:Vector.<Number> = this.vertices; var vertices:Vector.<Number> = this.vertices;
var verticesCount:int = vertices.length; var verticesCount:int = vertices.length;
if (slot.attachmentVertices.length == verticesCount) vertices = slot.attachmentVertices; if (slot.attachmentVertices.length == verticesCount) vertices = slot.attachmentVertices;

View File

@ -125,10 +125,10 @@ public dynamic class RegionAttachment extends Attachment {
public function computeWorldVertices (x:Number, y:Number, bone:Bone, worldVertices:Vector.<Number>) : void { public function computeWorldVertices (x:Number, y:Number, bone:Bone, worldVertices:Vector.<Number>) : void {
x += bone.worldX; x += bone.worldX;
y += bone.worldY; y += bone.worldY;
var m00:Number = bone.m00; var m00:Number = bone.a;
var m01:Number = bone.m01; var m01:Number = bone.b;
var m10:Number = bone.m10; var m10:Number = bone.c;
var m11:Number = bone.m11; var m11:Number = bone.d;
var x1:Number = offset[X1]; var x1:Number = offset[X1];
var y1:Number = offset[Y1]; var y1:Number = offset[Y1];
var x2:Number = offset[X2]; var x2:Number = offset[X2];

View File

@ -33,7 +33,7 @@ package spine.attachments {
import spine.Slot; import spine.Slot;
import spine.Bone; import spine.Bone;
public dynamic class SkinnedMeshAttachment extends Attachment { public dynamic class WeightedMeshAttachment extends Attachment {
public var bones:Vector.<int>; public var bones:Vector.<int>;
public var weights:Vector.<Number>; public var weights:Vector.<Number>;
public var uvs:Vector.<Number>; public var uvs:Vector.<Number>;
@ -64,7 +64,7 @@ public dynamic class SkinnedMeshAttachment extends Attachment {
public var width:Number; public var width:Number;
public var height:Number; public var height:Number;
public function SkinnedMeshAttachment (name:String) { public function WeightedMeshAttachment (name:String) {
super(name); super(name);
} }
@ -102,8 +102,8 @@ public dynamic class SkinnedMeshAttachment extends Attachment {
vx = weights[b]; vx = weights[b];
vy = weights[int(b + 1)]; vy = weights[int(b + 1)];
weight = weights[int(b + 2)]; weight = weights[int(b + 2)];
wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
} }
worldVertices[w] = wx + x; worldVertices[w] = wx + x;
worldVertices[int(w + 1)] = wy + y; worldVertices[int(w + 1)] = wy + y;
@ -119,8 +119,8 @@ public dynamic class SkinnedMeshAttachment extends Attachment {
vx = weights[b] + ffd[f]; vx = weights[b] + ffd[f];
vy = weights[int(b + 1)] + ffd[int(f + 1)]; vy = weights[int(b + 1)] + ffd[int(f + 1)];
weight = weights[int(b + 2)]; weight = weights[int(b + 2)];
wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
} }
worldVertices[w] = wx + x; worldVertices[w] = wx + x;
worldVertices[int(w + 1)] = wy + y; worldVertices[int(w + 1)] = wy + y;

View File

@ -129,12 +129,10 @@ public class SkeletonSprite extends Sprite {
var bone:Bone = slot.bone; var bone:Bone = slot.bone;
var flipX:int = skeleton.flipX ? -1 : 1; var flipX:int = skeleton.flipX ? -1 : 1;
var flipY:int = skeleton.flipY ? -1 : 1; var flipY:int = skeleton.flipY ? -1 : 1;
if (bone.worldFlipX) flipX = -flipX;
if (bone.worldFlipY) flipY = -flipY;
wrapper.x = bone.worldX; wrapper.x = bone.worldX;
wrapper.y = bone.worldY; wrapper.y = bone.worldY;
wrapper.rotation = -bone.worldRotation * flipX * flipY; wrapper.rotation = -bone.worldRotationX * flipX * flipY;
wrapper.scaleX = bone.worldScaleX * flipX; wrapper.scaleX = bone.worldScaleX * flipX;
wrapper.scaleY = bone.worldScaleY * flipY; wrapper.scaleY = bone.worldScaleY * flipY;
addChild(wrapper); addChild(wrapper);