diff --git a/spine-as3/spine-as3-example/.vscode/launch.json b/spine-as3/spine-as3-example/.vscode/launch.json index 8417cb33f..30f73a3df 100644 --- a/spine-as3/spine-as3-example/.vscode/launch.json +++ b/spine-as3/spine-as3-example/.vscode/launch.json @@ -4,7 +4,7 @@ { "type": "swf", "request": "launch", - "name": "Launch Spine AS3 SWF", + "name": "Launch spine-as3-example", "preLaunchTask": "Compile debug" } ] diff --git a/spine-as3/spine-as3-example/src/spine/examples/Main.as b/spine-as3/spine-as3-example/src/spine/examples/Main.as index c2eaf9af9..b699c21e9 100644 --- a/spine-as3/spine-as3-example/src/spine/examples/Main.as +++ b/spine-as3/spine-as3-example/src/spine/examples/Main.as @@ -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() { diff --git a/spine-as3/spine-as3/src/spine/BinaryInput.as b/spine-as3/spine-as3/src/spine/BinaryInput.as index 1d5fefac7..645f06418 100644 --- a/spine-as3/spine-as3/src/spine/BinaryInput.as +++ b/spine-as3/spine-as3/src/spine/BinaryInput.as @@ -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 { diff --git a/spine-as3/spine-as3/src/spine/Bone.as b/spine-as3/spine-as3/src/spine/Bone.as index 775603f13..637a39fab 100644 --- a/spine-as3/spine-as3/src/spine/Bone.as +++ b/spine-as3/spine-as3/src/spine/Bone.as @@ -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.) : 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.) : 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 { diff --git a/spine-as3/spine-as3/src/spine/Color.as b/spine-as3/spine-as3/src/spine/Color.as index ef54d60c7..97f1c6a19 100644 --- a/spine-as3/spine-as3/src/spine/Color.as +++ b/spine-as3/spine-as3/src/spine/Color.as @@ -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; } diff --git a/spine-as3/spine-as3/src/spine/IkConstraint.as b/spine-as3/spine-as3/src/spine/IkConstraint.as index 0847b5eb7..07996ef20 100644 --- a/spine-as3/spine-as3/src/spine/IkConstraint.as +++ b/spine-as3/spine-as3/src/spine/IkConstraint.as @@ -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; diff --git a/spine-as3/spine-as3/src/spine/PathConstraint.as b/spine-as3/spine-as3/src/spine/PathConstraint.as index 91908bd8a..1ee1bd12d 100644 --- a/spine-as3/spine-as3/src/spine/PathConstraint.as +++ b/spine-as3/spine-as3/src/spine/PathConstraint.as @@ -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.; 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. = new Vector.(); internal const _positions : Vector. = new Vector.(); internal const _world : Vector. = new Vector.(); internal const _curves : Vector. = new Vector.(); internal const _lengths : Vector. = new Vector.(); - internal const _segments : Vector. = new Vector.(10); + internal const _segments : Vector. = new Vector.(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. = this._bones; this._spaces.length = spacesCount; - var spaces : Vector. = this._spaces, lengths : Vector. = null; + var spaces : Vector. = this._spaces, lengths : Vector. = _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. = computeWorldPositions(attachment, spacesCount, tangents, data.positionMode == PositionMode.percent, percentSpacing); + var positions : Vector. = 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. { + protected function computeWorldPositions(path : PathAttachment, spacesCount : int, tangents : Boolean) : Vector. { var target : Slot = this.target; var position : Number = this.position; var spaces : Vector. = this._spaces; @@ -186,21 +217,29 @@ package spine { var out : Vector. = this._positions, world : Vector.; 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. = 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. = 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; diff --git a/spine-as3/spine-as3/src/spine/PathConstraintData.as b/spine-as3/spine-as3/src/spine/PathConstraintData.as index 69801ced9..d6a7f1e18 100644 --- a/spine-as3/spine-as3/src/spine/PathConstraintData.as +++ b/spine-as3/spine-as3/src/spine/PathConstraintData.as @@ -28,14 +28,14 @@ *****************************************************************************/ package spine { - public dynamic class PathConstraintData extends ConstraintData { + public class PathConstraintData extends ConstraintData { internal var _bones : Vector. = new Vector.(); 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); diff --git a/spine-as3/spine-as3/src/spine/Skeleton.as b/spine-as3/spine-as3/src/spine/Skeleton.as index 1d4428a7b..472b2338d 100644 --- a/spine-as3/spine-as3/src/spine/Skeleton.as +++ b/spine-as3/spine-as3/src/spine/Skeleton.as @@ -45,7 +45,6 @@ package spine { public var transformConstraints : Vector.; public var pathConstraints : Vector.; private var _updateCache : Vector. = new Vector.(); - private var _updateCacheReset : Vector. = new Vector.(); 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. = this._updateCache; updateCache.length = 0; - this._updateCacheReset.length = 0; var bones : Vector. = 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. = this.ikConstraints; var transformConstraints : Vector. = this.transformConstraints; var pathConstraints : Vector. = 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., 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. = 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. = 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. = 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. = 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. = _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. = _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; } } diff --git a/spine-as3/spine-as3/src/spine/SkeletonBinary.as b/spine-as3/spine-as3/src/spine/SkeletonBinary.as index e3cdf30d6..274622a32 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonBinary.as +++ b/spine-as3/spine-as3/src/spine/SkeletonBinary.as @@ -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. { + private function readFloatArray (input: BinaryInput, n: int, scale: Number): Vector. { var i : int = 0; var array : Vector. = new Vector.(); 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. = new Vector.(); 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. = 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.; 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); + } } } diff --git a/spine-as3/spine-as3/src/spine/SkeletonJson.as b/spine-as3/spine-as3/src/spine/SkeletonJson.as index 88623e090..f7c895255 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonJson.as +++ b/spine-as3/spine-as3/src/spine/SkeletonJson.as @@ -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 SkeletonJson { public var attachmentLoader : AttachmentLoader; public var scale : Number = 1; @@ -147,95 +119,98 @@ package spine { // IK constraints. for each (var constraintMap : Object in root["ik"]) { - var ikConstraintData : IkConstraintData = new IkConstraintData(constraintMap["name"]); - ikConstraintData.order = constraintMap["order"] || 0; - ikConstraintData.skinRequired = getValue(constraintMap, "skin", false); + var ikData : IkConstraintData = new IkConstraintData(constraintMap["name"]); + ikData.order = constraintMap["order"] || 0; + ikData.skinRequired = getValue(constraintMap, "skin", false); for each (boneName in constraintMap["bones"]) { var bone : BoneData = skeletonData.findBone(boneName); if (!bone) throw new Error("IK constraint bone not found: " + boneName); - ikConstraintData.bones.push(bone); + ikData.bones.push(bone); } - ikConstraintData.target = skeletonData.findBone(constraintMap["target"]); - if (!ikConstraintData.target) throw new Error("Target bone not found: " + constraintMap["target"]); + ikData.target = skeletonData.findBone(constraintMap["target"]); + if (!ikData.target) throw new Error("Target bone not found: " + constraintMap["target"]); - ikConstraintData.bendDirection = (!constraintMap.hasOwnProperty("bendPositive") || constraintMap["bendPositive"]) ? 1 : -1; - ikConstraintData.compress = getValue(constraintMap, "compress", false); - ikConstraintData.stretch = getValue(constraintMap, "stretch", false); - ikConstraintData.uniform = getValue(constraintMap, "uniform", false); - ikConstraintData.softness = getNumber(constraintMap, "softness", 0) * scale; - ikConstraintData.mix = getNumber(constraintMap, "mix", 1); + ikData.bendDirection = (!constraintMap.hasOwnProperty("bendPositive") || constraintMap["bendPositive"]) ? 1 : -1; + ikData.compress = getValue(constraintMap, "compress", false); + ikData.stretch = getValue(constraintMap, "stretch", false); + ikData.uniform = getValue(constraintMap, "uniform", false); + ikData.softness = getNumber(constraintMap, "softness", 0) * scale; + ikData.mix = getNumber(constraintMap, "mix", 1); - skeletonData.ikConstraints.push(ikConstraintData); + skeletonData.ikConstraints.push(ikData); } // Transform constraints. for each (constraintMap in root["transform"]) { - var transformConstraintData : TransformConstraintData = new TransformConstraintData(constraintMap["name"]); - transformConstraintData.order = constraintMap["order"] || 0; - transformConstraintData.skinRequired = getValue(constraintMap, "skin", false); + var transformData : TransformConstraintData = new TransformConstraintData(constraintMap["name"]); + transformData.order = constraintMap["order"] || 0; + transformData.skinRequired = getValue(constraintMap, "skin", false); for each (boneName in constraintMap["bones"]) { bone = skeletonData.findBone(boneName); if (!bone) throw new Error("Transform constraint bone not found: " + boneName); - transformConstraintData.bones.push(bone); + transformData.bones.push(bone); } - transformConstraintData.target = skeletonData.findBone(constraintMap["target"]); - if (!transformConstraintData.target) throw new Error("Target bone not found: " + constraintMap["target"]); + transformData.target = skeletonData.findBone(constraintMap["target"]); + if (!transformData.target) throw new Error("Target bone not found: " + constraintMap["target"]); - transformConstraintData.local = getValue(constraintMap, "local", false); - transformConstraintData.relative = getValue(constraintMap, "relative", false); + transformData.local = getValue(constraintMap, "local", false); + transformData.relative = getValue(constraintMap, "relative", false); - transformConstraintData.offsetRotation = Number(constraintMap["rotation"] || 0); - transformConstraintData.offsetX = Number(constraintMap["x"] || 0) * scale; - transformConstraintData.offsetY = Number(constraintMap["y"] || 0) * scale; - transformConstraintData.offsetScaleX = Number(constraintMap["scaleX"] || 0); - transformConstraintData.offsetScaleY = Number(constraintMap["scaleY"] || 0); - transformConstraintData.offsetShearY = Number(constraintMap["shearY"] || 0); + transformData.offsetRotation = Number(constraintMap["rotation"] || 0); + transformData.offsetX = Number(constraintMap["x"] || 0) * scale; + transformData.offsetY = Number(constraintMap["y"] || 0) * scale; + transformData.offsetScaleX = Number(constraintMap["scaleX"] || 0); + transformData.offsetScaleY = Number(constraintMap["scaleY"] || 0); + transformData.offsetShearY = Number(constraintMap["shearY"] || 0); - transformConstraintData.rotateMix = getNumber(constraintMap, "rotateMix", 1); - transformConstraintData.translateMix = getNumber(constraintMap, "translateMix", 1); - transformConstraintData.scaleMix = getNumber(constraintMap, "scaleMix", 1); - transformConstraintData.shearMix = getNumber(constraintMap, "shearMix", 1); + transformData.mixRotate = getNumber(constraintMap, "mixRotate", 1); + transformData.mixX = getNumber(constraintMap, "mixX", 1); + transformData.mixY = getNumber(constraintMap, "mixY", transformData.mixX); + transformData.mixScaleX = getNumber(constraintMap, "mixScaleX", 1); + transformData.mixScaleY = getNumber(constraintMap, "mixScaleY", transformData.mixScaleX); + transformData.mixShearY = getNumber(constraintMap, "mixShearY", 1); - skeletonData.transformConstraints.push(transformConstraintData); + skeletonData.transformConstraints.push(transformData); } // Path constraints. for each (constraintMap in root["path"]) { - var pathConstraintData : PathConstraintData = new PathConstraintData(constraintMap["name"]); - pathConstraintData.order = constraintMap["order"] || 0; - pathConstraintData.skinRequired = getValue(constraintMap, "skin", false); + var pathData : PathConstraintData = new PathConstraintData(constraintMap["name"]); + pathData.order = constraintMap["order"] || 0; + pathData.skinRequired = getValue(constraintMap, "skin", false); for each (boneName in constraintMap["bones"]) { bone = skeletonData.findBone(boneName); if (!bone) throw new Error("Path constraint bone not found: " + boneName); - pathConstraintData.bones.push(bone); + pathData.bones.push(bone); } - pathConstraintData.target = skeletonData.findSlot(constraintMap["target"]); - if (!pathConstraintData.target) throw new Error("Path target slot not found: " + constraintMap["target"]); + pathData.target = skeletonData.findSlot(constraintMap["target"]); + if (!pathData.target) throw new Error("Path target slot not found: " + constraintMap["target"]); - pathConstraintData.positionMode = PositionMode[constraintMap["positionMode"] || "percent"]; - pathConstraintData.spacingMode = SpacingMode[constraintMap["spacingMode"] || "length"]; - pathConstraintData.rotateMode = RotateMode[constraintMap["rotateMode"] || "tangent"]; - pathConstraintData.offsetRotation = Number(constraintMap["rotation"] || 0); - pathConstraintData.position = Number(constraintMap["position"] || 0); - if (pathConstraintData.positionMode == PositionMode.fixed) pathConstraintData.position *= scale; - pathConstraintData.spacing = Number(constraintMap["spacing"] || 0); - if (pathConstraintData.spacingMode == SpacingMode.length || pathConstraintData.spacingMode == SpacingMode.fixed) pathConstraintData.spacing *= scale; - pathConstraintData.rotateMix = getNumber(constraintMap, "rotateMix", 1); - pathConstraintData.translateMix = getNumber(constraintMap, "translateMix", 1); + pathData.positionMode = PositionMode[constraintMap["positionMode"] || "percent"]; + pathData.spacingMode = SpacingMode[constraintMap["spacingMode"] || "length"]; + pathData.rotateMode = RotateMode[constraintMap["rotateMode"] || "tangent"]; + pathData.offsetRotation = Number(constraintMap["rotation"] || 0); + pathData.position = Number(constraintMap["position"] || 0); + if (pathData.positionMode == PositionMode.fixed) pathData.position *= scale; + pathData.spacing = Number(constraintMap["spacing"] || 0); + if (pathData.spacingMode == SpacingMode.length || pathData.spacingMode == SpacingMode.fixed) pathData.spacing *= scale; + pathData.mixRotate = getNumber(constraintMap, "mixRotate", 1); + pathData.mixX = getNumber(constraintMap, "mixX", 1); + pathData.mixY = getNumber(constraintMap, "mixY", 1); - skeletonData.pathConstraints.push(pathConstraintData); + skeletonData.pathConstraints.push(pathData); } // Skins. var skins : Object = root["skins"]; - for (var i : Number = 0; i < skins.length; i++) { - var ii : Number; + for (var i : int = 0; i < skins.length; i++) { + var ii : int; var skinMap : Object = skins[i]; var skin : Skin = new Skin(skinMap["name"]); @@ -281,9 +256,8 @@ package spine { skin.setAttachment(slot.index, attachmentName, attachment); } } - skeletonData.skins[skeletonData.skins.length] = skin; - if (skin.name == "default") - skeletonData.defaultSkin = skin; + skeletonData.skins.push(skin); + if (skin.name == "default") skeletonData.defaultSkin = skin; } // Linked meshes. @@ -328,101 +302,97 @@ package spine { private function readAttachment(map : Object, skin : Skin, slotIndex : int, name : String, skeletonData: SkeletonData) : Attachment { name = map["name"] || name; - var typeName : String = map["type"] || "region"; - var type : AttachmentType = AttachmentType[typeName]; - var scale : Number = this.scale; var color : String; - switch (type) { - case AttachmentType.region: - var region : RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, map["path"] || name); - if (!region) return null; - region.path = map["path"] || name; - region.x = Number(map["x"] || 0) * scale; - region.y = Number(map["y"] || 0) * scale; - region.scaleX = getNumber(map, "scaleX", 1); - region.scaleY = getNumber(map, "scaleY", 1); - region.rotation = map["rotation"] || 0; - region.width = Number(map["width"] || 0) * scale; - region.height = Number(map["height"] || 0) * scale; + switch (AttachmentType[getValue(map, "type", "region")]) { + case AttachmentType.region: + var region : RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, map["path"] || name); + if (!region) return null; + region.path = map["path"] || name; + region.x = Number(map["x"] || 0) * scale; + region.y = Number(map["y"] || 0) * scale; + region.scaleX = getNumber(map, "scaleX", 1); + region.scaleY = getNumber(map, "scaleY", 1); + region.rotation = map["rotation"] || 0; + region.width = Number(map["width"] || 0) * scale; + region.height = Number(map["height"] || 0) * scale; - color = map["color"]; - if (color) region.color.setFromString(color); + color = map["color"]; + if (color) region.color.setFromString(color); - region.updateOffset(); - return region; - case AttachmentType.mesh: - case AttachmentType.linkedmesh: - var mesh : MeshAttachment = attachmentLoader.newMeshAttachment(skin, name, map["path"] || name); - if (!mesh) return null; - mesh.path = map["path"] || name; + region.updateOffset(); + return region; + case AttachmentType.mesh: + case AttachmentType.linkedmesh: + var mesh : MeshAttachment = attachmentLoader.newMeshAttachment(skin, name, map["path"] || name); + if (!mesh) return null; + mesh.path = map["path"] || name; - color = map["color"]; - if (color) mesh.color.setFromString(color); + color = map["color"]; + if (color) mesh.color.setFromString(color); - mesh.width = Number(map["width"] || 0) * scale; - mesh.height = Number(map["height"] || 0) * scale; - if (map["parent"]) { - var inheritDeform : Boolean = getValue(map, "deform", true); - linkedMeshes.push(new LinkedMesh(mesh, map["skin"], slotIndex, map["parent"], inheritDeform)); - return mesh; - } - var uvs : Vector. = getFloatArray(map, "uvs", 1); - readVertices(map, mesh, uvs.length); - mesh.triangles = getUintArray(map, "triangles"); - mesh.regionUVs = uvs; - mesh.updateUVs(); - mesh.hullLength = int(map["hull"] || 0) * 2; - if (map["edges"]) mesh.edges = getIntArray(map, "edges"); + mesh.width = Number(map["width"] || 0) * scale; + mesh.height = Number(map["height"] || 0) * scale; + if (map["parent"]) { + var inheritDeform : Boolean = getValue(map, "deform", true); + linkedMeshes.push(new LinkedMesh(mesh, map["skin"], slotIndex, map["parent"], inheritDeform)); return mesh; - case AttachmentType.boundingbox: - var box : BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name); - if (!box) return null; - readVertices(map, box, int(map["vertexCount"]) << 1); - return box; - case AttachmentType.path: - var path : PathAttachment = attachmentLoader.newPathAttachment(skin, name); - if (!path) return null; - path.closed = getValue(map, "closed", false); - path.constantSpeed = getValue(map, "constantSpeed", true); - var vertexCount : int = int(map["vertexCount"]); - readVertices(map, path, vertexCount << 1); - var lengths : Vector. = new Vector.(); - for each (var curves : Object in map["lengths"]) { - lengths.push(Number(curves) * scale); - } - path.lengths = lengths; - return path; - case AttachmentType.point: - var point : PointAttachment = attachmentLoader.newPointAttachment(skin, name); - if (!point) return null; - point.x = getNumber(map, "x", 0) * scale; - point.y = getNumber(map, "y", 0) * scale; - point.rotation = getNumber(map, "rotation", 0); + } + var uvs : Vector. = getFloatArray(map, "uvs", 1); + readVertices(map, mesh, uvs.length); + mesh.triangles = getUintArray(map, "triangles"); + mesh.regionUVs = uvs; + mesh.updateUVs(); + mesh.hullLength = int(map["hull"] || 0) * 2; + if (map["edges"]) mesh.edges = getIntArray(map, "edges"); + return mesh; + case AttachmentType.boundingbox: + var box : BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name); + if (!box) return null; + readVertices(map, box, int(map["vertexCount"]) << 1); + return box; + case AttachmentType.path: + var path : PathAttachment = attachmentLoader.newPathAttachment(skin, name); + if (!path) return null; + path.closed = getValue(map, "closed", false); + path.constantSpeed = getValue(map, "constantSpeed", true); + var vertexCount : int = int(map["vertexCount"]); + readVertices(map, path, vertexCount << 1); + var lengths : Vector. = new Vector.(); + for each (var curves : Object in map["lengths"]) { + lengths.push(Number(curves) * scale); + } + path.lengths = lengths; + return path; + case AttachmentType.point: + var point : PointAttachment = attachmentLoader.newPointAttachment(skin, name); + if (!point) return null; + point.x = getNumber(map, "x", 0) * scale; + point.y = getNumber(map, "y", 0) * scale; + point.rotation = getNumber(map, "rotation", 0); - color = map["color"]; - if (color) point.color.setFromString(color); + color = map["color"]; + if (color) point.color.setFromString(color); - return point; - case AttachmentType.clipping: - var clip : ClippingAttachment = attachmentLoader.newClippingAttachment(skin, name); - if (!clip) return null; - var end : String = map["end"]; - if (end != null) { - var slot : SlotData = skeletonData.findSlot(end); - if (slot == null) throw new Error("Clipping end slot not found: " + end); - clip.endSlot = slot; - } + return point; + case AttachmentType.clipping: + var clip : ClippingAttachment = attachmentLoader.newClippingAttachment(skin, name); + if (!clip) return null; + var end : String = map["end"]; + if (end != null) { + var slot : SlotData = skeletonData.findSlot(end); + if (slot == null) throw new Error("Clipping end slot not found: " + end); + clip.endSlot = slot; + } - vertexCount = int(map["vertexCount"]); - readVertices(map, clip, vertexCount << 1); + vertexCount = int(map["vertexCount"]); + readVertices(map, clip, vertexCount << 1); - color = map["color"]; - if (color) clip.color.setFromString(color); + color = map["color"]; + if (color) clip.color.setFromString(color); - return clip; + return clip; } - return null; } @@ -460,63 +430,156 @@ package spine { private function readAnimation(map : Object, name : String, skeletonData : SkeletonData) : void { var scale : Number = this.scale; var timelines : Vector. = new Vector.(); - var duration : Number = 0; var slotMap : Object, slotIndex : int, slotName : String; - var values : Array, valueMap : Object, frameIndex : int; - var i : int; + var timelineMap : Array, keyMap : Object, nextMap : Object; + var frame : int, bezier : int; + var time : Number, time2 : Number; + var curve : Object; var timelineName : String; + var i : int, n : int; + // Slot timelines. var slots : Object = map["slots"]; for (slotName in slots) { slotMap = slots[slotName]; slotIndex = skeletonData.findSlotIndex(slotName); for (timelineName in slotMap) { - values = slotMap[timelineName]; + timelineMap = slotMap[timelineName]; + if (!timelineMap) continue; if (timelineName == "attachment") { - var attachmentTimeline : AttachmentTimeline = new AttachmentTimeline(values.length); - attachmentTimeline.slotIndex = slotIndex; - - frameIndex = 0; - for each (valueMap in values) - attachmentTimeline.setFrame(frameIndex++, Number(valueMap["time"] || 0), valueMap["name"]); - timelines[timelines.length] = attachmentTimeline; - duration = Math.max(duration, attachmentTimeline.frames[attachmentTimeline.frameCount - 1]); - } else if (timelineName == "color") { - var colorTimeline : ColorTimeline = new ColorTimeline(values.length); - colorTimeline.slotIndex = slotIndex; - - frameIndex = 0; - for each (valueMap in values) { - var frameColor : Color = Color.fromString(valueMap["color"]); - colorTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), frameColor.r, frameColor.g, frameColor.b, frameColor.a); - readCurve(valueMap, colorTimeline, frameIndex); - frameIndex++; + var attachmentTimeline : AttachmentTimeline = new AttachmentTimeline(timelineMap.length, slotIndex); + for (frame = 0; frame < timelineMap.length; frame++) { + keyMap = timelineMap[frame]; + attachmentTimeline.setFrame(frame, getNumber(keyMap, "time", 0), keyMap.name); } - timelines[timelines.length] = colorTimeline; - duration = Math.max(duration, colorTimeline.frames[(colorTimeline.frameCount - 1) * ColorTimeline.ENTRIES]); - } else if (timelineName == "twoColor") { - var twoColorTimeline : TwoColorTimeline = new TwoColorTimeline(values.length); - twoColorTimeline.slotIndex = slotIndex; + timelines.push(attachmentTimeline); - frameIndex = 0; - for each (valueMap in values) { - var color : String = valueMap["light"]; - var darkColor : String = valueMap["dark"]; - var light : Color = Color.fromString(color); - var dark : Color = Color.fromString(darkColor); - twoColorTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), light.r, light.g, light.b, light.a, dark.r, dark.g, dark.b); - readCurve(valueMap, twoColorTimeline, frameIndex); - frameIndex++; + } else if (timelineName == "rgba") { + var rgbaTimeline : RGBATimeline = new RGBATimeline(timelineMap.length, timelineMap.length << 2, slotIndex); + keyMap = timelineMap[0]; + time = getNumber(keyMap, "time", 0); + var rgba : Color = Color.fromString(keyMap.color); + + for (frame = 0, bezier = 0;; frame++) { + rgbaTimeline.setFrame(frame, time, rgba.r, rgba.g, rgba.b, rgba.a); + if (timelineMap.length == frame + 1) break; + nextMap = timelineMap[frame + 1]; + time2 = getNumber(nextMap, "time", 0); + var newRgba : Color = Color.fromString(nextMap.color); + curve = keyMap.curve; + if (curve) { + bezier = readCurve(curve, rgbaTimeline, bezier, frame, 0, time, time2, rgba.r, newRgba.r, 1); + bezier = readCurve(curve, rgbaTimeline, bezier, frame, 1, time, time2, rgba.g, newRgba.g, 1); + bezier = readCurve(curve, rgbaTimeline, bezier, frame, 2, time, time2, rgba.b, newRgba.b, 1); + bezier = readCurve(curve, rgbaTimeline, bezier, frame, 3, time, time2, rgba.a, newRgba.a, 1); + } + time = time2; + rgba = newRgba; + keyMap = nextMap; } - timelines[timelines.length] = twoColorTimeline; - duration = Math.max(duration, twoColorTimeline.frames[(twoColorTimeline.frameCount - 1) * TwoColorTimeline.ENTRIES]); + + timelines.push(rgbaTimeline); + + } else if (timelineName == "rgb") { + var rgbTimeline : RGBTimeline = new RGBTimeline(timelineMap.length, timelineMap.length * 3, slotIndex); + keyMap = timelineMap[0]; + time = getNumber(keyMap, "time", 0); + var rgb : Color = Color.fromString(keyMap.color); + + for (frame = 0, bezier = 0;; frame++) { + rgbTimeline.setFrame(frame, time, rgb.r, rgb.g, rgb.b); + if (timelineMap.length == frame + 1) break; + nextMap = timelineMap[frame + 1]; + time2 = getNumber(nextMap, "time", 0); + var newRgb : Color = Color.fromString(nextMap.color); + curve = keyMap.curve; + if (curve) { + bezier = readCurve(curve, rgbTimeline, bezier, frame, 0, time, time2, rgb.r, newRgb.r, 1); + bezier = readCurve(curve, rgbTimeline, bezier, frame, 1, time, time2, rgb.g, newRgb.g, 1); + bezier = readCurve(curve, rgbTimeline, bezier, frame, 2, time, time2, rgb.b, newRgb.b, 1); + } + time = time2; + rgb = newRgb; + keyMap = nextMap; + } + + timelines.push(rgbTimeline); + + } else if (timelineName == "alpha") { + timelines.push(readTimeline(timelineMap, new AlphaTimeline(timelineMap.length, timelineMap.length, slotIndex), 0, 1)); + } else if (timelineName == "rgba2") { + var rgba2Timeline : RGBA2Timeline = new RGBA2Timeline(timelineMap.length, timelineMap.length * 7, slotIndex); + + keyMap = timelineMap[0]; + time = getNumber(keyMap, "time", 0); + var lighta : Color = Color.fromString(keyMap.light); + var darka : Color = Color.fromString(keyMap.dark); + + for (frame = 0, bezier = 0;; frame++) { + rgba2Timeline.setFrame(frame, time, lighta.r, lighta.g, lighta.b, lighta.a, darka.r, darka.g, darka.b); + if (timelineMap.length == frame + 1) break; + nextMap = timelineMap[frame + 1]; + time2 = getNumber(nextMap, "time", 0); + var newLighta : Color = Color.fromString(nextMap.light); + var newDarka : Color = Color.fromString(nextMap.dark); + curve = keyMap.curve; + if (curve) { + bezier = readCurve(curve, rgba2Timeline, bezier, frame, 0, time, time2, lighta.r, newLighta.r, 1); + bezier = readCurve(curve, rgba2Timeline, bezier, frame, 1, time, time2, lighta.g, newLighta.g, 1); + bezier = readCurve(curve, rgba2Timeline, bezier, frame, 2, time, time2, lighta.b, newLighta.b, 1); + bezier = readCurve(curve, rgba2Timeline, bezier, frame, 3, time, time2, lighta.a, newLighta.a, 1); + bezier = readCurve(curve, rgba2Timeline, bezier, frame, 4, time, time2, darka.r, newDarka.r, 1); + bezier = readCurve(curve, rgba2Timeline, bezier, frame, 5, time, time2, darka.g, newDarka.g, 1); + bezier = readCurve(curve, rgba2Timeline, bezier, frame, 6, time, time2, darka.b, newDarka.b, 1); + } + time = time2; + lighta = newLighta; + darka = newDarka; + keyMap = nextMap; + } + + timelines.push(rgba2Timeline); + + } else if (timelineName == "rgb2") { + var rgb2Timeline : RGB2Timeline = new RGB2Timeline(timelineMap.length, timelineMap.length * 6, slotIndex); + + keyMap = timelineMap[0]; + time = getNumber(keyMap, "time", 0); + var light : Color = Color.fromString(keyMap.light); + var dark : Color = Color.fromString(keyMap.dark); + + for (frame = 0, bezier = 0;; frame++) { + rgb2Timeline.setFrame(frame, time, light.r, light.g, light.b, dark.r, dark.g, dark.b); + if (timelineMap.length == frame + 1) break; + nextMap = timelineMap[frame + 1]; + time2 = getNumber(nextMap, "time", 0); + var newLight : Color = Color.fromString(nextMap.light); + var newDark : Color = Color.fromString(nextMap.dark); + curve = keyMap.curve; + if (curve) { + bezier = readCurve(curve, rgb2Timeline, bezier, frame, 0, time, time2, light.r, newLight.r, 1); + bezier = readCurve(curve, rgb2Timeline, bezier, frame, 1, time, time2, light.g, newLight.g, 1); + bezier = readCurve(curve, rgb2Timeline, bezier, frame, 2, time, time2, light.b, newLight.b, 1); + bezier = readCurve(curve, rgb2Timeline, bezier, frame, 3, time, time2, dark.r, newDark.r, 1); + bezier = readCurve(curve, rgb2Timeline, bezier, frame, 4, time, time2, dark.g, newDark.g, 1); + bezier = readCurve(curve, rgb2Timeline, bezier, frame, 5, time, time2, dark.b, newDark.b, 1); + } + time = time2; + light = newLight; + dark = newDark; + keyMap = nextMap; + } + + timelines.push(rgb2Timeline); + } else throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); } } + // Bone timelines. var bones : Object = map["bones"]; for (var boneName : String in bones) { var boneIndex : int = skeletonData.findBoneIndex(boneName); @@ -524,88 +587,132 @@ package spine { var boneMap : Object = bones[boneName]; for (timelineName in boneMap) { - values = boneMap[timelineName]; - if (timelineName == "rotate") { - var rotateTimeline : RotateTimeline = new RotateTimeline(values.length); - rotateTimeline.boneIndex = boneIndex; + timelineMap = boneMap[timelineName]; + if (timelineMap.length == 0) continue; - frameIndex = 0; - for each (valueMap in values) { - rotateTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), Number(valueMap["angle"] || 0)); - readCurve(valueMap, rotateTimeline, frameIndex); - frameIndex++; - } - timelines[timelines.length] = rotateTimeline; - duration = Math.max(duration, rotateTimeline.frames[(rotateTimeline.frameCount - 1) * RotateTimeline.ENTRIES]); - } else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") { - var translateTimeline : TranslateTimeline; - var timelineScale : Number = 1; - var defaultValue : Number = 0; - if (timelineName == "scale") { - translateTimeline = new ScaleTimeline(values.length); - defaultValue = 1; - } else if (timelineName == "shear") - translateTimeline = new ShearTimeline(values.length); - else { - translateTimeline = new TranslateTimeline(values.length); - timelineScale = scale; - } - translateTimeline.boneIndex = boneIndex; - - frameIndex = 0; - for each (valueMap in values) { - var x : Number = getNumber(valueMap, "x", defaultValue) * timelineScale; - var y : Number = getNumber(valueMap, "y", defaultValue) * timelineScale; - translateTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), x, y); - readCurve(valueMap, translateTimeline, frameIndex); - frameIndex++; - } - timelines[timelines.length] = translateTimeline; - duration = Math.max(duration, translateTimeline.frames[(translateTimeline.frameCount - 1) * TranslateTimeline.ENTRIES]); + if (timelineName === "rotate") { + timelines.push(readTimeline(timelineMap, new RotateTimeline(timelineMap.length, timelineMap.length, boneIndex), 0, 1)); + } else if (timelineName === "translate") { + var translateTimeline : TranslateTimeline = new TranslateTimeline(timelineMap.length, timelineMap.length << 1, boneIndex); + timelines.push(readTimeline2(timelineMap, translateTimeline, "x", "y", 0, scale)); + } else if (timelineName === "translatex") { + var translateXTimeline : TranslateXTimeline = new TranslateXTimeline(timelineMap.length, timelineMap.length, boneIndex); + timelines.push(readTimeline(timelineMap, translateXTimeline, 0, scale)); + } else if (timelineName === "translatey") { + var translateYTimeline : TranslateYTimeline = new TranslateYTimeline(timelineMap.length, timelineMap.length, boneIndex); + timelines.push(readTimeline(timelineMap, translateYTimeline, 0, scale)); + } else if (timelineName === "scale") { + var scaleTimeline : ScaleTimeline = new ScaleTimeline(timelineMap.length, timelineMap.length << 1, boneIndex); + timelines.push(readTimeline2(timelineMap, scaleTimeline, "x", "y", 1, 1)); + } else if (timelineName === "scalex") { + var scaleXTimeline : ScaleXTimeline = new ScaleXTimeline(timelineMap.length, timelineMap.length, boneIndex); + timelines.push(readTimeline(timelineMap, scaleXTimeline, 1, 1)); + } else if (timelineName === "scaley") { + var scaleYTimeline : ScaleYTimeline = new ScaleYTimeline(timelineMap.length, timelineMap.length, boneIndex); + timelines.push(readTimeline(timelineMap, scaleYTimeline, 1, 1)); + } else if (timelineName === "shear") { + var shearTimeline : ShearTimeline = new ShearTimeline(timelineMap.length, timelineMap.length << 1, boneIndex); + timelines.push(readTimeline2(timelineMap, shearTimeline, "x", "y", 0, 1)); + } else if (timelineName === "shearx") { + var shearXTimeline : ShearXTimeline = new ShearXTimeline(timelineMap.length, timelineMap.length, boneIndex); + timelines.push(readTimeline(timelineMap, shearXTimeline, 0, 1)); + } else if (timelineName === "sheary") { + var shearYTimeline : ShearYTimeline = new ShearYTimeline(timelineMap.length, timelineMap.length, boneIndex); + timelines.push(readTimeline(timelineMap, shearYTimeline, 0, 1)); } else throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); } } + // IK constraint timelines. var ikMap : Object = map["ik"]; for (var ikConstraintName : String in ikMap) { - var ikConstraint : IkConstraintData = skeletonData.findIkConstraint(ikConstraintName); - values = ikMap[ikConstraintName]; - var ikTimeline : IkConstraintTimeline = new IkConstraintTimeline(values.length); - ikTimeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint); - frameIndex = 0; - for each (valueMap in values) { - var mix : Number = getNumber(valueMap, "mix", 1); - var bendDirection : int = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1; - var compress : Boolean = getValue(valueMap, "compress", false); - var stretch : Boolean = getValue(valueMap, "stretch", false); - var softness : Number = getNumber(valueMap, "softness", 0) * scale; - ikTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), mix, softness, bendDirection, compress, stretch); - readCurve(valueMap, ikTimeline, frameIndex); - frameIndex++; + timelineMap = map.ik[ikConstraintName]; + keyMap = timelineMap[0]; + if (!keyMap) continue; + + var ikIndex : int = skeletonData.ikConstraints.indexOf(skeletonData.findIkConstraint(ikConstraintName)); + var ikTimeline : IkConstraintTimeline = new IkConstraintTimeline(timelineMap.length, timelineMap.length << 1, ikIndex); + + time = getNumber(keyMap, "time", 0); + var mix : Number = getNumber(keyMap, "mix", 1); + var softness : Number = getNumber(keyMap, "softness", 0) * scale; + + for (frame = 0, bezier = 0;; frame++) { + ikTimeline.setFrame(frame, time, mix, softness, getValue(keyMap, "bendPositive", true) ? 1 : -1, getValue(keyMap, "compress", false), getValue(keyMap, "stretch", false)); + nextMap = timelineMap[frame + 1]; + if (!nextMap) break; + + time2 = getNumber(nextMap, "time", 0); + var mix2 : Number = getNumber(nextMap, "mix", 1); + var softness2 : Number = getNumber(nextMap, "softness", 0) * scale; + curve = keyMap.curve; + if (curve) { + bezier = readCurve(curve, ikTimeline, bezier, frame, 0, time, time2, mix, mix2, 1); + bezier = readCurve(curve, ikTimeline, bezier, frame, 1, time, time2, softness, softness2, scale); + } + + time = time2; + mix = mix2; + softness = softness2; + keyMap = nextMap; } - timelines[timelines.length] = ikTimeline; - duration = Math.max(duration, ikTimeline.frames[(ikTimeline.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; var transformMap : Object = map["transform"]; for (var transformName : String in transformMap) { - var transformConstraint : TransformConstraintData = skeletonData.findTransformConstraint(transformName); - values = transformMap[transformName]; - var transformTimeline : TransformConstraintTimeline = new TransformConstraintTimeline(values.length); - transformTimeline.transformConstraintIndex = skeletonData.transformConstraints.indexOf(transformConstraint); - frameIndex = 0; - for each (valueMap in values) { - var rotateMix : Number = getNumber(valueMap, "rotateMix", 1); - var translateMix : Number = getNumber(valueMap, "translateMix", 1); - var scaleMix : Number = getNumber(valueMap, "scaleMix", 1); - var shearMix : Number = getNumber(valueMap, "shearMix", 1); - transformTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), rotateMix, translateMix, scaleMix, shearMix); - readCurve(valueMap, transformTimeline, frameIndex); - frameIndex++; + timelineMap = map.transform[transformName]; + keyMap = timelineMap[0]; + if (!keyMap) continue; + + var transformIndex : int = skeletonData.transformConstraints.indexOf(skeletonData.findTransformConstraint(transformName)); + var transformTimeline : TransformConstraintTimeline = new TransformConstraintTimeline(timelineMap.length, timelineMap.length << 2, transformIndex); + + time = getNumber(keyMap, "time", 0); + mixRotate = getNumber(keyMap, "mixRotate", 1); + var mixShearY : Number = getNumber(keyMap, "mixShearY", 1); + mixX = getNumber(keyMap, "mixX", 1); + mixY = getNumber(keyMap, "mixY", mixX); + var mixScaleX : Number = getNumber(keyMap, "mixScaleX", 1); + var mixScaleY : Number = getNumber(keyMap, "mixScaleY", mixScaleX); + + for (frame = 0, bezier = 0;; frame++) { + transformTimeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY); + nextMap = timelineMap[frame + 1]; + if (!nextMap) break; + + time2 = getNumber(nextMap, "time", 0); + mixRotate2 = getNumber(nextMap, "mixRotate", 1); + var mixShearY2 : Number = getNumber(nextMap, "mixShearY", 1); + mixX2 = getNumber(nextMap, "mixX", 1); + mixY2 = getNumber(nextMap, "mixY", mixX2); + var mixScaleX2 : Number = getNumber(nextMap, "mixScaleX", 1); + var mixScaleY2 : Number = getNumber(nextMap, "mixScaleY", mixScaleX2); + curve = keyMap.curve; + if (curve) { + bezier = readCurve(curve, transformTimeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1); + bezier = readCurve(curve, transformTimeline, bezier, frame, 1, time, time2, mixX, mixX2, 1); + bezier = readCurve(curve, transformTimeline, bezier, frame, 2, time, time2, mixY, mixY2, 1); + bezier = readCurve(curve, transformTimeline, bezier, frame, 3, time, time2, mixScaleX, mixScaleX2, 1); + bezier = readCurve(curve, transformTimeline, bezier, frame, 4, time, time2, mixScaleY, mixScaleY2, 1); + bezier = readCurve(curve, transformTimeline, bezier, frame, 5, time, time2, mixShearY, mixShearY2, 1); + } + + time = time2; + mixRotate = mixRotate2; + mixX = mixX2; + mixY = mixY2; + mixScaleX = mixScaleX2; + mixScaleY = mixScaleY2; + mixScaleX = mixScaleX2; + keyMap = nextMap; } timelines.push(transformTimeline); - duration = Math.max(duration, transformTimeline.frames[(transformTimeline.frameCount - 1) * TransformConstraintTimeline.ENTRIES]); } // Path constraint timelines. @@ -613,58 +720,65 @@ package spine { for (var pathName : String in paths) { var index : int = skeletonData.findPathConstraintIndex(pathName); if (index == -1) throw new Error("Path constraint not found: " + pathName); - var data : PathConstraintData = skeletonData.pathConstraints[index]; + var pathData : PathConstraintData = skeletonData.pathConstraints[index]; var pathMap : Object = paths[pathName]; for (timelineName in pathMap) { - values = pathMap[timelineName]; + timelineMap = pathMap[timelineName]; + keyMap = timelineMap[0]; + if (!keyMap) continue; - if (timelineName == "position" || timelineName == "spacing") { - var pathTimeline : PathConstraintPositionTimeline; - timelineScale = 1; - if (timelineName == "spacing") { - pathTimeline = new PathConstraintSpacingTimeline(values.length); - if (data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed) timelineScale = scale; - } else { - pathTimeline = new PathConstraintPositionTimeline(values.length); - if (data.positionMode == PositionMode.fixed) timelineScale = scale; + if (timelineName === "position") { + var positionTimeline : PathConstraintPositionTimeline = new PathConstraintPositionTimeline(timelineMap.length, timelineMap.length, index); + timelines.push(readTimeline(timelineMap, positionTimeline, 0, pathData.positionMode == PositionMode.fixed ? scale : 1)); + } else if (timelineName === "spacing") { + var spacingTimeline : PathConstraintSpacingTimeline = new PathConstraintSpacingTimeline(timelineMap.length, timelineMap.length, index); + timelines.push(readTimeline(timelineMap, spacingTimeline, 0, pathData.spacingMode == SpacingMode.length || pathData.spacingMode == SpacingMode.fixed ? scale : 1)); + } else if (timelineName === "mix") { + var mixTimeline : PathConstraintMixTimeline = new PathConstraintMixTimeline(timelineMap.size, timelineMap.size * 3, index); + time = getNumber(keyMap, "time", 0); + mixRotate = getNumber(keyMap, "mixRotate", 1); + mixX = getNumber(keyMap, "mixX", 1); + mixY = getNumber(keyMap, "mixY", mixX); + for (frame = 0, bezier = 0;; frame++) { + mixTimeline.setFrame(frame, time, mixRotate, mixX, mixY); + nextMap = timelineMap[frame + 1]; + if (!nextMap) break; + time2 = getNumber(nextMap, "time", 0); + mixRotate2 = getNumber(nextMap, "mixRotate", 1); + mixX2 = getNumber(nextMap, "mixX", 1); + mixY2 = getNumber(nextMap, "mixY", mixX2); + curve = keyMap.curve; + if (curve != null) { + bezier = readCurve(curve, mixTimeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1); + bezier = readCurve(curve, mixTimeline, bezier, frame, 1, time, time2, mixX, mixX2, 1); + bezier = readCurve(curve, mixTimeline, bezier, frame, 2, time, time2, mixY, mixY2, 1); + } + time = time2; + mixRotate = mixRotate2; + mixX = mixX2; + mixY = mixY2; + keyMap = nextMap; } - pathTimeline.pathConstraintIndex = index; - frameIndex = 0; - for each (valueMap in values) { - var value : Number = valueMap[timelineName] || 0; - pathTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), value * timelineScale); - readCurve(valueMap, pathTimeline, frameIndex); - frameIndex++; - } - timelines.push(pathTimeline); - duration = Math.max(duration, pathTimeline.frames[(pathTimeline.frameCount - 1) * PathConstraintPositionTimeline.ENTRIES]); - } else if (timelineName == "mix") { - var pathMixTimeline : PathConstraintMixTimeline = new PathConstraintMixTimeline(values.length); - pathMixTimeline.pathConstraintIndex = index; - frameIndex = 0; - for each (valueMap in values) { - rotateMix = getNumber(valueMap, "rotateMix", 1); - translateMix = getNumber(valueMap, "translateMix", 1); - pathMixTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), rotateMix, translateMix); - readCurve(valueMap, pathMixTimeline, frameIndex); - frameIndex++; - } - timelines.push(pathMixTimeline); - duration = Math.max(duration, pathMixTimeline.frames[(pathMixTimeline.frameCount - 1) * PathConstraintMixTimeline.ENTRIES]); + timelines.push(mixTimeline); } } } - var deformMap : Object = map["deform"]; - for (var skinName : String in deformMap) { - var skin : Skin = skeletonData.findSkin(skinName); - slotMap = deformMap[skinName]; - for (slotName in slotMap) { + // Deform timelines. + var deforms : Object = map["deform"]; + for (var deformName : String in deforms) { + var deformMap : Object = deforms[deformName]; + var skin : Skin = skeletonData.findSkin(deformName); + if (skin == null) throw new Error("Skin not found: " + deformName); + for (slotName in deformMap) { + slotMap = deformMap[slotName]; slotIndex = skeletonData.findSlotIndex(slotName); - var timelineMap : Object = slotMap[slotName]; - for (timelineName in timelineMap) { - values = timelineMap[timelineName]; + if (slotIndex == -1) throw new Error("Slot not found: " + slotMap.name); + for (timelineName in slotMap) { + timelineMap = slotMap[timelineName]; + keyMap = timelineMap[0]; + if (!keyMap) continue; var attachment : VertexAttachment = skin.getAttachment(slotIndex, timelineName) as VertexAttachment; if (attachment == null) throw new Error("Deform attachment not found: " + timelineName); @@ -672,25 +786,20 @@ package spine { var vertices : Vector. = attachment.vertices; var deformLength : int = weighted ? vertices.length / 3 * 2 : vertices.length; - var deformTimeline : DeformTimeline = new DeformTimeline(values.length); - deformTimeline.slotIndex = slotIndex; - deformTimeline.attachment = attachment; - - frameIndex = 0; - for each (valueMap in values) { + var deformTimeline : DeformTimeline = new DeformTimeline(timelineMap.length, timelineMap.length, slotIndex, attachment); + time = getNumber(keyMap, "time", 0); + for (frame = 0, bezier = 0;; frame++) { var deform : Vector.; - var verticesValue : Object = valueMap["vertices"]; + var verticesValue : Object = keyMap["vertices"]; if (verticesValue == null) deform = weighted ? new Vector.(deformLength, true) : vertices; else { deform = new Vector.(deformLength, true); - var start : int = Number(valueMap["offset"] || 0); - var temp : Vector. = getFloatArray(valueMap, "vertices", 1); - for (i = 0; i < temp.length; i++) { + var start : int = Number(keyMap["offset"] || 0); + var temp : Vector. = getFloatArray(keyMap, "vertices", 1); + for (i = 0; i < temp.length; i++) deform[start + i] = temp[i]; - } if (scale != 1) { - var n : int; for (i = start, n = i + temp.length; i < n; i++) deform[i] *= scale; } @@ -700,30 +809,35 @@ package spine { } } - deformTimeline.setFrame(frameIndex, Number(valueMap["time"] || 0), deform); - readCurve(valueMap, deformTimeline, frameIndex); - frameIndex++; + deformTimeline.setFrame(frame, time, deform); + nextMap = timelineMap[frame + 1]; + if (!nextMap) break; + time2 = getNumber(nextMap, "time", 0); + curve = keyMap.curve; + if (curve) bezier = readCurve(curve, deformTimeline, bezier, frame, 0, time, time2, 0, 1, 1); + time = time2; + keyMap = nextMap; } - timelines[timelines.length] = deformTimeline; - duration = Math.max(duration, deformTimeline.frames[deformTimeline.frameCount - 1]); + timelines.push(deformTimeline); } } } - var drawOrderValues : Array = map["drawOrder"]; - if (!drawOrderValues) drawOrderValues = map["draworder"]; - if (drawOrderValues) { - var drawOrderTimeline : DrawOrderTimeline = new DrawOrderTimeline(drawOrderValues.length); + // Draw order timelines. + var drawOrdersMap : Array = map["drawOrder"]; + if (!drawOrdersMap) drawOrdersMap = map["draworder"]; + if (drawOrdersMap) { + var drawOrderTimeline : DrawOrderTimeline = new DrawOrderTimeline(drawOrdersMap.length); var slotCount : int = skeletonData.slots.length; - frameIndex = 0; - for each (var drawOrderMap : Object in drawOrderValues) { + frame = 0; + for each (var drawOrderMap : Object in drawOrdersMap) { var drawOrder : Vector. = null; - if (drawOrderMap["offsets"]) { - drawOrder = new Vector.(slotCount); + var offsets : Array = drawOrderMap["offsets"]; + if (offsets) { + drawOrder = new Vector.(slotCount, true); for (i = slotCount - 1; i >= 0; i--) drawOrder[i] = -1; - var offsets : Array = drawOrderMap["offsets"]; - var unchanged : Vector. = new Vector.(slotCount - offsets.length); + var unchanged : Vector. = new Vector.(slotCount - offsets.length, true); var originalIndex : int = 0, unchangedIndex : int = 0; for each (var offsetMap : Object in offsets) { slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]); @@ -741,16 +855,16 @@ package spine { for (i = slotCount - 1; i >= 0; i--) if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; } - drawOrderTimeline.setFrame(frameIndex++, Number(drawOrderMap["time"] || 0), drawOrder); + drawOrderTimeline.setFrame(frame++, Number(drawOrderMap["time"] || 0), drawOrder); } - timelines[timelines.length] = drawOrderTimeline; - duration = Math.max(duration, drawOrderTimeline.frames[drawOrderTimeline.frameCount - 1]); + timelines.push(drawOrderTimeline); } + // Event timelines. var eventsMap : Array = map["events"]; if (eventsMap) { var eventTimeline : EventTimeline = new EventTimeline(eventsMap.length); - frameIndex = 0; + frame = 0; for each (var eventMap : Object in eventsMap) { var eventData : EventData = skeletonData.findEvent(eventMap["name"]); if (!eventData) throw new Error("Event not found: " + eventMap["name"]); @@ -762,27 +876,79 @@ package spine { event.volume = getNumber(eventMap, "volume", 1); event.balance = getNumber(eventMap, "balance", 0); } - eventTimeline.setFrame(frameIndex++, event); + eventTimeline.setFrame(frame++, event); } - timelines[timelines.length] = eventTimeline; - duration = Math.max(duration, eventTimeline.frames[eventTimeline.frameCount - 1]); + timelines.push(eventTimeline); } - skeletonData.animations[skeletonData.animations.length] = new Animation(name, timelines, duration); + var duration : Number = 0; + for (i = 0, n = timelines.length; i < n; i++) + duration = Math.max(duration, timelines[i].getDuration()); + if (isNaN(duration)) throw new Error("Animation duration is NaN."); + + skeletonData.animations.push(new Animation(name, timelines, duration)); } - static private function readCurve(map : Object, timeline : CurveTimeline, frameIndex : int) : void { - var curve : Object = map["curve"]; - if (curve == null) return; - if (curve == "stepped") - timeline.setStepped(frameIndex); - else { - var c1: Number = parseFloat(curve.toString()); - var c2: Number = map["c2"] == null ? 0 : Number(map["c2"]); - var c3: Number = map["c3"] == null ? 1 : Number(map["c3"]); - var c4: Number = map["c4"] == null ? 1 : Number(map["c4"]); - timeline.setCurve(frameIndex, c1, c2, c3, c4); + static private function readTimeline(keys : Array, timeline : CurveTimeline1, defaultValue : Number, scale : Number) : CurveTimeline1 { + var keyMap : Object = keys[0]; + var time : Number = getNumber(keyMap, "time", 0); + var value : Number = getNumber(keyMap, "value", defaultValue) * scale; + var bezier : int = 0; + for (var frame : int = 0;; frame++) { + timeline.setFrame(frame, time, value); + var nextMap : Object = keys[frame + 1]; + if (!nextMap) break; + var time2 : Number = getNumber(nextMap, "time", 0); + var value2 : Number = getNumber(nextMap, "value", defaultValue) * scale; + var curve : Object = keyMap.curve; + if (curve) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value, value2, scale); + time = time2; + value = value2; + keyMap = nextMap; } + return timeline; + } + + static private function readTimeline2(keys : Array, timeline : CurveTimeline2, name1 : String, name2 : String, defaultValue : Number, scale : Number) : CurveTimeline2 { + var keyMap : Object = keys[0]; + var time : Number = getNumber(keyMap, "time", 0); + var value1 : Number = getNumber(keyMap, name1, defaultValue) * scale; + var value2 : Number = getNumber(keyMap, name2, defaultValue) * scale; + var bezier : int = 0; + for (var frame : int = 0;; frame++) { + timeline.setFrame(frame, time, value1, value2); + var nextMap : Object = keys[frame + 1]; + if (!nextMap) break; + var time2 : Number = getNumber(nextMap, "time", 0); + var nvalue1 : Number = getNumber(nextMap, name1, defaultValue) * scale; + var nvalue2 : Number = getNumber(nextMap, name2, defaultValue) * scale; + var curve : Object = keyMap.curve; + if (curve != null) { + bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale); + bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale); + } + time = time2; + value1 = nvalue1; + value2 = nvalue2; + keyMap = nextMap; + } + timeline.shrink(bezier); + return timeline; + } + + static private function readCurve(curve : Object, timeline : CurveTimeline, bezier : int, frame : int, value : Number, time1 : Number, time2 : Number, + value1 : Number, value2 : Number, scale : Number) : int { + if (curve == "stepped") { + if (value != 0) timeline.setStepped(frame); + } else { + var i : int = value << 2; + var cx1 : Number = curve[i++]; + var cy1 : Number = curve[i++] * scale; + var cx2 : Number = curve[i++]; + var cy2 : Number = curve[i++] * scale; + timeline.setBezier(bezier++, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2); + } + return bezier; } static private function getValue(map : Object, property : String, defaultValue : Object) : Object { @@ -827,7 +993,6 @@ package spine { import spine.attachments.MeshAttachment; - class LinkedMesh { internal var parent : String, skin : String; internal var slotIndex : int; diff --git a/spine-as3/spine-as3/src/spine/Skin.as b/spine-as3/spine-as3/src/spine/Skin.as index 49151e181..b76690ea4 100644 --- a/spine-as3/spine-as3/src/spine/Skin.as +++ b/spine-as3/spine-as3/src/spine/Skin.as @@ -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; } diff --git a/spine-as3/spine-as3/src/spine/Slot.as b/spine-as3/spine-as3/src/spine/Slot.as index ac25f6515..288f90786 100644 --- a/spine-as3/spine-as3/src/spine/Slot.as +++ b/spine-as3/spine-as3/src/spine/Slot.as @@ -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. = new Vector.(); - 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); diff --git a/spine-as3/spine-as3/src/spine/SpacingMode.as b/spine-as3/spine-as3/src/spine/SpacingMode.as index bedea0c44..8e823f974 100644 --- a/spine-as3/spine-as3/src/spine/SpacingMode.as +++ b/spine-as3/spine-as3/src/spine/SpacingMode.as @@ -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 ]; } } diff --git a/spine-as3/spine-as3/src/spine/TransformConstraint.as b/spine-as3/spine-as3/src/spine/TransformConstraint.as index 97174d764..8a4ae3761 100644 --- a/spine-as3/spine-as3/src/spine/TransformConstraint.as +++ b/spine-as3/spine-as3/src/spine/TransformConstraint.as @@ -32,21 +32,23 @@ package spine { internal var _data : TransformConstraintData; internal var _bones : Vector.; public var target : Bone; - public var rotateMix : Number; - public var translateMix : Number; - public var scaleMix : Number; - public var shearMix : Number; - internal var _temp : Vector. = new Vector.(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. = new Vector.(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.(); 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. = this._bones; + var offsetRotation : Number = _data.offsetRotation * degRadReflect; + var offsetShearY : Number = _data.offsetShearY * degRadReflect; + + var bones : Vector. = _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. = this.bones; + var offsetRotation : Number = _data.offsetRotation * degRadReflect, offsetShearY : Number = _data.offsetShearY * degRadReflect; + + var bones : Vector. = _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. = this._temp; - temp[0] = this._data.offsetX; - temp[1] = this._data.offsetY; + if (translate) { + var temp : Vector. = _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. = this.bones; + + var bones : Vector. = _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. = this.bones; + + var bones : Vector. = _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); } diff --git a/spine-as3/spine-as3/src/spine/TransformConstraintData.as b/spine-as3/spine-as3/src/spine/TransformConstraintData.as index 1a3990454..09f6fb858 100644 --- a/spine-as3/spine-as3/src/spine/TransformConstraintData.as +++ b/spine-as3/spine-as3/src/spine/TransformConstraintData.as @@ -31,16 +31,17 @@ package spine { public class TransformConstraintData extends ConstraintData { internal var _bones : Vector. = new Vector.(); 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; diff --git a/spine-as3/spine-as3/src/spine/animation/AlphaTimeline.as b/spine-as3/spine-as3/src/spine/animation/AlphaTimeline.as new file mode 100644 index 000000000..539aab8bd --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/AlphaTimeline.as @@ -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., 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; + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/Animation.as b/spine-as3/spine-as3/src/spine/animation/Animation.as index 3482ef8f5..03b1b0e17 100644 --- a/spine-as3/spine-as3/src/spine/animation/Animation.as +++ b/spine-as3/spine-as3/src/spine/animation/Animation.as @@ -34,7 +34,7 @@ package spine.animation { public class Animation { internal var _name : String; - public var _timelines : Vector.; + private var _timelines : Vector.; 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. = 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. { - return _timelines; + public function hasTimeline(ids : Vector.) : 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., 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., 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., 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. { + return _timelines; } } } diff --git a/spine-as3/spine-as3/src/spine/animation/AnimationState.as b/spine-as3/spine-as3/src/spine/animation/AnimationState.as index ff7cb0d7c..ca07ba295 100644 --- a/spine-as3/spine-as3/src/spine/animation/AnimationState.as +++ b/spine-as3/spine-as3/src/spine/animation/AnimationState.as @@ -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("", new Vector.(), 0); - internal static var emptyAnimation : Animation = new Animation("", new Vector.(), 0); public var data : AnimationStateData; public var tracks : Vector. = new Vector.(); internal var events : Vector. = new Vector.(); @@ -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. = new Vector.(); 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. = events; + if (current.reverse) { + applyTime = current.animation.duration - applyTime; + applyEvents = null; + } var timelineCount : int = current.animation.timelines.length; var timelines : Vector. = 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. = 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. = 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. = 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. = 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. = from.timelineMode; var timelineHoldMix : Vector. = 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. = 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. = rotateTimeline.frames; - var bone : Bone = skeleton.bones[rotateTimeline.boneIndex]; + var bone : Bone = skeleton.bones[rotateTimeline.getBoneIndex()]; if (!bone.active) return; + var frames : Vector. = 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. = entry.animation.timelines; var timelinesCount : int = entry.animation.timelines.length; var timelineMode : Vector. = entry.timelineMode; timelineMode.length = timelinesCount; + entry.timelineHoldMix.length = 0; var timelineHoldMix : Vector. = 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. = 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.) : 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; + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/AttachmentTimeline.as b/spine-as3/spine-as3/src/spine/animation/AttachmentTimeline.as index df3d8d89d..10ae6b4c9 100644 --- a/spine-as3/spine-as3/src/spine/animation/AttachmentTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/AttachmentTimeline.as @@ -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.; // 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.; - public function AttachmentTimeline(frameCount : int) { - frames = new Vector.(frameCount, true); + public function AttachmentTimeline (frameCount : int, slotIndex : int) { + super(frameCount, [ + Property.attachment + "|" + slotIndex + ]); + this.slotIndex = slotIndex; attachmentNames = new Vector.(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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - var attachmentName : String; + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., 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. = 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); } } diff --git a/spine-as3/spine-as3/src/spine/animation/BoneTimeline.as b/spine-as3/spine-as3/src/spine/animation/BoneTimeline.as new file mode 100644 index 000000000..dbb71a46f --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/BoneTimeline.as @@ -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; + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/ColorTimeline.as b/spine-as3/spine-as3/src/spine/animation/ColorTimeline.as deleted file mode 100644 index 230997a1d..000000000 --- a/spine-as3/spine-as3/src/spine/animation/ColorTimeline.as +++ /dev/null @@ -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.; // time, r, g, b, a, ... - - public function ColorTimeline(frameCount : int) { - super(frameCount); - frames = new Vector.(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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - var frames : Vector. = 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; - } - } - } -} diff --git a/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as b/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as index 01c8cf5e9..b7102c950 100644 --- a/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as @@ -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.; // type, x, y, ... + internal var curves : Vector.; // type, x, y, ... - public function CurveTimeline(frameCount : int) { - curves = new Vector.((frameCount - 1) * BEZIER_SIZE, true); + public function CurveTimeline(frameCount : int, bezierCount : int, propertyIds : Array) { + super(frameCount, propertyIds); + curves = new Vector.(frameCount + bezierCount * BEZIER_SIZE, true); + curves[frameCount - 1] = STEPPED; } - public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector., 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 bezierCount (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. = 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. = new Vector.(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 bezierCount - 1 (specified + * in the constructor), inclusive. + * @param frame Between 0 and frameCount - 1, 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. = 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 time. + * @param valueOffset The offset from frameIndex 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. = 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); } } } diff --git a/spine-as3/spine-as3/src/spine/animation/CurveTimeline1.as b/spine-as3/spine-as3/src/spine/animation/CurveTimeline1.as new file mode 100644 index 000000000..3638715ba --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/CurveTimeline1.as @@ -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 frameCount, 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. = 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); + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/TimelineType.as b/spine-as3/spine-as3/src/spine/animation/CurveTimeline2.as similarity index 58% rename from spine-as3/spine-as3/src/spine/animation/TimelineType.as rename to spine-as3/spine-as3/src/spine/animation/CurveTimeline2.as index 441929065..7a4c086ad 100644 --- a/spine-as3/spine-as3/src/spine/animation/TimelineType.as +++ b/spine-as3/spine-as3/src/spine/animation/CurveTimeline2.as @@ -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 frameCount, 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. = this.frames; + frames[frame] = time; + frames[frame + VALUE1] = value1; + frames[frame + VALUE2] = value2; + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/DeformTimeline.as b/spine-as3/spine-as3/src/spine/animation/DeformTimeline.as index e02ed03a6..42456ff10 100644 --- a/spine-as3/spine-as3/src/spine/animation/DeformTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/DeformTimeline.as @@ -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.; - public var frameVertices : Vector.>; + + /** The attachment that will be deformed. */ public var attachment : VertexAttachment; - public function DeformTimeline(frameCount : int) { - super(frameCount); - frames = new Vector.(frameCount, true); - frameVertices = new Vector.>(frameCount, true); + /** The vertices for each key frame. */ + public var vertices : Vector.>; + + 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.>(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.) : 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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - var vertexAttachment : VertexAttachment; - var setupVertices : Vector.; + /** 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.) : 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. = 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. = 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., 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. = slot.deform; - if (deformArray.length == 0) blend = MixBlend.setup; + var deform : Vector. = slot.deform; + if (deform.length == 0) blend = MixBlend.setup; - var frameVertices : Vector.> = this.frameVertices; - var vertexCount : int = frameVertices[0].length; - var deform : Vector.; + var vertices : Vector.> = this.vertices; + var vertexCount : int = vertices[0].length; + + var i : int, setupVertices : Vector.; var frames : Vector. = 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. = frameVertices[frames.length - 1]; + var lastVertices : Vector. = 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. = frameVertices[frame - 1]; - var nextVertices : Vector. = 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. = vertices[frame], prev : Number; + var nextVertices : Vector. = 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; + } + } } } } diff --git a/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as b/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as index e07b4f13a..707a9b9a5 100644 --- a/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as @@ -32,12 +32,13 @@ package spine.animation { import spine.Skeleton; import spine.Slot; - public class DrawOrderTimeline implements Timeline { - public var frames : Vector.; // time, ... + public class DrawOrderTimeline extends Timeline { public var drawOrders : Vector.>; public function DrawOrderTimeline(frameCount : int) { - frames = new Vector.(frameCount, true); + super(frameCount, [ + Property.drawOrder + ]); drawOrders = new Vector.>(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.) : 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.) : void { - frames[frameIndex] = time; - drawOrders[frameIndex] = drawOrder; - } - - public function apply(skeleton : Skeleton, lastTime : Number, time : Number, firedEvents : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - if (direction == MixDirection.Out) { + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var drawOrder: Vector. = skeleton.drawOrder; + var slots : Vector. = 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. = skeleton.drawOrder; - var slots : Vector. = 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. = drawOrders[frameIndex]; - i = 0; - if (!drawOrderToSetupIndex) { - for each (slot in slots) - drawOrder[i++] = slot; + var drawOrderToSetupIndex : Vector. = 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]]; } } } diff --git a/spine-as3/spine-as3/src/spine/animation/EventTimeline.as b/spine-as3/spine-as3/src/spine/animation/EventTimeline.as index 87eaafcaa..d2a9633f7 100644 --- a/spine-as3/spine-as3/src/spine/animation/EventTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/EventTimeline.as @@ -31,53 +31,53 @@ package spine.animation { import spine.Event; import spine.Skeleton; - public class EventTimeline implements Timeline { - public var frames : Vector.; // time, ... + public class EventTimeline extends Timeline { public var events : Vector.; public function EventTimeline(frameCount : int) { - frames = new Vector.(frameCount, true); + super(frameCount, [ + Property.event + ]); events = new Vector.(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., 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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - if (!firedEvents) return; + var frames : Vector. = 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]); } } } diff --git a/spine-as3/spine-as3/src/spine/animation/IkConstraintTimeline.as b/spine-as3/spine-as3/src/spine/animation/IkConstraintTimeline.as index 50abd367c..4635459c9 100644 --- a/spine-as3/spine-as3/src/spine/animation/IkConstraintTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/IkConstraintTimeline.as @@ -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.; // time, mix, bendDirection, compress, stretch, ... - public function IkConstraintTimeline(frameCount : int) { - super(frameCount); - frames = new Vector.(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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { var constraint : IkConstraint = skeleton.ikConstraints[ikConstraintIndex]; if (!constraint.active) return; + + var frames : Vector. = 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; } } } diff --git a/spine-as3/spine-as3/src/spine/animation/MixBlend.as b/spine-as3/spine-as3/src/spine/animation/MixBlend.as index a818c437c..64c779093 100644 --- a/spine-as3/spine-as3/src/spine/animation/MixBlend.as +++ b/spine-as3/spine-as3/src/spine/animation/MixBlend.as @@ -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); diff --git a/spine-as3/spine-as3/src/spine/animation/MixDirection.as b/spine-as3/spine-as3/src/spine/animation/MixDirection.as index cf2b0ef6d..509c3c2b9 100644 --- a/spine-as3/spine-as3/src/spine/animation/MixDirection.as +++ b/spine-as3/spine-as3/src/spine/animation/MixDirection.as @@ -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); } } diff --git a/spine-as3/spine-as3/src/spine/animation/PathConstraintMixTimeline.as b/spine-as3/spine-as3/src/spine/animation/PathConstraintMixTimeline.as index 96f2025cc..0fa85d470 100644 --- a/spine-as3/spine-as3/src/spine/animation/PathConstraintMixTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/PathConstraintMixTimeline.as @@ -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.; // time, rotate mix, translate mix, ... - public function PathConstraintMixTimeline(frameCount : int) { - super(frameCount); - frames = new Vector.(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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { var constraint : PathConstraint = skeleton.pathConstraints[pathConstraintIndex]; if (!constraint.active) return; + + var data : PathConstraintData; + + var frames : Vector. = 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; } } } diff --git a/spine-as3/spine-as3/src/spine/animation/PathConstraintPositionTimeline.as b/spine-as3/spine-as3/src/spine/animation/PathConstraintPositionTimeline.as index 478da9d38..dc54a2a79 100644 --- a/spine-as3/spine-as3/src/spine/animation/PathConstraintPositionTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/PathConstraintPositionTimeline.as @@ -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.; // time, position, ... - public function PathConstraintPositionTimeline(frameCount : int) { - super(frameCount); - frames = new Vector.(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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { var constraint : PathConstraint = skeleton.pathConstraints[pathConstraintIndex]; if (!constraint.active) return; + + var frames : Vector. = 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 diff --git a/spine-as3/spine-as3/src/spine/animation/PathConstraintSpacingTimeline.as b/spine-as3/spine-as3/src/spine/animation/PathConstraintSpacingTimeline.as index 860000f4b..c96120563 100644 --- a/spine-as3/spine-as3/src/spine/animation/PathConstraintSpacingTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/PathConstraintSpacingTimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { var constraint : PathConstraint = skeleton.pathConstraints[pathConstraintIndex]; if (!constraint.active) return; + + var frames : Vector. = 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; diff --git a/spine-as3/spine-as3/src/spine/animation/Property.as b/spine-as3/spine-as3/src/spine/animation/Property.as new file mode 100644 index 000000000..55e99e563 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/Property.as @@ -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; + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/RGB2Timeline.as b/spine-as3/spine-as3/src/spine/animation/RGB2Timeline.as new file mode 100644 index 000000000..753ade0a5 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/RGB2Timeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var slot : Slot = skeleton.slots[slotIndex]; + if (!slot.bone.active) return; + + var frames : Vector. = 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; + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/RGBA2Timeline.as b/spine-as3/spine-as3/src/spine/animation/RGBA2Timeline.as new file mode 100644 index 000000000..5261d4fc6 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/RGBA2Timeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var slot : Slot = skeleton.slots[slotIndex]; + if (!slot.bone.active) return; + + var frames : Vector. = 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; + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/RGBATimeline.as b/spine-as3/spine-as3/src/spine/animation/RGBATimeline.as new file mode 100644 index 000000000..f82088c8e --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/RGBATimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var slot : Slot = skeleton.slots[slotIndex]; + if (!slot.bone.active) return; + + var frames : Vector. = 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); + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/RGBTimeline.as b/spine-as3/spine-as3/src/spine/animation/RGBTimeline.as new file mode 100644 index 000000000..db900756d --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/RGBTimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var slot : Slot = skeleton.slots[slotIndex]; + if (!slot.bone.active) return; + + var frames : Vector. = 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; + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as b/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as index 1b13a01c3..efa63aa8e 100644 --- a/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as @@ -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.; // time, value, ... + public class RotateTimeline extends CurveTimeline1 implements BoneTimeline { + private var boneIndex : int; - public function RotateTimeline(frameCount : int) { - super(frameCount); - frames = new Vector.(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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - var frames : Vector. = this.frames; - + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { var bone : Bone = skeleton.bones[boneIndex]; if (!bone.active) return; - var r : Number; + + var frames : Vector. = 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; } } } diff --git a/spine-as3/spine-as3/src/spine/animation/ScaleTimeline.as b/spine-as3/spine-as3/src/spine/animation/ScaleTimeline.as index 69bd638f8..da054f8cb 100644 --- a/spine-as3/spine-as3/src/spine/animation/ScaleTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/ScaleTimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - var frames : Vector. = this.frames; + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { var bone : Bone = skeleton.bones[boneIndex]; if (!bone.active) return; + var frames : Vector. = 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; } } } diff --git a/spine-as3/spine-as3/src/spine/animation/ScaleXTimeline.as b/spine-as3/spine-as3/src/spine/animation/ScaleXTimeline.as new file mode 100644 index 000000000..6661406fc --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/ScaleXTimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var bone : Bone = skeleton.bones[boneIndex]; + if (!bone.active) return; + + var frames : Vector. = 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; + } + } + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/ScaleYTimeline.as b/spine-as3/spine-as3/src/spine/animation/ScaleYTimeline.as new file mode 100644 index 000000000..d4ddb4758 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/ScaleYTimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var bone : Bone = skeleton.bones[boneIndex]; + if (!bone.active) return; + + var frames : Vector. = 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; + } + } + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/ShearTimeline.as b/spine-as3/spine-as3/src/spine/animation/ShearTimeline.as index 4cb0339f3..2ef20b21f 100644 --- a/spine-as3/spine-as3/src/spine/animation/ShearTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/ShearTimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - var frames : Vector. = this.frames; + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { var bone : Bone = skeleton.bones[boneIndex]; if (!bone.active) return; + var frames : Vector. = 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; } } } diff --git a/spine-as3/spine-as3/src/spine/animation/ShearXTimeline.as b/spine-as3/spine-as3/src/spine/animation/ShearXTimeline.as new file mode 100644 index 000000000..5c0b2c865 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/ShearXTimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var bone : Bone = skeleton.bones[boneIndex]; + if (!bone.active) return; + + var frames : Vector. = 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; + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/ShearYTimeline.as b/spine-as3/spine-as3/src/spine/animation/ShearYTimeline.as new file mode 100644 index 000000000..7437e83ce --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/ShearYTimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var bone : Bone = skeleton.bones[boneIndex]; + if (!bone.active) return; + + var frames : Vector. = 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; + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/SlotTimeline.as b/spine-as3/spine-as3/src/spine/animation/SlotTimeline.as new file mode 100644 index 000000000..b66ed4830 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/SlotTimeline.as @@ -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; + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/Timeline.as b/spine-as3/spine-as3/src/spine/animation/Timeline.as index 75cb52efd..f5ee23d78 100644 --- a/spine-as3/spine-as3/src/spine/animation/Timeline.as +++ b/spine-as3/spine-as3/src/spine/animation/Timeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void; + public class Timeline { + public var propertyIds : Vector.; + public var frames : Vector.; - function getPropertyId() : int; + public function Timeline(frameCount : int, propertyIds : Array) { + this.propertyIds = new Vector.(propertyIds.length, true); + for (var i : int = 0, n : int = propertyIds.length; i < n; i++) + this.propertyIds[i] = propertyIds[i]; + frames = new Vector.(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., alpha: Number, blend: MixBlend, direction: MixDirection) : void { + } + + static internal function search (frames : Vector., 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., 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; + } } } diff --git a/spine-as3/spine-as3/src/spine/animation/TrackEntry.as b/spine-as3/spine-as3/src/spine/animation/TrackEntry.as index e4495772f..4545847ea 100644 --- a/spine-as3/spine-as3/src/spine/animation/TrackEntry.as +++ b/spine-as3/spine-as3/src/spine/animation/TrackEntry.as @@ -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; diff --git a/spine-as3/spine-as3/src/spine/animation/TransformConstraintTimeline.as b/spine-as3/spine-as3/src/spine/animation/TransformConstraintTimeline.as index 7a56f158d..67a796fc0 100644 --- a/spine-as3/spine-as3/src/spine/animation/TransformConstraintTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/TransformConstraintTimeline.as @@ -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.; // time, rotate mix, translate mix, scale mix, shear mix, ... - public function TransformConstraintTimeline(frameCount : int) { - super(frameCount); - frames = new Vector.(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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - var frames : Vector. = this.frames; - + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { var constraint : TransformConstraint = skeleton.transformConstraints[transformConstraintIndex]; if (!constraint.active) return; + var data : TransformConstraintData; + + var frames : Vector. = 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; } } } diff --git a/spine-as3/spine-as3/src/spine/animation/TranslateTimeline.as b/spine-as3/spine-as3/src/spine/animation/TranslateTimeline.as index b8f363376..3b2ef61be 100644 --- a/spine-as3/spine-as3/src/spine/animation/TranslateTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/TranslateTimeline.as @@ -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.; // time, value, value, ... + public class TranslateTimeline extends CurveTimeline2 implements BoneTimeline { + private var boneIndex : int; - public function TranslateTimeline(frameCount : int) { - super(frameCount); - frames = new Vector.(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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - var frames : Vector. = this.frames; - + public override function apply (skeleton : Skeleton, lastTime : Number, time : Number, events : Vector., alpha : Number, blend : MixBlend, direction : MixDirection) : void { var bone : Bone = skeleton.bones[boneIndex]; if (!bone.active) return; + + var frames : Vector. = 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; } } } diff --git a/spine-as3/spine-as3/src/spine/animation/TranslateXTimeline.as b/spine-as3/spine-as3/src/spine/animation/TranslateXTimeline.as new file mode 100644 index 000000000..6fdcc7457 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/TranslateXTimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var bone : Bone = skeleton.bones[boneIndex]; + if (!bone.active) return; + + var frames : Vector. = 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; + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/TranslateYTimeline.as b/spine-as3/spine-as3/src/spine/animation/TranslateYTimeline.as new file mode 100644 index 000000000..f2d44e957 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/animation/TranslateYTimeline.as @@ -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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { + var bone : Bone = skeleton.bones[boneIndex]; + if (!bone.active) return; + + var frames : Vector. = 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; + } + } + } +} diff --git a/spine-as3/spine-as3/src/spine/animation/TwoColorTimeline.as b/spine-as3/spine-as3/src/spine/animation/TwoColorTimeline.as deleted file mode 100644 index 44efd9e46..000000000 --- a/spine-as3/spine-as3/src/spine/animation/TwoColorTimeline.as +++ /dev/null @@ -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.; // time, r, g, b, a, ... - - public function TwoColorTimeline(frameCount : int) { - super(frameCount); - frames = new Vector.(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., alpha : Number, blend : MixBlend, direction : MixDirection) : void { - var frames : Vector. = 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); - } - } - } -} diff --git a/spine-as3/spine-as3/src/spine/atlas/Atlas.as b/spine-as3/spine-as3/src/spine/atlas/Atlas.as index 96eb5c348..dfe6022d4 100644 --- a/spine-as3/spine-as3/src/spine/atlas/Atlas.as +++ b/spine-as3/spine-as3/src/spine/atlas/Atlas.as @@ -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. = new Vector.(); @@ -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. = new Vector.(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.; + var values : Vector.>; + 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.(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.(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.(); + values = new Vector.>(); + } + names.push(entry[0]); + var entryValues : Vector. = new Vector.(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., 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; } } diff --git a/spine-as3/spine-as3/src/spine/atlas/AtlasPage.as b/spine-as3/spine-as3/src/spine/atlas/AtlasPage.as index 52c0e0c28..722681c01 100644 --- a/spine-as3/spine-as3/src/spine/atlas/AtlasPage.as +++ b/spine-as3/spine-as3/src/spine/atlas/AtlasPage.as @@ -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() { } diff --git a/spine-as3/spine-as3/src/spine/atlas/AtlasRegion.as b/spine-as3/spine-as3/src/spine/atlas/AtlasRegion.as index 331358709..a38ecac6a 100644 --- a/spine-as3/spine-as3/src/spine/atlas/AtlasRegion.as +++ b/spine-as3/spine-as3/src/spine/atlas/AtlasRegion.as @@ -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.; public var pads : Vector.; public var rendererObject : Object; + public var names : Vector.; + public var values : Vector.>; public function AtlasRegion() { } diff --git a/spine-as3/spine-as3/src/spine/atlas/TextureFilter.as b/spine-as3/spine-as3/src/spine/atlas/TextureFilter.as index 8e07f7366..a69373499 100644 --- a/spine-as3/spine-as3/src/spine/atlas/TextureFilter.as +++ b/spine-as3/spine-as3/src/spine/atlas/TextureFilter.as @@ -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; diff --git a/spine-as3/spine-as3/src/spine/attachments/AtlasAttachmentLoader.as b/spine-as3/spine-as3/src/spine/attachments/AtlasAttachmentLoader.as index 9c522cf65..b3f2c5810 100644 --- a/spine-as3/spine-as3/src/spine/attachments/AtlasAttachmentLoader.as +++ b/spine-as3/spine-as3/src/spine/attachments/AtlasAttachmentLoader.as @@ -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; diff --git a/spine-as3/spine-as3/src/spine/attachments/Attachment.as b/spine-as3/spine-as3/src/spine/attachments/Attachment.as index 3e3cb5ca9..493ca199f 100644 --- a/spine-as3/spine-as3/src/spine/attachments/Attachment.as +++ b/spine-as3/spine-as3/src/spine/attachments/Attachment.as @@ -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; } diff --git a/spine-as3/spine-as3/src/spine/attachments/ClippingAttachment.as b/spine-as3/spine-as3/src/spine/attachments/ClippingAttachment.as index c94fa5d48..aa1dd090b 100644 --- a/spine-as3/spine-as3/src/spine/attachments/ClippingAttachment.as +++ b/spine-as3/spine-as3/src/spine/attachments/ClippingAttachment.as @@ -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); diff --git a/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as b/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as index 5f5f1c737..5ea6069d5 100644 --- a/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as +++ b/spine-as3/spine-as3/src/spine/attachments/MeshAttachment.as @@ -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; diff --git a/spine-as3/spine-as3/src/spine/attachments/RegionAttachment.as b/spine-as3/spine-as3/src/spine/attachments/RegionAttachment.as index 909adad59..45bcdb1af 100644 --- a/spine-as3/spine-as3/src/spine/attachments/RegionAttachment.as +++ b/spine-as3/spine-as3/src/spine/attachments/RegionAttachment.as @@ -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. = this.uvs; - if (rotate) { + if (degrees == 90) { uvs[4] = u; uvs[5] = v2; uvs[6] = u; diff --git a/spine-as3/spine-as3/src/spine/attachments/VertexAttachment.as b/spine-as3/spine-as3/src/spine/attachments/VertexAttachment.as index 89ad45d27..935bf9f3d 100644 --- a/spine-as3/spine-as3/src/spine/attachments/VertexAttachment.as +++ b/spine-as3/spine-as3/src/spine/attachments/VertexAttachment.as @@ -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.; public var vertices : Vector.; 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., offset : int, stride : int) : void { count = offset + (count >> 1) * stride; var skeleton : Skeleton = slot.skeleton; diff --git a/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as b/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as index d076834ab..f771a914e 100644 --- a/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as +++ b/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as @@ -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); } diff --git a/spine-as3/spine-as3/src/spine/interpolation/Pow.as b/spine-as3/spine-as3/src/spine/interpolation/Pow.as index 45db88fb2..3c011b5d5 100644 --- a/spine-as3/spine-as3/src/spine/interpolation/Pow.as +++ b/spine-as3/spine-as3/src/spine/interpolation/Pow.as @@ -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; } diff --git a/spine-as3/spine-as3/src/spine/vertexeffects/SwirlEffect.as b/spine-as3/spine-as3/src/spine/vertexeffects/SwirlEffect.as index 7d6fd4663..f75f47aa4 100644 --- a/spine-as3/spine-as3/src/spine/vertexeffects/SwirlEffect.as +++ b/spine-as3/spine-as3/src/spine/vertexeffects/SwirlEffect.as @@ -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; } diff --git a/spine-starling/spine-starling-example/.vscode/launch.json b/spine-starling/spine-starling-example/.vscode/launch.json index bf670b211..6a8f7b1b8 100644 --- a/spine-starling/spine-starling-example/.vscode/launch.json +++ b/spine-starling/spine-starling-example/.vscode/launch.json @@ -4,7 +4,7 @@ { "type": "swf", "request": "launch", - "name": "Launch Spine Starling Example", + "name": "Launch spine-starling-example", "preLaunchTask": "Compile debug" } ] diff --git a/spine-starling/spine-starling-example/src/spine/examples/Shape.as b/spine-starling/spine-starling-example/src/spine/examples/Shape.as index 29d98e286..0e32bbb2b 100644 --- a/spine-starling/spine-starling-example/src/spine/examples/Shape.as +++ b/spine-starling/spine-starling-example/src/spine/examples/Shape.as @@ -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(); diff --git a/spine-starling/spine-starling-example/src/spine/examples/SpineboyExample.as b/spine-starling/spine-starling-example/src/spine/examples/SpineboyExample.as index 7c999a68d..ca6823c60 100644 --- a/spine-starling/spine-starling-example/src/spine/examples/SpineboyExample.as +++ b/spine-starling/spine-starling-example/src/spine/examples/SpineboyExample.as @@ -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 [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); diff --git a/spine-starling/spine-starling-example/src/spine/examples/TankExample.as b/spine-starling/spine-starling-example/src/spine/examples/TankExample.as index cd45f7e0b..844fe1a19 100644 --- a/spine-starling/spine-starling-example/src/spine/examples/TankExample.as +++ b/spine-starling/spine-starling-example/src/spine/examples/TankExample.as @@ -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); diff --git a/spine-starling/spine-starling/src/spine/starling/StarlingAtlasAttachmentLoader.as b/spine-starling/spine-starling/src/spine/starling/StarlingAtlasAttachmentLoader.as index 1e1919588..ef91c28b6 100644 --- a/spine-starling/spine-starling/src/spine/starling/StarlingAtlasAttachmentLoader.as +++ b/spine-starling/spine-starling/src/spine/starling/StarlingAtlasAttachmentLoader.as @@ -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; diff --git a/spine-starling/spine-starling/src/spine/starling/StarlingTextureLoader.as b/spine-starling/spine-starling/src/spine/starling/StarlingTextureLoader.as index de48c21a0..13d2bc0c6 100644 --- a/spine-starling/spine-starling/src/spine/starling/StarlingTextureLoader.as +++ b/spine-starling/spine-starling/src/spine/starling/StarlingTextureLoader.as @@ -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);