[as3] SkeletonBinary 4.0 port.

#1796
This commit is contained in:
Nathan Sweet 2021-05-30 14:50:36 -04:00
parent 05327b8d11
commit 6ab69e62fc
73 changed files with 3937 additions and 2237 deletions

View File

@ -4,7 +4,7 @@
{
"type": "swf",
"request": "launch",
"name": "Launch Spine AS3 SWF",
"name": "Launch spine-as3-example",
"preLaunchTask": "Compile debug"
}
]

View File

@ -40,14 +40,15 @@ package spine.examples {
[SWF(width = "800", height = "600", frameRate = "60", backgroundColor = "#dddddd")]
public class Main extends Sprite {
[Embed(source = "/spineboy-ess.json", mimeType = "application/octet-stream")]
static public const SpineboyJson : Class;
[Embed(source = "/spineboy.atlas", mimeType = "application/octet-stream")]
static public const SpineboyAtlas : Class;
[Embed(source = "/spineboy.png")]
static public const SpineboyAtlasTexture : Class;
[Embed(source = "/spineboy-ess.json", mimeType = "application/octet-stream")]
static public const SpineboyJson : Class;
private var skeleton : SkeletonAnimation;
public function Main() {

View File

@ -37,8 +37,13 @@ package spine {
public function BinaryInput(bytes: ByteArray) {
this.bytes = bytes;
}
public function readByte() : int {
return bytes.readByte();
return bytes.readByte();
}
public function readUnsignedByte() : int {
return bytes.readUnsignedByte();
}
public function readShort() : int {

View File

@ -103,13 +103,12 @@ package spine {
var parent : Bone = _parent;
if (!parent) { // Root bone.
rotationY = rotation + 90 + shearY;
var skeleton : Skeleton = _skeleton;
this.a = MathUtils.cosDeg(rotation + shearX) * scaleX * sx;
this.b = MathUtils.cosDeg(rotationY) * scaleY * sx;
this.c = MathUtils.sinDeg(rotation + shearX) * scaleX * sy;
this.d = MathUtils.sinDeg(rotationY) * scaleY * sy;
worldX = x * sx + skeleton.x;
worldY = y * sy + skeleton.y;
a = MathUtils.cosDeg(rotation + shearX) * scaleX * sx;
b = MathUtils.cosDeg(rotationY) * scaleY * sx;
c = MathUtils.sinDeg(rotation + shearX) * scaleX * sy;
d = MathUtils.sinDeg(rotationY) * scaleY * sy;
worldX = x * sx + _skeleton.x;
worldY = y * sy + _skeleton.y;
return;
}
@ -117,25 +116,25 @@ package spine {
worldX = pa * x + pb * y + parent.worldX;
worldY = pc * x + pd * y + parent.worldY;
switch (this.data.transformMode) {
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;
this.a = pa * la + pb * lc;
this.b = pa * lb + pb * ld;
this.c = pc * la + pd * lc;
this.d = pc * lb + pd * ld;
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;
this.a = MathUtils.cosDeg(rotation + shearX) * scaleX;
this.b = MathUtils.cosDeg(rotationY) * scaleY;
this.c = MathUtils.sinDeg(rotation + shearX) * scaleX;
this.d = MathUtils.sinDeg(rotationY) * scaleY;
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: {
@ -143,8 +142,8 @@ package spine {
var prx : Number = 0;
if (s > 0.0001) {
s = Math.abs(pa * pd - pb * pc) / s;
pa /= this.skeleton.scaleX;
pc /= this.skeleton.scaleY;
pa /= _skeleton.scaleX;
pc /= _skeleton.scaleY;
pb = pc * s;
pd = pa * s;
prx = Math.atan2(pc, pa) * MathUtils.radDeg;
@ -159,10 +158,10 @@ package spine {
lb = MathUtils.cosDeg(ry) * scaleY;
lc = MathUtils.sinDeg(rx) * scaleX;
ld = MathUtils.sinDeg(ry) * scaleY;
this.a = pa * la - pb * lc;
this.b = pa * lb - pb * ld;
this.c = pc * la + pd * lc;
this.d = pc * lb + pd * ld;
a = pa * la - pb * lc;
b = pa * lb - pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
break;
}
case TransformMode.noScale:
@ -185,27 +184,27 @@ package spine {
lb = MathUtils.cosDeg(90 + shearY) * scaleY;
lc = MathUtils.sinDeg(shearX) * scaleX;
ld = MathUtils.sinDeg(90 + shearY) * scaleY;
this.a = za * la + zb * lc;
this.b = za * lb + zb * ld;
this.c = zc * la + zd * lc;
this.d = zc * lb + zd * ld;
a = za * la + zb * lc;
b = za * lb + zb * ld;
c = zc * la + zd * lc;
d = zc * lb + zd * ld;
break;
}
}
this.a *= sx;
this.b *= sx;
this.c *= sy;
this.d *= sy;
a *= sx;
b *= sx;
c *= sy;
d *= sy;
}
public function setToSetupPose() : void {
x = this.data.x;
y = this.data.y;
rotation = this.data.rotation;
scaleX = this.data.scaleX;
scaleY = this.data.scaleY;
shearX = this.data.shearX;
shearY = this.data.shearY;
x = data.x;
y = data.y;
rotation = data.rotation;
scaleX = data.scaleX;
scaleY = data.scaleY;
shearX = data.shearX;
shearY = data.shearY;
}
public function get data() : BoneData {
@ -226,19 +225,19 @@ package spine {
}
public function get worldRotationX() : Number {
return Math.atan2(this.c, this.a) * MathUtils.radDeg;
return Math.atan2(c, a) * MathUtils.radDeg;
}
public function get worldRotationY() : Number {
return Math.atan2(this.d, this.b) * MathUtils.radDeg;
return Math.atan2(d, b) * MathUtils.radDeg;
}
public function get worldScaleX() : Number {
return Math.sqrt(this.a * this.a + this.c * this.c);
return Math.sqrt(a * a + c * c);
}
public function get worldScaleY() : Number {
return Math.sqrt(this.b * this.b + this.d * this.d);
return Math.sqrt(b * b + d * d);
}
/** Computes the individual applied transform values from the world transform. This can be useful to perform processing using
@ -287,37 +286,36 @@ package spine {
}
public function worldToLocal(world : Vector.<Number>) : void {
var a : Number = this.a, b : Number = this.b, c : Number = this.c, d : Number = this.d;
var invDet : Number = 1 / (a * d - b * c);
var x : Number = world[0] - this.worldX, y : Number = world[1] - this.worldY;
world[0] = (x * d * invDet - y * b * invDet);
world[1] = (y * a * invDet - x * c * invDet);
var x : Number = world[0] - worldX, y : Number = world[1] - worldY;
world[0] = x * d * invDet - y * b * invDet;
world[1] = y * a * invDet - x * c * invDet;
}
public function localToWorld(local : Vector.<Number>) : void {
var localX : Number = local[0], localY : Number = local[1];
local[0] = localX * this.a + localY * this.b + this.worldX;
local[1] = localX * this.c + localY * this.d + this.worldY;
local[0] = localX * a + localY * b + worldX;
local[1] = localX * c + localY * d + worldY;
}
public function worldToLocalRotation(worldRotation : Number) : Number {
var sin : Number = MathUtils.sinDeg(worldRotation), cos : Number = MathUtils.cosDeg(worldRotation); return Math.atan2(this.a * sin - this.c * cos, this.d * cos - this.b * sin) * MathUtils.radDeg + rotation - shearX;
var sin : Number = MathUtils.sinDeg(worldRotation), cos : Number = MathUtils.cosDeg(worldRotation);
return Math.atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.radDeg + rotation - shearX;
}
public function localToWorldRotation(localRotation : Number) : Number {
localRotation -= rotation - shearX;
var sin : Number = MathUtils.sinDeg(localRotation), cos : Number = MathUtils.cosDeg(localRotation);
return Math.atan2(cos * this.c + sin * this.d, cos * this.a + sin * this.b) * MathUtils.radDeg;
return Math.atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.radDeg;
}
public function rotateWorld(degrees : Number) : void {
var a : Number = this.a, b : Number = this.b, c : Number = this.c, d : Number = this.d;
var cos : Number = MathUtils.cosDeg(degrees), sin : Number = MathUtils.sinDeg(degrees);
var a : Number = this.a, b : Number = this.b, c : Number = this.c, d : Number = this.d;
this.a = cos * a - sin * c;
this.b = cos * b - sin * d;
this.c = sin * a + cos * c;
this.d = sin * b + cos * d;
this.appliedValid = false;
}
public function toString() : String {

View File

@ -34,10 +34,11 @@ package spine {
public static var GREEN : Color = new Color(0, 1, 0, 1);
public static var BLUE : Color = new Color(0, 0, 1, 1);
public static var MAGENTA : Color = new Color(1, 0, 1, 1);
public var r : Number = 0;
public var g : Number = 0;
public var b : Number = 0;
public var a : Number = 0;
public var r : Number;
public var g : Number;
public var b : Number;
public var a : Number;
public function Color(r : Number, g : Number, b : Number, a : Number = 0) {
this.r = r;
@ -46,30 +47,30 @@ package spine {
this.a = a;
}
public function setFrom(r : Number, g : Number, b : Number, a : Number) : Color {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
this.clamp();
return this;
}
public function setFromColor(c : Color) : Color {
this.r = c.r;
this.g = c.g;
this.b = c.b;
this.a = c.a;
r = c.r;
g = c.g;
b = c.b;
a = c.a;
return this;
}
public function setFromString(hex : String) : Color {
if (hex.length != 8 && hex.length != 6) throw new ArgumentError("Hexadecimal color length must be 6 or 8: " + hex);
hex = hex.charAt(0) == '#' ? hex.substr(1) : hex;
this.r = parseInt(hex.substr(0, 2), 16) / 255.0;
this.g = parseInt(hex.substr(2, 2), 16) / 255.0;
this.b = parseInt(hex.substr(4, 2), 16) / 255.0;
this.a = (hex.length != 8 ? 255 : parseInt(hex.substr(6, 2), 16)) / 255.0;
r = parseInt(hex.substr(0, 2), 16) / 255.0;
g = parseInt(hex.substr(2, 2), 16) / 255.0;
b = parseInt(hex.substr(4, 2), 16) / 255.0;
a = (hex.length != 8 ? 255 : parseInt(hex.substr(6, 2), 16)) / 255.0;
return this;
}
public function set(r : Number, g : Number, b : Number, a : Number) : Color {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
clamp();
return this;
}
@ -78,22 +79,22 @@ package spine {
this.g += g;
this.b += b;
this.a += a;
this.clamp();
clamp();
return this;
}
public function clamp() : Color {
if (this.r < 0) this.r = 0;
else if (this.r > 1) this.r = 1;
if (r < 0) r = 0;
else if (r > 1) r = 1;
if (this.g < 0) this.g = 0;
else if (this.g > 1) this.g = 1;
if (g < 0) g = 0;
else if (g > 1) g = 1;
if (this.b < 0) this.b = 0;
else if (this.b > 1) this.b = 1;
if (b < 0) b = 0;
else if (b > 1) b = 1;
if (this.a < 0) this.a = 0;
else if (this.a > 1) this.a = 1;
if (a < 0) a = 0;
else if (a > 1) a = 1;
return this;
}

View File

@ -59,11 +59,8 @@ package spine {
return active;
}
public function apply() : void {
update();
}
public function update() : void {
if (mix == 0) return;
switch (bones.length) {
case 1:
apply1(bones[0], target.worldX, target.worldY, compress, stretch, _data.uniform, mix);
@ -138,10 +135,6 @@ package spine {
* target is specified in the world coordinate system.
* @param child Any descendant bone of the parent. */
static public function apply2(parent : Bone, child : Bone, targetX : Number, targetY : Number, bendDir : int, stretch : Boolean, softness: Number, alpha : Number) : void {
if (alpha == 0) {
child.updateWorldTransform();
return;
}
if (!parent.appliedValid) parent.updateAppliedTransform();
if (!child.appliedValid) child.updateAppliedTransform();
var px : Number = parent.ax, py : Number = parent.ay, psx : Number = parent.ascaleX, sx : Number = psx, psy : Number = parent.ascaleY, csx : Number = child.ascaleX;

View File

@ -33,16 +33,17 @@ package spine {
public class PathConstraint implements Updatable {
private static const NONE : int = -1, BEFORE : int = -2, AFTER : int = -3;
private static const epsilon : Number = 0.00001;
internal var _data : PathConstraintData;
internal var _bones : Vector.<Bone>;
public var target : Slot;
public var position : Number, spacing : Number, rotateMix : Number, translateMix : Number;
public var position : Number, spacing : Number, mixRotate : Number, mixX : Number, mixY : Number;
internal const _spaces : Vector.<Number> = new Vector.<Number>();
internal const _positions : Vector.<Number> = new Vector.<Number>();
internal const _world : Vector.<Number> = new Vector.<Number>();
internal const _curves : Vector.<Number> = new Vector.<Number>();
internal const _lengths : Vector.<Number> = new Vector.<Number>();
internal const _segments : Vector.<Number> = new Vector.<Number>(10);
internal const _segments : Vector.<Number> = new Vector.<Number>(10, true);
public var active : Boolean;
public function PathConstraint(data : PathConstraintData, skeleton : Skeleton) {
@ -55,8 +56,9 @@ package spine {
target = skeleton.findSlot(data.target.name);
position = data.position;
spacing = data.spacing;
rotateMix = data.rotateMix;
translateMix = data.translateMix;
mixRotate = data.mixRotate;
mixX = data.mixX;
mixY = data.mixY;
}
public function isActive() : Boolean {
@ -71,80 +73,109 @@ package spine {
var attachment : PathAttachment = target.attachment as PathAttachment;
if (attachment == null) return;
var rotateMix : Number = this.rotateMix, translateMix : Number = this.translateMix;
var translate : Boolean = translateMix > 0, rotate : Boolean = rotateMix > 0;
if (!translate && !rotate) return;
var mixRotate : Number = this.mixRotate, mixX : Number = this.mixX, mixY : Number = this.mixY;
if (mixRotate == 0 && mixX == 0 && mixY == 0) return;
var data : PathConstraintData = this._data;
var percentSpacing : Boolean = data.spacingMode == SpacingMode.percent;
var rotateMode : RotateMode = data.rotateMode;
var tangents : Boolean = rotateMode == RotateMode.tangent, scale : Boolean = rotateMode == RotateMode.chainScale;
var tangents : Boolean = data.rotateMode == RotateMode.tangent, scale : Boolean = data.rotateMode == RotateMode.chainScale;
var boneCount : int = this._bones.length, spacesCount : int = tangents ? boneCount : boneCount + 1;
var bones : Vector.<Bone> = this._bones;
this._spaces.length = spacesCount;
var spaces : Vector.<Number> = this._spaces, lengths : Vector.<Number> = null;
var spaces : Vector.<Number> = this._spaces, lengths : Vector.<Number> = _lengths;
if (scale) lengths.length = boneCount;
var spacing : Number = this.spacing;
if (scale || !percentSpacing) {
var i : int, n : int, bone : Bone, setupLength : int, x : Number, y : Number, length : Number;
switch (data.spacingMode) {
case SpacingMode.percent:
if (scale) {
this._lengths.length = boneCount;
lengths = this._lengths;
}
var lengthSpacing : Boolean = data.spacingMode == SpacingMode.length;
for (var i : int = 0, n : int = spacesCount - 1; i < n;) {
var bone : Bone = bones[i];
var setupLength : Number = bone.data.length;
if (setupLength < epsilon) {
if (scale) lengths[i] = 0;
spaces[++i] = 0;
} else if (percentSpacing) {
if (scale) {
var x_l : Number = setupLength * bone.a;
var y_l : Number = setupLength * bone.c;
var length_l : Number = Math.sqrt(x_l * x_l + y_l * y_l);
lengths[i] = length_l;
for (i = 0, n = spacesCount - 1; i < n; i++) {
bone = bones[i];
setupLength = bone.data.length;
if (setupLength < PathConstraint.epsilon)
lengths[i] = 0;
else {
x = setupLength * bone.a;
y = setupLength * bone.c;
lengths[i] = Math.sqrt(x * x + y * y);
}
}
}
for (i = 1; i < spacesCount; i++)
spaces[i] = spacing;
break;
case SpacingMode.proportional:
var sum : Number = 0;
for (i = 0; i < boneCount;) {
bone = bones[i];
setupLength = bone.data.length;
if (setupLength < PathConstraint.epsilon) {
if (scale) lengths[i] = 0;
spaces[++i] = spacing;
} else {
var x : Number = setupLength * bone.a, y : Number = setupLength * bone.c;
var length : Number = Math.sqrt(x * x + y * y);
x = setupLength * bone.a;
y = setupLength * bone.c;
length = Math.sqrt(x * x + y * y);
if (scale) lengths[i] = length;
spaces[++i] = length;
sum += length;
}
}
if (sum > 0) {
sum = spacesCount / sum * spacing;
for (i = 1; i < spacesCount; i++)
spaces[i] *= sum;
}
break;
default:
var lengthSpacing : Boolean = data.spacingMode == SpacingMode.length;
for (i = 0, n = spacesCount - 1; i < n;) {
bone = bones[i];
setupLength = bone.data.length;
if (setupLength < PathConstraint.epsilon) {
if (scale) lengths[i] = 0;
spaces[++i] = spacing;
} else {
x = setupLength * bone.a;
y = setupLength * bone.c;
length = Math.sqrt(x * x + y * y);
if (scale) lengths[i] = length;
spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
}
}
} else {
for (i = 1; i < spacesCount; i++)
spaces[i] = spacing;
}
var positions : Vector.<Number> = computeWorldPositions(attachment, spacesCount, tangents, data.positionMode == PositionMode.percent, percentSpacing);
var positions : Vector.<Number> = computeWorldPositions(attachment, spacesCount, tangents);
var boneX : Number = positions[0], boneY : Number = positions[1], offsetRotation : Number = data.offsetRotation;
var tip : Boolean = false;
if (offsetRotation == 0)
tip = rotateMode == RotateMode.chain;
tip = data.rotateMode == RotateMode.chain;
else {
tip = false;
var pa : Bone = target.bone;
offsetRotation *= pa.a * pa.d - pa.b * pa.c > 0 ? MathUtils.degRad : -MathUtils.degRad;
}
var p : Number;
var p : int;
for (i = 0, p = 3; i < boneCount; i++, p += 3) {
bone = bones[i];
bone.worldX += (boneX - bone.worldX) * translateMix;
bone.worldY += (boneY - bone.worldY) * translateMix;
bone.worldX += (boneX - bone.worldX) * mixX;
bone.worldY += (boneY - bone.worldY) * mixY;
x = positions[p];
y = positions[p + 1];
var dx : Number = x - boneX, dy : Number = y - boneY;
if (scale) {
length = lengths[i];
if (length != 0) {
var s : Number = (Math.sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1;
var s : Number = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1;
bone.a *= s;
bone.c *= s;
}
}
boneX = x;
boneY = y;
if (rotate) {
if (mixRotate > 0) {
var a : Number = bone.a, b : Number = bone.b, c : Number = bone.c, d : Number = bone.d, r : Number, cos : Number, sin : Number;
if (tangents)
r = positions[p - 1];
@ -157,8 +188,8 @@ package spine {
cos = Math.cos(r);
sin = Math.sin(r);
length = bone.data.length;
boneX += (length * (cos * a - sin * c) - dx) * rotateMix;
boneY += (length * (sin * a + cos * c) - dy) * rotateMix;
boneX += (length * (cos * a - sin * c) - dx) * mixRotate;
boneY += (length * (sin * a + cos * c) - dy) * mixRotate;
} else {
r += offsetRotation;
}
@ -166,7 +197,7 @@ package spine {
r -= (Math.PI * 2);
else if (r < -Math.PI) //
r += (Math.PI * 2);
r *= rotateMix;
r *= mixRotate;
cos = Math.cos(r);
sin = Math.sin(r);
bone.a = cos * a - sin * c;
@ -178,7 +209,7 @@ package spine {
}
}
protected function computeWorldPositions(path : PathAttachment, spacesCount : int, tangents : Boolean, percentPosition : Boolean, percentSpacing : Boolean) : Vector.<Number> {
protected function computeWorldPositions(path : PathAttachment, spacesCount : int, tangents : Boolean) : Vector.<Number> {
var target : Slot = this.target;
var position : Number = this.position;
var spaces : Vector.<Number> = this._spaces;
@ -186,21 +217,29 @@ package spine {
var out : Vector.<Number> = this._positions, world : Vector.<Number>;
var closed : Boolean = path.closed;
var verticesLength : int = path.worldVerticesLength, curveCount : int = verticesLength / 6, prevCurve : int = NONE;
var multiplier : Number, i : int;
if (!path.constantSpeed) {
var lengths : Vector.<Number> = path.lengths;
curveCount -= closed ? 1 : 2;
var pathLength : Number = lengths[curveCount];
if (percentPosition) position *= pathLength;
if (percentSpacing) {
for (var i : int = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
if (data.positionMode == PositionMode.percent) position *= pathLength;
switch (data.spacingMode) {
case SpacingMode.percent:
multiplier = pathLength;
break;
case SpacingMode.proportional:
multiplier = pathLength / spacesCount;
break;
default:
multiplier = 1;
}
this._world.length = 8;
world = this._world;
var o : int, curve : int;
for (i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
var space : Number = spaces[i];
var space : Number = spaces[i] * multiplier;
position += space;
var p : Number = position;
@ -304,20 +343,25 @@ package spine {
x1 = x2;
y1 = y2;
}
if (percentPosition)
position *= pathLength;
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
if (data.positionMode == PositionMode.percent) position *= pathLength;
switch (data.spacingMode) {
case SpacingMode.percent:
multiplier = pathLength;
break;
case SpacingMode.proportional:
multiplier = pathLength / spacesCount;
break;
default:
multiplier = 1;
}
var segments : Vector.<Number> = this._segments;
var curveLength : Number = 0;
var segment : int;
for (i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
space = spaces[i];
space = spaces[i] * multiplier;
position += space;
p = position;

View File

@ -28,14 +28,14 @@
*****************************************************************************/
package spine {
public dynamic class PathConstraintData extends ConstraintData {
public class PathConstraintData extends ConstraintData {
internal var _bones : Vector.<BoneData> = new Vector.<BoneData>();
public var target : SlotData;
public var positionMode : PositionMode;
public var spacingMode : SpacingMode;
public var rotateMode : RotateMode;
public var offsetRotation : Number;
public var position : Number, spacing : Number, rotateMix : Number, translateMix : Number;
public var position : Number, spacing : Number, mixRotate : Number, mixX : Number, mixY : Number;
public function PathConstraintData(name : String) {
super(name, 0, false);

View File

@ -45,7 +45,6 @@ package spine {
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 color : Color = new Color(1, 1, 1, 1);
public var time : Number = 0;
@ -99,11 +98,10 @@ package spine {
public function updateCache() : void {
var updateCache : Vector.<Updatable> = this._updateCache;
updateCache.length = 0;
this._updateCacheReset.length = 0;
var bones : Vector.<Bone> = this.bones;
var i : Number = 0;
var n : Number = 0;
var i : int = 0;
var n : int = 0;
var bone : Bone;
for (i = 0, n = bones.length; i < n; i++) {
bone = bones[i];
@ -127,12 +125,12 @@ package spine {
var ikConstraints : Vector.<IkConstraint> = this.ikConstraints;
var transformConstraints : Vector.<TransformConstraint> = this.transformConstraints;
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;
var ikCount : int = ikConstraints.length, transformCount : int = transformConstraints.length, pathCount : int = pathConstraints.length;
var constraintCount : int = ikCount + transformCount + pathCount;
outer:
for (i = 0; i < constraintCount; i++) {
var ii : Number = 0;
var ii : int = 0;
for (ii = 0; ii < ikCount; ii++) {
var ikConstraint : IkConstraint = ikConstraints[ii];
if (ikConstraint.data.order == i) {
@ -161,7 +159,7 @@ package spine {
}
private static function contains(list : Vector.<ConstraintData>, element : ConstraintData) : Boolean {
for (var i : Number = 0; i < list.length; i++)
for (var i : int = 0; i < list.length; i++)
if (list[i] == element) return true;
return false;
}
@ -177,9 +175,17 @@ package spine {
var parent : Bone = constrained[0];
sortBone(parent);
if (constrained.length > 1) {
if (constrained.length == 1) {
_updateCache.push(constraint);
sortReset(parent.children);
} else {
var child : Bone = constrained[constrained.length - 1];
if (!(_updateCache.indexOf(child) > -1)) _updateCacheReset.push(child);
sortBone(child);
_updateCache.push(constraint);
sortReset(parent.children);
child._sorted = true;
}
_updateCache.push(constraint);
@ -193,13 +199,12 @@ package spine {
if (!constraint.active) return;
var slot : Slot = constraint.target;
var slotIndex : Number = slot.data.index;
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 i : Number = 0;
var n : Number = 0;
var i : int = 0, n : int = 0;
for (i = 0, n = data.skins.length; i < n; i++)
sortPathConstraintAttachment(data.skins[i], slotIndex, slotBone);
@ -207,7 +212,7 @@ package spine {
if (attachment is PathAttachment) sortPathConstraintAttachment2(attachment, slotBone);
var constrained : Vector.<Bone> = constraint.bones;
var boneCount : Number = constrained.length;
var boneCount : int = constrained.length;
for (i = 0; i < boneCount; i++)
sortBone(constrained[i]);
@ -226,13 +231,13 @@ package spine {
sortBone(constraint.target);
var constrained : Vector.<Bone> = constraint.bones;
var boneCount : Number = constrained.length;
var i : Number = 0;
var boneCount : int = constrained.length;
var i : int = 0;
if (constraint.data.local) {
for (i = 0; i < boneCount; i++) {
var child : Bone = constrained[i];
sortBone(child.parent);
if (!(_updateCache.indexOf(child) > -1)) _updateCacheReset.push(child);
sortBone(child);
}
} else {
for (i = 0; i < boneCount; i++)
@ -265,12 +270,11 @@ package spine {
sortBone(slotBone);
else {
var bones : Vector.<Bone> = this.bones;
var i : int = 0;
while (i < pathBones.length) {
var boneCount : int = pathBones[i++];
for (var n : int = i + boneCount; i < n; i++) {
sortBone(bones[pathBones[i]]);
}
for (var i : int = 0, n : int = pathBones.length; i < n;) {
var nn : int = pathBones[i++];
nn += i;
while (i < nn)
sortBone(bones[pathBones[i++]]);
}
}
}
@ -294,19 +298,34 @@ package spine {
/** 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;
var updateCache : Vector.<Updatable> = _updateCache;
for (var i : int = 0, n : int = updateCache.length; i < n; i++)
updateCache[i].update();
}
public function updateWorldTransformWith (parent : Bone) : void {
// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
var rootBone : Bone = rootBone;
var pa : Number = parent.a, pb : Number = parent.b, pc : Number = parent.c, pd : Number = parent.d;
rootBone.worldX = pa * x + pb * y + parent.worldX;
rootBone.worldY = pc * x + pd * y + parent.worldY;
var rotationY : Number = rootBone.rotation + 90 + rootBone.shearY;
var la : Number = MathUtils.cosDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
var lb : Number = MathUtils.cosDeg(rotationY) * rootBone.scaleY;
var lc : Number = MathUtils.sinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
var ld : Number = MathUtils.sinDeg(rotationY) * rootBone.scaleY;
rootBone.a = (pa * la + pb * lc) * scaleX;
rootBone.b = (pa * lb + pb * ld) * scaleX;
rootBone.c = (pc * la + pd * lc) * scaleY;
rootBone.d = (pc * lb + pd * ld) * scaleY;
// Update everything except root bone.
var updateCache : Vector.<Updatable> = _updateCache;
for (var i : int = 0, n : int = updateCache.length; i < n; i++) {
var updatable : Updatable = updateCache[i];
if (updatable != rootBone) updatable.update();
}
for each (var updatable : Updatable in _updateCache)
updatable.update();
}
/** Sets the bones, constraints, and slots to their setup pose values. */
@ -321,6 +340,7 @@ package spine {
bone.setToSetupPose();
for each (var ikConstraint : IkConstraint in ikConstraints) {
var ikData : IkConstraintData = ikConstraint._data;
ikConstraint.mix = ikConstraint._data.mix;
ikConstraint.softness = ikConstraint._data.softness;
ikConstraint.bendDirection = ikConstraint._data.bendDirection;
@ -329,17 +349,22 @@ package spine {
}
for each (var transformConstraint : TransformConstraint in transformConstraints) {
transformConstraint.rotateMix = transformConstraint._data.rotateMix;
transformConstraint.translateMix = transformConstraint._data.translateMix;
transformConstraint.scaleMix = transformConstraint._data.scaleMix;
transformConstraint.shearMix = transformConstraint._data.shearMix;
var transformData : TransformConstraintData = transformConstraint._data;
transformConstraint.mixRotate = transformData.mixRotate;
transformConstraint.mixX = transformData.mixX;
transformConstraint.mixY = transformData.mixY;
transformConstraint.mixScaleX = transformData.mixScaleX;
transformConstraint.mixScaleY = transformData.mixScaleY;
transformConstraint.mixShearY = transformData.mixShearY;
}
for each (var pathConstraint : PathConstraint in pathConstraints) {
pathConstraint.position = pathConstraint._data.position;
pathConstraint.spacing = pathConstraint._data.spacing;
pathConstraint.rotateMix = pathConstraint._data.rotateMix;
pathConstraint.translateMix = pathConstraint._data.translateMix;
var pathData : PathConstraintData = pathConstraint._data;
pathConstraint.position = pathData.position;
pathConstraint.spacing = pathData.spacing;
pathConstraint.mixRotate = pathData.mixRotate;
pathConstraint.mixX = pathData.mixX;
pathConstraint.mixY = pathData.mixY;
}
}

View File

@ -28,38 +28,10 @@
*****************************************************************************/
package spine {
import spine.attachments.ClippingAttachment;
import spine.animation.TwoColorTimeline;
import spine.attachments.PointAttachment;
import spine.animation.PathConstraintMixTimeline;
import spine.animation.PathConstraintSpacingTimeline;
import spine.animation.PathConstraintPositionTimeline;
import spine.animation.TransformConstraintTimeline;
import spine.animation.ShearTimeline;
import spine.attachments.PathAttachment;
import spine.attachments.VertexAttachment;
import spine.animation.*;
import spine.attachments.*;
import flash.utils.ByteArray;
import spine.animation.Animation;
import spine.animation.AttachmentTimeline;
import spine.animation.ColorTimeline;
import spine.animation.CurveTimeline;
import spine.animation.DrawOrderTimeline;
import spine.animation.EventTimeline;
import spine.animation.DeformTimeline;
import spine.animation.IkConstraintTimeline;
import spine.animation.RotateTimeline;
import spine.animation.ScaleTimeline;
import spine.animation.Timeline;
import spine.animation.TranslateTimeline;
import spine.attachments.Attachment;
import spine.attachments.AttachmentLoader;
import spine.attachments.AttachmentType;
import spine.attachments.BoundingBoxAttachment;
import spine.attachments.MeshAttachment;
import spine.attachments.RegionAttachment;
public class SkeletonBinary {
public var attachmentLoader : AttachmentLoader;
public var scale : Number = 1;
@ -67,12 +39,21 @@ package spine {
private static const BONE_ROTATE : int = 0;
private static const BONE_TRANSLATE : int = 1;
private static const BONE_SCALE : int = 2;
private static const BONE_SHEAR : int = 3;
private static const BONE_TRANSLATEX : int = 2;
private static const BONE_TRANSLATEY : int = 3;
private static const BONE_SCALE : int = 4;
private static const BONE_SCALEX : int = 5;
private static const BONE_SCALEY : int = 6;
private static const BONE_SHEAR : int = 7;
private static const BONE_SHEARX : int = 8;
private static const BONE_SHEARY : int = 9;
private static const SLOT_ATTACHMENT : int = 0;
private static const SLOT_COLOR : int = 1;
private static const SLOT_TWO_COLOR : int = 2;
private static const SLOT_RGBA : int = 1;
private static const SLOT_RGB : int = 2;
private static const SLOT_RGBA2 : int = 3;
private static const SLOT_RGB2 : int = 4;
private static const SLOT_ALPHA : int = 5;
private static const PATH_POSITION : int = 0;
private static const PATH_SPACING : int = 1;
@ -98,7 +79,9 @@ package spine {
var input : BinaryInput = new BinaryInput(object);
skeletonData.hash = input.readString();
var lowHash : int = input.readInt32();
var highHash : int = input.readInt32();
skeletonData.hash = highHash == 0 && lowHash == 0 ? null : highHash.toString(16) + lowHash.toString(16);
skeletonData.version = input.readString();
if ("3.8.75" == skeletonData.version)
throw new Error("Unsupported skeleton data, please export with a newer version of Spine.");
@ -182,26 +165,28 @@ package spine {
// Transform constraints.
n = input.readInt(true);
for (i = 0, nn; i < n; i++) {
var transData : TransformConstraintData = new TransformConstraintData(input.readString());
transData.order = input.readInt(true);
transData.skinRequired = input.readBoolean();
var transformData : TransformConstraintData = new TransformConstraintData(input.readString());
transformData.order = input.readInt(true);
transformData.skinRequired = input.readBoolean();
nn = input.readInt(true);
for (ii = 0; ii < nn; ii++)
transData.bones.push(skeletonData.bones[input.readInt(true)]);
transData.target = skeletonData.bones[input.readInt(true)];
transData.local = input.readBoolean();
transData.relative = input.readBoolean();
transData.offsetRotation = input.readFloat();
transData.offsetX = input.readFloat() * scale;
transData.offsetY = input.readFloat() * scale;
transData.offsetScaleX = input.readFloat();
transData.offsetScaleY = input.readFloat();
transData.offsetShearY = input.readFloat();
transData.rotateMix = input.readFloat();
transData.translateMix = input.readFloat();
transData.scaleMix = input.readFloat();
transData.shearMix = input.readFloat();
skeletonData.transformConstraints.push(transData);
transformData.bones.push(skeletonData.bones[input.readInt(true)]);
transformData.target = skeletonData.bones[input.readInt(true)];
transformData.local = input.readBoolean();
transformData.relative = input.readBoolean();
transformData.offsetRotation = input.readFloat();
transformData.offsetX = input.readFloat() * scale;
transformData.offsetY = input.readFloat() * scale;
transformData.offsetScaleX = input.readFloat();
transformData.offsetScaleY = input.readFloat();
transformData.offsetShearY = input.readFloat();
transformData.mixRotate = input.readFloat();
transformData.mixX = input.readFloat();
transformData.mixY = input.readFloat();
transformData.mixScaleX = input.readFloat();
transformData.mixScaleY = input.readFloat();
transformData.mixShearY = input.readFloat();
skeletonData.transformConstraints.push(transformData);
}
// Path constraints.
@ -222,8 +207,9 @@ package spine {
if (pathData.positionMode == PositionMode.fixed) pathData.position *= scale;
pathData.spacing = input.readFloat();
if (pathData.spacingMode == SpacingMode.length || pathData.spacingMode == SpacingMode.fixed) pathData.spacing *= scale;
pathData.rotateMix = input.readFloat();
pathData.translateMix = input.readFloat();
pathData.mixRotate = input.readFloat();
pathData.mixX = input.readFloat();
pathData.mixY = input.readFloat();
skeletonData.pathConstraints.push(pathData);
}
@ -317,7 +303,7 @@ package spine {
return skin;
}
private function readAttachment(input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: Number, attachmentName: String, nonessential: Boolean): Attachment {
private function readAttachment(input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: int, attachmentName: String, nonessential: Boolean): Attachment {
var scale : Number = this.scale;
var i : int = 0;
var n : int = 0;
@ -338,10 +324,8 @@ package spine {
var name : String = input.readStringRef();
if (name == null) name = attachmentName;
var typeIndex : int = input.readByte();
var type : AttachmentType = AttachmentType.values[typeIndex];
switch (type) {
case AttachmentType.region: {
switch (AttachmentType.values[input.readByte()]) {
case AttachmentType.region:
path = input.readStringRef();
rotation = input.readFloat();
x = input.readFloat();
@ -366,8 +350,7 @@ package spine {
region.color.setFromRgba8888(color);
region.updateOffset();
return region;
}
case AttachmentType.boundingbox: {
case AttachmentType.boundingbox:
vertexCount = input.readInt(true);
vertices = readVertices(input, vertexCount);
color = nonessential ? input.readInt32() : 0;
@ -379,8 +362,7 @@ package spine {
box.bones = vertices.bones;
if (nonessential) box.color.setFromRgba8888(color);
return box;
}
case AttachmentType.mesh: {
case AttachmentType.mesh:
path = input.readStringRef();
color = input.readInt32();
vertexCount = input.readInt(true);
@ -413,8 +395,7 @@ package spine {
mesh.height = height * scale;
}
return mesh;
}
case AttachmentType.linkedmesh: {
case AttachmentType.linkedmesh:
path = input.readStringRef();
color = input.readInt32();
var skinName : String = input.readStringRef();
@ -436,8 +417,7 @@ package spine {
}
this.linkedMeshes.push(new LinkedMesh(mesh, skinName, slotIndex, parent, inheritDeform));
return mesh;
}
case AttachmentType.path: {
case AttachmentType.path:
var closed : Boolean = input.readBoolean();
var constantSpeed : Boolean = input.readBoolean();
vertexCount = input.readInt(true);
@ -458,8 +438,7 @@ package spine {
pathAttachment.lengths = lengths;
if (nonessential) pathAttachment.color.setFromRgba8888(color);
return pathAttachment;
}
case AttachmentType.point: {
case AttachmentType.point:
rotation = input.readFloat();
x = input.readFloat();
y = input.readFloat();
@ -472,8 +451,7 @@ package spine {
point.rotation = rotation;
if (nonessential) point.color.setFromRgba8888(color);
return point;
}
case AttachmentType.clipping: {
case AttachmentType.clipping:
var endSlotIndex : int = input.readInt(true);
vertexCount = input.readInt(true);
vertices = this.readVertices(input, vertexCount);
@ -488,14 +466,13 @@ package spine {
if (nonessential) clip.color.setFromRgba8888(color);
return clip;
}
}
return null;
}
private function readVertices (input: BinaryInput, vertexCount: int): Vertices {
var scale : Number = this.scale;
var verticesLength : int = vertexCount << 1;
var vertices : Vertices = new Vertices();
var scale : Number = this.scale;
if (!input.readBoolean()) {
vertices.vertices = readFloatArray(input, verticesLength, scale);
return vertices;
@ -517,7 +494,7 @@ package spine {
return vertices;
}
private function readFloatArray (input: BinaryInput, n: Number, scale: Number): Vector.<Number> {
private function readFloatArray (input: BinaryInput, n: int, scale: Number): Vector.<Number> {
var i : int = 0;
var array : Vector.<Number> = new Vector.<Number>();
array.length = n;
@ -550,65 +527,218 @@ package spine {
}
private function readAnimation (input: BinaryInput, name: String, skeletonData: SkeletonData): Animation {
input.readInt(true); // Number of timelines.
var timelines : Vector.<Timeline> = new Vector.<Timeline>();
var scale : Number = this.scale;
var duration : Number = 0;
var tempColor1 : Color = new Color(0, 0, 0, 0);
var tempColor2 : Color = new Color(0, 0, 0, 0);
var i : int = 0, n : int = 0, ii : int = 0, nn : int = 0;
var slotIndex : int;
var timelineType : int;
var frameCount : int;
var frameIndex : int;
var timelineScale : Number;
var index : int;
var time : Number;
var index : int, slotIndex : int, timelineType : int, timelineScale : Number;
var frameCount : int, frameLast : int, frame : int, bezierCount : int, bezier : int;
var time : Number, time2 : Number;
// Slot timelines.
var r : Number, g : Number, b : Number, a : Number;
var r2 : Number, g2 : Number, b2 : Number, a2 : Number;
var nr : Number, ng : Number, nb : Number, na : Number;
var nr2 : Number, ng2 : Number, nb2 : Number, na2 : Number;
for (i = 0, n = input.readInt(true); i < n; i++) {
slotIndex = input.readInt(true);
for (ii = 0, nn = input.readInt(true); ii < nn; ii++) {
timelineType = input.readByte();
frameCount = input.readInt(true);
frameIndex = 0;
frameLast = frameCount - 1;
switch (timelineType) {
case SkeletonBinary.SLOT_ATTACHMENT: {
var attachmentTimeline : AttachmentTimeline = new AttachmentTimeline(frameCount);
attachmentTimeline.slotIndex = slotIndex;
for (frameIndex = 0; frameIndex < frameCount; frameIndex++)
attachmentTimeline.setFrame(frameIndex, input.readFloat(), input.readStringRef());
case SLOT_ATTACHMENT:
var attachmentTimeline : AttachmentTimeline = new AttachmentTimeline(frameCount, slotIndex);
for (frame = 0; frame < frameCount; frame++)
attachmentTimeline.setFrame(frame, input.readFloat(), input.readStringRef());
timelines.push(attachmentTimeline);
duration = Math.max(duration, attachmentTimeline.frames[frameCount - 1]);
break;
}
case SkeletonBinary.SLOT_COLOR: {
var colorTimeline : ColorTimeline = new ColorTimeline(frameCount);
colorTimeline.slotIndex = slotIndex;
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
time = input.readFloat();
tempColor1.setFromRgba8888(input.readInt32());
colorTimeline.setFrame(frameIndex, time, tempColor1.r, tempColor1.g, tempColor1.b, tempColor1.a);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, colorTimeline);
case SLOT_RGBA:
bezierCount = input.readInt(true);
var rgbaTimeline : RGBATimeline = new RGBATimeline(frameCount, bezierCount, slotIndex);
time = input.readFloat();
r = input.readUnsignedByte() / 255.0;
g = input.readUnsignedByte() / 255.0;
b = input.readUnsignedByte() / 255.0;
a = input.readUnsignedByte() / 255.0;
for (frame = 0, bezier = 0;; frame++) {
rgbaTimeline.setFrame(frame, time, r, g, b, a);
if (frame == frameLast) break;
time2 = input.readFloat();
r2 = input.readUnsignedByte() / 255.0;
g2 = input.readUnsignedByte() / 255.0;
b2 = input.readUnsignedByte() / 255.0;
a2 = input.readUnsignedByte() / 255.0;
switch (input.readByte()) {
case CURVE_STEPPED:
rgbaTimeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, rgbaTimeline, bezier++, frame, 0, time, time2, r, r2, 1);
setBezier(input, rgbaTimeline, bezier++, frame, 1, time, time2, g, g2, 1);
setBezier(input, rgbaTimeline, bezier++, frame, 2, time, time2, b, b2, 1);
setBezier(input, rgbaTimeline, bezier++, frame, 3, time, time2, a, a2, 1);
}
time = time2;
r = r2;
g = g2;
b = b2;
a = a2;
}
timelines.push(colorTimeline);
duration = Math.max(duration, colorTimeline.frames[(frameCount - 1) * ColorTimeline.ENTRIES]);
timelines.push(rgbaTimeline);
break;
}
case SkeletonBinary.SLOT_TWO_COLOR: {
var twoColorTimeline : TwoColorTimeline = new TwoColorTimeline(frameCount);
twoColorTimeline.slotIndex = slotIndex;
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
time = input.readFloat();
tempColor1.setFromRgba8888(input.readInt32());
tempColor2.setFromRgb888(input.readInt32());
twoColorTimeline.setFrame(frameIndex, time, tempColor1.r, tempColor1.g, tempColor1.b, tempColor1.a, tempColor2.r,
tempColor2.g, tempColor2.b);
if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, twoColorTimeline);
case SLOT_RGB:
bezierCount = input.readInt(true);
var rgbTimeline : RGBTimeline = new RGBTimeline(frameCount, bezierCount, slotIndex);
time = input.readFloat();
r = input.readUnsignedByte() / 255.0;
g = input.readUnsignedByte() / 255.0;
b = input.readUnsignedByte() / 255.0;
for (frame = 0, bezier = 0;; frame++) {
rgbTimeline.setFrame(frame, time, r, g, b);
if (frame == frameLast) break;
time2 = input.readFloat();
r2 = input.readUnsignedByte() / 255.0;
g2 = input.readUnsignedByte() / 255.0;
b2 = input.readUnsignedByte() / 255.0;
switch (input.readByte()) {
case CURVE_STEPPED:
rgbTimeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, rgbTimeline, bezier++, frame, 0, time, time2, r, r2, 1);
setBezier(input, rgbTimeline, bezier++, frame, 1, time, time2, g, g2, 1);
setBezier(input, rgbTimeline, bezier++, frame, 2, time, time2, b, b2, 1);
}
time = time2;
r = r2;
g = g2;
b = b2;
}
timelines.push(twoColorTimeline);
duration = Math.max(duration, twoColorTimeline.frames[(frameCount - 1) * TwoColorTimeline.ENTRIES]);
timelines.push(rgbTimeline);
break;
}
case SLOT_RGBA2:
bezierCount = input.readInt(true);
var rgba2Timeline : RGBA2Timeline = new RGBA2Timeline(frameCount, bezierCount, slotIndex);
time = input.readFloat();
r = input.readUnsignedByte() / 255.0;
g = input.readUnsignedByte() / 255.0;
b = input.readUnsignedByte() / 255.0;
a = input.readUnsignedByte() / 255.0;
r2 = input.readUnsignedByte() / 255.0;
g2 = input.readUnsignedByte() / 255.0;
b2 = input.readUnsignedByte() / 255.0;
for (frame = 0, bezier = 0;; frame++) {
rgba2Timeline.setFrame(frame, time, r, g, b, a, r2, g2, b2);
if (frame == frameLast) break;
time2 = input.readFloat();
nr = input.readUnsignedByte() / 255.0;
ng = input.readUnsignedByte() / 255.0;
nb = input.readUnsignedByte() / 255.0;
na = input.readUnsignedByte() / 255.0;
nr2 = input.readUnsignedByte() / 255.0;
ng2 = input.readUnsignedByte() / 255.0;
nb2 = input.readUnsignedByte() / 255.0;
switch (input.readByte()) {
case CURVE_STEPPED:
rgba2Timeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, rgba2Timeline, bezier++, frame, 0, time, time2, r, nr, 1);
setBezier(input, rgba2Timeline, bezier++, frame, 1, time, time2, g, ng, 1);
setBezier(input, rgba2Timeline, bezier++, frame, 2, time, time2, b, nb, 1);
setBezier(input, rgba2Timeline, bezier++, frame, 3, time, time2, a, na, 1);
setBezier(input, rgba2Timeline, bezier++, frame, 4, time, time2, r2, nr2, 1);
setBezier(input, rgba2Timeline, bezier++, frame, 5, time, time2, g2, ng2, 1);
setBezier(input, rgba2Timeline, bezier++, frame, 6, time, time2, b2, nb2, 1);
}
time = time2;
r = nr;
g = ng;
b = nb;
a = na;
r2 = nr2;
g2 = ng2;
b2 = nb2;
}
timelines.push(rgba2Timeline);
break;
case SLOT_RGB2:
bezierCount = input.readInt(true);
var rgb2Timeline : RGB2Timeline = new RGB2Timeline(frameCount, bezierCount, slotIndex);
time = input.readFloat();
r = input.readUnsignedByte() / 255.0;
g = input.readUnsignedByte() / 255.0;
b = input.readUnsignedByte() / 255.0;
r2 = input.readUnsignedByte() / 255.0;
g2 = input.readUnsignedByte() / 255.0;
b2 = input.readUnsignedByte() / 255.0;
for (frame = 0, bezier = 0;; frame++) {
rgb2Timeline.setFrame(frame, time, r, g, b, r2, g2, b2);
if (frame == frameLast) break;
time2 = input.readFloat();
nr = input.readUnsignedByte() / 255.0;
ng = input.readUnsignedByte() / 255.0;
nb = input.readUnsignedByte() / 255.0;
nr2 = input.readUnsignedByte() / 255.0;
ng2 = input.readUnsignedByte() / 255.0;
nb2 = input.readUnsignedByte() / 255.0;
switch (input.readByte()) {
case CURVE_STEPPED:
rgb2Timeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, rgb2Timeline, bezier++, frame, 0, time, time2, r, nr, 1);
setBezier(input, rgb2Timeline, bezier++, frame, 1, time, time2, g, ng, 1);
setBezier(input, rgb2Timeline, bezier++, frame, 2, time, time2, b, nb, 1);
setBezier(input, rgb2Timeline, bezier++, frame, 3, time, time2, r2, nr2, 1);
setBezier(input, rgb2Timeline, bezier++, frame, 4, time, time2, g2, ng2, 1);
setBezier(input, rgb2Timeline, bezier++, frame, 5, time, time2, b2, nb2, 1);
}
time = time2;
r = nr;
g = ng;
b = nb;
r2 = nr2;
g2 = ng2;
b2 = nb2;
}
timelines.push(rgb2Timeline);
break;
case SLOT_ALPHA:
var alphaTimeline : AlphaTimeline = new AlphaTimeline(frameCount, input.readInt(true), slotIndex);
time = input.readFloat();
a = input.readUnsignedByte() / 255;
for (frame = 0, bezier = 0;; frame++) {
alphaTimeline.setFrame(frame, time, a);
if (frame == frameLast) break;
time2 = input.readFloat();
a2 = input.readUnsignedByte() / 255;
switch (input.readByte()) {
case CURVE_STEPPED:
alphaTimeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, alphaTimeline, bezier++, frame, 0, time, time2, a, a2, 1);
}
time = time2;
a = a2;
}
timelines.push(alphaTimeline);
}
}
}
@ -619,42 +749,37 @@ package spine {
for (ii = 0, nn = input.readInt(true); ii < nn; ii++) {
timelineType = input.readByte();
frameCount = input.readInt(true);
frameIndex = 0;
bezierCount = input.readInt(true);
switch (timelineType) {
case SkeletonBinary.BONE_ROTATE: {
var rotateTimeline : RotateTimeline = new RotateTimeline(frameCount);
rotateTimeline.boneIndex = boneIndex;
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
rotateTimeline.setFrame(frameIndex, input.readFloat(), input.readFloat());
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, rotateTimeline);
}
timelines.push(rotateTimeline);
duration = Math.max(duration, rotateTimeline.frames[(frameCount - 1) * RotateTimeline.ENTRIES]);
case BONE_ROTATE:
timelines.push(readTimeline(input, new RotateTimeline(frameCount, bezierCount, boneIndex), 1));
break;
}
case SkeletonBinary.BONE_TRANSLATE:
case SkeletonBinary.BONE_SCALE:
case SkeletonBinary.BONE_SHEAR: {
var translateTimeline : TranslateTimeline;
timelineScale = 1;
if (timelineType == SkeletonBinary.BONE_SCALE)
translateTimeline = new ScaleTimeline(frameCount);
else if (timelineType == SkeletonBinary.BONE_SHEAR)
translateTimeline = new ShearTimeline(frameCount);
else {
translateTimeline = new TranslateTimeline(frameCount);
timelineScale = scale;
}
translateTimeline.boneIndex = boneIndex;
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
translateTimeline.setFrame(frameIndex, input.readFloat(), input.readFloat() * timelineScale,
input.readFloat() * timelineScale);
if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, translateTimeline);
}
timelines.push(translateTimeline);
duration = Math.max(duration, translateTimeline.frames[(frameCount - 1) * TranslateTimeline.ENTRIES]);
case BONE_TRANSLATE:
timelines.push(readTimeline2(input, new TranslateTimeline(frameCount, bezierCount, boneIndex), scale));
break;
}
case BONE_TRANSLATEX:
timelines.push(readTimeline(input, new TranslateXTimeline(frameCount, bezierCount, boneIndex), scale));
break;
case BONE_TRANSLATEY:
timelines.push(readTimeline(input, new TranslateYTimeline(frameCount, bezierCount, boneIndex), scale));
break;
case BONE_SCALE:
timelines.push(readTimeline2(input, new ScaleTimeline(frameCount, bezierCount, boneIndex), 1));
break;
case BONE_SCALEX:
timelines.push(readTimeline(input, new ScaleXTimeline(frameCount, bezierCount, boneIndex), 1));
break;
case BONE_SCALEY:
timelines.push(readTimeline(input, new ScaleYTimeline(frameCount, bezierCount, boneIndex), 1));
break;
case BONE_SHEAR:
timelines.push(readTimeline2(input, new ShearTimeline(frameCount, bezierCount, boneIndex), 1));
break;
case BONE_SHEARX:
timelines.push(readTimeline(input, new ShearXTimeline(frameCount, bezierCount, boneIndex), 1));
break;
case BONE_SHEARY:
timelines.push(readTimeline(input, new ShearYTimeline(frameCount, bezierCount, boneIndex), 1));
}
}
}
@ -663,31 +788,73 @@ package spine {
for (i = 0, n = input.readInt(true); i < n; i++) {
index = input.readInt(true);
frameCount = input.readInt(true);
var ikConstraintTimeline : IkConstraintTimeline = new IkConstraintTimeline(frameCount);
frameIndex = 0;
ikConstraintTimeline.ikConstraintIndex = index;
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
ikConstraintTimeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat() * scale, input.readByte(), input.readBoolean(),
input.readBoolean());
if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, ikConstraintTimeline);
frameLast = frameCount - 1;
var ikTimeline : IkConstraintTimeline = new IkConstraintTimeline(frameCount, input.readInt(true), index);
time = input.readFloat();
var mix : Number = input.readFloat(), softness : Number = input.readFloat() * scale;
for (frame = 0, bezier = 0;; frame++) {
ikTimeline.setFrame(frame, time, mix, softness, input.readByte(), input.readBoolean(), input.readBoolean());
if (frame == frameLast) break;
time2 = input.readFloat();
var mix2 : Number = input.readFloat(), softness2 : Number = input.readFloat() * scale;
switch (input.readByte()) {
case CURVE_STEPPED:
ikTimeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, ikTimeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
setBezier(input, ikTimeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
}
time = time2;
mix = mix2;
softness = softness2;
}
timelines.push(ikConstraintTimeline);
duration = Math.max(duration, ikConstraintTimeline.frames[(frameCount - 1) * IkConstraintTimeline.ENTRIES]);
timelines.push(ikTimeline);
}
// Transform constraint timelines.
var mixRotate : Number, mixRotate2 : Number;
var mixX : Number, mixX2 : Number;
var mixY : Number, mixY2 : Number;
for (i = 0, n = input.readInt(true); i < n; i++) {
index = input.readInt(true);
frameCount = input.readInt(true);
var transformConstraintTimeline : TransformConstraintTimeline = new TransformConstraintTimeline(frameCount);
transformConstraintTimeline.transformConstraintIndex = index;
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
transformConstraintTimeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(),
input.readFloat());
if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, transformConstraintTimeline);
frameLast = frameCount - 1;
var transformTimeline : TransformConstraintTimeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index);
time = input.readFloat();
mixRotate = input.readFloat();
mixX = input.readFloat();
mixY = input.readFloat();
var mixScaleX : Number = input.readFloat(), mixScaleY : Number = input.readFloat(), mixShearY : Number = input.readFloat();
for (frame = 0, bezier = 0;; frame++) {
transformTimeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
if (frame == frameLast) break;
time2 = input.readFloat()
mixRotate2 = input.readFloat();
mixX2 = input.readFloat();
mixY2 = input.readFloat();
var mixScaleX2 : Number = input.readFloat(), mixScaleY2 : Number = input.readFloat(), mixShearY2 : Number = input.readFloat();
switch (input.readByte()) {
case CURVE_STEPPED:
transformTimeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, transformTimeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
setBezier(input, transformTimeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
setBezier(input, transformTimeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
setBezier(input, transformTimeline, bezier++, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
setBezier(input, transformTimeline, bezier++, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
setBezier(input, transformTimeline, bezier++, frame, 5, time, time2, mixShearY, mixShearY2, 1);
}
time = time2;
mixRotate = mixRotate2;
mixX = mixX2;
mixY = mixY2;
mixScaleX = mixScaleX2;
mixScaleY = mixScaleY2;
mixShearY = mixShearY2;
}
timelines.push(transformConstraintTimeline);
duration = Math.max(duration, transformConstraintTimeline.frames[(frameCount - 1) * TransformConstraintTimeline.ENTRIES]);
timelines.push(transformTimeline);
}
// Path constraint timelines.
@ -695,40 +862,45 @@ package spine {
index = input.readInt(true);
var data : PathConstraintData = skeletonData.pathConstraints[index];
for (ii = 0, nn = input.readInt(true); ii < nn; ii++) {
timelineType = input.readByte();
frameCount = input.readInt(true);
switch (timelineType) {
case SkeletonBinary.PATH_POSITION:
case SkeletonBinary.PATH_SPACING: {
var pathConstraintPositionTimeline : PathConstraintPositionTimeline;
timelineScale = 1;
if (timelineType == SkeletonBinary.PATH_SPACING) {
pathConstraintPositionTimeline = new PathConstraintSpacingTimeline(frameCount);
if (data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed) timelineScale = scale;
} else {
pathConstraintPositionTimeline = new PathConstraintPositionTimeline(frameCount);
if (data.positionMode == PositionMode.fixed) timelineScale = scale;
}
pathConstraintPositionTimeline.pathConstraintIndex = index;
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
pathConstraintPositionTimeline.setFrame(frameIndex, input.readFloat(), input.readFloat() * timelineScale);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, pathConstraintPositionTimeline);
}
timelines.push(pathConstraintPositionTimeline);
duration = Math.max(duration, pathConstraintPositionTimeline.frames[(frameCount - 1) * PathConstraintPositionTimeline.ENTRIES]);
switch (input.readByte()) {
case PATH_POSITION:
timelines
.push(readTimeline(input, new PathConstraintPositionTimeline(input.readInt(true), input.readInt(true), index),
data.positionMode == PositionMode.fixed ? scale : 1));
break;
}
case SkeletonBinary.PATH_MIX: {
var pathConstraintMixTimeline : PathConstraintMixTimeline = new PathConstraintMixTimeline(frameCount);
pathConstraintMixTimeline.pathConstraintIndex = index;
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
pathConstraintMixTimeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat());
if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, pathConstraintMixTimeline);
}
timelines.push(pathConstraintMixTimeline);
duration = Math.max(duration, pathConstraintMixTimeline.frames[(frameCount - 1) * PathConstraintMixTimeline.ENTRIES]);
case PATH_SPACING:
timelines
.push(readTimeline(input, new PathConstraintSpacingTimeline(input.readInt(true), input.readInt(true), index),
data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1));
break;
}
case PATH_MIX:
var mixTimeline : PathConstraintMixTimeline = new PathConstraintMixTimeline(input.readInt(true), input.readInt(true), index);
time = input.readFloat();
mixRotate = input.readFloat();
mixX = input.readFloat();
mixY = input.readFloat();
for (frame = 0, bezier = 0, frameLast = mixTimeline.getFrameCount() - 1;; frame++) {
mixTimeline.setFrame(frame, time, mixRotate, mixX, mixY);
if (frame == frameLast) break;
time2 = input.readFloat();
mixRotate2 = input.readFloat();
mixX2 = input.readFloat();
mixY2 = input.readFloat();
switch (input.readByte()) {
case CURVE_STEPPED:
mixTimeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, mixTimeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
setBezier(input, mixTimeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
setBezier(input, mixTimeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
}
time = time2;
mixRotate = mixRotate2;
mixX = mixX2;
mixY = mixY2;
}
timelines.push(mixTimeline);
}
}
}
@ -739,18 +911,20 @@ package spine {
for (ii = 0, nn = input.readInt(true); ii < nn; ii++) {
slotIndex = input.readInt(true);
for (var iii : int = 0, nnn : int = input.readInt(true); iii < nnn; iii++) {
var attachment : VertexAttachment = skin.getAttachment(slotIndex, input.readStringRef()) as VertexAttachment;
var attachmentName : String = input.readStringRef();
var attachment : VertexAttachment = skin.getAttachment(slotIndex, attachmentName) as VertexAttachment;
if (attachment == null) throw Error("Vertex attachment not found: " + attachmentName);
var weighted : Boolean = attachment.bones != null;
var vertices : Vector.<Number> = attachment.vertices;
var deformLength : int = weighted ? vertices.length / 3 * 2 : vertices.length;
frameCount = input.readInt(true);
var deformTimeline : DeformTimeline= new DeformTimeline(frameCount);
deformTimeline.slotIndex = slotIndex;
deformTimeline.attachment = attachment;
frameLast = frameCount - 1;
bezierCount = input.readInt(true);
var deformTimeline : DeformTimeline = new DeformTimeline(frameCount, bezierCount, slotIndex, attachment);
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
time = input.readFloat();
time = input.readFloat();
for (frame = 0, bezier = 0;; frame++) {
var deform : Vector.<Number>;
var end : int = input.readInt(true);
if (end == 0) {
@ -778,16 +952,24 @@ package spine {
}
}
deformTimeline.setFrame(frameIndex, time, deform);
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, deformTimeline);
deformTimeline.setFrame(frame, time, deform);
if (frame == frameLast) break;
time2 = input.readFloat();
switch(input.readByte()) {
case CURVE_STEPPED:
deformTimeline.setStepped(frame);
break;
case CURVE_BEZIER:
SkeletonBinary.setBezier(input, deformTimeline, bezier++, frame, 0, time, time2, 0, 1, 1);
}
time = time2;
}
timelines.push(deformTimeline);
duration = Math.max(duration, deformTimeline.frames[frameCount - 1]);
}
}
}
// Draw order timeline.
// Draw order timelines.
var drawOrderCount : int = input.readInt(true);
if (drawOrderCount > 0) {
var drawOrderTimeline : DrawOrderTimeline = new DrawOrderTimeline(drawOrderCount);
@ -819,10 +1001,9 @@ package spine {
drawOrderTimeline.setFrame(i, time, drawOrder);
}
timelines.push(drawOrderTimeline);
duration = Math.max(duration, drawOrderTimeline.frames[drawOrderCount - 1]);
}
// Event timeline.
// Event timelines.
var eventCount : int = input.readInt(true);
if (eventCount > 0) {
var eventTimeline : EventTimeline = new EventTimeline(eventCount);
@ -840,27 +1021,58 @@ package spine {
eventTimeline.setFrame(i, event);
}
timelines.push(eventTimeline);
duration = Math.max(duration, eventTimeline.frames[eventCount - 1]);
}
var duration : Number = 0;
for (i = 0, n = timelines.length; i < n; i++)
duration = Math.max(duration, timelines[i].getDuration());
return new Animation(name, timelines, duration);
}
private function readCurve (input: BinaryInput, frameIndex: Number, timeline: CurveTimeline) : void {
switch (input.readByte()) {
case SkeletonBinary.CURVE_STEPPED:
timeline.setStepped(frameIndex);
break;
case SkeletonBinary.CURVE_BEZIER:
setCurve(timeline, frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat());
break;
static private function readTimeline (input: BinaryInput, timeline: CurveTimeline1, scale: Number) : CurveTimeline1 {
var time : Number = input.readFloat(), value : Number = input.readFloat() * scale;
for (var frame : int = 0, bezier : int = 0, frameLast : int = timeline.getFrameCount() - 1;; frame++) {
timeline.setFrame(frame, time, value);
if (frame == frameLast) break;
var time2 : Number = input.readFloat(), value2 : Number = input.readFloat() * scale;
switch (input.readByte()) {
case CURVE_STEPPED:
timeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, timeline, bezier++, frame, 0, time, time2, value, value2, 1);
}
time = time2;
value = value2;
}
return timeline;
}
public function setCurve (timeline: CurveTimeline, frameIndex: Number, cx1: Number, cy1: Number, cx2: Number, cy2: Number) : void {
timeline.setCurve(frameIndex, cx1, cy1, cx2, cy2);
static private function readTimeline2 (input: BinaryInput, timeline: CurveTimeline2, scale: Number) : CurveTimeline2 {
var time : Number = input.readFloat(), value1 : Number = input.readFloat() * scale, value2 : Number = input.readFloat() * scale;
for (var frame : int = 0, bezier : int = 0, frameLast : int = timeline.getFrameCount() - 1;; frame++) {
timeline.setFrame(frame, time, value1, value2);
if (frame == frameLast) break;
var time2 : Number = input.readFloat(), nvalue1 : Number = input.readFloat() * scale, nvalue2 : Number = input.readFloat() * scale;
switch (input.readByte()) {
case CURVE_STEPPED:
timeline.setStepped(frame);
break;
case CURVE_BEZIER:
setBezier(input, timeline, bezier++, frame, 0, time, time2, value1, nvalue1, scale);
setBezier(input, timeline, bezier++, frame, 1, time, time2, value2, nvalue2, scale);
}
time = time2;
value1 = nvalue1;
value2 = nvalue2;
}
return timeline;
}
static private function setBezier (input: BinaryInput, timeline: CurveTimeline, bezier: Number, frame: Number, value: Number,
time1: Number, time2: Number, value1: Number, value2: Number, scale: Number) : void {
timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat() * scale, input.readFloat(), input.readFloat() * scale, time2, value2);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,7 @@ package spine {
}
public function addSkin (skin: Skin) : void {
var i : Number = 0, j : Number = 0;
var i : int = 0, j : int = 0;
var contained : Boolean = false;
for(i = 0; i < skin._bones.length; i++) {
@ -88,7 +88,7 @@ package spine {
}
public function copySkin (skin: Skin) : void {
var i : Number = 0, j : Number = 0;
var i : int = 0, j : int = 0;
var contained : Boolean = false;
var attachment : SkinEntry;
@ -136,7 +136,7 @@ package spine {
return dictionary ? dictionary[name] : null;
}
public function removeAttachment (slotIndex : Number, name : String) : void {
public function removeAttachment(slotIndex : int, name : String) : void {
var dictionary : Dictionary = _attachments[slotIndex];
if (dictionary) dictionary[name] = null;
}

View File

@ -37,10 +37,9 @@ package spine {
public var darkColor : Color;
internal var _attachment : Attachment;
private var _attachmentTime : Number;
private var _attachmentState : Number;
public var attachmentState : int;
public var deform : Vector.<Number> = new Vector.<Number>();
public function Slot(data : SlotData, bone : Bone) {
if (data == null) throw new ArgumentError("data cannot be null.");
if (bone == null) throw new ArgumentError("bone cannot be null.");
@ -86,14 +85,6 @@ package spine {
return _bone._skeleton.time - _attachmentTime;
}
public function get attachmentState() : Number {
return _attachmentState;
}
public function set attachmentState(state : Number) : void {
_attachmentState = state;
}
public function setToSetupPose() : void {
color.setFromColor(data.color);
if (darkColor != null) darkColor.setFromColor(this.data.darkColor);

View File

@ -32,7 +32,8 @@ package spine {
public static const length : SpacingMode = new SpacingMode();
public static const fixed : SpacingMode = new SpacingMode();
public static const percent : SpacingMode = new SpacingMode();
public static const values : Array = [ length, fixed, percent ];
public static const proportional : SpacingMode = new SpacingMode();
public static const values : Array = [ length, fixed, percent, proportional ];
}
}

View File

@ -32,21 +32,23 @@ package spine {
internal var _data : TransformConstraintData;
internal var _bones : Vector.<Bone>;
public var target : Bone;
public var rotateMix : Number;
public var translateMix : Number;
public var scaleMix : Number;
public var shearMix : Number;
internal var _temp : Vector.<Number> = new Vector.<Number>(2);
public var mixRotate : Number;
public var mixX : Number, mixY : Number;
public var mixScaleX : Number, mixScaleY : Number;
public var mixShearY : Number;
internal var _temp : Vector.<Number> = new Vector.<Number>(2, true);
public var active : Boolean;
public function TransformConstraint(data : TransformConstraintData, skeleton : Skeleton) {
if (data == null) throw new ArgumentError("data cannot be null.");
if (skeleton == null) throw new ArgumentError("skeleton cannot be null.");
_data = data;
rotateMix = data.rotateMix;
translateMix = data.translateMix;
scaleMix = data.scaleMix;
shearMix = data.shearMix;
mixRotate = data.mixRotate;
mixX = data.mixX;
mixY = data.mixY;
mixScaleX = data.mixScaleX;
mixScaleY = data.mixScaleY;
mixShearY = data.mixShearY;
_bones = new Vector.<Bone>();
for each (var boneData : BoneData in data.bones)
_bones.push(skeleton.findBone(boneData.name));
@ -57,18 +59,16 @@ package spine {
return active;
}
public function apply() : void {
update();
}
public function update() : void {
if (data.local) {
if (data.relative)
if (mixRotate == 0 && mixX == 0 && mixY == 0 && mixScaleX == 0 && mixScaleX == 0 && mixShearY == 0) return;
if (_data.local) {
if (_data.relative)
applyRelativeLocal();
else
applyAbsoluteLocal();
} else {
if (data.relative)
if (_data.relative)
applyRelativeWorld();
else
applyAbsoluteWorld();
@ -76,172 +76,172 @@ package spine {
}
internal function applyAbsoluteWorld() : void {
var rotateMix : Number = this.rotateMix, translateMix : Number = this.translateMix, scaleMix : Number = this.scaleMix, shearMix : Number = this.shearMix;
var mixRotate : Number = this.mixRotate, mixX : Number = this.mixX, mixY : Number = this.mixY;
var mixScaleX : Number = this.mixScaleX, mixScaleY : Number = this.mixScaleY, mixShearY : Number = this.mixShearY;
var translate : Boolean = mixX != 0 || mixY != 0;
var target : Bone = this.target;
var ta : Number = target.a, tb : Number = target.b, tc : Number = target.c, td : Number = target.d;
var degRadReflect : Number = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad;
var offsetRotation : Number = data.offsetRotation * degRadReflect;
var offsetShearY : Number = data.offsetShearY * degRadReflect;
var bones : Vector.<Bone> = this._bones;
var offsetRotation : Number = _data.offsetRotation * degRadReflect;
var offsetShearY : Number = _data.offsetShearY * degRadReflect;
var bones : Vector.<Bone> = _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 (mixRotate != 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) + offsetRotation;
if (r > Math.PI)
r -= Math.PI * 2;
else if (r < -Math.PI) r += Math.PI * 2;
r *= rotateMix;
else if (r < -Math.PI) //
r += Math.PI * 2;
r *= mixRotate;
var cos : Number = Math.cos(r), sin : Number = Math.sin(r);
bone.a = cos * a - sin * c;
bone.b = cos * b - sin * d;
bone.c = sin * a + cos * c;
bone.d = sin * b + cos * d;
modified = true;
}
if (translateMix != 0) {
_temp[0] = data.offsetX;
_temp[1] = data.offsetY;
if (translate) {
_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;
bone.worldX += (_temp[0] - bone.worldX) * mixX;
bone.worldY += (_temp[1] - bone.worldY) * mixY;
}
if (scaleMix > 0) {
var s : Number = Math.sqrt(bone.a * bone.a + bone.c * bone.c);
var ts : Number = Math.sqrt(ta * ta + tc * tc);
if (s > 0.00001) s = (s + (ts - s + data.offsetScaleX) * scaleMix) / s;
var s : Number;
if (mixScaleX != 0) {
s = Math.sqrt(bone.a * bone.a + bone.c * bone.c);
if (s != 0) s = (s + (Math.sqrt(ta * ta + tc * tc) - s + _data.offsetScaleX) * mixScaleX) / s;
bone.a *= s;
bone.c *= s;
}
if (mixScaleY != 0) {
s = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
ts = Math.sqrt(tb * tb + td * td);
if (s > 0.00001) s = (s + (ts - s + data.offsetScaleY) * scaleMix) / s;
if (s != 0) s = (s + (Math.sqrt(tb * tb + td * td) - s + _data.offsetScaleY) * mixScaleY) / s;
bone.b *= s;
bone.d *= s;
modified = true;
}
if (shearMix > 0) {
b = bone.b
,
d = bone.d;
var by : Number = Math.atan2(d, b);
if (mixShearY > 0) {
var by : Number = Math.atan2(bone.d, bone.b);
r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(bone.c, bone.a));
if (r > Math.PI)
r -= Math.PI * 2;
else if (r < -Math.PI) r += Math.PI * 2;
r = by + (r + offsetShearY) * shearMix;
s = Math.sqrt(b * b + d * d);
else if (r < -Math.PI) //
r += Math.PI * 2;
r = by + (r + offsetShearY) * mixShearY;
s = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
bone.b = Math.cos(r) * s;
bone.d = Math.sin(r) * s;
modified = true;
}
if (modified) bone.appliedValid = false;
}
}
public function applyRelativeWorld() : void {
var rotateMix : Number = this.rotateMix, translateMix : Number = this.translateMix, scaleMix : Number = this.scaleMix, shearMix : Number = this.shearMix;
var mixRotate : Number = this.mixRotate, mixX : Number = this.mixX, mixY : Number = this.mixY;
var mixScaleX : Number = this.mixScaleX, mixScaleY : Number = this.mixScaleY, mixShearY : Number = this.mixShearY;
var translate : Boolean = mixX != 0 || mixY != 0;
var target : Bone = this.target;
var ta : Number = target.a, tb : Number = target.b, tc : Number = target.c, td : Number = target.d;
var degRadReflect : Number = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad;
var offsetRotation : Number = this.data.offsetRotation * degRadReflect, offsetShearY : Number = this.data.offsetShearY * degRadReflect;
var bones : Vector.<Bone> = this.bones;
var offsetRotation : Number = _data.offsetRotation * degRadReflect, offsetShearY : Number = _data.offsetShearY * degRadReflect;
var bones : Vector.<Bone> = _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 (mixRotate != 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) + offsetRotation;
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) r += MathUtils.PI2;
r *= rotateMix;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
r *= mixRotate;
var cos : Number = Math.cos(r), sin : Number = Math.sin(r);
bone.a = cos * a - sin * c;
bone.b = cos * b - sin * d;
bone.c = sin * a + cos * c;
bone.d = sin * b + cos * d;
modified = true;
}
if (translateMix != 0) {
var temp : Vector.<Number> = this._temp;
temp[0] = this._data.offsetX;
temp[1] = this._data.offsetY;
if (translate) {
var temp : Vector.<Number> = _temp;
temp[0] = _data.offsetX;
temp[1] = _data.offsetY;
target.localToWorld(temp);
bone.worldX += temp[0] * translateMix;
bone.worldY += temp[1] * translateMix;
modified = true;
bone.worldX += temp[0] * mixX;
bone.worldY += temp[1] * mixY;
}
if (scaleMix > 0) {
var s : Number = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * scaleMix + 1;
var s : Number;
if (mixScaleX != 0) {
s = (Math.sqrt(ta * ta + tc * tc) - 1 + _data.offsetScaleX) * mixScaleX + 1;
bone.a *= s;
bone.c *= s;
s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * scaleMix + 1;
}
if (mixScaleY != 0) {
s = (Math.sqrt(tb * tb + td * td) - 1 + _data.offsetScaleY) * mixScaleY + 1;
bone.b *= s;
bone.d *= s;
modified = true;
}
if (shearMix > 0) {
if (mixShearY > 0) {
r = Math.atan2(td, tb) - Math.atan2(tc, ta);
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) r += MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
b = bone.b;
d = bone.d;
r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * shearMix;
r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY;
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 applyAbsoluteLocal() : void {
var rotateMix : Number = this.rotateMix, translateMix : Number = this.translateMix, scaleMix : Number = this.scaleMix, shearMix : Number = this.shearMix;
var mixRotate : Number = this.mixRotate, mixX : Number = this.mixX, mixY : Number = this.mixY;
var mixScaleX : Number = this.mixScaleX, mixScaleY : Number = this.mixScaleY, mixShearY : Number = this.mixShearY;
var target : Bone = this.target;
if (!target.appliedValid) target.updateAppliedTransform();
var bones : Vector.<Bone> = this.bones;
var bones : Vector.<Bone> = _bones;
for (var i : int = 0, n : int = bones.length; i < n; i++) {
var bone : Bone = bones[i];
if (!bone.appliedValid) bone.updateAppliedTransform();
var rotation : Number = bone.arotation;
if (rotateMix != 0) {
var r : Number = target.arotation - rotation + this.data.offsetRotation;
if (mixRotate != 0) {
var r : Number = target.arotation - rotation + _data.offsetRotation;
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
rotation += r * rotateMix;
rotation += r * mixRotate;
}
var x : Number = bone.ax, y : Number = bone.ay;
if (translateMix != 0) {
x += (target.ax - x + this.data.offsetX) * translateMix;
y += (target.ay - y + this.data.offsetY) * translateMix;
}
x += (target.ax - x + _data.offsetX) * mixX;
y += (target.ay - y + _data.offsetY) * mixY;
var scaleX : Number = bone.ascaleX, scaleY : Number = bone.ascaleY;
if (scaleMix != 0) {
if (scaleX > 0.00001) scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * scaleMix) / scaleX;
if (scaleY > 0.00001) scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * scaleMix) / scaleY;
}
if (mixScaleX != 0 && scaleX != 0)
scaleX = (scaleX + (target.ascaleX - scaleX + _data.offsetScaleX) * mixScaleX) / scaleX;
if (mixScaleY != 0 && scaleY != 0)
scaleY = (scaleY + (target.ascaleY - scaleY + _data.offsetScaleY) * mixScaleY) / scaleY;
var shearY : Number = bone.ashearY;
if (shearMix != 0) {
r = target.ashearY - shearY + this.data.offsetShearY;
if (mixShearY != 0) {
r = target.ashearY - shearY + _data.offsetShearY;
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
bone.shearY += r * shearMix;
bone.shearY += r * mixShearY;
}
bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
@ -249,31 +249,23 @@ package spine {
}
public function applyRelativeLocal() : void {
var rotateMix : Number = this.rotateMix, translateMix : Number = this.translateMix, scaleMix : Number = this.scaleMix, shearMix : Number = this.shearMix;
var mixRotate : Number = this.mixRotate, mixX : Number = this.mixX, mixY : Number = this.mixY;
var mixScaleX : Number = this.mixScaleX, mixScaleY : Number = this.mixScaleY, mixShearY : Number = this.mixShearY;
var target : Bone = this.target;
if (!target.appliedValid) target.updateAppliedTransform();
var bones : Vector.<Bone> = this.bones;
var bones : Vector.<Bone> = _bones;
for (var i : int = 0, n : int = bones.length; i < n; i++) {
var bone : Bone = bones[i];
if (!bone.appliedValid) bone.updateAppliedTransform();
var rotation : Number = bone.arotation;
if (rotateMix != 0) rotation += (target.arotation + this.data.offsetRotation) * rotateMix;
var x : Number = bone.ax, y : Number = bone.ay;
if (translateMix != 0) {
x += (target.ax + this.data.offsetX) * translateMix;
y += (target.ay + this.data.offsetY) * translateMix;
}
var scaleX : Number = bone.ascaleX, scaleY : Number = bone.ascaleY;
if (scaleMix != 0) {
if (scaleX > 0.00001) scaleX *= ((target.ascaleX - 1 + this.data.offsetScaleX) * scaleMix) + 1;
if (scaleY > 0.00001) scaleY *= ((target.ascaleY - 1 + this.data.offsetScaleY) * scaleMix) + 1;
}
var shearY : Number = bone.ashearY;
if (shearMix != 0) shearY += (target.ashearY + this.data.offsetShearY) * shearMix;
var rotation : Number = bone.arotation + (target.arotation + _data.offsetRotation) * mixRotate;
var x : Number = bone.ax + (target.ax + _data.offsetX) * mixX;
var y : Number = bone.ay + (target.ay + _data.offsetY) * mixY;
var scaleX : Number = (bone.ascaleX * ((target.ascaleX - 1 + _data.offsetScaleX) * mixScaleX) + 1);
var scaleY : Number = (bone.ascaleY * ((target.ascaleY - 1 + _data.offsetScaleY) * mixScaleY) + 1);
var shearY : Number = bone.ashearY + (target.ashearY + _data.offsetShearY) * mixShearY;
bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
}

View File

@ -31,16 +31,17 @@ package spine {
public class TransformConstraintData extends ConstraintData {
internal var _bones : Vector.<BoneData> = new Vector.<BoneData>();
public var target : BoneData;
public var rotateMix : Number;
public var translateMix : Number;
public var scaleMix : Number;
public var shearMix : Number;
public var mixRotate : Number;
public var mixX : Number, mixY : Number;
public var mixScaleX : Number, mixScaleY : Number;
public var mixShearY : Number;
public var offsetRotation : Number;
public var offsetX : Number;
public var offsetY : Number;
public var offsetScaleX : Number;
public var offsetScaleY : Number;
public var offsetX : Number, offsetY : Number;
public var offsetScaleX : Number, offsetScaleY : Number;
public var offsetShearY : Number;
public var relative : Boolean = false;
public var local : Boolean = false;

View File

@ -0,0 +1,85 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Color;
import spine.Event;
import spine.Skeleton;
import spine.Slot;
public class AlphaTimeline extends CurveTimeline1 implements SlotTimeline {
static internal const ENTRIES : Number = 4;
static internal const R : Number = 1;
static internal const G : Number = 2;
static internal const B : Number = 3;
private var slotIndex : int;
public function AlphaTimeline (frameCount : Number, bezierCount : Number, slotIndex : Number) {
super(frameCount, bezierCount, [
Property.alpha + "|" + slotIndex
]);
this.slotIndex = slotIndex;
}
public override function getFrameEntries() : int {
return ENTRIES;
}
public function getSlotIndex() : int {
return slotIndex;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var slot : Slot = skeleton.slots[slotIndex];
if (!slot.bone.active) return;
var color : Color = slot.color;
if (time < frames[0]) { // Time is before first frame.
var setup : Color = slot.data.color;
switch (blend) {
case MixBlend.setup:
color.a = setup.a;
return;
case MixBlend.first:
color.a += (setup.a - color.a) * alpha;
}
return;
}
var a : Number = getCurveValue(time);
if (alpha == 1)
color.a = a;
else {
if (blend == MixBlend.setup) color.a = slot.data.color.a;
color.a += (a - color.a) * alpha;
}
}
}
}

View File

@ -34,7 +34,7 @@ package spine.animation {
public class Animation {
internal var _name : String;
public var _timelines : Vector.<Timeline>;
private var _timelines : Vector.<Timeline>;
internal var _timelineIds : Dictionary = new Dictionary();
public var duration : Number;
@ -43,17 +43,18 @@ package spine.animation {
if (timelines == null) throw new ArgumentError("timelines cannot be null.");
_name = name;
_timelines = timelines;
for (var i : Number = 0; i < timelines.length; i++)
_timelineIds[timelines[i].getPropertyId()] = true;
for (var i : int = 0, n : int = timelines.length; i < n; i++) {
var ids : Vector.<String> = timelines[i].propertyIds;
for (var ii : int = 0, nn : int = ids.length; ii < nn; ii++)
_timelineIds[ids[ii]] = true;
}
this.duration = duration;
}
public function hasTimeline(id: Number) : Boolean {
return _timelineIds[id] == true;
}
public function get timelines() : Vector.<Timeline> {
return _timelines;
public function hasTimeline(ids : Vector.<String>) : Boolean {
for (var i : int = 0, n : int = ids.length; i < n; i++)
if (_timelineIds[ids[i]]) return true;
return false;
}
/** Poses the skeleton at the specified time for this animation. */
@ -77,49 +78,8 @@ package spine.animation {
return _name;
}
/** @param target After the first and before the last entry. */
static public function binarySearch(values : Vector.<Number>, target : Number, step : int) : int {
var low : int = 0;
var high : int = values.length / step - 2;
if (high == 0)
return step;
var current : int = high >>> 1;
while (true) {
if (values[int((current + 1) * step)] <= target)
low = current + 1;
else
high = current;
if (low == high)
return (low + 1) * step;
current = (low + high) >>> 1;
}
return 0; // Can't happen.
}
/** @param target After the first and before the last entry. */
static public function binarySearch1(values : Vector.<Number>, target : Number) : int {
var low : int = 0;
var high : int = values.length - 2;
if (high == 0)
return 1;
var current : int = high >>> 1;
while (true) {
if (values[int(current + 1)] <= target)
low = current + 1;
else
high = current;
if (low == high)
return low + 1;
current = (low + high) >>> 1;
}
return 0; // Can't happen.
}
static public function linearSearch(values : Vector.<Number>, target : Number, step : int) : int {
for (var i : int = 0, last : int = values.length - step; i <= last; i += step)
if (values[i] > target)
return i;
return -1;
public function get timelines() : Vector.<Timeline> {
return _timelines;
}
}
}

View File

@ -28,24 +28,20 @@
*****************************************************************************/
package spine.animation {
import spine.Bone;
import spine.Event;
import spine.MathUtils;
import spine.Pool;
import spine.Skeleton;
import spine.*;
import flash.utils.Dictionary;
import spine.Slot;
public class AnimationState {
public static var SUBSEQUENT : int = 0;
public static var FIRST : int = 1;
public static var HOLD_SUBSEQUENT : int = 2;
public static var HOLD_FIRST : int = 3;
public static var HOLD_MIX : int = 4;
public static var SETUP : int = 1;
public static var CURRENT : int = 2;
static private var SUBSEQUENT : int = 0;
static private var FIRST : int = 1;
static private var HOLD_SUBSEQUENT : int = 2;
static private var HOLD_FIRST : int = 3;
static private var HOLD_MIX : int = 4;
static private var SETUP : int = 1;
static private var CURRENT : int = 2;
static private var emptyAnimation : Animation = new Animation("<empty>", new Vector.<Timeline>(), 0);
internal static var emptyAnimation : Animation = new Animation("<empty>", new Vector.<Timeline>(), 0);
public var data : AnimationStateData;
public var tracks : Vector.<TrackEntry> = new Vector.<TrackEntry>();
internal var events : Vector.<Event> = new Vector.<Event>();
@ -56,7 +52,7 @@ package spine.animation {
public var onComplete : Listeners = new Listeners();
public var onEvent : Listeners = new Listeners();
internal var queue : EventQueue;
internal var propertyIDs : Dictionary = new Dictionary();
internal var propertyIDs : StringSet = new StringSet();
internal var mixingTo : Vector.<TrackEntry> = new Vector.<TrackEntry>();
internal var animationsChanged : Boolean;
public var timeScale : Number = 1;
@ -178,7 +174,12 @@ package spine.animation {
mix = 0;
// Apply current entry.
var animationLast : Number = current.animationLast, animationTime : Number = current.getAnimationTime();
var animationLast : Number = current.animationLast, animationTime : Number = current.getAnimationTime(), applyTime : Number = animationTime;
var applyEvents : Vector.<Event> = events;
if (current.reverse) {
applyTime = current.animation.duration - applyTime;
applyEvents = null;
}
var timelineCount : int = current.animation.timelines.length;
var timelines : Vector.<Timeline> = current.animation.timelines;
var ii : int = 0;
@ -186,11 +187,10 @@ package spine.animation {
if ((i == 0 && mix == 1) || blend == MixBlend.add) {
for (ii = 0; ii < timelineCount; ii++) {
timeline = timelines[ii];
if (timeline is AttachmentTimeline) {
applyAttachmentTimeline(AttachmentTimeline(timeline), skeleton, animationTime, blend, true);
} else {
timeline.apply(skeleton, animationLast, animationTime, events, mix, blend, MixDirection.In);
}
if (timeline is AttachmentTimeline)
applyAttachmentTimeline(AttachmentTimeline(timeline), skeleton, applyTime, blend, true);
else
timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection.mixIn);
}
} else {
var timelineMode : Vector.<int> = current.timelineMode;
@ -202,12 +202,12 @@ package spine.animation {
for (ii = 0; ii < timelineCount; ii++) {
timeline = timelines[ii];
var timelineBlend : MixBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup;
if (timeline is RotateTimeline) {
applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
} else if (timeline is AttachmentTimeline) {
applyAttachmentTimeline(AttachmentTimeline(timeline), skeleton, animationTime, timelineBlend, true);
} else
timeline.apply(skeleton, animationLast, animationTime, events, mix, timelineBlend, MixDirection.In);
if (timeline is RotateTimeline)
applyRotateTimeline(timeline, skeleton, applyTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
else if (timeline is AttachmentTimeline)
applyAttachmentTimeline(AttachmentTimeline(timeline), skeleton, applyTime, timelineBlend, true);
else
timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend, MixDirection.mixIn);
}
}
queueEvents(current, animationTime);
@ -225,7 +225,7 @@ package spine.animation {
var slot : Slot = slots[si];
if (slot.attachmentState == setupState) {
var attachmentName : String = slot.data.attachmentName;
slot.attachment = (attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slot.data.index, attachmentName));
slot.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slot.data.index, attachmentName);
}
}
this.unkeyedState += 2; // Increasing after each use avoids the need to reset attachmentState for every slot.
@ -248,17 +248,21 @@ package spine.animation {
if (blend != MixBlend.first) blend = from.mixBlend;
}
var events : Vector.<Event> = mix < from.eventThreshold ? this.events : null;
var attachments : Boolean = mix < from.attachmentThreshold, drawOrder : Boolean = mix < from.drawOrderThreshold;
var animationLast : Number = from.animationLast, animationTime : Number = from.getAnimationTime();
var timelineCount : int = from.animation.timelines.length;
var timelines : Vector.<Timeline> = from.animation.timelines;
var alphaHold : Number = from.alpha * to.interruptAlpha;
var alphaMix : Number = alphaHold * (1 - mix);
var alphaHold : Number = from.alpha * to.interruptAlpha, alphaMix : Number = alphaHold * (1 - mix);
var animationLast : Number = from.animationLast, animationTime : Number = from.getAnimationTime(), applyTime : Number = animationTime;
var events : Vector.<Event> = null;
if (from.reverse)
applyTime = from.animation.duration - applyTime;
else if (mix < from.eventThreshold)
events = this.events;
var i : int = 0;
if (blend == MixBlend.add) {
for (i = 0; i < timelineCount; i++)
timelines[i].apply(skeleton, animationLast, animationTime, events, alphaMix, blend, MixDirection.Out);
timelines[i].apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.mixOut);
} else {
var timelineMode : Vector.<int> = from.timelineMode;
var timelineHoldMix : Vector.<TrackEntry> = from.timelineHoldMix;
@ -270,7 +274,7 @@ package spine.animation {
from.totalAlpha = 0;
for (i = 0; i < timelineCount; i++) {
var timeline : Timeline = timelines[i];
var direction : MixDirection = MixDirection.Out;
var direction : MixDirection = MixDirection.mixOut;
var timelineBlend: MixBlend;
var alpha : Number = 0;
switch (timelineMode[i]) {
@ -299,12 +303,12 @@ package spine.animation {
}
from.totalAlpha += alpha;
if (timeline is RotateTimeline)
applyRotateTimeline(timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame);
applyRotateTimeline(timeline, skeleton, applyTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame);
else if (timeline is AttachmentTimeline) {
applyAttachmentTimeline(AttachmentTimeline(timeline), skeleton, animationTime, timelineBlend, attachments);
applyAttachmentTimeline(AttachmentTimeline(timeline), skeleton, applyTime, timelineBlend, attachments);
} else {
if (drawOrder && timeline is DrawOrderTimeline && timelineBlend == MixBlend.setup) direction = MixDirection.In;
timeline.apply(skeleton, animationLast, animationTime, events, alpha, timelineBlend, direction);
if (drawOrder && timeline is DrawOrderTimeline && timelineBlend == MixBlend.setup) direction = MixDirection.mixIn;
timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction);
}
}
}
@ -318,22 +322,15 @@ package spine.animation {
}
private function applyAttachmentTimeline (timeline: AttachmentTimeline, skeleton: Skeleton, time: Number, blend: MixBlend, attachments: Boolean) : void {
var slot : Slot = skeleton.slots[timeline.slotIndex];
var slot : Slot = skeleton.slots[timeline.getSlotIndex()];
if (!slot.bone.active) return;
var frames : Vector.<Number> = timeline.frames;
if (time < frames[0]) { // Time is before first frame.
if (blend == MixBlend.setup || blend == MixBlend.first)
setAttachment(skeleton, slot, slot.data.attachmentName, attachments);
}
else {
var frameIndex : Number;
if (time >= frames[frames.length - 1]) // Time is after last frame.
frameIndex = frames.length - 1;
else
frameIndex = Animation.binarySearch1(frames, time) - 1;
setAttachment(skeleton, slot, timeline.attachmentNames[frameIndex], attachments);
}
} else
setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search(frames, time)], attachments);
// If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later.
if (slot.attachmentState <= unkeyedState) slot.attachmentState = unkeyedState + SETUP;
@ -348,14 +345,14 @@ package spine.animation {
if (firstFrame) timelinesRotation[i] = 0;
if (alpha == 1) {
timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.In);
timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn);
return;
}
var rotateTimeline : RotateTimeline = RotateTimeline(timeline);
var frames : Vector.<Number> = rotateTimeline.frames;
var bone : Bone = skeleton.bones[rotateTimeline.boneIndex];
var bone : Bone = skeleton.bones[rotateTimeline.getBoneIndex()];
if (!bone.active) return;
var frames : Vector.<Number> = rotateTimeline.frames;
var r1 : Number, r2 : Number;
if (time < frames[0]) {
switch (blend) {
@ -369,20 +366,7 @@ package spine.animation {
}
} else {
r1 = blend == MixBlend.setup ? bone.data.rotation : bone.rotation;
if (time >= frames[frames.length - RotateTimeline.ENTRIES]) // Time is after last frame.
r2 = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION];
else {
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES);
var prevRotation : Number = frames[frame + RotateTimeline.PREV_ROTATION];
var frameTime : Number = frames[frame];
var percent : Number = rotateTimeline.getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime));
r2 = frames[frame + RotateTimeline.ROTATION] - prevRotation;
r2 -= (16384 - int((16384.499999999996 - r2 / 360))) * 360;
r2 = prevRotation + r2 * percent + bone.data.rotation;
r2 -= (16384 - int((16384.499999999996 - r2 / 360))) * 360;
}
r2 = bone.data.rotation + rotateTimeline.getCurveValue(time);
}
// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
@ -411,8 +395,7 @@ package spine.animation {
timelinesRotation[i] = total;
}
timelinesRotation[i + 1] = diff;
r1 += total * alpha;
bone.rotation = r1 - (16384 - int((16384.499999999996 - r1 / 360))) * 360;
bone.rotation = r1 + total * alpha;
}
private function queueEvents(entry : TrackEntry, animationTime : Number) : void {
@ -443,7 +426,7 @@ package spine.animation {
for (; i < n; i++) {
event = events[i];
if (event.time < animationStart) continue; // Discard events outside animation start/end.
queue.event(entry, events[i]);
queue.event(entry, event);
}
}
@ -481,9 +464,15 @@ package spine.animation {
queue.drain();
}
/** Removes the {@link TrackEntry#getNext() next entry} and all entries after it for the specified entry. */
private function clearNext(entry : TrackEntry) : void {
disposeNext(entry.next);
}
private function setCurrent(index : int, current : TrackEntry, interrupt : Boolean) : void {
var from : TrackEntry = expandToIndex(index);
tracks[index] = current;
current.previous = null;
if (from != null) {
if (interrupt) queue.interrupt(from);
@ -551,16 +540,8 @@ package spine.animation {
queue.drain();
} else {
last.next = entry;
if (delay <= 0) {
var duration : Number = last.animationEnd - last.animationStart;
if (duration != 0) {
if (last.loop)
delay += duration * (1 + (int)(last.trackTime / duration));
else
delay += Math.max(duration, last.trackTime);
} else
delay = last.trackTime;
}
entry.previous = last;
if (delay <= 0) delay += last.getTrackComplete() - entry.mixDuration;
}
entry.delay = delay;
@ -575,10 +556,10 @@ package spine.animation {
}
public function addEmptyAnimation(trackIndex : int, mixDuration : Number, delay : Number) : TrackEntry {
if (delay <= 0) delay -= mixDuration;
var entry : TrackEntry = addAnimation(trackIndex, emptyAnimation, false, delay);
var entry : TrackEntry = addAnimation(trackIndex, emptyAnimation, false, delay <= 0 ? 1 : delay);
entry.mixDuration = mixDuration;
entry.trackEnd = mixDuration;
if (delay <= 0 && entry.previous != null) entry.delay = entry.previous.getTrackComplete() - entry.mixDuration;
return entry;
}
@ -641,7 +622,7 @@ package spine.animation {
private function _animationsChanged() : void {
animationsChanged = false;
propertyIDs = new Dictionary();
propertyIDs.clear();
var i : int = 0;
var n: int = 0;
var entry : TrackEntry = null;
@ -658,52 +639,44 @@ package spine.animation {
}
private function computeHold (entry: TrackEntry) : void {
var to: TrackEntry = entry.mixingTo;
var to : TrackEntry = entry.mixingTo;
var timelines : Vector.<Timeline> = entry.animation.timelines;
var timelinesCount : int = entry.animation.timelines.length;
var timelineMode : Vector.<int> = entry.timelineMode;
timelineMode.length = timelinesCount;
entry.timelineHoldMix.length = 0;
var timelineHoldMix : Vector.<TrackEntry> = entry.timelineHoldMix;
timelineHoldMix.length = 0;
var propertyIDs: Dictionary = this.propertyIDs;
timelineHoldMix.length = timelinesCount;
var propertyIDs : StringSet = this.propertyIDs;
var i : int;
var i : int = 0;
if (to != null && to.holdPrevious) {
for (i = 0; i < timelinesCount; i++) {
if (!propertyIDs[timelines[i].getPropertyId().toString()]) {
timelineMode[i] = HOLD_FIRST;
} else {
timelineMode[i] = HOLD_SUBSEQUENT;
}
propertyIDs[timelines[i].getPropertyId().toString()] = true;
}
for (i = 0; i < timelinesCount; i++)
timelineMode[i] = propertyIDs.addAll(timelines[i].propertyIds) ? HOLD_FIRST : HOLD_SUBSEQUENT;
return;
}
outer:
for (i = 0; i < timelinesCount; i++) {
var timeline : Timeline = Timeline(timelines[i]);
var intId : int = timeline.getPropertyId();
var id : String = intId.toString();
var contained: Object = propertyIDs[id];
propertyIDs[id] = true;
if (contained != null) {
timelineMode[i] = AnimationState.SUBSEQUENT;
} else if (to == null || timeline is AttachmentTimeline || timeline is DrawOrderTimeline
|| timeline is EventTimeline || !to.animation.hasTimeline(intId)) {
timelineMode[i] = AnimationState.FIRST;
} else {
var timeline : Timeline = timelines[i];
var ids : Vector.<String> = timeline.propertyIds;
if (!propertyIDs.addAll(ids))
timelineMode[i] = SUBSEQUENT;
else if (to == null || timeline is AttachmentTimeline || timeline is DrawOrderTimeline
|| timeline is EventTimeline || !to.animation.hasTimeline(ids)) {
timelineMode[i] = FIRST;
} else {
for (var next : TrackEntry = to.mixingTo; next != null; next = next.mixingTo) {
if (next.animation.hasTimeline(intId)) continue;
if (next.animation.hasTimeline(ids)) continue;
if (entry.mixDuration > 0) {
timelineMode[i] = AnimationState.HOLD_MIX;
timelineHoldMix[i] = entry;
timelineMode[i] = HOLD_MIX;
timelineHoldMix[i] = next;
continue outer;
}
break;
}
timelineMode[i] = AnimationState.HOLD_FIRST;
timelineMode[i] = HOLD_FIRST;
}
}
}
@ -727,3 +700,36 @@ package spine.animation {
}
}
}
import flash.utils.Dictionary;
class StringSet {
private var entries : Dictionary = new Dictionary();
private var size : int = 0;
public function add (value : String): Boolean {
var contains : Boolean = entries[value];
entries[value] = true;
if (!contains) {
size++;
return true;
}
return false;
}
public function addAll (values : Vector.<String>) : Boolean {
var oldSize : int = size;
for (var i : int = 0, n : int = values.length; i < n; i++)
add(values[i]);
return oldSize != size;
}
public function contains (value : String) : Boolean {
return entries[value];
}
public function clear () : void {
entries = new Dictionary();
size = 0;
}
}

View File

@ -32,57 +32,52 @@ package spine.animation {
import spine.Event;
import spine.Skeleton;
public class AttachmentTimeline implements Timeline {
public var slotIndex : int;
public var frames : Vector.<Number>; // time, ...
public class AttachmentTimeline extends Timeline implements SlotTimeline {
private var slotIndex : int;
/** The attachment name for each key frame. May contain null values to clear the attachment. */
public var attachmentNames : Vector.<String>;
public function AttachmentTimeline(frameCount : int) {
frames = new Vector.<Number>(frameCount, true);
public function AttachmentTimeline (frameCount : int, slotIndex : int) {
super(frameCount, [
Property.attachment + "|" + slotIndex
]);
this.slotIndex = slotIndex;
attachmentNames = new Vector.<String>(frameCount, true);
}
public function get frameCount() : int {
public override function getFrameCount () : int {
return frames.length;
}
public function getPropertyId() : int {
return (TimelineType.attachment.ordinal << 24) + slotIndex;
public function getSlotIndex() : int {
return slotIndex;
}
/** Sets the time and value of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, attachmentName : String) : void {
frames[frameIndex] = time;
attachmentNames[frameIndex] = attachmentName;
/** Sets the time in seconds and the attachment name for the specified key frame. */
public function setFrame (frame : int, time : Number, attachmentName : String) : void {
frames[frame] = time;
attachmentNames[frame] = attachmentName;
}
public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var attachmentName : String;
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var slot : Slot = skeleton.slots[slotIndex];
if (!slot.bone.active) return;
if (direction == MixDirection.Out) {
if (direction == MixDirection.mixOut) {
if (blend == MixBlend.setup) setAttachment(skeleton, slot, slot.data.attachmentName);
return;
}
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
if (blend == MixBlend.setup || blend == MixBlend.first) {
setAttachment(skeleton, slot, slot.data.attachmentName);
}
if (blend == MixBlend.setup || blend == MixBlend.first) setAttachment(skeleton, slot, slot.data.attachmentName);
return;
}
var frameIndex : int;
if (time >= frames[frames.length - 1]) // Time is after last frame.
frameIndex = frames.length - 1;
else
frameIndex = Animation.binarySearch(frames, time, 1) - 1;
attachmentName = attachmentNames[frameIndex];
skeleton.slots[slotIndex].attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slotIndex, attachmentName);
setAttachment(skeleton, slot, attachmentNames[search(frames, time)]);
}
private function setAttachment(skeleton: Skeleton, slot: Slot, attachmentName: String) : void {
private function setAttachment(skeleton : Skeleton, slot : Slot, attachmentName : String) : void {
slot.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slotIndex, attachmentName);
}
}

View File

@ -0,0 +1,34 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
public interface BoneTimeline {
function getBoneIndex() : int;
}
}

View File

@ -1,115 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Color;
import spine.Event;
import spine.Skeleton;
import spine.Slot;
public class ColorTimeline extends CurveTimeline {
static public const ENTRIES : int = 5;
static internal const PREV_TIME : int = -5, PREV_R : int = -4, PREV_G : int = -3, PREV_B : int = -2, PREV_A : int = -1;
static internal const R : int = 1, G : int = 2, B : int = 3, A : int = 4;
public var slotIndex : int;
public var frames : Vector.<Number>; // time, r, g, b, a, ...
public function ColorTimeline(frameCount : int) {
super(frameCount);
frames = new Vector.<Number>(frameCount * 5, true);
}
override public function getPropertyId() : int {
return (TimelineType.color.ordinal << 24) + slotIndex;
}
/** Sets the time and value of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, r : Number, g : Number, b : Number, a : Number) : void {
frameIndex *= ENTRIES;
frames[frameIndex] = time;
frames[int(frameIndex + R)] = r;
frames[int(frameIndex + G)] = g;
frames[int(frameIndex + B)] = b;
frames[int(frameIndex + A)] = a;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var frames : Vector.<Number> = this.frames;
var slot : Slot = skeleton.slots[slotIndex];
if (!slot.bone.active) return;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
slot.color.setFromColor(slot.data.color);
return;
case MixBlend.first:
var color : Color = slot.color, setup : Color = slot.data.color;
color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha,
(setup.a - color.a) * alpha);
}
return;
}
var r : Number, g : Number, b : Number, a : Number;
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
var i : int = frames.length;
r = frames[i + PREV_R];
g = frames[i + PREV_G];
b = frames[i + PREV_B];
a = frames[i + PREV_A];
} else {
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
r = frames[frame + PREV_R];
g = frames[frame + PREV_G];
b = frames[frame + PREV_B];
a = frames[frame + PREV_A];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
r += (frames[frame + R] - r) * percent;
g += (frames[frame + G] - g) * percent;
b += (frames[frame + B] - b) * percent;
a += (frames[frame + A] - a) * percent;
}
if (alpha == 1) {
slot.color.setFrom(r, g, b, a);
} else {
if (blend == MixBlend.setup) {
slot.color.setFromColor(slot.data.color);
}
slot.color.r += (r - slot.color.r) * alpha;
slot.color.g += (g - slot.color.g) * alpha;
slot.color.b += (b - slot.color.b) * alpha;
slot.color.a += (a - slot.color.a) * alpha;
}
}
}
}

View File

@ -28,92 +28,104 @@
*****************************************************************************/
package spine.animation {
import spine.MathUtils;
import spine.Event;
import spine.Skeleton;
/** The base class for timelines that interpolate between frame values using stepped, linear, or a Bezier curve. */
public class CurveTimeline extends Timeline {
static internal const LINEAR : Number = 0;
static internal const STEPPED : Number = 1;
static internal const BEZIER : Number = 2;
static internal const BEZIER_SIZE : int = 18;
/** Base class for frames that use an interpolation bezier curve. */
public class CurveTimeline implements Timeline {
static private const LINEAR : Number = 0;
static private const STEPPED : Number = 1;
static private const BEZIER : Number = 2;
static private const BEZIER_SIZE : int = 10 * 2 - 1;
private var curves : Vector.<Number>; // type, x, y, ...
internal var curves : Vector.<Number>; // type, x, y, ...
public function CurveTimeline(frameCount : int) {
curves = new Vector.<Number>((frameCount - 1) * BEZIER_SIZE, true);
public function CurveTimeline(frameCount : int, bezierCount : int, propertyIds : Array) {
super(frameCount, propertyIds);
curves = new Vector.<Number>(frameCount + bezierCount * BEZIER_SIZE, true);
curves[frameCount - 1] = STEPPED;
}
public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
/** Sets the specified key frame to linear interpolation. */
public function setLinear(frame : int) : void {
curves[frame] = LINEAR;
}
public function getPropertyId() : int {
return 0;
/** Sets the specified key frame to stepped interpolation. */
public function setStepped(frame : int) : void{
curves[frame] = STEPPED;
}
public function get frameCount() : int {
return curves.length / BEZIER_SIZE + 1;
}
public function setLinear(frameIndex : int) : void {
curves[int(frameIndex * BEZIER_SIZE)] = LINEAR;
}
public function setStepped(frameIndex : int) : void {
curves[int(frameIndex * BEZIER_SIZE)] = STEPPED;
}
/** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
* cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
* the difference between the keyframe's values. */
public function setCurve(frameIndex : int, cx1 : Number, cy1 : Number, cx2 : Number, cy2 : Number) : void {
var tmpx : Number = (-cx1 * 2 + cx2) * 0.03, tmpy : Number = (-cy1 * 2 + cy2) * 0.03;
var dddfx : Number = ((cx1 - cx2) * 3 + 1) * 0.006, dddfy : Number = ((cy1 - cy2) * 3 + 1) * 0.006;
var ddfx : Number = tmpx * 2 + dddfx, ddfy : Number = tmpy * 2 + dddfy;
var dfx : Number = cx1 * 0.3 + tmpx + dddfx * 0.16666667, dfy : Number = cy1 * 0.3 + tmpy + dddfy * 0.16666667;
var i : int = frameIndex * BEZIER_SIZE;
/** Shrinks the storage for Bezier curves, for use when <code>bezierCount</code> (specified in the constructor) was larger
* than the actual number of Bezier curves. */
public function shrink(bezierCount : int) : void {
var size : int = getFrameCount() + bezierCount * BEZIER_SIZE;
var curves : Vector.<Number> = this.curves;
curves[int(i++)] = BEZIER;
var x : Number = dfx, y : Number = dfy;
for (var n : int = i + BEZIER_SIZE - 1; i < n; i += 2) {
curves[i] = x;
curves[int(i + 1)] = y;
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
x += dfx;
y += dfy;
if (curves.length > size) {
var newCurves : Vector.<Number> = new Vector.<Number>(size, true);
for (var i : int = 0; i < size; i++)
newCurves[i] = curves[i];
curves = newCurves;
}
}
public function getCurvePercent(frameIndex : int, percent : Number) : Number {
percent = MathUtils.clamp(percent, 0, 1);
/** Stores the segments for the specified Bezier curve. For timelines that modify multiple values, there may be more than
* one curve per frame.
* @param bezier The ordinal of this Bezier curve for this timeline, between 0 and <code>bezierCount - 1</code> (specified
* in the constructor), inclusive.
* @param frame Between 0 and <code>frameCount - 1</code>, inclusive.
* @param value The index of the value for this frame that this curve is used for.
* @param time1 The time for the first key.
* @param value1 The value for the first key.
* @param cx1 The time for the first Bezier handle.
* @param cy1 The value for the first Bezier handle.
* @param cx2 The time of the second Bezier handle.
* @param cy2 The value for the second Bezier handle.
* @param time2 The time for the second key.
* @param value2 The value for the second key. */
public function setBezier(bezier : int, frame : int, value : Number, time1 : Number, value1 : Number, cx1 : Number, cy1 : Number, cx2 : Number,
cy2 : Number, time2 : Number, value2 : Number) : void {
var curves : Vector.<Number> = this.curves;
var i : int = frameIndex * BEZIER_SIZE;
var type : Number = curves[i];
if (type == LINEAR) return percent;
if (type == STEPPED) return 0;
i++;
var x : Number = 0;
for (var start : int = i, n : int = i + BEZIER_SIZE - 1; i < n; i += 2) {
x = curves[i];
if (x >= percent) {
var prevX : Number, prevY : Number;
if (i == start) {
prevX = 0;
prevY = 0;
} else {
prevX = curves[int(i - 2)];
prevY = curves[int(i - 1)];
}
return prevY + (curves[int(i + 1)] - prevY) * (percent - prevX) / (x - prevX);
var i : int = getFrameCount() + bezier * BEZIER_SIZE;
if (value == 0) curves[frame] = BEZIER + i;
var tmpx : Number = (time1 - cx1 * 2 + cx2) * 0.03, tmpy : Number = (value1 - cy1 * 2 + cy2) * 0.03;
var dddx : Number = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy : Number = ((cy1 - cy2) * 3 - value1 + value2) * 0.006;
var ddx : Number = tmpx * 2 + dddx, ddy : Number = tmpy * 2 + dddy;
var dx : Number = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy : Number = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667;
var x : Number = time1 + dx, y : Number = value1 + dy;
for (var n : int = i + BEZIER_SIZE; i < n; i += 2) {
curves[i] = x;
curves[i + 1] = y;
dx += ddx;
dy += ddy;
ddx += dddx;
ddy += dddy;
x += dx;
y += dy;
}
}
/** Returns the Bezier interpolated value for the specified time.
* @param frameIndex The index into {@link #getFrames()} for the values of the frame before <code>time</code>.
* @param valueOffset The offset from <code>frameIndex</code> to the value this curve is used for.
* @param i The index of the Bezier segments. See {@link #getCurveType(int)}. */
public function getBezierValue(time : Number, frameIndex : int, valueOffset : int, i : int) : Number {
var curves : Vector.<Number> = this.curves;
var x : Number, y : Number;
if (curves[i] > time) {
x = frames[frameIndex];
y = frames[frameIndex + valueOffset];
return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
}
var n : int = i + BEZIER_SIZE;
for (i += 2; i < n; i += 2) {
if (curves[i] >= time) {
x = curves[i - 2];
y = curves[i - 1];
return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
}
}
var y : Number = curves[int(i - 1)];
return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1.
frameIndex += getFrameEntries();
x = curves[n - 2];
y = curves[n - 1];
return y + (time - x) / (frames[frameIndex] - x) * (frames[frameIndex + valueOffset] - y);
}
}
}

View File

@ -0,0 +1,77 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
/** The base class for a {@link CurveTimeline} that sets one property. */
public class CurveTimeline1 extends CurveTimeline {
static private const ENTRIES : Number = 2;
static private const VALUE : Number = 1;
/** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(int)}.
* @param propertyIds Unique identifiers for the properties the timeline modifies. */
public function CurveTimeline1 (frameCount : int, bezierCount : int, propertyIds : Array) {
super(frameCount, bezierCount, propertyIds);
}
public override function getFrameEntries() : int {
return ENTRIES;
}
/** Sets the time and values for the specified frame.
* @param frame Between 0 and <code>frameCount</code>, inclusive.
* @param time The frame time in seconds. */
public function setFrame(frame : int, time : Number, value1 : Number) : void {
frame <<= 1;
frames[frame] = time;
frames[frame + VALUE] = value1;
}
/** Returns the interpolated value for the specified time. */
public function getCurveValue(time : Number) : Number {
var frames : Vector.<Number> = this.frames;
var i : int = frames.length - 2;
for (var ii : int = 2; ii <= i; ii += 2) {
if (frames[ii] > time) {
i = ii - 2;
break;
}
}
var curveType : Number = curves[i >> 1];
switch (curveType) {
case LINEAR:
var before : Number = frames[i], value : Number = frames[i + VALUE];
return value + (time - before) / (frames[i + ENTRIES] - before) * (frames[i + ENTRIES + VALUE] - value);
case STEPPED:
return frames[i + VALUE];
}
return getBezierValue(time, i, VALUE, curveType - BEZIER);
}
}
}

View File

@ -1,54 +1,58 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
public class TimelineType {
public var ordinal : int;
public function TimelineType(order : int) {
this.ordinal = order;
}
public static const rotate : TimelineType = new TimelineType(0);
public static const translate : TimelineType = new TimelineType(1);
public static const scale : TimelineType = new TimelineType(2);
public static const shear : TimelineType = new TimelineType(3);
public static const attachment : TimelineType = new TimelineType(4);
public static const color : TimelineType = new TimelineType(5);
public static const deform : TimelineType = new TimelineType(6);
public static const event : TimelineType = new TimelineType(7);
public static const drawOrder : TimelineType = new TimelineType(8);
public static const ikConstraint : TimelineType = new TimelineType(9);
public static const transformConstraint : TimelineType = new TimelineType(10);
public static const pathConstraintPosition : TimelineType = new TimelineType(11);
public static const pathConstraintSpacing : TimelineType = new TimelineType(12);
public static const pathConstraintMix : TimelineType = new TimelineType(13);
public static const twoColor : TimelineType = new TimelineType(14);
}
}
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
/** The base class for a {@link CurveTimeline} which sets two properties. */
public class CurveTimeline2 extends CurveTimeline {
static internal const ENTRIES : Number = 3;
static internal const VALUE1 : Number = 1;
static internal const VALUE2 : Number = 2;
/** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(int)}.
* @param propertyIds Unique identifiers for the properties the timeline modifies. */
public function CurveTimeline2 (frameCount : int, bezierCount : int, propertyIds : Array) {
super(frameCount, bezierCount, propertyIds);
}
public override function getFrameEntries() : int {
return ENTRIES;
}
/** Sets the time and values for the specified frame.
* @param frame Between 0 and <code>frameCount</code>, inclusive.
* @param time The frame time in seconds. */
public function setFrame(frame : int, time : Number, value1 : Number, value2 : Number) : void {
frame *= ENTRIES;
var frames : Vector.<Number> = this.frames;
frames[frame] = time;
frames[frame + VALUE1] = value1;
frames[frame + VALUE2] = value2;
}
}
}

View File

@ -34,58 +34,119 @@ package spine.animation {
import spine.Skeleton;
import spine.Slot;
public class DeformTimeline extends CurveTimeline {
public class DeformTimeline extends CurveTimeline implements SlotTimeline {
public var slotIndex : int;
public var frames : Vector.<Number>;
public var frameVertices : Vector.<Vector.<Number>>;
/** The attachment that will be deformed. */
public var attachment : VertexAttachment;
public function DeformTimeline(frameCount : int) {
super(frameCount);
frames = new Vector.<Number>(frameCount, true);
frameVertices = new Vector.<Vector.<Number>>(frameCount, true);
/** The vertices for each key frame. */
public var vertices : Vector.<Vector.<Number>>;
public function DeformTimeline (frameCount : int, bezierCount : int, slotIndex : int, attachment : VertexAttachment) {
super(frameCount, bezierCount, [
Property.deform + "|" + slotIndex + "|" + attachment.id
]);
this.slotIndex = slotIndex;
this.attachment = attachment;
vertices = new Vector.<Vector.<Number>>(frameCount, true);
}
override public function getPropertyId() : int {
return (TimelineType.deform.ordinal << 27) + attachment.id + slotIndex;
public override function getFrameCount () : int {
return frames.length;
}
/** Sets the time and value of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, vertices : Vector.<Number>) : void {
frames[frameIndex] = time;
frameVertices[frameIndex] = vertices;
public function getSlotIndex() : int {
return slotIndex;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var vertexAttachment : VertexAttachment;
var setupVertices : Vector.<Number>;
/** Sets the time in seconds and the vertices for the specified key frame.
* @param vertices Vertex positions for an unweighted VertexAttachment, or deform offsets if it has weights. */
public function setFrame (frame : int, time : Number, vertices : Vector.<Number>) : void {
frames[frame] = time;
this.vertices[frame] = vertices;
}
/** @param value1 Ignored (0 is used for a deform timeline).
* @param value2 Ignored (1 is used for a deform timeline). */
public override function setBezier (bezier : int, frame: int, value : Number, time1 : Number, value1 : Number, cx1 : Number, cy1: Number, cx2 : Number,
cy2 : Number, time2 : Number, value2 : Number) : void {
var curves : Vector.<Number> = this.curves;
var i : int = getFrameCount() + bezier * BEZIER_SIZE;
if (value == 0) curves[frame] = BEZIER + i;
var tmpx : Number = (time1 - cx1 * 2 + cx2) * 0.03, tmpy : Number = cy2 * 0.03 - cy1 * 0.06;
var dddx : Number = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy : Number = (cy1 - cy2 + 0.33333333) * 0.018;
var ddx : Number = tmpx * 2 + dddx, ddy : Number = tmpy * 2 + dddy;
var dx : Number = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy : Number = cy1 * 0.3 + tmpy + dddy * 0.16666667;
var x : Number = time1 + dx, y : Number = dy;
for (var n : int = i + BEZIER_SIZE; i < n; i += 2) {
curves[i] = x;
curves[i + 1] = y;
dx += ddx;
dy += ddy;
ddx += dddx;
ddy += dddy;
x += dx;
y += dy;
}
}
private function getCurvePercent (time : Number, frame : int) : Number {
var curves : Vector.<Number> = this.curves;
var i : int = curves[frame];
var x : Number;
switch (i) {
case LINEAR:
x = frames[frame];
return (time - x) / (frames[frame + getFrameEntries()] - x);
case STEPPED:
return 0;
}
i -= BEZIER;
if (curves[i] > time) {
x = frames[frame];
return curves[i + 1] * (time - x) / (curves[i] - x);
}
var n : int = i + BEZIER_SIZE, y : Number;
for (i += 2; i < n; i += 2) {
if (curves[i] >= time) {
x = curves[i - 2];
y = curves[i - 1];
return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
}
}
x = curves[n - 2];
y = curves[n - 1];
return y + (1 - y) * (time - x) / (frames[frame + getFrameEntries()] - x);
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var slot : Slot = skeleton.slots[slotIndex];
if (!slot.bone.active) return;
var slotAttachment : Attachment = slot.attachment;
if (!(slotAttachment is VertexAttachment) || !(VertexAttachment(slotAttachment).deformAttachment == attachment)) return;
if (!(slotAttachment is VertexAttachment) || VertexAttachment(slotAttachment).deformAttachment != attachment) return;
var vertexAttachment : VertexAttachment = VertexAttachment(slotAttachment);
var deformArray : Vector.<Number> = slot.deform;
if (deformArray.length == 0) blend = MixBlend.setup;
var deform : Vector.<Number> = slot.deform;
if (deform.length == 0) blend = MixBlend.setup;
var frameVertices : Vector.<Vector.<Number>> = this.frameVertices;
var vertexCount : int = frameVertices[0].length;
var deform : Vector.<Number>;
var vertices : Vector.<Vector.<Number>> = this.vertices;
var vertexCount : int = vertices[0].length;
var i : int, setupVertices : Vector.<Number>;
var frames : Vector.<Number> = this.frames;
var i : int;
if (time < frames[0]) {
vertexAttachment = VertexAttachment(slotAttachment);
switch (blend) {
case MixBlend.setup:
deformArray.length = 0;
deform.length = 0;
return;
case MixBlend.first:
if (alpha == 1) {
deformArray.length = 0;
deform.length = 0;
return;
}
deformArray.length = vertexCount;
deform = deformArray;
deform.length = vertexCount;
if (vertexAttachment.bones == null) {
// Unweighted vertex positions.
setupVertices = vertexAttachment.vertices;
@ -101,82 +162,82 @@ package spine.animation {
return;
}
deformArray.length = vertexCount;
deform = deformArray;
var n : int;
var setup : Number, prev : Number;
deform.length = vertexCount;
var setup : Number;
if (time >= frames[frames.length - 1]) { // Time is after last frame.
var lastVertices : Vector.<Number> = frameVertices[frames.length - 1];
var lastVertices : Vector.<Number> = vertices[frames.length - 1];
if (alpha == 1) {
if (blend == MixBlend.add) {
vertexAttachment = VertexAttachment(slotAttachment);
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
setupVertices = vertexAttachment.vertices;
for (i = 0; i < vertexCount; i++) {
for (i = 0; i < vertexCount; i++)
deform[i] += lastVertices[i] - setupVertices[i];
}
} else {
// Weighted deform offsets, with alpha.
for (i = 0; i < vertexCount; i++)
deform[i] += lastVertices[i];
}
} else {
for (i = 0, n = vertexCount; i < n; i++)
for (i = 0; i < vertexCount; i++)
deform[i] = lastVertices[i];
}
} else {
} else {
switch (blend) {
case MixBlend.setup:
vertexAttachment = VertexAttachment(slotAttachment);
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
setupVertices = vertexAttachment.vertices;
for (i = 0; i < vertexCount; i++) {
setup = setupVertices[i];
deform[i] = setup + (lastVertices[i] - setup) * alpha;
}
} else {
// Weighted deform offsets, with alpha.
for (i = 0; i < vertexCount; i++)
deform[i] = lastVertices[i] * alpha;
case MixBlend.setup: {
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
setupVertices = vertexAttachment.vertices;
for (i = 0; i < vertexCount; i++) {
setup = setupVertices[i];
deform[i] = setup + (lastVertices[i] - setup) * alpha;
}
break;
case MixBlend.first:
case MixBlend.replace:
} else {
// Weighted deform offsets, with alpha.
for (i = 0; i < vertexCount; i++)
deform[i] += (lastVertices[i] - deform[i]) * alpha;
case MixBlend.add:
vertexAttachment = VertexAttachment(slotAttachment);
if (vertexAttachment.bones == null) {
setupVertices = vertexAttachment.vertices;
for (i = 0; i < vertexCount; i++) {
deform[i] += (lastVertices[i] - setupVertices[i]) * alpha;
}
} else {
for (i = 0; i < vertexCount; i++)
deform[i] += lastVertices[i] * alpha;
}
deform[i] = lastVertices[i] * alpha;
}
break;
}
case MixBlend.first:
case MixBlend.replace:
for (i = 0; i < vertexCount; i++)
deform[i] += (lastVertices[i] - deform[i]) * alpha;
break;
case MixBlend.add:
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
setupVertices = vertexAttachment.vertices;
for (i = 0; i < vertexCount; i++)
deform[i] += (lastVertices[i] - setupVertices[i]) * alpha;
} else {
// Weighted deform offsets, with alpha.
for (i = 0; i < vertexCount; i++)
deform[i] += lastVertices[i] * alpha;
}
}
}
return;
}
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch1(frames, time);
var prevVertices : Vector.<Number> = frameVertices[frame - 1];
var nextVertices : Vector.<Number> = frameVertices[frame];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));
var frame : int = search(frames, time);
var percent : Number = getCurvePercent(time, frame);
var prevVertices : Vector.<Number> = vertices[frame], prev : Number;
var nextVertices : Vector.<Number> = vertices[frame + 1];
if (alpha == 1) {
if (blend == MixBlend.add) {
vertexAttachment = VertexAttachment(slotAttachment);
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
setupVertices = vertexAttachment.vertices;
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i];
deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i];
}
} else {
// Weighted deform offsets, with alpha.
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i];
deform[i] += prev + (nextVertices[i] - prev) * percent;
@ -190,44 +251,46 @@ package spine.animation {
}
} else {
switch (blend) {
case MixBlend.setup:
vertexAttachment = VertexAttachment(slotAttachment);
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
setupVertices = vertexAttachment.vertices;
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i], setup = setupVertices[i];
deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
}
} else {
// Weighted deform offsets, with alpha.
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i];
deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
}
}
break;
case MixBlend.first:
case MixBlend.replace:
case MixBlend.setup: {
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
setupVertices = vertexAttachment.vertices;
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i];
deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha;
setup = setupVertices[i];
deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
}
break;
case MixBlend.add:
vertexAttachment = VertexAttachment(slotAttachment);
if (vertexAttachment.bones == null) {
setupVertices = vertexAttachment.vertices;
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i], setup = setupVertices[i];
deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha;
}
} else {
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i];
deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha;
}
} else {
// Weighted deform offsets, with alpha.
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i];
deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
}
}
break;
}
case MixBlend.first:
case MixBlend.replace:
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i];
deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha;
}
break;
case MixBlend.add:
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
setupVertices = vertexAttachment.vertices;
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i];
deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha;
}
} else {
// Weighted deform offsets, with alpha.
for (i = 0; i < vertexCount; i++) {
prev = prevVertices[i];
deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha;
}
}
}
}
}

View File

@ -32,12 +32,13 @@ package spine.animation {
import spine.Skeleton;
import spine.Slot;
public class DrawOrderTimeline implements Timeline {
public var frames : Vector.<Number>; // time, ...
public class DrawOrderTimeline extends Timeline {
public var drawOrders : Vector.<Vector.<int>>;
public function DrawOrderTimeline(frameCount : int) {
frames = new Vector.<Number>(frameCount, true);
super(frameCount, [
Property.drawOrder
]);
drawOrders = new Vector.<Vector.<int>>(frameCount, true);
}
@ -45,51 +46,42 @@ package spine.animation {
return frames.length;
}
public function getPropertyId() : int {
return TimelineType.drawOrder.ordinal << 24;
/** Sets the time in seconds and the draw order for the specified key frame.
* @param drawOrder For each slot in {@link Skeleton#slots}, the index of the new draw order. May be null to use setup pose
* draw order. */
public function setFrame(frame : int, time : Number, drawOrder : Vector.<int>) : void {
frames[frame] = time;
drawOrders[frame] = drawOrder;
}
/** Sets the time and value of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, drawOrder : Vector.<int>) : void {
frames[frameIndex] = time;
drawOrders[frameIndex] = drawOrder;
}
public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
if (direction == MixDirection.Out) {
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var drawOrder: Vector.<Slot> = skeleton.drawOrder;
var slots : Vector.<Slot> = skeleton.slots;
var i : int = 0, n : int = slots.length;
if (direction == MixDirection.mixOut) {
if (blend == MixBlend.setup) {
for (var ii : int = 0, n : int = skeleton.slots.length; ii < n; ii++)
skeleton.drawOrder[ii] = skeleton.slots[ii];
for (i = 0; i < n; i++)
drawOrder[i] = slots[i];
}
return;
}
var drawOrder : Vector.<Slot> = skeleton.drawOrder;
var slots : Vector.<Slot> = skeleton.slots;
var slot : Slot;
var i : int = 0;
if (time < frames[0]) {
if (blend == MixBlend.setup || blend == MixBlend.first) {
for each (slot in slots)
drawOrder[i++] = slot;
for (i = 0; i < n; i++)
drawOrder[i] = slots[i];
}
return;
}
var frameIndex : int;
if (time >= frames[int(frames.length - 1)]) // Time is after last frame.
frameIndex = frames.length - 1;
else
frameIndex = Animation.binarySearch1(frames, time) - 1;
var drawOrderToSetupIndex : Vector.<int> = drawOrders[frameIndex];
i = 0;
if (!drawOrderToSetupIndex) {
for each (slot in slots)
drawOrder[i++] = slot;
var drawOrderToSetupIndex : Vector.<int> = drawOrders[search(frames, time)];
if (drawOrderToSetupIndex == null) {
for (i = 0; i < n; i++)
drawOrder[i] = slots[i];
} else {
for each (var setupIndex : int in drawOrderToSetupIndex)
drawOrder[i++] = slots[setupIndex];
for (i = 0; i < n; i++)
drawOrder[i] = slots[drawOrderToSetupIndex[i]];
}
}
}

View File

@ -31,53 +31,53 @@ package spine.animation {
import spine.Event;
import spine.Skeleton;
public class EventTimeline implements Timeline {
public var frames : Vector.<Number>; // time, ...
public class EventTimeline extends Timeline {
public var events : Vector.<Event>;
public function EventTimeline(frameCount : int) {
frames = new Vector.<Number>(frameCount, true);
super(frameCount, [
Property.event
]);
events = new Vector.<Event>(frameCount, true);
}
public function get frameCount() : int {
public override function getFrameCount () : int {
return frames.length;
}
public function getPropertyId() : int {
return TimelineType.event.ordinal << 24;
/** Sets the time in seconds and the event for the specified key frame. */
public function setFrame (frame : int, event : Event) : void {
frames[frame] = event.time;
events[frame] = event;
}
/** Sets the time and value of the specified keyframe. */
public function setFrame(frameIndex : int, event : Event) : void {
frames[frameIndex] = event.time;
events[frameIndex] = event;
}
/** Fires events for frames > `lastTime` and <= `time`. */
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
if (firedEvents == null) return;
/** Fires events for frames > lastTime and <= time. */
public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
if (!firedEvents) return;
var frames : Vector.<Number> = this.frames;
var frameCount : int = frames.length;
if (lastTime > time) { // Fire events after last time for looped animations.
apply(skeleton, lastTime, int.MAX_VALUE, firedEvents, alpha, blend, direction);
apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha, blend, direction);
lastTime = -1;
} else if (lastTime >= frames[int(frameCount - 1)]) // Last time is after last frame.
} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
return;
if (time < frames[0]) return; // Time is before first frame.
var frame : int;
var i : int = 0;
if (lastTime < frames[0])
frame = 0;
i = 0;
else {
frame = Animation.binarySearch1(frames, lastTime);
var frameTime : Number = frames[frame];
while (frame > 0) { // Fire multiple events with the same frame.
if (frames[int(frame - 1)] != frameTime) break;
frame--;
i = search(frames, lastTime) + 1;
var frameTime : Number = frames[i];
while (i > 0) { // Fire multiple events with the same frame.
if (frames[i - 1] != frameTime) break;
i--;
}
}
for (; frame < frameCount && time >= frames[frame]; frame++)
firedEvents[firedEvents.length] = events[frame];
for (; i < frameCount && time >= frames[i]; i++)
firedEvents.push(events[i]);
}
}
}

View File

@ -33,35 +33,39 @@ package spine.animation {
import spine.Skeleton;
public class IkConstraintTimeline extends CurveTimeline {
static public const ENTRIES : int = 6;
static internal const PREV_TIME : int = -6, PREV_MIX : int = -5, PREV_SOFTNESS : int = -4, PREV_BEND_DIRECTION : int = -3, PREV_COMPRESS : int = -2, PREV_STRETCH : int = -1;
static internal const ENTRIES : int = 6;
static internal const MIX : int = 1, SOFTNESS : int = 2, BEND_DIRECTION : int = 3, COMPRESS : int = 4, STRETCH : int = 5;
/** The index of the IK constraint slot in {@link Skeleton#ikConstraints} that will be changed. */
public var ikConstraintIndex : int;
public var frames : Vector.<Number>; // time, mix, bendDirection, compress, stretch, ...
public function IkConstraintTimeline(frameCount : int) {
super(frameCount);
frames = new Vector.<Number>(frameCount * ENTRIES, true);
public function IkConstraintTimeline(frameCount : int, bezierCount : int, ikConstraintIndex : int) {
super(frameCount, bezierCount, [
Property.ikConstraint + "|" + ikConstraintIndex
]);
this.ikConstraintIndex = ikConstraintIndex;
}
override public function getPropertyId() : int {
return (TimelineType.ikConstraint.ordinal << 24) + ikConstraintIndex;
public override function getFrameEntries() : int {
return ENTRIES;
}
/** Sets the time, mix and bend direction of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, mix : Number, softness: Number, bendDirection : int, compress: Boolean, stretch: Boolean) : void {
frameIndex *= ENTRIES;
frames[frameIndex] = time;
frames[int(frameIndex + MIX)] = mix;
frames[int(frameIndex + SOFTNESS)] = softness;
frames[int(frameIndex + BEND_DIRECTION)] = bendDirection;
frames[int(frameIndex + COMPRESS)] = compress ? 1 : 0;
frames[int(frameIndex + STRETCH)] = stretch ? 1 : 0;
/** Sets the time in seconds, mix, softness, bend direction, compress, and stretch for the specified key frame. */
public function setFrame (frame : int, time : Number, mix : Number, softness : Number, bendDirection : int, compress: Boolean, stretch : Boolean) : void {
frame *= ENTRIES;
frames[frame] = time;
frames[frame + MIX] = mix;
frames[frame + SOFTNESS] = softness;
frames[frame + BEND_DIRECTION] = bendDirection;
frames[frame + COMPRESS] = compress ? 1 : 0;
frames[frame + STRETCH] = stretch ? 1 : 0;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var constraint : IkConstraint = skeleton.ikConstraints[ikConstraintIndex];
if (!constraint.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
@ -81,59 +85,47 @@ package spine.animation {
return;
}
if (time >= frames[int(frames.length - ENTRIES)]) { // Time is after last frame.
if (blend == MixBlend.setup) {
constraint.mix = constraint.data.mix + (frames[frames.length + PREV_MIX] - constraint.data.mix) * alpha;
constraint.softness = constraint.data.softness
+ (frames[frames.length + PREV_SOFTNESS] - constraint.data.softness) * alpha;
if (direction == MixDirection.Out) {
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
} else {
constraint.bendDirection = int(frames[frames.length + PREV_BEND_DIRECTION]);
constraint.compress = int(frames[frames.length + PREV_COMPRESS]) != 0;
constraint.stretch = int(frames[frames.length + PREV_STRETCH]) != 0;
}
} else {
constraint.mix += (frames[frames.length + PREV_MIX] - constraint.mix) * alpha;
constraint.softness += (frames[frames.length + PREV_SOFTNESS] - constraint.softness) * alpha;
if (direction == MixDirection.In) {
constraint.bendDirection = int(frames[frames.length + PREV_BEND_DIRECTION]);
constraint.compress = int(frames[frames.length + PREV_COMPRESS]) != 0;
constraint.stretch = int(frames[frames.length + PREV_STRETCH]) != 0;
}
}
return;
var mix : Number = 0, softness : Number = 0;
var i : int = search2(frames, time, ENTRIES)
var curveType : Number = curves[i / ENTRIES];
switch (curveType) {
case LINEAR:
var before : Number = frames[i];
mix = frames[i + MIX];
softness = frames[i + SOFTNESS];
var t : Number = (time - before) / (frames[i + ENTRIES] - before);
mix += (frames[i + ENTRIES + MIX] - mix) * t;
softness += (frames[i + ENTRIES + SOFTNESS] - softness) * t;
break;
case STEPPED:
mix = frames[i + MIX];
softness = frames[i + SOFTNESS];
break;
default:
mix = getBezierValue(time, i, MIX, curveType - BEZIER);
softness = getBezierValue(time, i, SOFTNESS, curveType + BEZIER_SIZE - BEZIER);
}
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
var mix : Number = frames[int(frame + PREV_MIX)];
var softness : Number = frames[frame + PREV_SOFTNESS];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
if (blend == MixBlend.setup) {
constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
constraint.softness = constraint.data.softness
+ (softness + (frames[frame + SOFTNESS] - softness) * percent - constraint.data.softness) * alpha;
if (direction == MixDirection.Out) {
constraint.mix = constraint.data.mix + (mix - constraint.data.mix) * alpha;
constraint.softness = constraint.data.softness + (softness - constraint.data.softness) * alpha;
if (direction == MixDirection.mixOut) {
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
} else {
constraint.bendDirection = int(frames[frame + PREV_BEND_DIRECTION]);
constraint.compress = int(frames[frame + PREV_COMPRESS]) != 0;
constraint.stretch = int(frames[frame + PREV_STRETCH]) != 0;
constraint.bendDirection = frames[i + BEND_DIRECTION];
constraint.compress = frames[i + COMPRESS] != 0;
constraint.stretch = frames[i + STRETCH] != 0;
}
} else {
constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
constraint.softness += (softness + (frames[frame + SOFTNESS] - softness) * percent - constraint.softness) * alpha;
if (direction == MixDirection.In) {
constraint.bendDirection = int(frames[frame + PREV_BEND_DIRECTION]);
constraint.compress = int(frames[frame + PREV_COMPRESS]) != 0;
constraint.stretch = int(frames[frame + PREV_STRETCH]) != 0;
constraint.mix += (mix - constraint.mix) * alpha;
constraint.softness += (softness - constraint.softness) * alpha;
if (direction == MixDirection.mixIn) {
constraint.bendDirection = frames[i + BEND_DIRECTION];
constraint.compress = frames[i + COMPRESS] != 0;
constraint.stretch = frames[i + STRETCH] != 0;
}
}
}

View File

@ -31,8 +31,8 @@ package spine.animation {
public class MixBlend {
public var ordinal : int;
public function MixBlend(order : int) {
this.ordinal = order;
public function MixBlend(ordinal : int) {
this.ordinal = ordinal;
}
public static const setup : MixBlend = new MixBlend(0);

View File

@ -31,11 +31,11 @@ package spine.animation {
public class MixDirection {
public var ordinal : int;
public function MixDirection(order : int) {
this.ordinal = order;
public function MixDirection(ordinal : int) {
this.ordinal = ordinal;
}
public static const In : MixDirection = new MixDirection(0);
public static const Out : MixDirection = new MixDirection(1);
public static const mixIn : MixDirection = new MixDirection(0);
public static const mixOut : MixDirection = new MixDirection(1);
}
}

View File

@ -31,69 +31,91 @@ package spine.animation {
import spine.Event;
import spine.Skeleton;
import spine.PathConstraint;
import spine.PathConstraintData;
public class PathConstraintMixTimeline extends CurveTimeline {
static public const ENTRIES : int = 3;
static internal const PREV_TIME : int = -3, PREV_ROTATE : int = -2, PREV_TRANSLATE : int = -1;
static internal const ROTATE : int = 1, TRANSLATE : int = 2;
static internal const ENTRIES : int = 4;
static internal const ROTATE : int = 1, X : int = 2, Y : int = 3;
/** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed. */
public var pathConstraintIndex : int;
public var frames : Vector.<Number>; // time, rotate mix, translate mix, ...
public function PathConstraintMixTimeline(frameCount : int) {
super(frameCount);
frames = new Vector.<Number>(frameCount * ENTRIES, true);
public function PathConstraintMixTimeline (frameCount : int, bezierCount : int, pathConstraintIndex : int) {
super(frameCount, bezierCount, [
Property.pathConstraintMix + "|" + pathConstraintIndex
]);
this.pathConstraintIndex = pathConstraintIndex;
}
override public function getPropertyId() : int {
return (TimelineType.pathConstraintMix.ordinal << 24) + pathConstraintIndex;
public override function getFrameEntries() : int {
return ENTRIES;
}
/** Sets the time and mixes of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, rotateMix : Number, translateMix : Number) : void {
frameIndex *= ENTRIES;
frames[frameIndex] = time;
frames[frameIndex + ROTATE] = rotateMix;
frames[frameIndex + TRANSLATE] = translateMix;
public function setFrame (frame : int, time : Number, mixRotate : Number, mixX : Number, mixY : Number) : void {
frame <<= 2;
frames[frame] = time;
frames[frame + ROTATE] = mixRotate;
frames[frame + X] = mixX;
frames[frame + Y] = mixY;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var constraint : PathConstraint = skeleton.pathConstraints[pathConstraintIndex];
if (!constraint.active) return;
var data : PathConstraintData;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
data = constraint.data;
switch (blend) {
case MixBlend.setup:
constraint.rotateMix = constraint.data.rotateMix;
constraint.translateMix = constraint.data.translateMix;
constraint.mixRotate = data.mixRotate;
constraint.mixX = data.mixX;
constraint.mixY = data.mixY;
return;
case MixBlend.first:
constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha;
constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha;
constraint.mixRotate += (data.mixRotate - constraint.mixRotate) * alpha;
constraint.mixX += (data.mixX - constraint.mixX) * alpha;
constraint.mixY += (data.mixY - constraint.mixY) * alpha;
}
return;
}
var rotate : Number, translate : Number;
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
rotate = frames[frames.length + PREV_ROTATE];
translate = frames[frames.length + PREV_TRANSLATE];
} else {
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
rotate = frames[frame + PREV_ROTATE];
translate = frames[frame + PREV_TRANSLATE];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
rotate += (frames[frame + ROTATE] - rotate) * percent;
translate += (frames[frame + TRANSLATE] - translate) * percent;
var rotate : Number, x : Number, y : Number;
var i : int = search2(frames, time, ENTRIES);
var curveType : Number = curves[i >> 2];
switch (curveType) {
case LINEAR:
var before : Number = frames[i];
rotate = frames[i + ROTATE];
x = frames[i + X];
y = frames[i + Y];
var t : Number = (time - before) / (frames[i + ENTRIES] - before);
rotate += (frames[i + ENTRIES + ROTATE] - rotate) * t;
x += (frames[i + ENTRIES + X] - x) * t;
y += (frames[i + ENTRIES + Y] - y) * t;
break;
case STEPPED:
rotate = frames[i + ROTATE];
x = frames[i + X];
y = frames[i + Y];
break;
default:
rotate = getBezierValue(time, i, ROTATE, curveType - BEZIER);
x = getBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER);
y = getBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER);
}
if (blend == MixBlend.setup) {
constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha;
constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha;
data = constraint.data;
constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
constraint.mixX = data.mixX + (x - data.mixX) * alpha;
constraint.mixY = data.mixY + (y - data.mixY) * alpha;
} else {
constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
constraint.translateMix += (translate - constraint.translateMix) * alpha;
constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
constraint.mixX += (x - constraint.mixX) * alpha;
constraint.mixY += (y - constraint.mixY) * alpha;
}
}
}

View File

@ -32,32 +32,22 @@ package spine.animation {
import spine.Event;
import spine.Skeleton;
public class PathConstraintPositionTimeline extends CurveTimeline {
static public const ENTRIES : int = 2;
static internal const PREV_TIME : int = -2, PREV_VALUE : int = -1;
static internal const VALUE : int = 1;
public class PathConstraintPositionTimeline extends CurveTimeline1 {
/** The index of the path constraint slot in {@link Skeleton#pathConstraints} that will be changed. */
public var pathConstraintIndex : int;
public var frames : Vector.<Number>; // time, position, ...
public function PathConstraintPositionTimeline(frameCount : int) {
super(frameCount);
frames = new Vector.<Number>(frameCount * ENTRIES, true);
public function PathConstraintPositionTimeline (frameCount : int, bezierCount : int, pathConstraintIndex : int) {
super(frameCount, bezierCount, [
Property.pathConstraintPosition + "|" + pathConstraintIndex
]);
this.pathConstraintIndex = pathConstraintIndex;
}
override public function getPropertyId() : int {
return (TimelineType.pathConstraintPosition.ordinal << 24) + pathConstraintIndex;
}
/** Sets the time and value of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, value : Number) : void {
frameIndex *= ENTRIES;
frames[frameIndex] = time;
frames[frameIndex + VALUE] = value;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var constraint : PathConstraint = skeleton.pathConstraints[pathConstraintIndex];
if (!constraint.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
@ -69,18 +59,8 @@ package spine.animation {
return;
}
var position : Number;
if (time >= frames[frames.length - ENTRIES]) // Time is after last frame.
position = frames[frames.length + PREV_VALUE];
else {
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
position = frames[frame + PREV_VALUE];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
var position : Number = getCurveValue(time);
position += (frames[frame + VALUE] - position) * percent;
}
if (blend == MixBlend.setup)
constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
else

View File

@ -32,18 +32,22 @@ package spine.animation {
import spine.Event;
import spine.PathConstraint;
public class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline {
public function PathConstraintSpacingTimeline(frameCount : int) {
super(frameCount);
public class PathConstraintSpacingTimeline extends CurveTimeline1 {
/** The index of the path constraint slot in {@link Skeleton#pathConstraints} that will be changed. */
public var pathConstraintIndex : int;
public function PathConstraintSpacingTimeline (frameCount : int, bezierCount : int, pathConstraintIndex : int) {
super(frameCount, bezierCount, [
Property.pathConstraintSpacing + "|" + pathConstraintIndex
]);
this.pathConstraintIndex = pathConstraintIndex;
}
override public function getPropertyId() : int {
return (TimelineType.pathConstraintSpacing.ordinal << 24) + pathConstraintIndex;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var constraint : PathConstraint = skeleton.pathConstraints[pathConstraintIndex];
if (!constraint.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
@ -55,18 +59,7 @@ package spine.animation {
return;
}
var spacing : Number;
if (time >= frames[frames.length - ENTRIES]) // Time is after last frame.
spacing = frames[frames.length + PREV_VALUE];
else {
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
spacing = frames[frame + PREV_VALUE];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
spacing += (frames[frame + VALUE] - spacing) * percent;
}
var spacing : Number = getCurveValue(time);
if (blend == MixBlend.setup)
constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;

View File

@ -0,0 +1,57 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
public class Property {
public static const rotate : int = 0;
public static const x : int = 1;
public static const y : int = 2;
public static const scaleX : int = 3;
public static const scaleY : int = 4;
public static const shearX : int = 5;
public static const shearY : int = 6;
public static const rgb : int = 7;
public static const alpha : int = 8;
public static const rgb2 : int = 9;
public static const attachment : int = 10;
public static const deform : int = 11;
public static const event : int = 12;
public static const drawOrder : int = 13;
public static const ikConstraint : int = 14;
public static const transformConstraint : int = 15;
public static const pathConstraintPosition : int = 16;
public static const pathConstraintSpacing : int = 17;
public static const pathConstraintMix : int = 18;
}
}

View File

@ -0,0 +1,169 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Color;
import spine.Event;
import spine.Skeleton;
import spine.Slot;
public class RGB2Timeline extends CurveTimeline implements SlotTimeline {
static internal const ENTRIES : Number = 7;
static internal const R : Number = 1;
static internal const G : Number = 2;
static internal const B : Number = 3;
static internal const R2 : Number = 4;
static internal const G2 : Number = 5;
static internal const B2 : Number = 6;
private var slotIndex : int;
public function RGB2Timeline (frameCount : Number, bezierCount : Number, slotIndex : Number) {
super(frameCount, bezierCount, [
Property.rgb + "|" + slotIndex,
Property.rgb2 + "|" + slotIndex
]);
this.slotIndex = slotIndex;
}
public override function getFrameEntries() : int {
return ENTRIES;
}
public function getSlotIndex() : int {
return slotIndex;
}
/** Sets the time in seconds, light, and dark colors for the specified key frame. */
public function setFrame (frame: Number, time: Number, r: Number, g: Number, b: Number, r2: Number, g2: Number, b2: Number) : void {
frame *= ENTRIES;
frames[frame] = time;
frames[frame + R] = r;
frames[frame + G] = g;
frames[frame + B] = b;
frames[frame + R2] = r2;
frames[frame + G2] = g2;
frames[frame + B2] = b2;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var slot : Slot = skeleton.slots[slotIndex];
if (!slot.bone.active) return;
var frames : Vector.<Number> = this.frames;
var light : Color = slot.color, dark : Color = slot.darkColor;
var setupLight : Color, setupDark : Color;
if (time < frames[0]) {
setupLight = slot.data.color;
setupDark = slot.data.darkColor;
switch (blend) {
case MixBlend.setup:
light.r = setupLight.r;
light.g = setupLight.g;
light.b = setupLight.b;
dark.r = setupDark.r;
dark.g = setupDark.g;
dark.b = setupDark.b;
return;
case MixBlend.first:
light.r += (setupLight.r - light.r) * alpha;
light.g += (setupLight.g - light.g) * alpha;
light.b += (setupLight.b - light.b) * alpha;
dark.r += (setupDark.r - dark.r) * alpha;
dark.g += (setupDark.g - dark.g) * alpha;
dark.b += (setupDark.b - dark.b) * alpha;
}
return;
}
var r : Number = 0, g : Number = 0, b : Number = 0, a : Number = 0, r2 : Number = 0, g2 : Number = 0, b2 : Number = 0;
var i : int = search2(frames, time, ENTRIES);
var curveType : Number = curves[i / ENTRIES];
switch (curveType) {
case LINEAR:
var before : Number = frames[i];
r = frames[i + R];
g = frames[i + G];
b = frames[i + B];
r2 = frames[i + R2];
g2 = frames[i + G2];
b2 = frames[i + B2];
var t : Number = (time - before) / (frames[i + ENTRIES] - before);
r += (frames[i + ENTRIES + R] - r) * t;
g += (frames[i + ENTRIES + G] - g) * t;
b += (frames[i + ENTRIES + B] - b) * t;
r2 += (frames[i + ENTRIES + R2] - r2) * t;
g2 += (frames[i + ENTRIES + G2] - g2) * t;
b2 += (frames[i + ENTRIES + B2] - b2) * t;
break;
case STEPPED:
r = frames[i + R];
g = frames[i + G];
b = frames[i + B];
r2 = frames[i + R2];
g2 = frames[i + G2];
b2 = frames[i + B2];
break;
default:
r = getBezierValue(time, i, R, curveType - BEZIER);
g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 3 - BEZIER);
g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 4 - BEZIER);
b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 5 - BEZIER);
}
if (alpha == 1) {
light.r = r;
light.g = g;
light.b = b;
dark.r = r2;
dark.g = g2;
dark.b = b2;
} else {
if (blend == MixBlend.setup) {
setupLight = slot.data.color;
setupDark = slot.data.darkColor;
light.r = setupLight.r;
light.g = setupLight.g;
light.b = setupLight.b;
dark.r = setupDark.r;
dark.g = setupDark.g;
dark.b = setupDark.b;
}
light.r += (r - light.r) * alpha;
light.g += (g - light.g) * alpha;
light.b += (b - light.b) * alpha;
dark.r += (r2 - dark.r) * alpha;
dark.g += (g2 - dark.g) * alpha;
dark.b += (b2 - dark.b) * alpha;
}
}
}
}

View File

@ -0,0 +1,161 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Color;
import spine.Event;
import spine.Skeleton;
import spine.Slot;
public class RGBA2Timeline extends CurveTimeline implements SlotTimeline {
static internal const ENTRIES : Number = 8;
static internal const R : Number = 1;
static internal const G : Number = 2;
static internal const B : Number = 3;
static internal const A : Number = 4;
static internal const R2 : Number = 5;
static internal const G2 : Number = 6;
static internal const B2 : Number = 7;
private var slotIndex : int;
public function RGBA2Timeline (frameCount : Number, bezierCount : Number, slotIndex : Number) {
super(frameCount, bezierCount, [
Property.rgb + "|" + slotIndex,
Property.alpha + "|" + slotIndex,
Property.rgb2 + "|" + slotIndex
]);
this.slotIndex = slotIndex;
}
public override function getFrameEntries() : int {
return ENTRIES;
}
public function getSlotIndex() : int {
return slotIndex;
}
/** Sets the time in seconds, light, and dark colors for the specified key frame. */
public function setFrame (frame: Number, time: Number, r: Number, g: Number, b: Number, a: Number, r2: Number, g2: Number, b2: Number) : void {
frame <<= 3;
frames[frame] = time;
frames[frame + R] = r;
frames[frame + G] = g;
frames[frame + B] = b;
frames[frame + A] = a;
frames[frame + R2] = r2;
frames[frame + G2] = g2;
frames[frame + B2] = b2;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var slot : Slot = skeleton.slots[slotIndex];
if (!slot.bone.active) return;
var frames : Vector.<Number> = this.frames;
var light : Color = slot.color, dark : Color = slot.darkColor;
if (time < frames[0]) {
var setupLight : Color = slot.data.color, setupDark : Color = slot.data.darkColor;
switch (blend) {
case MixBlend.setup:
light.setFromColor(setupLight);
dark.r = setupDark.r;
dark.g = setupDark.g;
dark.b = setupDark.b;
return;
case MixBlend.first:
light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha,
(setupLight.a - light.a) * alpha);
dark.r += (setupDark.r - dark.r) * alpha;
dark.g += (setupDark.g - dark.g) * alpha;
dark.b += (setupDark.b - dark.b) * alpha;
}
return;
}
var r : Number = 0, g : Number = 0, b : Number = 0, a : Number = 0, r2 : Number = 0, g2 : Number = 0, b2 : Number = 0;
var i : int = search2(frames, time, ENTRIES);
var curveType : Number = curves[i >> 3];
switch (curveType) {
case LINEAR:
var before : Number = frames[i];
r = frames[i + R];
g = frames[i + G];
b = frames[i + B];
a = frames[i + A];
r2 = frames[i + R2];
g2 = frames[i + G2];
b2 = frames[i + B2];
var t : Number = (time - before) / (frames[i + ENTRIES] - before);
r += (frames[i + ENTRIES + R] - r) * t;
g += (frames[i + ENTRIES + G] - g) * t;
b += (frames[i + ENTRIES + B] - b) * t;
a += (frames[i + ENTRIES + A] - a) * t;
r2 += (frames[i + ENTRIES + R2] - r2) * t;
g2 += (frames[i + ENTRIES + G2] - g2) * t;
b2 += (frames[i + ENTRIES + B2] - b2) * t;
break;
case STEPPED:
r = frames[i + R];
g = frames[i + G];
b = frames[i + B];
a = frames[i + A];
r2 = frames[i + R2];
g2 = frames[i + G2];
b2 = frames[i + B2];
break;
default:
r = getBezierValue(time, i, R, curveType - BEZIER);
g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
a = getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER);
r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 4 - BEZIER);
g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 5 - BEZIER);
b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 6 - BEZIER);
}
if (alpha == 1) {
light.set(r, g, b, a);
dark.r = r2;
dark.g = g2;
dark.b = b2;
} else {
if (blend == MixBlend.setup) {
light.setFromColor(slot.data.color);
dark.setFromColor(slot.data.darkColor);
}
light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha);
dark.r += (r2 - dark.r) * alpha;
dark.g += (g2 - dark.g) * alpha;
dark.b += (b2 - dark.b) * alpha;
}
}
}
}

View File

@ -0,0 +1,126 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Color;
import spine.Event;
import spine.Skeleton;
import spine.Slot;
public class RGBATimeline extends CurveTimeline implements SlotTimeline {
static internal const ENTRIES : Number = 5;
static internal const R : Number = 1;
static internal const G : Number = 2;
static internal const B : Number = 3;
static internal const A : Number = 4;
private var slotIndex : int;
public function RGBATimeline (frameCount : Number, bezierCount : Number, slotIndex : Number) {
super(frameCount, bezierCount, [
Property.rgb + "|" + slotIndex,
Property.alpha + "|" + slotIndex
]);
this.slotIndex = slotIndex;
}
public override function getFrameEntries() : int {
return ENTRIES;
}
public function getSlotIndex() : int {
return slotIndex;
}
/** Sets the time in seconds, red, green, blue, and alpha for the specified key frame. */
public function setFrame (frame: Number, time: Number, r: Number, g: Number, b: Number, a: Number) : void {
frame *= ENTRIES;
frames[frame] = time;
frames[frame + R] = r;
frames[frame + G] = g;
frames[frame + B] = b;
frames[frame + A] = a;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var slot : Slot = skeleton.slots[slotIndex];
if (!slot.bone.active) return;
var frames : Vector.<Number> = this.frames;
var color : Color = slot.color;
if (time < frames[0]) {
var setup : Color = slot.data.color;
switch (blend) {
case MixBlend.setup:
color.setFromColor(slot.data.color);
return;
case MixBlend.first:
color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha,
(setup.a - color.a) * alpha);
}
return;
}
var r : Number = 0, g : Number = 0, b : Number = 0, a : Number = 0;
var i : int = search2(frames, time, ENTRIES);
var curveType : Number = curves[i / ENTRIES];
switch (curveType) {
case LINEAR:
var before : Number = frames[i];
r = frames[i + R];
g = frames[i + G];
b = frames[i + B];
a = frames[i + A];
var t : Number = (time - before) / (frames[i + ENTRIES] - before);
r += (frames[i + ENTRIES + R] - r) * t;
g += (frames[i + ENTRIES + G] - g) * t;
b += (frames[i + ENTRIES + B] - b) * t;
a += (frames[i + ENTRIES + A] - a) * t;
break;
case STEPPED:
r = frames[i + R];
g = frames[i + G];
b = frames[i + B];
a = frames[i + A];
break;
default:
r = getBezierValue(time, i, R, curveType - BEZIER);
g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
a = getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER);
}
if (alpha == 1)
color.set(r, g, b, a);
else {
if (blend == MixBlend.setup) color.setFromColor(slot.data.color);
color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha);
}
}
}
}

View File

@ -0,0 +1,131 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Color;
import spine.Event;
import spine.Skeleton;
import spine.Slot;
public class RGBTimeline extends CurveTimeline implements SlotTimeline {
static internal const ENTRIES : Number = 4;
static internal const R : Number = 1;
static internal const G : Number = 2;
static internal const B : Number = 3;
private var slotIndex : int;
public function RGBTimeline (frameCount : Number, bezierCount : Number, slotIndex : Number) {
super(frameCount, bezierCount, [
Property.rgb + "|" + slotIndex
]);
this.slotIndex = slotIndex;
}
public override function getFrameEntries() : int {
return ENTRIES;
}
public function getSlotIndex() : int {
return slotIndex;
}
/** Sets the time in seconds, red, green, and blue for the specified key frame. */
public function setFrame (frame: Number, time: Number, r: Number, g: Number, b: Number) : void {
frame <<= 2;
frames[frame] = time;
frames[frame + R] = r;
frames[frame + G] = g;
frames[frame + B] = b;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var slot : Slot = skeleton.slots[slotIndex];
if (!slot.bone.active) return;
var frames : Vector.<Number> = this.frames;
var color : Color = slot.color, setup : Color;
if (time < frames[0]) {
setup = slot.data.color;
switch (blend) {
case MixBlend.setup:
color.r = setup.r;
color.g = setup.g;
color.b = setup.b;
return;
case MixBlend.first:
color.r += (setup.r - color.r) * alpha;
color.g += (setup.g - color.g) * alpha;
color.b += (setup.b - color.b) * alpha;
}
return;
}
var r : Number = 0, g : Number = 0, b : Number = 0;
var i : int = search2(frames, time, ENTRIES);
var curveType : Number = curves[i / ENTRIES];
switch (curveType) {
case LINEAR:
var before : Number = frames[i];
r = frames[i + R];
g = frames[i + G];
b = frames[i + B];
var t : Number = (time - before) / (frames[i + ENTRIES] - before);
r += (frames[i + ENTRIES + R] - r) * t;
g += (frames[i + ENTRIES + G] - g) * t;
b += (frames[i + ENTRIES + B] - b) * t;
break;
case STEPPED:
r = frames[i + R];
g = frames[i + G];
b = frames[i + B];
break;
default:
r = getBezierValue(time, i, R, curveType - BEZIER);
g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
}
if (alpha == 1) {
color.r = r;
color.g = g;
color.b = b;
} else {
if (blend == MixBlend.setup) {
setup = slot.data.color;
color.r = setup.r;
color.g = setup.g;
color.b = setup.b;
}
color.r += (r - color.r) * alpha;
color.g += (g - color.g) * alpha;
color.b += (b - color.b) * alpha;
}
}
}
}

View File

@ -32,80 +32,46 @@ package spine.animation {
import spine.Event;
import spine.Skeleton;
public class RotateTimeline extends CurveTimeline {
static public const ENTRIES : int = 2;
static public const PREV_TIME : int = -2, PREV_ROTATION : int = -1;
static public const ROTATION : int = 1;
public var boneIndex : int;
public var frames : Vector.<Number>; // time, value, ...
public class RotateTimeline extends CurveTimeline1 implements BoneTimeline {
private var boneIndex : int;
public function RotateTimeline(frameCount : int) {
super(frameCount);
frames = new Vector.<Number>(frameCount * 2, true);
public function RotateTimeline(frameCount : int, bezierCount : int, boneIndex : int) {
super(frameCount, bezierCount, [
Property.rotate + "|" + boneIndex
]);
this.boneIndex = boneIndex;
}
override public function getPropertyId() : int {
return (TimelineType.rotate.ordinal << 24) + boneIndex;
public function getBoneIndex() : int {
return boneIndex;
}
/** Sets the time and angle of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, degrees : Number) : void {
frameIndex <<= 1;
frames[frameIndex] = time;
frames[int(frameIndex + ROTATION)] = degrees;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var frames : Vector.<Number> = this.frames;
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var bone : Bone = skeleton.bones[boneIndex];
if (!bone.active) return;
var r : Number;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
bone.rotation = bone.data.rotation;
return;
case MixBlend.first:
r = bone.data.rotation - bone.rotation;
bone.rotation += (r - (16384 - int((16384.499999999996 - r / 360))) * 360) * alpha;
bone.rotation += (bone.data.rotation - bone.rotation) * alpha;
}
return;
}
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
r = frames[frames.length + PREV_ROTATION];
switch (blend) {
case MixBlend.setup:
bone.rotation = bone.data.rotation + r * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
r += bone.data.rotation - bone.rotation;
r -= (16384 - int((16384.499999999996 - r / 360))) * 360; // Wrap within -180 and 180.
case MixBlend.add:
bone.rotation += r * alpha;
}
return;
}
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
var prevRotation : Number = frames[frame + PREV_ROTATION];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
r = frames[frame + ROTATION] - prevRotation;
r = prevRotation + (r - (16384 - int((16384.499999999996 - r / 360))) * 360) * percent;
var r : Number = getCurveValue(time);
switch (blend) {
case MixBlend.setup:
bone.rotation = bone.data.rotation + (r - (16384 - int((16384.499999999996 - r / 360))) * 360) * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
r += bone.data.rotation - bone.rotation;
case MixBlend.add:
bone.rotation += (r - (16384 - int((16384.499999999996 - r / 360))) * 360) * alpha;
case MixBlend.setup:
bone.rotation = bone.data.rotation + r * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
r += bone.data.rotation - bone.rotation;
case MixBlend.add:
bone.rotation += r * alpha;
}
}
}

View File

@ -33,20 +33,26 @@ package spine.animation {
import spine.Event;
import spine.Skeleton;
public class ScaleTimeline extends TranslateTimeline {
public function ScaleTimeline(frameCount : int) {
super(frameCount);
public class ScaleTimeline extends CurveTimeline2 implements BoneTimeline {
private var boneIndex : int;
public function ScaleTimeline(frameCount : int, bezierCount : int, boneIndex : int) {
super(frameCount, bezierCount, [
Property.scaleX + "|" + boneIndex,
Property.scaleY + "|" + boneIndex
]);
this.boneIndex = boneIndex;
}
override public function getPropertyId() : int {
return (TimelineType.scale.ordinal << 24) + boneIndex;
public function getBoneIndex() : int {
return boneIndex;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var frames : Vector.<Number> = this.frames;
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var bone : Bone = skeleton.bones[boneIndex];
if (!bone.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
@ -60,21 +66,29 @@ package spine.animation {
return;
}
var x : Number, y : Number;
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
x = frames[frames.length + PREV_X] * bone.data.scaleX;
y = frames[frames.length + PREV_Y] * bone.data.scaleY;
} else {
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
x = frames[frame + PREV_X];
y = frames[frame + PREV_Y];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
x = (x + (frames[frame + X] - x) * percent) * bone.data.scaleX;
y = (y + (frames[frame + Y] - y) * percent) * bone.data.scaleY;
var x : Number = 0, y : Number = 0;
var i : int = search2(frames, time, ENTRIES);
var curveType : Number = curves[i / ENTRIES];
switch (curveType) {
case LINEAR:
var before : Number = frames[i];
x = frames[i + VALUE1];
y = frames[i + VALUE2];
var t : Number = (time - before) / (frames[i + ENTRIES] - before);
x += (frames[i + ENTRIES + VALUE1] - x) * t;
y += (frames[i + ENTRIES + VALUE2] - y) * t;
break;
case STEPPED:
x = frames[i + VALUE1];
y = frames[i + VALUE2];
break;
default:
x = getBezierValue(time, i, VALUE1, curveType - BEZIER);
y = getBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER);
}
x *= bone.data.scaleX;
y *= bone.data.scaleY;
if (alpha == 1) {
if (blend == MixBlend.add) {
bone.scaleX += x - bone.data.scaleX;
@ -84,48 +98,48 @@ package spine.animation {
bone.scaleY = y;
}
} else {
var bx : Number, by : Number;
if (direction == MixDirection.Out) {
var bx : Number = 0, by : Number = 0;
if (direction == MixDirection.mixOut) {
switch (blend) {
case MixBlend.setup:
bx = bone.data.scaleX;
by = bone.data.scaleY;
bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bx = bone.scaleX;
by = bone.scaleY;
bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
break;
case MixBlend.add:
bx = bone.scaleX;
by = bone.scaleY;
bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bone.data.scaleX) * alpha;
bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - bone.data.scaleY) * alpha;
case MixBlend.setup:
bx = bone.data.scaleX;
by = bone.data.scaleY;
bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bx = bone.scaleX;
by = bone.scaleY;
bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
break;
case MixBlend.add:
bx = bone.scaleX;
by = bone.scaleY;
bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bone.data.scaleX) * alpha;
bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - bone.data.scaleY) * alpha;
}
} else {
switch (blend) {
case MixBlend.setup:
bx = Math.abs(bone.data.scaleX) * MathUtils.signum(x);
by = Math.abs(bone.data.scaleY) * MathUtils.signum(y);
bone.scaleX = bx + (x - bx) * alpha;
bone.scaleY = by + (y - by) * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bx = Math.abs(bone.scaleX) * MathUtils.signum(x);
by = Math.abs(bone.scaleY) * MathUtils.signum(y);
bone.scaleX = bx + (x - bx) * alpha;
bone.scaleY = by + (y - by) * alpha;
break;
case MixBlend.add:
bx = MathUtils.signum(x);
by = MathUtils.signum(y);
bone.scaleX = Math.abs(bone.scaleX) * bx + (x - Math.abs(bone.data.scaleX) * bx) * alpha;
bone.scaleY = Math.abs(bone.scaleY) * by + (y - Math.abs(bone.data.scaleY) * by) * alpha;
case MixBlend.setup:
bx = Math.abs(bone.data.scaleX) * MathUtils.signum(x);
by = Math.abs(bone.data.scaleY) * MathUtils.signum(y);
bone.scaleX = bx + (x - bx) * alpha;
bone.scaleY = by + (y - by) * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bx = Math.abs(bone.scaleX) * MathUtils.signum(x);
by = Math.abs(bone.scaleY) * MathUtils.signum(y);
bone.scaleX = bx + (x - bx) * alpha;
bone.scaleY = by + (y - by) * alpha;
break;
case MixBlend.add:
bx = MathUtils.signum(x);
by = MathUtils.signum(y);
bone.scaleX = Math.abs(bone.scaleX) * bx + (x - Math.abs(bone.data.scaleX) * bx) * alpha;
bone.scaleY = Math.abs(bone.scaleY) * by + (y - Math.abs(bone.data.scaleY) * by) * alpha;
}
}
}

View File

@ -0,0 +1,109 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Bone;
import spine.Event;
import spine.Skeleton;
import spine.MathUtils;
public class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline {
private var boneIndex : int;
public function ScaleXTimeline(frameCount : int, bezierCount : int, boneIndex : int) {
super(frameCount, bezierCount, [
Property.scaleX + "|" + boneIndex
]);
this.boneIndex = boneIndex;
}
public function getBoneIndex() : int {
return boneIndex;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var bone : Bone = skeleton.bones[boneIndex];
if (!bone.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
bone.scaleX = bone.data.scaleX;
return;
case MixBlend.first:
bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
}
return;
}
var x : Number = getCurveValue(time) * bone.data.scaleX;
if (alpha == 1) {
if (blend == MixBlend.add)
bone.scaleX += x - bone.data.scaleX;
else
bone.scaleX = x;
} else {
// Mixing out uses sign of setup or current pose, else use sign of key.
var bx : Number = 0;
if (direction == MixDirection.mixOut) {
switch (blend) {
case MixBlend.setup:
bx = bone.data.scaleX;
bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bx = bone.scaleX;
bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
break;
case MixBlend.add:
bx = bone.scaleX;
bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bone.data.scaleX) * alpha;
}
} else {
switch (blend) {
case MixBlend.setup:
bx = Math.abs(bone.data.scaleX) * MathUtils.signum(x);
bone.scaleX = bx + (x - bx) * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bx = Math.abs(bone.scaleX) * MathUtils.signum(x);
bone.scaleX = bx + (x - bx) * alpha;
break;
case MixBlend.add:
bx = MathUtils.signum(x);
bone.scaleX = Math.abs(bone.scaleX) * bx + (x - Math.abs(bone.data.scaleX) * bx) * alpha;
}
}
}
}
}
}

View File

@ -0,0 +1,109 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Bone;
import spine.Event;
import spine.Skeleton;
import spine.MathUtils;
public class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline {
private var boneIndex : int;
public function ScaleYTimeline(frameCount : int, bezierCount : int, boneIndex : int) {
super(frameCount, bezierCount, [
Property.scaleY + "|" + boneIndex
]);
this.boneIndex = boneIndex;
}
public function getBoneIndex() : int {
return boneIndex;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var bone : Bone = skeleton.bones[boneIndex];
if (!bone.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
bone.scaleY = bone.data.scaleY;
return;
case MixBlend.first:
bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
}
return;
}
var y : Number = getCurveValue(time) * bone.data.scaleY;
if (alpha == 1) {
if (blend == MixBlend.add)
bone.scaleY += y - bone.data.scaleY;
else
bone.scaleY = y;
} else {
// Mixing out uses sign of setup or current pose, else use sign of key.
var by : Number = 0;
if (direction == MixDirection.mixOut) {
switch (blend) {
case MixBlend.setup:
by = bone.data.scaleY;
bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
by = bone.scaleY;
bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
break;
case MixBlend.add:
by = bone.scaleY;
bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - bone.data.scaleY) * alpha;
}
} else {
switch (blend) {
case MixBlend.setup:
by = Math.abs(bone.data.scaleY) * MathUtils.signum(y);
bone.scaleY = by + (y - by) * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
by = Math.abs(bone.scaleY) * MathUtils.signum(y);
bone.scaleY = by + (y - by) * alpha;
break;
case MixBlend.add:
by = MathUtils.signum(y);
bone.scaleY = Math.abs(bone.scaleY) * by + (y - Math.abs(bone.data.scaleY) * by) * alpha;
}
}
}
}
}
}

View File

@ -32,20 +32,26 @@ package spine.animation {
import spine.Skeleton;
import spine.Bone;
public class ShearTimeline extends TranslateTimeline {
public function ShearTimeline(frameCount : int) {
super(frameCount);
public class ShearTimeline extends CurveTimeline2 implements BoneTimeline {
private var boneIndex : int;
public function ShearTimeline(frameCount : int, bezierCount : int, boneIndex : int) {
super(frameCount, bezierCount, [
Property.shearX + "|" + boneIndex,
Property.shearY + "|" + boneIndex
]);
this.boneIndex = boneIndex;
}
override public function getPropertyId() : int {
return (TimelineType.shear.ordinal << 24) + boneIndex;
public function getBoneIndex() : int {
return boneIndex;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var frames : Vector.<Number> = this.frames;
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var bone : Bone = skeleton.bones[boneIndex];
if (!bone.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
@ -59,34 +65,40 @@ package spine.animation {
return;
}
var x : Number, y : Number;
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
x = frames[frames.length + PREV_X];
y = frames[frames.length + PREV_Y];
} else {
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
x = frames[frame + PREV_X];
y = frames[frame + PREV_Y];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
x = x + (frames[frame + X] - x) * percent;
y = y + (frames[frame + Y] - y) * percent;
var x : Number = 0, y : Number = 0;
var i : int = search2(frames, time, ENTRIES);
var curveType : Number = curves[i / ENTRIES];
switch (curveType) {
case LINEAR:
var before : Number = frames[i];
x = frames[i + VALUE1];
y = frames[i + VALUE2];
var t : Number = (time - before) / (frames[i + ENTRIES] - before);
x += (frames[i + ENTRIES + VALUE1] - x) * t;
y += (frames[i + ENTRIES + VALUE2] - y) * t;
break;
case STEPPED:
x = frames[i + VALUE1];
y = frames[i + VALUE2];
break;
default:
x = getBezierValue(time, i, VALUE1, curveType - BEZIER);
y = getBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER);
}
switch (blend) {
case MixBlend.setup:
bone.shearX = bone.data.shearX + x * alpha;
bone.shearY = bone.data.shearY + y * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
break;
case MixBlend.add:
bone.shearX += x * alpha;
bone.shearY += y * alpha;
case MixBlend.setup:
bone.shearX = bone.data.shearX + x * alpha;
bone.shearY = bone.data.shearY + y * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
break;
case MixBlend.add:
bone.shearX += x * alpha;
bone.shearY += y * alpha;
}
}
}

View File

@ -0,0 +1,79 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Bone;
import spine.Event;
import spine.Skeleton;
public class ShearXTimeline extends CurveTimeline1 implements BoneTimeline {
private var boneIndex : int;
public function ShearXTimeline(frameCount : int, bezierCount : int, boneIndex : int) {
super(frameCount, bezierCount, [
Property.shearX + "|" + boneIndex
]);
this.boneIndex = boneIndex;
}
public function getBoneIndex() : int {
return boneIndex;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var bone : Bone = skeleton.bones[boneIndex];
if (!bone.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
bone.shearX = bone.data.shearX;
return;
case MixBlend.first:
bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
}
return;
}
var x : Number = getCurveValue(time);
switch (blend) {
case MixBlend.setup:
bone.shearX = bone.data.shearX + x * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
break;
case MixBlend.add:
bone.shearX += x * alpha;
}
}
}
}

View File

@ -0,0 +1,79 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Bone;
import spine.Event;
import spine.Skeleton;
public class ShearYTimeline extends CurveTimeline1 implements BoneTimeline {
private var boneIndex : int;
public function ShearYTimeline(frameCount : int, bezierCount : int, boneIndex : int) {
super(frameCount, bezierCount, [
Property.shearY + "|" + boneIndex
]);
this.boneIndex = boneIndex;
}
public function getBoneIndex() : int {
return boneIndex;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var bone : Bone = skeleton.bones[boneIndex];
if (!bone.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
bone.shearY = bone.data.shearY;
return;
case MixBlend.first:
bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
}
return;
}
var y : Number = getCurveValue(time);
switch (blend) {
case MixBlend.setup:
bone.shearY = bone.data.shearY + y * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
break;
case MixBlend.add:
bone.shearY += y * alpha;
}
}
}
}

View File

@ -0,0 +1,34 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
public interface SlotTimeline {
function getSlotIndex() : int;
}
}

View File

@ -31,10 +31,44 @@ package spine.animation {
import spine.Event;
import spine.Skeleton;
public interface Timeline {
/** Sets the value(s) for the specified time. */
function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void;
public class Timeline {
public var propertyIds : Vector.<String>;
public var frames : Vector.<Number>;
function getPropertyId() : int;
public function Timeline(frameCount : int, propertyIds : Array) {
this.propertyIds = new Vector.<String>(propertyIds.length, true);
for (var i : int = 0, n : int = propertyIds.length; i < n; i++)
this.propertyIds[i] = propertyIds[i];
frames = new Vector.<Number>(frameCount * getFrameEntries(), true);
}
public function getFrameEntries() : int {
return 1;
}
public function getFrameCount() : int {
return frames.length / getFrameEntries();
}
public function getDuration() : Number {
return frames[frames.length - getFrameEntries()];
}
public function apply (skeleton: Skeleton, lastTime: Number, time: Number, events: Vector.<Event>, alpha: Number, blend: MixBlend, direction: MixDirection) : void {
}
static internal function search (frames : Vector.<Number>, time : Number) : int {
var n : int = frames.length;
for (var i : int = 1; i < n; i++)
if (frames[i] > time) return i - 1;
return n - 1;
}
static internal function search2 (values : Vector.<Number>, time : Number, step: int) : int {
var n : int = values.length;
for (var i : int = step; i < n; i += step)
if (values[i] > time) return i - step;
return n - step;
}
}
}

View File

@ -33,7 +33,8 @@ package spine.animation {
public class TrackEntry implements Poolable {
public var animation : Animation;
public var next : TrackEntry, mixingFrom : TrackEntry, mixingTo: TrackEntry;
public var next : TrackEntry, previous : TrackEntry;
public var mixingFrom : TrackEntry, mixingTo: TrackEntry;
public var onStart : Listeners = new Listeners();
public var onInterrupt : Listeners = new Listeners();
public var onEnd : Listeners = new Listeners();
@ -41,7 +42,7 @@ package spine.animation {
public var onComplete : Listeners = new Listeners();
public var onEvent : Listeners = new Listeners();
public var trackIndex : int;
public var loop : Boolean, holdPrevious: Boolean;
public var loop : Boolean, reverse : Boolean, holdPrevious: Boolean;
public var eventThreshold : Number, attachmentThreshold : Number, drawOrderThreshold : Number;
public var animationStart : Number, animationEnd : Number, animationLast : Number, nextAnimationLast : Number;
public var delay : Number, trackTime : Number, trackLast : Number, nextTrackLast : Number, trackEnd : Number, timeScale : Number;
@ -63,8 +64,21 @@ package spine.animation {
return Math.min(trackTime + animationStart, animationEnd);
}
/** If this track entry is non-looping, the track time in seconds when {@link #getAnimationEnd()} is reached, or the current
* {@link #getTrackTime()} if it has already been reached. If this track entry is looping, the track time when this
* animation will reach its next {@link #getAnimationEnd()} (the next loop completion). */
public function getTrackComplete () : Number {
var duration : Number = animationEnd - animationStart;
if (duration != 0) {
if (loop) return duration * (1 + int(trackTime / duration)); // Completion of next loop.
if (trackTime < duration) return duration; // Before duration.
}
return trackTime; // Next update.
}
public function reset() : void {
next = null;
previous = null;
mixingFrom = null;
mixingTo = null;
animation = null;

View File

@ -34,88 +34,116 @@ package spine.animation {
import spine.TransformConstraint;
public class TransformConstraintTimeline extends CurveTimeline {
static public const ENTRIES : int = 5;
static internal const PREV_TIME : int = -5, PREV_ROTATE : int = -4, PREV_TRANSLATE : int = -3, PREV_SCALE : int = -2, PREV_SHEAR : int = -1;
static internal const ROTATE : int = 1, TRANSLATE : int = 2, SCALE : int = 3, SHEAR : int = 4;
static internal const ENTRIES : int = 7;
static internal const ROTATE : int = 1, X : int = 2, Y : int = 3, SCALEX : int = 4, SCALEY : int = 5, SHEARY : int = 6;
/** The index of the transform constraint slot in {@link Skeleton#transformConstraints} that will be changed. */
public var transformConstraintIndex : int;
public var frames : Vector.<Number>; // time, rotate mix, translate mix, scale mix, shear mix, ...
public function TransformConstraintTimeline(frameCount : int) {
super(frameCount);
frames = new Vector.<Number>(frameCount * ENTRIES, true);
public function TransformConstraintTimeline(frameCount : int, bezierCount : int, transformConstraintIndex : int) {
super(frameCount, bezierCount, [
Property.transformConstraint + "|" + transformConstraintIndex
]);
this.transformConstraintIndex = transformConstraintIndex;
}
override public function getPropertyId() : int {
return (TimelineType.transformConstraint.ordinal << 24) + transformConstraintIndex;
public override function getFrameEntries() : int {
return ENTRIES;
}
/** Sets the time and mixes of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, rotateMix : Number, translateMix : Number, scaleMix : Number, shearMix : Number) : void {
frameIndex *= ENTRIES;
frames[frameIndex] = time;
frames[frameIndex + ROTATE] = rotateMix;
frames[frameIndex + TRANSLATE] = translateMix;
frames[frameIndex + SCALE] = scaleMix;
frames[frameIndex + SHEAR] = shearMix;
/** The time in seconds, rotate mix, translate mix, scale mix, and shear mix for the specified key frame. */
public function setFrame (frame : int, time : Number, mixRotate: Number, mixX: Number, mixY: Number, mixScaleX: Number, mixScaleY: Number, mixShearY: Number) : void {
frame *= ENTRIES;
frames[frame] = time;
frames[frame + ROTATE] = mixRotate;
frames[frame + X] = mixX;
frames[frame + Y] = mixY;
frames[frame + SCALEX] = mixScaleX;
frames[frame + SCALEY] = mixScaleY;
frames[frame + SHEARY] = mixShearY;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var frames : Vector.<Number> = this.frames;
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var constraint : TransformConstraint = skeleton.transformConstraints[transformConstraintIndex];
if (!constraint.active) return;
var data : TransformConstraintData;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
data = constraint.data;
switch (blend) {
case MixBlend.setup:
constraint.rotateMix = data.rotateMix;
constraint.translateMix = data.translateMix;
constraint.scaleMix = data.scaleMix;
constraint.shearMix = data.shearMix;
constraint.mixRotate = data.mixRotate;
constraint.mixX = data.mixX;
constraint.mixY = data.mixY;
constraint.mixScaleX = data.mixScaleX;
constraint.mixScaleY = data.mixScaleY;
constraint.mixShearY = data.mixShearY;
return;
case MixBlend.first:
constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha;
constraint.translateMix += (data.translateMix - constraint.translateMix) * alpha;
constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha;
constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha;
constraint.mixRotate += (data.mixRotate - constraint.mixRotate) * alpha;
constraint.mixX += (data.mixX - constraint.mixX) * alpha;
constraint.mixY += (data.mixY - constraint.mixY) * alpha;
constraint.mixScaleX += (data.mixScaleX - constraint.mixScaleX) * alpha;
constraint.mixScaleY += (data.mixScaleY - constraint.mixScaleY) * alpha;
constraint.mixShearY += (data.mixShearY - constraint.mixShearY) * alpha;
}
return;
}
var rotate : Number, translate : Number, scale : Number, shear : Number;
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
var i : int = frames.length;
rotate = frames[i + PREV_ROTATE];
translate = frames[i + PREV_TRANSLATE];
scale = frames[i + PREV_SCALE];
shear = frames[i + PREV_SHEAR];
} else {
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
rotate = frames[frame + PREV_ROTATE];
translate = frames[frame + PREV_TRANSLATE];
scale = frames[frame + PREV_SCALE];
shear = frames[frame + PREV_SHEAR];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
rotate += (frames[frame + ROTATE] - rotate) * percent;
translate += (frames[frame + TRANSLATE] - translate) * percent;
scale += (frames[frame + SCALE] - scale) * percent;
shear += (frames[frame + SHEAR] - shear) * percent;
var rotate : Number, x : Number, y : Number, scaleX : Number, scaleY : Number, shearY : Number;
var i : int = search2(frames, time, ENTRIES);
var curveType : Number = curves[i / ENTRIES];
switch (curveType) {
case LINEAR:
var before : Number = frames[i];
rotate = frames[i + ROTATE];
x = frames[i + X];
y = frames[i + Y];
scaleX = frames[i + SCALEX];
scaleY = frames[i + SCALEY];
shearY = frames[i + SHEARY];
var t : Number = (time - before) / (frames[i + ENTRIES] - before);
rotate += (frames[i + ENTRIES + ROTATE] - rotate) * t;
x += (frames[i + ENTRIES + X] - x) * t;
y += (frames[i + ENTRIES + Y] - y) * t;
scaleX += (frames[i + ENTRIES + SCALEX] - scaleX) * t;
scaleY += (frames[i + ENTRIES + SCALEY] - scaleY) * t;
shearY += (frames[i + ENTRIES + SHEARY] - shearY) * t;
break;
case STEPPED:
rotate = frames[i + ROTATE];
x = frames[i + X];
y = frames[i + Y];
scaleX = frames[i + SCALEX];
scaleY = frames[i + SCALEY];
shearY = frames[i + SHEARY];
break;
default:
rotate = getBezierValue(time, i, ROTATE, curveType - BEZIER);
x = getBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER);
y = getBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER);
scaleX = getBezierValue(time, i, SCALEX, curveType + BEZIER_SIZE * 3 - BEZIER);
scaleY = getBezierValue(time, i, SCALEY, curveType + BEZIER_SIZE * 4 - BEZIER);
shearY = getBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER);
}
if (blend == MixBlend.setup) {
data = constraint.data;
constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha;
constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha;
constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha;
constraint.shearMix = data.shearMix + (shear - data.shearMix) * alpha;
constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
constraint.mixX = data.mixX + (x - data.mixX) * alpha;
constraint.mixY = data.mixY + (y - data.mixY) * alpha;
constraint.mixScaleX = data.mixScaleX + (scaleX - data.mixScaleX) * alpha;
constraint.mixScaleY = data.mixScaleY + (scaleY - data.mixScaleY) * alpha;
constraint.mixShearY = data.mixShearY + (shearY - data.mixShearY) * alpha;
} else {
constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
constraint.translateMix += (translate - constraint.translateMix) * alpha;
constraint.scaleMix += (scale - constraint.scaleMix) * alpha;
constraint.shearMix += (shear - constraint.shearMix) * alpha;
constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
constraint.mixX += (x - constraint.mixX) * alpha;
constraint.mixY += (y - constraint.mixY) * alpha;
constraint.mixScaleX += (scaleX - constraint.mixScaleX) * alpha;
constraint.mixScaleY += (scaleY - constraint.mixScaleY) * alpha;
constraint.mixShearY += (shearY - constraint.mixShearY) * alpha;
}
}
}

View File

@ -32,35 +32,26 @@ package spine.animation {
import spine.Event;
import spine.Skeleton;
public class TranslateTimeline extends CurveTimeline {
static public const ENTRIES : int = 3;
static internal const PREV_TIME : int = -3, PREV_X : int = -2, PREV_Y : int = -1;
static internal const X : int = 1, Y : int = 2;
public var boneIndex : int;
public var frames : Vector.<Number>; // time, value, value, ...
public class TranslateTimeline extends CurveTimeline2 implements BoneTimeline {
private var boneIndex : int;
public function TranslateTimeline(frameCount : int) {
super(frameCount);
frames = new Vector.<Number>(frameCount * ENTRIES, true);
public function TranslateTimeline(frameCount : int, bezierCount : int, boneIndex : int) {
super(frameCount, bezierCount, [
Property.x + "|" + boneIndex,
Property.y + "|" + boneIndex
]);
this.boneIndex = boneIndex;
}
override public function getPropertyId() : int {
return (TimelineType.translate.ordinal << 24) + boneIndex;
public function getBoneIndex() : int {
return boneIndex;
}
/** Sets the time and value of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, x : Number, y : Number) : void {
frameIndex *= ENTRIES;
frames[frameIndex] = time;
frames[int(frameIndex + X)] = x;
frames[int(frameIndex + Y)] = y;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var frames : Vector.<Number> = this.frames;
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var bone : Bone = skeleton.bones[boneIndex];
if (!bone.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
@ -74,34 +65,40 @@ package spine.animation {
return;
}
var x : Number, y : Number;
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
x = frames[frames.length + PREV_X];
y = frames[frames.length + PREV_Y];
} else {
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
x = frames[frame + PREV_X];
y = frames[frame + PREV_Y];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
x += (frames[frame + X] - x) * percent;
y += (frames[frame + Y] - y) * percent;
var x : Number = 0, y : Number = 0;
var i : int = search2(frames, time, ENTRIES);
var curveType : Number = curves[i / ENTRIES];
switch (curveType) {
case LINEAR:
var before : Number = frames[i];
x = frames[i + VALUE1];
y = frames[i + VALUE2];
var t : Number = (time - before) / (frames[i + ENTRIES] - before);
x += (frames[i + ENTRIES + VALUE1] - x) * t;
y += (frames[i + ENTRIES + VALUE2] - y) * t;
break;
case STEPPED:
x = frames[i + VALUE1];
y = frames[i + VALUE2];
break;
default:
x = getBezierValue(time, i, VALUE1, curveType - BEZIER);
y = getBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER);
}
switch (blend) {
case MixBlend.setup:
bone.x = bone.data.x + x * alpha;
bone.y = bone.data.y + y * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bone.x += (bone.data.x + x - bone.x) * alpha;
bone.y += (bone.data.y + y - bone.y) * alpha;
break;
case MixBlend.add:
bone.x += x * alpha;
bone.y += y * alpha;
case MixBlend.setup:
bone.x = bone.data.x + x * alpha;
bone.y = bone.data.y + y * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bone.x += (bone.data.x + x - bone.x) * alpha;
bone.y += (bone.data.y + y - bone.y) * alpha;
break;
case MixBlend.add:
bone.x += x * alpha;
bone.y += y * alpha;
}
}
}

View File

@ -0,0 +1,79 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Bone;
import spine.Event;
import spine.Skeleton;
public class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline {
private var boneIndex : int;
public function TranslateXTimeline(frameCount : int, bezierCount : int, boneIndex : int) {
super(frameCount, bezierCount, [
Property.x + "|" + boneIndex
]);
this.boneIndex = boneIndex;
}
public function getBoneIndex() : int {
return boneIndex;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var bone : Bone = skeleton.bones[boneIndex];
if (!bone.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
bone.x = bone.data.x;
return;
case MixBlend.first:
bone.x += (bone.data.x - bone.x) * alpha;
}
return;
}
var x : Number = getCurveValue(time);
switch (blend) {
case MixBlend.setup:
bone.x = bone.data.x + x * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bone.x += (bone.data.x + x - bone.x) * alpha;
break;
case MixBlend.add:
bone.x += x * alpha;
}
}
}
}

View File

@ -0,0 +1,79 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Bone;
import spine.Event;
import spine.Skeleton;
public class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline {
private var boneIndex : int;
public function TranslateYTimeline(frameCount : int, bezierCount : int, boneIndex : int) {
super(frameCount, bezierCount, [
Property.y + "|" + boneIndex
]);
this.boneIndex = boneIndex;
}
public function getBoneIndex() : int {
return boneIndex;
}
public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var bone : Bone = skeleton.bones[boneIndex];
if (!bone.active) return;
var frames : Vector.<Number> = this.frames;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
bone.y = bone.data.y;
return;
case MixBlend.first:
bone.y += (bone.data.y - bone.y) * alpha;
}
return;
}
var y : Number = getCurveValue(time);
switch (blend) {
case MixBlend.setup:
bone.y = bone.data.y + y * alpha;
break;
case MixBlend.first:
case MixBlend.replace:
bone.y += (bone.data.y + y - bone.y) * alpha;
break;
case MixBlend.add:
bone.y += y * alpha;
}
}
}
}

View File

@ -1,135 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.animation {
import spine.Color;
import spine.Event;
import spine.Skeleton;
import spine.Slot;
public class TwoColorTimeline extends CurveTimeline {
static public const ENTRIES : int = 8;
static internal const PREV_TIME : int = -8, PREV_R : int = -7, PREV_G : int = -6, PREV_B : int = -5, PREV_A : int = -4;
static internal const PREV_R2 : int = -3, PREV_G2 : int = -2, PREV_B2 : int = -1;
static internal const R : int = 1, G : int = 2, B : int = 3, A : int = 4, R2 : int = 5, G2 : int = 6, B2 : int = 7;
public var slotIndex : int;
public var frames : Vector.<Number>; // time, r, g, b, a, ...
public function TwoColorTimeline(frameCount : int) {
super(frameCount);
frames = new Vector.<Number>(frameCount * ENTRIES, true);
}
override public function getPropertyId() : int {
return (TimelineType.twoColor.ordinal << 24) + slotIndex;
}
/** Sets the time and value of the specified keyframe. */
public function setFrame(frameIndex : int, time : Number, r : Number, g : Number, b : Number, a : Number, r2 : Number, g2 : Number, b2 : Number) : void {
frameIndex *= TwoColorTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + TwoColorTimeline.R] = r;
this.frames[frameIndex + TwoColorTimeline.G] = g;
this.frames[frameIndex + TwoColorTimeline.B] = b;
this.frames[frameIndex + TwoColorTimeline.A] = a;
this.frames[frameIndex + TwoColorTimeline.R2] = r2;
this.frames[frameIndex + TwoColorTimeline.G2] = g2;
this.frames[frameIndex + TwoColorTimeline.B2] = b2;
}
override public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector.<Event>, alpha : Number, blend : MixBlend, direction : MixDirection) : void {
var frames : Vector.<Number> = this.frames;
var slot : Slot = skeleton.slots[slotIndex];
var light : Color, dark : Color;
if (!slot.bone.active) return;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
slot.color.setFromColor(slot.data.color);
slot.darkColor.setFromColor(slot.data.darkColor);
return;
case MixBlend.first:
light = slot.color;
dark = slot.darkColor;
var setupLight : Color = slot.data.color, setupDark : Color = slot.data.darkColor;
light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha,
(setupLight.a - light.a) * alpha);
dark.add((setupDark.r - dark.r) * alpha, (setupDark.g - dark.g) * alpha, (setupDark.b - dark.b) * alpha, 0);
}
return;
}
var r : Number, g : Number, b : Number, a : Number, r2 : Number, g2 : Number, b2 : Number;
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
var i : int = frames.length;
r = frames[i + PREV_R];
g = frames[i + PREV_G];
b = frames[i + PREV_B];
a = frames[i + PREV_A];
r2 = frames[i + PREV_R2];
g2 = frames[i + PREV_G2];
b2 = frames[i + PREV_B2];
} else {
// Interpolate between the previous frame and the current frame.
var frame : int = Animation.binarySearch(frames, time, ENTRIES);
r = frames[frame + PREV_R];
g = frames[frame + PREV_G];
b = frames[frame + PREV_B];
a = frames[frame + PREV_A];
r2 = frames[frame + PREV_R2];
g2 = frames[frame + PREV_G2];
b2 = frames[frame + PREV_B2];
var frameTime : Number = frames[frame];
var percent : Number = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
r += (frames[frame + R] - r) * percent;
g += (frames[frame + G] - g) * percent;
b += (frames[frame + B] - b) * percent;
a += (frames[frame + A] - a) * percent;
r2 += (frames[frame + R2] - r2) * percent;
g2 += (frames[frame + G2] - g2) * percent;
b2 += (frames[frame + B2] - b2) * percent;
}
if (alpha == 1) {
slot.color.setFrom(r, g, b, a);
slot.darkColor.setFrom(r2, g2, b2, 1);
} else {
light = slot.color;
dark = slot.darkColor;
if (blend == MixBlend.setup) {
light.setFromColor(slot.data.color);
dark.setFromColor(slot.data.darkColor);
}
light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha);
dark.add((r2 - dark.r) * alpha, (g2 - dark.g) * alpha, (b2 - dark.b) * alpha, 0);
}
}
}
}

View File

@ -28,8 +28,8 @@
*****************************************************************************/
package spine.atlas {
import flash.trace.Trace;
import flash.utils.ByteArray;
import flash.utils.Dictionary;
public class Atlas {
private var pages : Vector.<AtlasPage> = new Vector.<AtlasPage>();
@ -38,8 +38,7 @@ package spine.atlas {
/** @param object A String or ByteArray. */
public function Atlas(object : *, textureLoader : TextureLoader) {
if (!object)
return;
if (!object) return;
if (object is String)
load(String(object), textureLoader);
else if (object is ByteArray)
@ -49,107 +48,147 @@ package spine.atlas {
}
protected function load(atlasText : String, textureLoader : TextureLoader) : void {
if (textureLoader == null)
throw new ArgumentError("textureLoader cannot be null.");
if (textureLoader == null) throw new ArgumentError("textureLoader cannot be null.");
this.textureLoader = textureLoader;
var reader : Reader = new Reader(atlasText);
var tuple : Array = new Array();
tuple.length = 4;
var page : AtlasPage = null;
var entry : Vector.<String> = new Vector.<String>(5, true);
var page : AtlasPage;
var region : AtlasRegion;
var pageFields : Dictionary = new Dictionary();
pageFields["size"] = function() : void {
page.width = parseInt(entry[1]);
page.height = parseInt(entry[2]);
};
pageFields["format"] = function() : void {
page.format = Format[entry[0]];
};
pageFields["filter"] = function() : void {
page.minFilter = TextureFilter[entry[1]];
page.magFilter = TextureFilter[entry[2]];
};
pageFields["repeat"] = function() : void {
if (entry[1].indexOf('x') != -1) page.uWrap = TextureWrap.repeat;
if (entry[1].indexOf('y') != -1) page.vWrap = TextureWrap.repeat;
};
pageFields["pma"] = function() : void {
page.pma = entry[1] == "true";
};
var regionFields : Dictionary = new Dictionary();
regionFields["xy"] = function() : void { // Deprecated, use bounds.
region.x = parseInt(entry[1]);
region.y = parseInt(entry[2]);
};
regionFields["size"] = function() : void { // Deprecated, use bounds.
region.width = parseInt(entry[1]);
region.height = parseInt(entry[2]);
};
regionFields["bounds"] = function() : void {
region.x = parseInt(entry[1]);
region.y = parseInt(entry[2]);
region.width = parseInt(entry[3]);
region.height = parseInt(entry[4]);
};
regionFields["offset"] = function() : void { // Deprecated, use offsets.
region.offsetX = parseInt(entry[1]);
region.offsetY = parseInt(entry[2]);
};
regionFields["orig"] = function() : void { // Deprecated, use offsets.
region.originalWidth = parseInt(entry[1]);
region.originalHeight = parseInt(entry[2]);
};
regionFields["offsets"] = function() : void {
region.offsetX = parseInt(entry[1]);
region.offsetY = parseInt(entry[2]);
region.originalWidth = parseInt(entry[3]);
region.originalHeight = parseInt(entry[4]);
};
regionFields["rotate"] = function() : void {
var value : String = entry[1];
if (value == "true")
region.degrees = 90;
else if (value != "false")
region.degrees = parseInt(value);
};
regionFields["index"] = function() : void {
region.index = parseInt(entry[1]);
};
var line : String = reader.readLine();
// Ignore empty lines before first entry.
while (line != null && line.length == 0)
line = reader.readLine();
// Header entries.
while (true) {
var line : String = reader.readLine();
if (line == null)
break;
line = reader.trim(line);
if (line.length == 0)
if (line == null || line.length == 0) break;
if (reader.readEntry(entry, line) == 0) break; // Silently ignore all header fields.
line = reader.readLine();
}
// Page and region entries.
var names : Vector.<String>;
var values : Vector.<Vector.<Number>>;
var field : Function;
while (true) {
if (line == null) break;
if (line.length == 0) {
page = null;
else if (!page) {
line = reader.readLine();
} else if (page == null) {
page = new AtlasPage();
page.name = line;
if (reader.readTuple(tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker.
page.width = parseInt(tuple[0]);
page.height = parseInt(tuple[1]);
reader.readTuple(tuple);
while (true) {
if (reader.readEntry(entry, line = reader.readLine()) == 0) break;
field = pageFields[entry[0]];
if (field) field();
}
page.format = Format[tuple[0]];
reader.readTuple(tuple);
page.minFilter = TextureFilter[tuple[0]];
page.magFilter = TextureFilter[tuple[1]];
var direction : String = reader.readValue();
page.uWrap = TextureWrap.clampToEdge;
page.vWrap = TextureWrap.clampToEdge;
if (direction == "x")
page.uWrap = TextureWrap.repeat;
else if (direction == "y")
page.vWrap = TextureWrap.repeat;
else if (direction == "xy")
page.uWrap = page.vWrap = TextureWrap.repeat;
textureLoader.loadPage(page, line);
pages[pages.length] = page;
pages.push(page);
} else {
var region : AtlasRegion = new AtlasRegion();
region.name = line;
region = new AtlasRegion();
region.page = page;
var rotateValue : String = reader.readValue();
if (rotateValue == "true") {
region.degrees = 90;
} else if (rotateValue == "false") {
region.degrees = 0;
} else {
region.degrees = parseInt(rotateValue);
}
region.rotate = region.degrees == 90;
reader.readTuple(tuple);
var x : int = parseInt(tuple[0]);
var y : int = parseInt(tuple[1]);
reader.readTuple(tuple);
var width : int = parseInt(tuple[0]);
var height : int = parseInt(tuple[1]);
region.u = x / page.width;
region.v = y / page.height;
if (region.rotate) {
region.u2 = (x + height) / page.width;
region.v2 = (y + width) / page.height;
} else {
region.u2 = (x + width) / page.width;
region.v2 = (y + height) / page.height;
}
region.x = x;
region.y = y;
region.width = Math.abs(width);
region.height = Math.abs(height);
if (reader.readTuple(tuple) == 4) { // split is optional
region.splits = new Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3]));
if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits
region.pads = new Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3]));
reader.readTuple(tuple);
region.name = line;
while (true) {
var count : int = reader.readEntry(entry, line = reader.readLine());
if (count == 0) break;
field = regionFields[entry[0]];
if (field)
field();
else {
if (names == null) {
names = new Vector.<String>();
values = new Vector.<Vector.<Number>>();
}
names.push(entry[0]);
var entryValues : Vector.<Number> = new Vector.<Number>(count, true);
for (var i : int = 0; i < count; i++)
entryValues[i] = parseInt(entry[i + 1]);
values.push(entryValues);
}
}
region.originalWidth = parseInt(tuple[0]);
region.originalHeight = parseInt(tuple[1]);
reader.readTuple(tuple);
region.offsetX = parseInt(tuple[0]);
region.offsetY = parseInt(tuple[1]);
region.index = parseInt(reader.readValue());
if (region.originalWidth == 0 && region.originalHeight == 0) {
region.originalWidth = region.width;
region.originalHeight = region.height;
}
if (names != null && names.length > 0) {
region.names = names;
region.values = values;
names = null;
values = null;
}
region.u = region.x / page.width;
region.v = region.y / page.height;
if (region.degrees == 90) {
region.u2 = (region.x + region.height) / page.width;
region.v2 = (region.y + region.width) / page.height;
} else {
region.u2 = (region.x + region.width) / page.width;
region.v2 = (region.y + region.height) / page.height;
}
textureLoader.loadRegion(region);
regions[regions.length] = region;
regions.push(region);
}
}
}
@ -172,45 +211,39 @@ package spine.atlas {
}
class Reader {
static private const trimRegex : RegExp = /^\s+|\s+$/gs;
private var lines : Array;
private var index : int;
public function Reader(text : String) {
lines = text.split(/\r\n|\r|\n/);
function Reader(text : String) {
lines = trim(text).split(/[ \t]*(?:\r\n|\r|\n)[ \t]*/);
}
public function trim(value : String) : String {
return value.replace(/^\s+|\s+$/gs, "");
function trim (value : String) : String {
return value.replace(trimRegex, "");
}
public function readLine() : String {
if (index >= lines.length)
return null;
return lines[index++];
function readLine() : String {
return index >= lines.length ? null : lines[index++];
}
public function readValue() : String {
var line : String = readLine();
var colon : int = line.indexOf(":");
if (colon == -1)
throw new Error("Invalid line: " + line);
return trim(line.substring(colon + 1));
}
function readEntry(entry : Vector.<String>, line : String) : int {
if (line == null) return 0;
if (line.length == 0) return 0;
/** Returns the number of tuple values read (1, 2 or 4). */
public function readTuple(tuple : Array) : int {
var line : String = readLine();
var colon : int = line.indexOf(":");
if (colon == -1)
throw new Error("Invalid line: " + line);
var i : int = 0, lastMatch : int = colon + 1;
for (; i < 3; i++) {
var comma : int = line.indexOf(",", lastMatch);
if (comma == -1) break;
tuple[i] = trim(line.substr(lastMatch, comma - lastMatch));
var colon : int = line.indexOf(':');
if (colon == -1) return 0;
entry[0] = trim(line.substr(0, colon));
for (var i : int = 1, lastMatch : int = colon + 1;; i++) {
var comma : int = line.indexOf(',', lastMatch);
if (comma == -1) {
entry[i] = trim(line.substr(lastMatch));
return i;
}
entry[i] = trim(line.substr(lastMatch, comma - lastMatch));
lastMatch = comma + 1;
if (i == 4) return 4;
}
tuple[i] = trim(line.substring(lastMatch));
return i + 1;
}
}

View File

@ -31,13 +31,13 @@ package spine.atlas {
public class AtlasPage {
public var name : String;
public var format : Format;
public var minFilter : TextureFilter;
public var magFilter : TextureFilter;
public var uWrap : TextureWrap;
public var vWrap : TextureWrap;
public var minFilter : TextureFilter = TextureFilter.nearest;
public var magFilter : TextureFilter = TextureFilter.nearest;
public var uWrap : TextureWrap = TextureWrap.clampToEdge;
public var vWrap : TextureWrap = TextureWrap.clampToEdge;
public var width : int, height : int;
public var pma : Boolean;
public var rendererObject : Object;
public var width : int;
public var height : int;
public function AtlasPage() {
}

View File

@ -31,24 +31,18 @@ package spine.atlas {
public class AtlasRegion {
public var page : AtlasPage;
public var name : String;
public var x : int;
public var y : int;
public var width : int;
public var height : int;
public var u : Number;
public var v : Number;
public var u2 : Number;
public var v2 : Number;
public var offsetX : Number;
public var offsetY : Number;
public var originalWidth : int;
public var originalHeight : int;
public var x : int, y : int;
public var width : int, height : int;
public var u : Number, v : Number, u2 : Number, v2 : Number;
public var offsetX : Number = 0, offsetY : Number = 0;
public var originalWidth : int, originalHeight : int;
public var index : int;
public var rotate : Boolean;
public var degrees : int;
public var splits : Vector.<int>;
public var pads : Vector.<int>;
public var rendererObject : Object;
public var names : Vector.<String>;
public var values : Vector.<Vector.<Number>>;
public function AtlasRegion() {
}

View File

@ -28,6 +28,8 @@
*****************************************************************************/
package spine.atlas {
import flash.utils.Dictionary;
public class TextureFilter {
public static const nearest : TextureFilter = new TextureFilter(0, "nearest");
public static const linear : TextureFilter = new TextureFilter(1, "linear");
@ -36,6 +38,7 @@ package spine.atlas {
public static const mipMapLinearNearest : TextureFilter = new TextureFilter(4, "mipMapLinearNearest");
public static const mipMapNearestLinear : TextureFilter = new TextureFilter(5, "mipMapNearestLinear");
public static const mipMapLinearLinear : TextureFilter = new TextureFilter(6, "mipMapLinearLinear");
public var ordinal : int;
public var name : String;

View File

@ -49,7 +49,7 @@ package spine.attachments {
attachment.rendererObject = region;
var scaleX : Number = 1;
var scaleY : Number = 1;
attachment.setUVs(region.u * scaleX, region.v * scaleY, region.u2 * scaleX, region.v2 * scaleY, region.rotate);
attachment.setUVs(region.u * scaleX, region.v * scaleY, region.u2 * scaleX, region.v2 * scaleY, region.degrees);
attachment.regionOffsetX = region.offsetX;
attachment.regionOffsetY = region.offsetY;
attachment.regionWidth = region.width;
@ -71,7 +71,6 @@ package spine.attachments {
attachment.regionV = region.v * scaleY;
attachment.regionU2 = region.u2 * scaleX;
attachment.regionV2 = region.v2 * scaleY;
attachment.regionRotate = region.rotate;
attachment.regionDegrees = region.degrees;
attachment.regionOffsetX = region.offsetX;
attachment.regionOffsetY = region.offsetY;

View File

@ -33,8 +33,7 @@ package spine.attachments {
internal var _name : String;
public function Attachment(name : String) {
if (name == null)
throw new ArgumentError("name cannot be null.");
if (name == null) throw new ArgumentError("name cannot be null.");
_name = name;
}

View File

@ -31,7 +31,7 @@ package spine.attachments {
import spine.Color;
import spine.SlotData;
public class ClippingAttachment extends VertexAttachment {
public dynamic class ClippingAttachment extends VertexAttachment {
public var endSlot : SlotData;
public var color : Color = new Color(0.2275, 0.2275, 0.2275, 1);

View File

@ -43,7 +43,6 @@ package spine.attachments {
public var regionV : Number;
public var regionU2 : Number;
public var regionV2 : Number;
public var regionRotate : Boolean;
public var regionDegrees : int;
public var regionOffsetX : Number; // Pixels stripped from the bottom left, unrotated.
public var regionOffsetY : Number;
@ -148,7 +147,6 @@ package spine.attachments {
copy.regionV = regionV;
copy.regionU2 = regionU2;
copy.regionV2 = regionV2;
copy.regionRotate = regionRotate;
copy.regionDegrees = regionDegrees;
copy.regionOffsetX = regionOffsetX;
copy.regionOffsetY = regionOffsetY;
@ -186,7 +184,6 @@ package spine.attachments {
copy.regionV = regionV;
copy.regionU2 = regionU2;
copy.regionV2 = regionV2;
copy.regionRotate = regionRotate;
copy.regionDegrees = regionDegrees;
copy.regionOffsetX = regionOffsetX;
copy.regionOffsetY = regionOffsetY;

View File

@ -93,9 +93,9 @@ package spine.attachments {
offset[BRY] = Math.sin(radians - brAngle) * brDist + y;
}
public function setUVs(u : Number, v : Number, u2 : Number, v2 : Number, rotate : Boolean) : void {
public function setUVs(u : Number, v : Number, u2 : Number, v2 : Number, degrees : int) : void {
var uvs : Vector.<Number> = this.uvs;
if (rotate) {
if (degrees == 90) {
uvs[4] = u;
uvs[5] = v2;
uvs[6] = u;

View File

@ -32,13 +32,13 @@ package spine.attachments {
import spine.Skeleton;
import spine.Slot;
public dynamic class VertexAttachment extends Attachment {
public class VertexAttachment extends Attachment {
private static var nextID : int = 0;
public var bones : Vector.<int>;
public var vertices : Vector.<Number>;
public var worldVerticesLength : int;
public var id : int = (nextID++ & 65535) << 11;
public var id : int = nextID++;
public var deformAttachment : VertexAttachment;
public function VertexAttachment(name : String) {
@ -46,11 +46,17 @@ package spine.attachments {
deformAttachment = this;
}
/** Transforms local vertices to world coordinates.
* @param start The index of the first local vertex value to transform. Each vertex has 2 values, x and y.
* @param count The number of world vertex values to output. Must be <= {@link #getWorldVerticesLength()} - start.
* @param worldVertices The output world vertices. Must have a length >= offset + count.
* @param offset The worldVertices index to begin writing values. */
/** Transforms the attachment's local {@link #vertices} to world coordinates. If the slot's {@link Slot#deform} is
* not empty, it is used to deform the vertices.
*
* See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
* Runtimes Guide.
* @param start The index of the first {@link #vertices} value to transform. Each vertex has 2 values, x and y.
* @param count The number of world vertex values to output. Must be <= {@link #worldVerticesLength} - `start`.
* @param worldVertices The output world vertices. Must have a length >= `offset` + `count` *
* `stride` / 2.
* @param offset The `worldVertices` index to begin writing values.
* @param stride The number of `worldVertices` entries between the value pairs written. */
public function computeWorldVertices(slot : Slot, start : int, count : int, worldVertices : Vector.<Number>, offset : int, stride : int) : void {
count = offset + (count >> 1) * stride;
var skeleton : Skeleton = slot.skeleton;

View File

@ -98,11 +98,11 @@ package spine.flash {
var wrapper : Sprite = wrappers[regionAttachment];
if (!wrapper) {
var region : AtlasRegion = AtlasRegion(regionAttachment.rendererObject);
var regionHeight : Number = region.rotate ? region.width : region.height;
var regionHeight : Number = region.degrees == 90 ? region.width : region.height;
var regionData : BitmapData = region.rendererObject as BitmapData;
if (!regionData) {
var bitmapData : BitmapData = region.page.rendererObject as BitmapData;
var regionWidth : Number = region.rotate ? region.height : region.width;
var regionWidth : Number = region.degrees == 90 ? region.height : region.width;
regionData = new BitmapData(regionWidth, regionHeight);
regionData.copyPixels(bitmapData, new Rectangle(region.x, region.y, regionWidth, regionHeight), new Point());
region.rendererObject = regionData;
@ -122,7 +122,7 @@ package spine.flash {
var sin : Number = Math.sin(radians);
var shiftX : Number = -regionAttachment.width / 2 * regionAttachment.scaleX;
var shiftY : Number = -regionAttachment.height / 2 * regionAttachment.scaleY;
if (region.rotate) {
if (region.degrees == 90) {
bitmap.rotation += 90;
shiftX += regionHeight * (regionAttachment.width / region.width);
}

View File

@ -31,9 +31,9 @@ package spine.interpolation {
import spine.Interpolation;
public class Pow extends Interpolation {
protected var power : Number;
protected var power : int;
public function Pow(power : Number) {
public function Pow(power : int) {
this.power = power;
}

View File

@ -41,7 +41,7 @@ package spine.vertexeffects {
private var _centerX : Number = 0, _centerY : Number = 0;
public function SwirlEffect(radius : Number) {
this._interpolation = new Pow(2);;
this._interpolation = new Pow(2);
this._radius = radius;
}

View File

@ -4,7 +4,7 @@
{
"type": "swf",
"request": "launch",
"name": "Launch Spine Starling Example",
"name": "Launch spine-starling-example",
"preLaunchTask": "Compile debug"
}
]

View File

@ -85,8 +85,7 @@ package spine.examples {
mesh.getVertexData().numVertices = idx;
var rgb: uint = Color.rgb(r * 255, g * 255, b * 255);
var alpha: uint = a * 255;
mesh.getVertexData().colorize("color", 0xffffffff, 0xff);
mesh.getVertexData().colorize("color", rgb, a);
mesh.setVertexDataChanged();
mesh.setIndexDataChanged();

View File

@ -57,6 +57,7 @@ package spine.examples {
[Embed(source = "/spineboy.png")]
static public const SpineboyAtlasTexture : Class;
private var skeleton : SkeletonAnimation;
private var shape: Shape;
@ -78,7 +79,7 @@ package spine.examples {
skeleton.y = 560;
skeleton.scale = 0.5;
skeleton.state.onStart.add(function(entry : TrackEntry) : void {
/* skeleton.state.onStart.add(function(entry : TrackEntry) : void {
trace(entry.trackIndex + " start: " + entry.animation.name);
});
skeleton.state.onInterrupt.add(function(entry : TrackEntry) : void {
@ -95,7 +96,7 @@ package spine.examples {
});
skeleton.state.onEvent.add(function(entry : TrackEntry, event : Event) : void {
trace(entry.trackIndex + " event: " + entry.animation.name + ", " + event.data.name + ": " + event.intValue + ", " + event.floatValue + ", " + event.stringValue + ", " + event.volume + ", " + event.balance);
});
});*/
skeleton.skeleton.setToSetupPose();
skeleton.state.setAnimationByName(0, "walk", true);
@ -108,7 +109,7 @@ package spine.examples {
shape = new Shape();
shape.setVertices(new <Number>[0, 0, 400, 600, 800, 0]);
shape.setColor(1, 0, 0, 1);
shape.setColor(0, 1, 0, 0.5);
addChild(shape);
Starling.juggler.add(shape);

View File

@ -59,11 +59,11 @@ package spine.examples {
attachmentLoader = new AtlasAttachmentLoader(spineAtlas);
var json : SkeletonJson = new SkeletonJson(attachmentLoader);
json.scale = 0.5;
json.scale = 0.2;
var skeletonData : SkeletonData = json.readSkeletonData(new TankJson());
skeleton = new SkeletonAnimation(skeletonData);
skeleton.x = 400;
skeleton.x = 700;
skeleton.y = 560;
skeleton.state.setAnimationByName(0, "drive", true);

View File

@ -59,8 +59,7 @@ package spine.starling {
public function newRegionAttachment(skin : Skin, name : String, path : String) : RegionAttachment {
var texture : SubTexture = getTexture(path) as SubTexture;
if (texture == null)
throw new Error("Region not found in Starling atlas: " + path + " (region attachment: " + name + ")");
if (texture == null) throw new Error("Region not found in Starling atlas: " + path + " (region attachment: " + name + ")");
var attachment : RegionAttachment = new RegionAttachment(name);
var rotated : Boolean = texture.rotated;
attachment.rendererObject = new Image(Texture.fromTexture(texture)); // Discard frame.
@ -78,24 +77,23 @@ package spine.starling {
tmp = attachment.regionWidth;
attachment.regionWidth = attachment.regionHeight;
attachment.regionHeight = tmp;
attachment["regionU2"] = 0;
attachment["regionV2"] = 1;
attachment["regionU"] = 1;
attachment["regionV"] = 0;
attachment.regionU2 = 0;
attachment.regionV2 = 1;
attachment.regionU = 1;
attachment.regionV = 0;
} else {
attachment["regionU"] = 0;
attachment["regionV"] = 0;
attachment["regionU2"] = 1;
attachment["regionV2"] = 1;
attachment.regionU = 0;
attachment.regionV = 0;
attachment.regionU2 = 1;
attachment.regionV2 = 1;
}
attachment.setUVs(attachment["regionU"], attachment["regionV"], attachment["regionU2"], attachment["regionV2"], rotated);
attachment.setUVs(attachment.regionU, attachment.regionV, attachment.regionU2, attachment.regionV2, rotated ? 90 : 0);
return attachment;
}
public function newMeshAttachment(skin : Skin, name : String, path : String) : MeshAttachment {
var texture : SubTexture = getTexture(path) as SubTexture;
if (texture == null)
throw new Error("Region not found in Starling atlas: " + path + " (mesh attachment: " + name + ")");
if (texture == null) throw new Error("Region not found in Starling atlas: " + path + " (mesh attachment: " + name + ")");
var rotated : Boolean = texture.rotated;
var attachment : MeshAttachment = new MeshAttachment(name);
attachment.regionRotate = rotated;

View File

@ -93,7 +93,7 @@ package spine.starling {
public function loadRegion(region : AtlasRegion) : void {
var image : Image = new Image(Texture(region.page.rendererObject));
if (region.rotate) {
if (region.degrees == 90) {
image.setTexCoords(0, region.u, region.v2);
image.setTexCoords(1, region.u, region.v);
image.setTexCoords(2, region.u2, region.v2);