[as3][starling] Updated to 3.5, path constraint may still have an issue, see stretchyman

This commit is contained in:
badlogic 2016-10-17 16:49:51 +02:00
parent 71702a451a
commit 8b327f3d54
20 changed files with 1311 additions and 253 deletions

View File

@ -152,6 +152,10 @@ cp -f ../vine/export/vine.json ../../spine-starling/spine-starling-example/src/
cp -f ../vine/export/vine.atlas ../../spine-starling/spine-starling-example/src/
cp -f ../vine/export/vine.png ../../spine-starling/spine-starling-example/src/
cp -f ../stretchyman/export/stretchyman.json ../../spine-starling/spine-starling-example/src/
cp -f ../stretchyman/export/stretchyman.atlas ../../spine-starling/spine-starling-example/src/
cp -f ../stretchyman/export/stretchyman.png ../../spine-starling/spine-starling-example/src/
echo "spine-ts"
rm -f ../../spine-ts/webgl/example/assets/*

View File

@ -44,7 +44,14 @@ public class Bone implements Updatable {
public var scaleY:Number;
public var shearX:Number;
public var shearY:Number;
public var appliedRotation:Number;
public var ax:Number;
public var ay:Number;
public var arotation:Number;
public var ascaleX:Number;
public var ascaleY:Number;
public var ashearX:Number;
public var ashearY:Number;
public var appliedValid:Boolean;
internal var _a:Number;
internal var _b:Number;
@ -52,8 +59,6 @@ public class Bone implements Updatable {
internal var _d:Number;
internal var _worldX:Number;
internal var _worldY:Number;
internal var _worldSignX:Number;
internal var _worldSignY:Number;
internal var _sorted:Boolean;
@ -79,14 +84,26 @@ public class Bone implements Updatable {
/** 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, shearX:Number, shearY:Number) : void {
appliedRotation = rotation;
var rotationY:Number = rotation + 90 + shearY;
var la:Number = MathUtils.cosDeg(rotation + shearX) * scaleX, lb:Number = MathUtils.cosDeg(rotationY) * scaleY;
var lc:Number = MathUtils.sinDeg(rotation + shearX) * scaleX, ld:Number = MathUtils.sinDeg(rotationY) * scaleY;
ax = x;
ay = y;
arotation = rotation;
ascaleX = scaleX;
ascaleY = scaleY;
ashearX = shearX;
ashearY = shearY;
appliedValid = true;
var rotationY:Number = 0, la:Number = 0, lb:Number = 0, lc:Number = 0, ld:Number = 0;
var sin:Number = 0, cos:Number = 0;
var s:Number = 0;
var parent:Bone = _parent;
if (!parent) { // Root bone.
rotationY = rotation + 90 + shearY;
la = MathUtils.cosDeg(rotation + shearX) * scaleX;
lb = MathUtils.cosDeg(rotationY) * scaleY;
lc = MathUtils.sinDeg(rotation + shearX) * scaleX;
ld = MathUtils.sinDeg(rotationY) * scaleY;
var skeleton:Skeleton = _skeleton;
if (skeleton.flipX) {
x = -x;
@ -102,91 +119,103 @@ public class Bone implements Updatable {
_b = lb;
_c = lc;
_d = ld;
_worldX = x;
_worldY = y;
_worldSignX = scaleX < 0 ? -1 : 1;
_worldSignY = scaleY < 0 ? -1 : 1;
_worldX = x + skeleton.x;
_worldY = y + skeleton.y;
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);
_worldY = pc * x + pd * y + parent._worldY;
if (data.inheritRotation && data.inheritScale) {
switch (_data.transformMode) {
case TransformMode.normal: {
rotationY = rotation + 90 + shearY;
la = MathUtils.cosDeg(rotation + shearX) * scaleX;
lb = MathUtils.cosDeg(rotationY) * scaleY;
lc = MathUtils.sinDeg(rotation + shearX) * scaleX;
ld = MathUtils.sinDeg(rotationY) * scaleY;
_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;
do {
var cos:Number = MathUtils.cosDeg(parent.appliedRotation), sin:Number = MathUtils.sinDeg(parent.appliedRotation);
var temp:Number = pa * cos + pb * sin;
pb = pb * cos - pa * sin;
pa = temp;
temp = pc * cos + pd * sin;
pd = pd * cos - pc * sin;
pc = temp;
if (!parent.data.inheritRotation) break;
parent = parent.parent;
} while (parent != null);
_a = pa * la + pb * lc;
_b = pa * lb + pb * ld;
_c = pc * la + pd * lc;
_d = pc * lb + pd * ld;
} else if (data.inheritScale) { // No rotation inheritance.
pa = 1;
pb = 0;
pc = 0;
pd = 1;
do {
cos = MathUtils.cosDeg(parent.appliedRotation), sin = MathUtils.sinDeg(parent.appliedRotation);
var psx:Number = parent.scaleX, psy:Number = parent.scaleY;
var za:Number = cos * psx, zb:Number = sin * psy, zc:Number = sin * psx, zd:Number = cos * psy;
temp = pa * za + pb * zc;
pb = pb * zd - pa * zb;
pa = temp;
temp = pc * za + pd * zc;
pd = pd * zd - pc * zb;
pc = temp;
if (psx >= 0) sin = -sin;
temp = pa * cos + pb * sin;
pb = pb * cos - pa * sin;
pa = temp;
temp = pc * cos + pd * sin;
pd = pd * cos - pc * sin;
pc = temp;
if (!parent.data.inheritScale) break;
parent = parent.parent;
} while (parent != null);
_a = pa * la + pb * lc;
_b = pa * lb + pb * ld;
_c = pc * la + pd * lc;
_d = pc * lb + pd * ld;
return;
}
case TransformMode.onlyTranslation: {
rotationY = rotation + 90 + shearY;
_a = MathUtils.cosDeg(rotation + shearX) * scaleX;
_b = MathUtils.cosDeg(rotationY) * scaleY;
_c = MathUtils.sinDeg(rotation + shearX) * scaleX;
_d = MathUtils.sinDeg(rotationY) * scaleY;
break;
}
case TransformMode.noRotationOrReflection: {
var psx:Number = Math.sqrt(pa * pa + pc * pc);
var psy:Number = 0;
var prx:Number = 0;
if (psx > 0.0001) {
psy = Math.abs((pa * pd - pb * pc) / psx);
prx = Math.atan2(pc, pa) * MathUtils.radDeg;
} else {
_a = la;
_b = lb;
_c = lc;
_d = ld;
psx = 0;
psy = Math.sqrt(pb * pb + pd * pd);
prx = 90 - Math.atan2(pd, pb) * MathUtils.radDeg;
}
if (_skeleton.flipX) {
_a = -_a;
cos = MathUtils.cosDeg(prx);
sin = MathUtils.sinDeg(prx);
pa = cos * psx;
pb = -sin * psy;
pc = sin * psx;
pd = cos * psy;
var rx:Number = rotation + shearX - prx;
var ry:Number = rotation + shearY - prx + 90;
la = MathUtils.cosDeg(rx) * scaleX;
lb = MathUtils.cosDeg(ry) * scaleY;
lc = MathUtils.sinDeg(rx) * scaleX;
ld = MathUtils.sinDeg(ry) * scaleY;
_a = pa * la + pb * lc;
_b = pa * lb + pb * ld;
_c = pc * la + pd * lc;
_d = pc * lb + pd * ld;
break;
}
case TransformMode.noScale:
case TransformMode.noScaleOrReflection: {
cos = MathUtils.cosDeg(rotation);
sin = MathUtils.sinDeg(rotation);
var za:Number = pa * cos + pb * sin;
var zc:Number = pc * cos + pd * sin;
s = Math.sqrt(za * za + zc * zc);
if (s > 0.00001) s = 1 / s;
za *= s;
zc *= s;
s = Math.sqrt(za * za + zc * zc);
var r:Number = Math.PI / 2 + Math.atan2(zc, za);
var zb:Number = Math.cos(r) * s;
var zd:Number = Math.sin(r) * s;
la = MathUtils.cosDeg(shearX) * scaleX;
lb = MathUtils.cosDeg(90 + shearY) * scaleY;
lc = MathUtils.sinDeg(shearX) * scaleX;
ld = MathUtils.sinDeg(90 + shearY) * scaleY;
_a = za * la + zb * lc;
_b = za * lb + zb * ld;
_c = zc * la + zd * lc;
_d = zc * lb + zd * ld;
if (_data.transformMode != TransformMode.noScaleOrReflection ? pa * pd - pb * pc < 0 : skeleton.flipX != skeleton.flipY) {
_b = -_b;
}
if (_skeleton.flipY != yDown) {
_c = -_c;
_d = -_d;
}
return;
}
}
if (_skeleton.flipX) {
_a = -_a;
_b = -_b;
}
if (_skeleton.flipY != yDown) {
_c = -_c;
_d = -_d;
}
}
@ -240,14 +269,6 @@ public class Bone implements Updatable {
return _worldY;
}
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;
}
@ -257,23 +278,23 @@ public class Bone implements Updatable {
}
public function get worldScaleX () : Number {
return Math.sqrt(_a * _a + _b * _b) * _worldSignX;
return Math.sqrt(_a * _a + _c * _c);
}
public function get worldScaleY () : Number {
return Math.sqrt(_c * _c + _d * _d) * _worldSignY;
return Math.sqrt(_b * _b + _d * _d);
}
public function worldToLocalRotationX () : Number {
var parent:Bone = _parent;
if (parent == null) return rotation;
if (parent == null) return arotation;
var pa:Number = parent.a, pb:Number = parent.b, pc:Number = parent.c, pd:Number = parent.d, a:Number = this.a, c:Number = this.c;
return Math.atan2(pa * c - pc * a, pd * a - pb * c) * MathUtils.radDeg;
}
public function worldToLocalRotationY () : Number {
var parent:Bone = _parent;
if (parent == null) return rotation;
if (parent == null) return arotation;
var pa:Number = parent.a, pb:Number = parent.b, pc:Number = parent.c, pd:Number = parent.d, b:Number = this.b, d:Number = this.d;
return Math.atan2(pa * d - pc * b, pd * b - pb * d) * MathUtils.radDeg;
}
@ -285,31 +306,31 @@ public class Bone implements Updatable {
this._b = cos * b - sin * d;
this._c = sin * a + cos * c;
this._d = sin * b + cos * d;
this.appliedValid = false;
}
/** Computes the local transform from the world transform. This can be useful to perform processing on the local transform
* after the world transform has been modified directly (eg, by a constraint).
/** Computes the individual applied transform values from the world transform. This can be useful to perform processing using
* the applied transform after the world transform has been modified directly (eg, by a constraint).
* <p>
* Some redundant information is lost by the world transform, such as -1,-1 scale versus 180 rotation. The computed local
* transform values may differ from the original values but are functionally the same. */
public function updateLocalTransform () : void {
* Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. */
public function updateAppliedTransform () : void {
appliedValid = true;
var parent:Bone = this.parent;
if (parent == null) {
x = worldX;
y = worldY;
rotation = Math.atan2(c, a) * MathUtils.radDeg;
scaleX = Math.sqrt(a * a + c * c);
scaleY = Math.sqrt(b * b + d * d);
var det:Number = a * d - b * c;
shearX = 0;
shearY = Math.atan2(a * b + c * d, det) * MathUtils.radDeg;
ax = worldX;
ay = worldY;
arotation = Math.atan2(c, a) * MathUtils.radDeg;
ascaleX = Math.sqrt(a * a + c * c);
ascaleY = Math.sqrt(b * b + d * d);
ashearX = 0;
ashearY = Math.atan2(a * b + c * d, a * d - b * c) * MathUtils.radDeg;
return;
}
var pa:Number = parent.a, pb:Number = parent.b, pc:Number = parent.c, pd:Number = parent.d;
var pid:Number = 1 / (pa * pd - pb * pc);
var dx:Number = worldX - parent.worldX, dy:Number = worldY - parent.worldY;
x = (dx * pd * pid - dy * pb * pid);
y = (dy * pa * pid - dx * pc * pid);
ax = (dx * pd * pid - dy * pb * pid);
ay = (dy * pa * pid - dx * pc * pid);
var ia:Number = pid * pd;
var id:Number = pid * pa;
var ib:Number = pid * pb;
@ -318,20 +339,19 @@ public class Bone implements Updatable {
var rb:Number = ia * b - ib * d;
var rc:Number = id * c - ic * a;
var rd:Number = id * d - ic * b;
shearX = 0;
scaleX = Math.sqrt(ra * ra + rc * rc);
ashearX = 0;
ascaleX = Math.sqrt(ra * ra + rc * rc);
if (scaleX > 0.0001) {
det = ra * rd - rb * rc;
scaleY = det / scaleX;
shearY = Math.atan2(ra * rb + rc * rd, det) * MathUtils.radDeg;
rotation = Math.atan2(rc, ra) * MathUtils.radDeg;
var det:Number = ra * rd - rb * rc;
ascaleY = det /ascaleX;
ashearY = Math.atan2(ra * rb + rc * rd, det) * MathUtils.radDeg;
arotation = Math.atan2(rc, ra) * MathUtils.radDeg;
} else {
scaleX = 0;
scaleY = Math.sqrt(rb * rb + rd * rd);
shearY = 0;
rotation = 90 - Math.atan2(rd, rb) * MathUtils.radDeg;
}
appliedRotation = rotation;
ascaleX = 0;
ascaleY = Math.sqrt(rb * rb + rd * rd);
ashearY = 0;
arotation = 90 - Math.atan2(rd, rb) * MathUtils.radDeg;
}
}
public function worldToLocal (world:Vector.<Number>) : void {

View File

@ -42,8 +42,7 @@ public class BoneData {
public var scaleY:Number = 1;
public var shearX:Number;
public var shearY:Number;
public var inheritRotation:Boolean = true;
public var inheritScale:Boolean = true;
public var transformMode:TransformMode = TransformMode.normal;
/** @param parent May be null. */
public function BoneData (index:int, name:String, parent:BoneData) {

View File

@ -0,0 +1,37 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, 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 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 develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes 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, BUSINESS INTERRUPTION, OR LOSS OF
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine {
public interface Constraint extends Updatable {
function getOrder () : Number;
}
}

View File

@ -30,14 +30,12 @@
package spine {
public class IkConstraint implements Updatable {
public class IkConstraint implements Constraint {
internal var _data:IkConstraintData;
public var bones:Vector.<Bone>;
public var target:Bone;
public var mix:Number;
public var bendDirection:int;
public var level:int;
public function IkConstraint (data:IkConstraintData, skeleton:Skeleton) {
if (data == null) throw new ArgumentError("data cannot be null.");
@ -66,6 +64,10 @@ public class IkConstraint implements Updatable {
break;
}
}
public function getOrder() : Number {
return _data.order;
}
public function get data () : IkConstraintData {
return _data;
@ -78,17 +80,18 @@ public class IkConstraint implements Updatable {
/** 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 pp:Bone = bone.parent;
var id:Number = 1 / (pp.a * pp.d - pp.b * pp.c);
var x:Number = targetX - pp.worldX, y:Number = targetY - pp.worldY;
var tx:Number = (x * pp.d - y * pp.b) * id - bone.x, ty:Number = (y * pp.a - x * pp.c) * id - bone.y;
var rotationIK:Number = Math.atan2(ty, tx) * MathUtils.radDeg - bone.shearX - bone.rotation;
if (bone.scaleX < 0) rotationIK += 180;
if (!bone.appliedValid) bone.updateAppliedTransform();
var p:Bone = bone.parent;
var id:Number = 1 / (p.a * p.d - p.b * p.c);
var x:Number = targetX - p.worldX, y:Number = targetY - p.worldY;
var tx:Number = (x * p.d - y * p.b) * id - bone.ax, ty:Number = (y * p.a - x * p.c) * id - bone.ay;
var rotationIK:Number = Math.atan2(ty, tx) * MathUtils.radDeg - bone.ashearX - bone.arotation;
if (bone.ascaleX < 0) rotationIK += 180;
if (rotationIK > 180)
rotationIK -= 360;
else if (rotationIK < -180) rotationIK += 360;
bone.updateWorldTransformWith(bone.x, bone.y, bone.rotation + rotationIK * alpha, bone.scaleX, bone.scaleY, bone.shearX,
bone.shearY);
bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, bone.ascaleX, bone.ascaleY, bone.ashearX,
bone.ashearY);
}
/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The
@ -99,7 +102,9 @@ public class IkConstraint implements Updatable {
child.updateWorldTransform();
return;
}
var px:Number = parent.x, py:Number = parent.y, psx:Number = parent.scaleX, psy:Number = parent.scaleY, csx:Number = child.scaleX;;
if (!parent.appliedValid) parent.updateAppliedTransform();
if (!child.appliedValid) child.updateAppliedTransform();
var px:Number = parent.ax, py:Number = parent.ay, psx:Number = parent.ascaleX, psy:Number = parent.ascaleY, csx:Number = child.ascaleX;
var os1:int, os2:int, s2:int;
if (psx < 0) {
psx = -psx;
@ -118,14 +123,14 @@ public class IkConstraint implements Updatable {
os2 = 180;
} else
os2 = 0;
var cx:Number = child.x, cy:Number, cwx:Number, cwy:Number, a:Number = parent.a, b:Number = parent.b, c:Number = parent.c, d:Number = parent.d;
var cx:Number = child.ax, cy:Number, cwx:Number, cwy:Number, a:Number = parent.a, b:Number = parent.b, c:Number = parent.c, d:Number = parent.d;
var u:Boolean = Math.abs(psx - psy) <= 0.0001;
if (!u) {
cy = 0;
cwx = a * cx + parent.worldX;
cwy = c * cx + parent.worldY;
} else {
cy = child.y;
cy = child.ay;
cwx = a * cx + b * cy + parent.worldX;
cwy = c * cx + d * cy + parent.worldY;
}
@ -212,18 +217,18 @@ public class IkConstraint implements Updatable {
}
}
var os:Number = Math.atan2(cy, cx) * s2;
var rotation:Number = parent.rotation;
var rotation:Number = parent.arotation;
a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation;
if (a1 > 180)
a1 -= 360;
else if (a1 < -180) a1 += 360;
parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, parent.scaleX, parent.scaleY, 0, 0);
rotation = child.rotation;
a2 = ((a2 + os) * MathUtils.radDeg - child.shearX) * s2 + os2 - rotation;
parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, parent.ascaleX, parent.ascaleY, 0, 0);
rotation = child.arotation;
a2 = ((a2 + os) * MathUtils.radDeg - child.ashearX) * s2 + os2 - rotation;
if (a2 > 180)
a2 -= 360;
else if (a2 < -180) a2 += 360;
child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.scaleX, child.scaleY, child.shearX, child.shearY);
child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
}
}

View File

@ -32,6 +32,7 @@ package spine {
public class IkConstraintData {
internal var _name:String;
public var order:Number;
public var bones:Vector.<BoneData> = new Vector.<BoneData>();
public var target:BoneData;
public var bendDirection:int = 1;

View File

@ -31,7 +31,7 @@
package spine {
import spine.attachments.PathAttachment;
public class PathConstraint implements Updatable {
public class PathConstraint implements Constraint {
private static const NONE:int = -1, BEFORE:int = -2, AFTER:int = -3;
internal var _data:PathConstraintData;
@ -100,16 +100,14 @@ public class PathConstraint implements Updatable {
}
var positions:Vector.<Number> = computeWorldPositions(attachment, spacesCount, tangents,
data.positionMode == PositionMode.percent, spacingMode == SpacingMode.percent);
var skeleton:Skeleton = target.skeleton;
var skeletonX:Number = skeleton.x, skeletonY:Number = skeleton.y;
data.positionMode == PositionMode.percent, spacingMode == SpacingMode.percent);
var boneX:Number = positions[0], boneY:Number = positions[1], offsetRotation:Number = data.offsetRotation;
var tip:Boolean = rotateMode == RotateMode.chain && offsetRotation == 0;
var p:Number;
for (i = 0, p = 3; i < boneCount; i++, p += 3) {
bone = bones[i];
bone._worldX += (boneX - skeletonX - bone.worldX) * translateMix;
bone._worldY += (boneY - skeletonY - bone.worldY) * translateMix;
bone._worldX += (boneX - bone.worldX) * translateMix;
bone._worldY += (boneY - bone.worldY) * translateMix;
x = positions[p]; y = positions[p + 1]; var dx:Number = x - boneX, dy:Number = y - boneY;
if (scale) {
length = lengths[i];
@ -149,6 +147,7 @@ public class PathConstraint implements Updatable {
bone._c = sin * a + cos * c;
bone._d = sin * b + cos * d;
}
bone.appliedValid = false;
}
}
@ -394,7 +393,7 @@ public class PathConstraint implements Updatable {
private function addCurvePosition (p:Number, x1:Number, y1:Number, cx1:Number, cy1:Number, cx2:Number, cy2:Number, x2:Number, y2:Number,
out:Vector.<Number>, o:int, tangents:Boolean) : void {
if (p == 0) p = 0.0001;
if (p == 0 || isNaN(p)) p = 0.0001;
var tt:Number = p * p, ttt:Number = tt * p, u:Number = 1 - p, uu:Number = u * u, uuu:Number = uu * u;
var ut:Number = u * p, ut3:Number = ut * 3, uut3:Number = u * ut3, utt3:Number = ut3 * p;
var x:Number = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y:Number = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
@ -410,6 +409,10 @@ public class PathConstraint implements Updatable {
public function get data () : PathConstraintData {
return _data;
}
public function getOrder () : Number {
return _data.order;
}
public function toString () : String {
return _data.name;

View File

@ -32,6 +32,7 @@ package spine {
public dynamic class PathConstraintData {
internal var _name:String;
public var order:Number;
internal var _bones:Vector.<BoneData> = new Vector.<BoneData>();
public var target:SlotData;
public var positionMode:PositionMode;

View File

@ -38,10 +38,11 @@ public class Skeleton {
public var bones:Vector.<Bone>;
public var slots:Vector.<Slot>;
public var drawOrder:Vector.<Slot>;
public var ikConstraints:Vector.<IkConstraint>, ikConstraintsSorted:Vector.<IkConstraint>;
public var ikConstraints:Vector.<IkConstraint>;
public var transformConstraints:Vector.<TransformConstraint>;
public var pathConstraints:Vector.<PathConstraint>;
private var _updateCache:Vector.<Updatable> = new Vector.<Updatable>();
private var _updateCacheReset:Vector.<Bone> = new Vector.<Bone>();
private var _skin:Skin;
public var r:Number = 1, g:Number = 1, b:Number = 1, a:Number = 1;
public var time:Number = 0;
@ -75,8 +76,7 @@ public class Skeleton {
drawOrder[drawOrder.length] = slot;
}
ikConstraints = new Vector.<IkConstraint>();
ikConstraintsSorted = new Vector.<IkConstraint>();
ikConstraints = new Vector.<IkConstraint>();
for each (var ikConstraintData:IkConstraintData in data.ikConstraints)
ikConstraints.push(new IkConstraint(ikConstraintData, this));
@ -98,103 +98,112 @@ public class Skeleton {
updateCache.length = 0;
var bones:Vector.<Bone> = this.bones;
for (var i:int = 0, n:int = bones.length; i < n; i++)
var i:Number = 0;
var n:Number = 0;
for (i = 0, n = bones.length; i < n; i++)
bones[i]._sorted = false;
// IK first, lowest hierarchy depth first.
var ikConstraints:Vector.<IkConstraint> = this.ikConstraintsSorted;
ikConstraints.length = 0;
for each (var c:IkConstraint in this.ikConstraints)
ikConstraints.push(c);
var ikCount:int = ikConstraints.length;
var level:int;
for (i = 0, n = ikCount; i < n; i++) {
var ik:IkConstraint = ikConstraints[i];
var bone:Bone = ik.bones[0].parent;
for (level = 0; bone != null; level++)
bone = bone.parent;
ik.level = level;
}
var ii:int;
for (i = 1; i < ikCount; i++) {
ik = ikConstraints[i];
level = ik.level;
for (ii = i - 1; ii >= 0; ii--) {
var other:IkConstraint = ikConstraints[ii];
if (other.level < level) break;
ikConstraints[ii + 1] = other;
}
ikConstraints[ii + 1] = ik;
}
for (i = 0, n = ikConstraints.length; i < n; i++) {
var ikConstraint:IkConstraint = ikConstraints[i];
var target:Bone = ikConstraint.target;
sortBone(target);
var constrained:Vector.<Bone> = ikConstraint.bones;
var parent:Bone = constrained[0];
sortBone(parent);
updateCache.push(ikConstraint);
sortReset(parent.children);
constrained[constrained.length - 1]._sorted = true;
}
var pathConstraints:Vector.<PathConstraint> = this.pathConstraints;
for (i = 0, n = pathConstraints.length; i < n; i++) {
var pathConstraint:PathConstraint = pathConstraints[i];
var slot:Slot = pathConstraint.target;
var slotIndex:int = slot.data.index;
var slotBone:Bone = slot.bone;
if (skin != null) sortPathConstraintAttachment(skin, slotIndex, slotBone);
if (_data.defaultSkin != null && _data.defaultSkin != skin)
sortPathConstraintAttachment(_data.defaultSkin, slotIndex, slotBone);
var nn:int;
for (ii = 0, nn = _data.skins.length; ii < nn; ii++)
sortPathConstraintAttachment(_data.skins[ii], slotIndex, slotBone);
var attachment:PathAttachment = slot.attachment as PathAttachment;
if (attachment != null) sortPathConstraintAttachment2(attachment, slotBone);
constrained = pathConstraint.bones;
var boneCount:int = constrained.length;
for (ii = 0; ii < boneCount; ii++)
sortBone(constrained[ii]);
updateCache.push(pathConstraint);
for (ii = 0; ii < boneCount; ii++)
sortReset(constrained[ii].children);
for (ii = 0; ii < boneCount; ii++)
constrained[ii]._sorted = true;
}
var ikConstraints:Vector.<IkConstraint> = this.ikConstraints;
var transformConstraints:Vector.<TransformConstraint> = this.transformConstraints;
for (i = 0, n = transformConstraints.length; i < n; i++) {
var transformConstraint:TransformConstraint = transformConstraints[i];
sortBone(transformConstraint.target);
constrained = transformConstraint.bones;
boneCount = constrained.length;
for (ii = 0; ii < boneCount; ii++)
sortBone(constrained[ii]);
updateCache.push(transformConstraint);
for (ii = 0; ii < boneCount; ii++)
sortReset(constrained[ii].children);
for (ii = 0; ii < boneCount; ii++)
constrained[ii]._sorted = true;
var pathConstraints:Vector.<PathConstraint> = this.pathConstraints;
var ikCount:Number = ikConstraints.length, transformCount:Number = transformConstraints.length, pathCount:Number = pathConstraints.length;
var constraintCount:Number = ikCount + transformCount + pathCount;
outer:
for (i = 0; i < constraintCount; i++) {
var ii:Number = 0;
for (ii = 0; ii < ikCount; ii++) {
var ikConstraint:IkConstraint = ikConstraints[ii];
if (ikConstraint.data.order == i) {
sortIkConstraint(ikConstraint);
continue outer;
}
}
for (ii = 0; ii < transformCount; ii++) {
var transformConstraint:TransformConstraint = transformConstraints[ii];
if (transformConstraint.data.order == i) {
sortTransformConstraint(transformConstraint);
continue outer;
}
}
for (ii = 0; ii < pathCount; ii++) {
var pathConstraint:PathConstraint = pathConstraints[ii];
if (pathConstraint.data.order == i) {
sortPathConstraint(pathConstraint);
continue outer;
}
}
}
for (i = 0, n = bones.length; i < n; i++)
sortBone(bones[i]);
}
private function sortIkConstraint (constraint:IkConstraint): void {
var target:Bone = constraint.target;
sortBone(target);
var constrained:Vector.<Bone> = constraint.bones;
var parent:Bone = constrained[0];
sortBone(parent);
if (constrained.length > 1) {
var child:Bone = constrained[constrained.length - 1];
if (!(_updateCache.indexOf(child) > -1)) _updateCacheReset.push(child);
}
_updateCache.push(constraint);
sortReset(parent.children);
constrained[constrained.length - 1]._sorted = true;
}
private function sortPathConstraint (constraint:PathConstraint): void {
var slot:Slot = constraint.target;
var slotIndex:Number = slot.data.index;
var slotBone:Bone = slot.bone;
if (skin != null) sortPathConstraintAttachment(skin, slotIndex, slotBone);
if (data.defaultSkin != null && data.defaultSkin != skin)
sortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone);
var ii:Number = 0;
var nn:Number = 0;
for (ii = 0, nn = data.skins.length; ii < nn; ii++)
sortPathConstraintAttachment(data.skins[ii], slotIndex, slotBone);
var attachment:Attachment = slot.attachment;
if (attachment is PathAttachment) sortPathConstraintAttachment2(attachment, slotBone);
var constrained:Vector.<Bone> = constraint.bones;
var boneCount:Number = constrained.length;
for (ii = 0; ii < boneCount; ii++)
sortBone(constrained[ii]);
_updateCache.push(constraint);
for (ii = 0; ii < boneCount; ii++)
sortReset(constrained[ii].children);
for (ii = 0; ii < boneCount; ii++)
constrained[ii]._sorted = true;
}
private function sortTransformConstraint (constraint:TransformConstraint): void {
sortBone(constraint.target);
var constrained:Vector.<Bone> = constraint.bones;
var boneCount:Number = constrained.length;
var ii:Number = 0;
for (ii = 0; ii < boneCount; ii++)
sortBone(constrained[ii]);
_updateCache.push(constraint);
for (ii = 0; ii < boneCount; ii++)
sortReset(constrained[ii].children);
for (ii = 0; ii < boneCount; ii++)
constrained[ii]._sorted = true;
}
private function sortPathConstraintAttachment (skin:Skin, slotIndex:int, slotBone:Bone) : void {
var dict:Dictionary = skin.attachments[slotIndex];
if (!dict) return;
@ -240,6 +249,17 @@ public class Skeleton {
/** Updates the world transform for each bone and applies constraints. */
public function updateWorldTransform () : void {
var updateCacheReset:Vector.<Bone> = this._updateCacheReset;
for each (var bone:Bone in updateCacheReset) {
bone.ax = bone.x;
bone.ay = bone.y;
bone.arotation = bone.rotation;
bone.ascaleX = bone.scaleX;
bone.ascaleY = bone.scaleY;
bone.ashearX = bone.shearX;
bone.ashearY = bone.shearY;
bone.appliedValid = true;
}
for each (var updatable:Updatable in _updateCache)
updatable.update();
}

View File

@ -46,6 +46,9 @@ public class SkeletonData {
public var width:Number, height:Number;
public var version:String, hash:String;
public var fps:Number;
public var imagesPath:String;
public function SkeletonData () {
}

View File

@ -89,7 +89,9 @@ public class SkeletonJson {
skeletonData.hash = skeletonMap["hash"];
skeletonData.version = skeletonMap["spine"];
skeletonData.width = skeletonMap["width"] || 0;
skeletonData.height = skeletonMap["height"] || 0;
skeletonData.height = skeletonMap["height"] || 0;
skeletonData.fps = skeletonMap["fps"] || 0;
skeletonData.imagesPath = skeletonMap["images"];
}
// Bones.
@ -110,8 +112,7 @@ public class SkeletonJson {
boneData.scaleY = boneMap.hasOwnProperty("scaleY") ? boneMap["scaleY"] : 1;
boneData.shearX = Number(boneMap["shearX"] || 0);
boneData.shearY = Number(boneMap["shearY"] || 0);
boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? Boolean(boneMap["inheritRotation"]) : true;
boneData.inheritScale = boneMap.hasOwnProperty("inheritScale") ? Boolean(boneMap["inheritScale"]) : true;
boneData.transformMode = TransformMode[boneMap["transform"] || "normal"];
skeletonData.bones.push(boneData);
}
@ -139,6 +140,7 @@ public class SkeletonJson {
// IK constraints.
for each (var constraintMap:Object in root["ik"]) {
var ikConstraintData:IkConstraintData = new IkConstraintData(constraintMap["name"]);
ikConstraintData.order = constraintMap["order"] || 0;
for each (boneName in constraintMap["bones"]) {
var bone:BoneData = skeletonData.findBone(boneName);
@ -158,6 +160,7 @@ public class SkeletonJson {
// Transform constraints.
for each (constraintMap in root["transform"]) {
var transformConstraintData:TransformConstraintData = new TransformConstraintData(constraintMap["name"]);
transformConstraintData.order = constraintMap["order"] || 0;
for each (boneName in constraintMap["bones"]) {
bone = skeletonData.findBone(boneName);
@ -186,6 +189,7 @@ public class SkeletonJson {
// Path constraints.
for each (constraintMap in root["path"]) {
var pathConstraintData:PathConstraintData = new PathConstraintData(constraintMap["name"]);
pathConstraintData.order = constraintMap["order"] || 0;
for each (boneName in constraintMap["bones"]) {
bone = skeletonData.findBone(boneName);

View File

@ -30,7 +30,7 @@
package spine {
public class TransformConstraint implements Updatable {
public class TransformConstraint implements Constraint {
internal var _data:TransformConstraintData;
internal var _bones:Vector.<Bone>;
public var target:Bone;
@ -65,8 +65,9 @@ public class TransformConstraint implements Updatable {
var bones:Vector.<Bone> = this._bones;
for (var i:int = 0, n:int = bones.length; i < n; i++) {
var bone:Bone = bones[i];
var modified:Boolean = false;
if (rotateMix > 0) {
if (rotateMix != 0) {
var a:Number = bone.a, b:Number = bone.b, c:Number = bone.c, d:Number = bone.d;
var r:Number = Math.atan2(tc, ta) - Math.atan2(c, a) + data.offsetRotation * MathUtils.degRad;
if (r > Math.PI)
@ -78,27 +79,30 @@ public class TransformConstraint implements Updatable {
bone._b = cos * b - sin * d;
bone._c = sin * a + cos * c;
bone._d = sin * b + cos * d;
modified = true;
}
if (translateMix > 0) {
if (translateMix != 0) {
_temp[0] = data.offsetX;
_temp[1] = data.offsetY;
target.localToWorld(_temp);
bone._worldX += (_temp[0] - bone.worldX) * translateMix;
bone._worldY += (_temp[1] - bone.worldY) * translateMix;
modified = true;
}
if (scaleMix > 0) {
var bs:Number = Math.sqrt(bone.a * bone.a + bone.c * bone.c);
var s:Number = Math.sqrt(bone.a * bone.a + bone.c * bone.c);
var ts:Number = Math.sqrt(ta * ta + tc * tc);
var s:Number = bs > 0.00001 ? (bs + (ts - bs + data.offsetScaleX) * scaleMix) / bs : 0;
if (s > 0.00001) s = (s + (ts - s + data.offsetScaleX) * scaleMix) / s;
bone._a *= s;
bone._c *= s;
bs = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
s = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
ts = Math.sqrt(tb * tb + td * td);
s = bs > 0.00001 ? (bs + (ts - bs + data.offsetScaleY) * scaleMix) / bs : 0;
if (s > 0.00001) s = (s + (ts - s + data.offsetScaleY) * scaleMix) / s;
bone._b *= s;
bone._d *= s;
modified = true;
}
if (shearMix > 0) {
@ -112,9 +116,16 @@ public class TransformConstraint implements Updatable {
s = Math.sqrt(b * b + d * d);
bone._b = Math.cos(r) * s;
bone._d = Math.sin(r) * s;
modified = true;
}
if (modified) bone.appliedValid = false;
}
}
public function getOrder () : Number {
return _data.order;
}
public function get data () : TransformConstraintData {
return _data;

View File

@ -32,6 +32,7 @@ package spine {
public class TransformConstraintData {
internal var _name:String;
public var order:Number;
internal var _bones:Vector.<BoneData> = new Vector.<BoneData>();
public var target:BoneData;
public var rotateMix:Number;

View File

@ -0,0 +1,41 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, 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 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 develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes 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, BUSINESS INTERRUPTION, OR LOSS OF
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine {
public class TransformMode {
public static const normal:TransformMode = new TransformMode();
public static const onlyTranslation:TransformMode = new TransformMode();
public static const noRotationOrReflection:TransformMode = new TransformMode();
public static const noScale:TransformMode = new TransformMode();
public static const noScaleOrReflection:TransformMode = new TransformMode();
}
}

View File

@ -53,8 +53,7 @@ public dynamic class VertexAttachment extends Attachment {
* @param offset The worldVertices index to begin writing values. */
public function computeWorldVertices2 (slot:Slot, start:int, count:int, worldVertices:Vector.<Number>, offset:int): void {
count += offset;
var skeleton:Skeleton = slot.skeleton;
var x:Number = skeleton.x, y:Number = skeleton.y;
var skeleton:Skeleton = slot.skeleton;
var deformArray:Vector.<Number> = slot.attachmentVertices;
var vertices:Vector.<Number> = this.vertices;
var bones:Vector.<int> = this.bones;
@ -68,8 +67,8 @@ public dynamic class VertexAttachment extends Attachment {
if (bones == null) {
if (deformArray.length > 0) vertices = deformArray;
bone = slot.bone;
x += bone.worldX;
y += bone.worldY;
var x:Number = bone.worldX;
var y:Number = bone.worldY;
var a:Number = bone.a, bb:Number = bone.b, c:Number = bone.c, d:Number = bone.d;
for (v = start, w = offset; w < count; v += 2, w += 2) {
vx = vertices[v], vy = vertices[v + 1];
@ -87,7 +86,7 @@ public dynamic class VertexAttachment extends Attachment {
var skeletonBones:Vector.<Bone> = skeleton.bones;
if (deformArray.length == 0) {
for (w = offset, b = skip * 3; w < count; w += 2) {
wx = x, wy = y;
wx = 0, wy = 0;
n = bones[v++];
n += v;
for (; v < n; v++, b += 3) {
@ -102,7 +101,7 @@ public dynamic class VertexAttachment extends Attachment {
} else {
deform = deformArray;
for (w = offset, b = skip * 3, f = skip << 1; w < count; w += 2) {
wx = x; wy = y;
wx = 0; wy = 0;
n = bones[v++];
n += v;
for (; v < n; v++, b += 3, f += 2) {

View File

@ -43,8 +43,9 @@ public class Main extends Sprite {
// example = SpineboyExample;
// example = GoblinsExample;
// example = RaptorExample;
example = TankExample;
// example = TankExample;
// example = VineExample;
example = StretchymanExample;
_starling = new Starling(example, stage);
_starling.enableErrorChecking = true;

View File

@ -0,0 +1,94 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, 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 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 develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes 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, BUSINESS INTERRUPTION, OR LOSS OF
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.examples {
import spine.animation.AnimationStateData;
import spine.*;
import spine.atlas.Atlas;
import spine.attachments.AtlasAttachmentLoader;
import spine.attachments.AttachmentLoader;
import spine.starling.SkeletonAnimation;
import spine.starling.StarlingTextureLoader;
import starling.core.Starling;
import starling.display.Sprite;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
public class StretchymanExample extends Sprite {
[Embed(source = "/stretchyman.json", mimeType = "application/octet-stream")]
static public const StretchymanJson:Class;
[Embed(source = "/stretchyman.atlas", mimeType = "application/octet-stream")]
static public const StretchymanAtlas:Class;
[Embed(source = "/stretchyman.png")]
static public const StretchymanAtlasTexture:Class;
private var skeleton:SkeletonAnimation;
public function StretchymanExample () {
var spineAtlas:Atlas = new Atlas(new StretchymanAtlas(), new StarlingTextureLoader(new StretchymanAtlasTexture()));
var attachmentLoader:AttachmentLoader = new AtlasAttachmentLoader(spineAtlas);
var json:SkeletonJson = new SkeletonJson(attachmentLoader);
json.scale = 0.4;
var skeletonData:SkeletonData = json.readSkeletonData(new StretchymanJson());
var stateData:AnimationStateData = new AnimationStateData(skeletonData);
skeleton = new SkeletonAnimation(skeletonData, stateData);
skeleton.x = 100;
skeleton.y = 560;
skeleton.state.timeScale = 0.1;
skeleton.state.onStart.add(function (trackIndex:int) : void {
trace(trackIndex + " start: " + skeleton.state.getCurrent(trackIndex));
});
skeleton.state.onEnd.add(function (trackIndex:int) : void {
trace(trackIndex + " end: " + skeleton.state.getCurrent(trackIndex));
});
skeleton.state.onComplete.add(function (trackIndex:int, count:int) : void {
trace(trackIndex + " complete: " + skeleton.state.getCurrent(trackIndex) + ", " + count);
});
skeleton.state.onEvent.add(function (trackIndex:int, event:Event) : void {
trace(trackIndex + " event: " + skeleton.state.getCurrent(trackIndex) + ", "
+ event.data.name + ": " + event.intValue + ", " + event.floatValue + ", " + event.stringValue);
});
skeleton.skeleton.setToSetupPose();
skeleton.state.setAnimationByName(0, "sneak", true);
addChild(skeleton);
Starling.juggler.add(skeleton);
}
}
}

View File

@ -0,0 +1,41 @@
stretchyman.png
size: 1024,256
format: RGBA8888
filter: Linear,Linear
repeat: none
back arm
rotate: true
xy: 679, 173
size: 72, 202
orig: 72, 202
offset: 0, 0
index: -1
back leg
rotate: true
xy: 2, 2
size: 100, 318
orig: 100, 318
offset: 0, 0
index: -1
body
rotate: true
xy: 2, 104
size: 141, 452
orig: 141, 452
offset: 0, 0
index: -1
front arm
rotate: true
xy: 456, 100
size: 145, 221
orig: 145, 221
offset: 0, 0
index: -1
head
rotate: true
xy: 322, 15
size: 87, 102
orig: 87, 102
offset: 0, 0
index: -1

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB