From 4daeefd5cf209a2634aaaee9456354d7cffa6b0b Mon Sep 17 00:00:00 2001 From: Davide Tantillo Date: Wed, 4 Jun 2025 17:40:33 +0200 Subject: [PATCH] [haxe] Port to 4.3 (WIP) --- spine-haxe/spine-haxe/spine/Bone.hx | 436 +------------ spine-haxe/spine-haxe/spine/BoneData.hx | 96 +-- spine-haxe/spine-haxe/spine/BoneLocal.hx | 79 +++ spine-haxe/spine-haxe/spine/BonePose.hx | 381 +++++++++++ spine-haxe/spine-haxe/spine/Constraint.hx | 49 ++ spine-haxe/spine-haxe/spine/ConstraintData.hx | 24 +- spine-haxe/spine-haxe/spine/Event.hx | 30 +- spine-haxe/spine-haxe/spine/IkConstraint.hx | 254 +++----- .../spine-haxe/spine/IkConstraintData.hx | 45 +- .../spine-haxe/spine/IkConstraintPose.hx | 67 ++ spine-haxe/spine-haxe/spine/PathConstraint.hx | 437 ++++++------- .../spine-haxe/spine/PathConstraintData.hx | 61 +- .../spine-haxe/spine/PathConstraintPose.hx | 59 ++ .../spine-haxe/spine/PhysicsConstraint.hx | 488 +++++++------- .../spine-haxe/spine/PhysicsConstraintData.hx | 56 +- .../spine-haxe/spine/PhysicsConstraintPose.hx | 56 ++ spine-haxe/spine-haxe/spine/Pose.hx | 34 + spine-haxe/spine-haxe/spine/Posed.hx | 59 ++ spine-haxe/spine-haxe/spine/PosedActive.hx | 55 ++ spine-haxe/spine-haxe/spine/PosedData.hx | 54 ++ spine-haxe/spine-haxe/spine/Sequence.hx | 2 +- spine-haxe/spine-haxe/spine/Skeleton.hx | 611 +++++------------- .../spine-haxe/spine/SkeletonClipping.hx | 93 ++- spine-haxe/spine-haxe/spine/SkeletonData.hx | 198 ++---- spine-haxe/spine-haxe/spine/Skin.hx | 126 ++-- spine-haxe/spine-haxe/spine/Slider.hx | 112 ++++ spine-haxe/spine-haxe/spine/SliderData.hx | 53 ++ spine-haxe/spine-haxe/spine/SliderPose.hx | 41 ++ spine-haxe/spine-haxe/spine/Slot.hx | 120 +--- spine-haxe/spine-haxe/spine/SlotData.hx | 61 +- spine-haxe/spine-haxe/spine/SlotPose.hx | 87 +++ .../spine-haxe/spine/TransformConstraint.hx | 320 +++------ .../spine/TransformConstraintData.hx | 329 +++++++++- .../spine/TransformConstraintPose.hx | 65 ++ .../spine/{Updatable.hx => Update.hx} | 13 +- .../spine/animation/AlphaTimeline.hx | 18 +- .../spine-haxe/spine/animation/Animation.hx | 75 +-- .../spine/animation/AnimationState.hx | 48 +- .../spine/animation/AttachmentTimeline.hx | 36 +- .../spine/animation/BoneTimeline.hx | 2 +- .../spine/animation/BoneTimeline1.hx | 52 ++ .../{CurveTimeline2.hx => BoneTimeline2.hx} | 28 +- .../spine/animation/ConstraintTimeline.hx | 35 + .../spine/animation/ConstraintTimeline1.hx | 43 ++ .../spine/animation/CurveTimeline.hx | 6 +- .../spine/animation/CurveTimeline1.hx | 68 +- .../spine/animation/DeformTimeline.hx | 165 ++--- .../spine/animation/DrawOrderTimeline.hx | 7 +- .../spine-haxe/spine/animation/EventQueue.hx | 3 +- .../spine/animation/EventTimeline.hx | 19 +- .../spine/animation/IkConstraintTimeline.hx | 95 +-- .../spine/animation/InheritTimeline.hx | 25 +- .../spine-haxe/spine/animation/MixBlend.hx | 40 +- .../spine/animation/MixDirection.hx | 2 +- .../animation/PathConstraintMixTimeline.hx | 61 +- .../PathConstraintPositionTimeline.hx | 29 +- .../PathConstraintSpacingTimeline.hx | 23 +- .../PhysicsConstraintDampingTimeline.hx | 18 +- .../PhysicsConstraintGravityTimeline.hx | 18 +- .../PhysicsConstraintInertiaTimeline.hx | 18 +- .../PhysicsConstraintMassTimeline.hx | 18 +- .../animation/PhysicsConstraintMixTimeline.hx | 18 +- .../PhysicsConstraintResetTimeline.hx | 42 +- .../PhysicsConstraintStrengthTimeline.hx | 18 +- .../animation/PhysicsConstraintTimeline.hx | 40 +- .../PhysicsConstraintWindTimeline.hx | 18 +- .../spine-haxe/spine/animation/Property.hx | 65 +- .../spine/animation/RGB2Timeline.hx | 33 +- .../spine/animation/RGBA2Timeline.hx | 33 +- .../spine/animation/RGBATimeline.hx | 26 +- .../spine-haxe/spine/animation/RGBTimeline.hx | 24 +- .../spine/animation/RotateTimeline.hx | 21 +- .../spine/animation/ScaleTimeline.hx | 99 ++- .../spine/animation/ScaleXTimeline.hx | 22 +- .../spine/animation/ScaleYTimeline.hx | 23 +- .../spine/animation/SequenceTimeline.hx | 24 +- .../spine/animation/ShearTimeline.hx | 64 +- .../spine/animation/ShearXTimeline.hx | 21 +- .../spine/animation/ShearYTimeline.hx | 21 +- .../spine/animation/SliderMixTimeline.hx | 49 ++ .../spine/animation/SliderTimeline.hx | 47 ++ .../spine/animation/SlotCurveTimeline.hx | 52 ++ .../spine-haxe/spine/animation/Timeline.hx | 11 +- .../animation/TransformConstraintTimeline.hx | 93 +-- .../spine/animation/TranslateTimeline.hx | 60 +- .../spine/animation/TranslateXTimeline.hx | 16 +- .../spine/animation/TranslateYTimeline.hx | 21 +- .../spine/attachments/MeshAttachment.hx | 15 +- .../spine/attachments/PointAttachment.hx | 6 +- .../spine/attachments/RegionAttachment.hx | 11 +- .../spine/attachments/VertexAttachment.hx | 78 +-- .../spine-haxe/spine/flixel/SkeletonSprite.hx | 6 +- .../spine/starling/SkeletonSprite.hx | 8 +- 93 files changed, 3696 insertions(+), 3392 deletions(-) create mode 100644 spine-haxe/spine-haxe/spine/BoneLocal.hx create mode 100644 spine-haxe/spine-haxe/spine/BonePose.hx create mode 100644 spine-haxe/spine-haxe/spine/Constraint.hx create mode 100644 spine-haxe/spine-haxe/spine/IkConstraintPose.hx create mode 100644 spine-haxe/spine-haxe/spine/PathConstraintPose.hx create mode 100644 spine-haxe/spine-haxe/spine/PhysicsConstraintPose.hx create mode 100644 spine-haxe/spine-haxe/spine/Pose.hx create mode 100644 spine-haxe/spine-haxe/spine/Posed.hx create mode 100644 spine-haxe/spine-haxe/spine/PosedActive.hx create mode 100644 spine-haxe/spine-haxe/spine/PosedData.hx create mode 100644 spine-haxe/spine-haxe/spine/Slider.hx create mode 100644 spine-haxe/spine-haxe/spine/SliderData.hx create mode 100644 spine-haxe/spine-haxe/spine/SliderPose.hx create mode 100644 spine-haxe/spine-haxe/spine/SlotPose.hx create mode 100644 spine-haxe/spine-haxe/spine/TransformConstraintPose.hx rename spine-haxe/spine-haxe/spine/{Updatable.hx => Update.hx} (81%) create mode 100644 spine-haxe/spine-haxe/spine/animation/BoneTimeline1.hx rename spine-haxe/spine-haxe/spine/animation/{CurveTimeline2.hx => BoneTimeline2.hx} (67%) create mode 100644 spine-haxe/spine-haxe/spine/animation/ConstraintTimeline.hx create mode 100644 spine-haxe/spine-haxe/spine/animation/ConstraintTimeline1.hx create mode 100644 spine-haxe/spine-haxe/spine/animation/SliderMixTimeline.hx create mode 100644 spine-haxe/spine-haxe/spine/animation/SliderTimeline.hx create mode 100644 spine-haxe/spine-haxe/spine/animation/SlotCurveTimeline.hx diff --git a/spine-haxe/spine-haxe/spine/Bone.hx b/spine-haxe/spine-haxe/spine/Bone.hx index 47c691f7f..30d704425 100644 --- a/spine-haxe/spine-haxe/spine/Bone.hx +++ b/spine-haxe/spine-haxe/spine/Bone.hx @@ -29,437 +29,33 @@ package spine; -/** Stores a bone's current pose. +/** The current pose for a bone, before constraints are applied. * * A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a * local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a * constraint or application code modifies the world transform after it was computed from the local transform. */ -class Bone implements Updatable { +class Bone extends PosedActive { static public var yDown:Bool = false; - private var _data:BoneData; - private var _skeleton:Skeleton; - private var _parent:Bone; - private var _children:Array = new Array(); - - /** The local x translation. */ - public var x:Float = 0; - /** The local y translation. */ - public var y:Float = 0; - /** The local rotation in degrees, counter clockwise. */ - public var rotation:Float = 0; - /** The local scaleX. */ - public var scaleX:Float = 0; - /** The local scaleY. */ - public var scaleY:Float = 0; - /** The local shearX. */ - public var shearX:Float = 0; - /** The local shearY. */ - public var shearY:Float = 0; - /** The applied local x translation. */ - public var ax:Float = 0; - /** The applied local y translation. */ - public var ay:Float = 0; - /** The applied local rotation in degrees, counter clockwise. */ - public var arotation:Float = 0; - /** The applied local scaleX. */ - public var ascaleX:Float = 0; - /** The applied local scaleY. */ - public var ascaleY:Float = 0; - /** The applied local shearX. */ - public var ashearX:Float = 0; - /** The applied local shearY. */ - public var ashearY:Float = 0; - /** Part of the world transform matrix for the X axis. If changed, updateAppliedTransform() should be called. */ - public var a:Float = 0; - /** Part of the world transform matrix for the Y axis. If changed, updateAppliedTransform() should be called. */ - public var b:Float = 0; - /** Part of the world transform matrix for the X axis. If changed, updateAppliedTransform() should be called. */ - public var c:Float = 0; - /** Part of the world transform matrix for the Y axis. If changed, updateAppliedTransform() should be called. */ - public var d:Float = 0; - /** The world X position. If changed, updateAppliedTransform() should be called. */ - public var worldX:Float = 0; - /** The world Y position. If changed, updateAppliedTransform() should be called. */ - public var worldY:Float = 0; - /** Determines how parent world transforms affect this bone. */ - public var inherit:Inherit = Inherit.normal; - public var sorted:Bool = false; - public var active:Bool = false; - - /** The bone's setup pose data. */ - public var data(get, never):BoneData; - - private function get_data():BoneData { - return _data; - } - - /** The skeleton this bone belongs to. */ - public var skeleton(get, never):Skeleton; - - private function get_skeleton():Skeleton { - return _skeleton; - } - /** The parent bone, or null if this is the root bone. */ - public var parent(get, never):Bone; - - private function get_parent():Bone { - return _parent; - } + public final parent:Bone = null; /** The immediate children of this bone. */ - public var children(get, never):Array; + public final children:Array = new Array(); - private function get_children():Array { - return _children; + public var sorted = false; + + public function new (data:BoneData, parent:Bone) { + super(data, new BonePose(), new BonePose()); + this.parent = parent; + applied.bone = this; + constrained.bone = this; } - /** Copy constructor. Does not copy the children bones. */ - public function new(data:BoneData, skeleton:Skeleton, parent:Bone) { - if (data == null) - throw new SpineException("data cannot be null."); - if (skeleton == null) - throw new SpineException("skeleton cannot be null."); - _data = data; - _skeleton = skeleton; - _parent = parent; - setToSetupPose(); - } - - public function isActive():Bool { - return active; - } - - /** Computes the world transform using the parent bone and this bone's local applied transform. */ - public function update(physics:Physics):Void { - updateWorldTransformWith(ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY); - } - - /** Computes the world transform using the parent bone and this bone's local transform. - * - * See updateWorldTransformWith(). */ - public function updateWorldTransform():Void { - updateWorldTransformWith(x, y, rotation, scaleX, scaleY, shearX, shearY); - } - - /** Computes the world transform using the parent bone and the specified local transform. The applied transform is set to the - * specified local transform. Child bones are not updated. - * - * @see https://esotericsoftware.com/spine-runtime-skeletons#World-transforms World transforms in the Spine Runtimes Guide - */ - public function updateWorldTransformWith(x:Float, y:Float, rotation:Float, scaleX:Float, scaleY:Float, shearX:Float, shearY:Float):Void { - ax = x; - ay = y; - arotation = rotation; - ascaleX = scaleX; - ascaleY = scaleY; - ashearX = shearX; - ashearY = shearY; - - var la:Float = 0; - var lb:Float = 0; - var lc:Float = 0; - var ld:Float = 0; - var sin:Float = 0; - var cos:Float = 0; - var s:Float = 0; - var sx:Float = skeleton.scaleX; - var sy:Float = skeleton.scaleY; - - var parent:Bone = _parent; - if (parent == null) { - // Root bone. - var rx:Float = (rotation + shearX) * MathUtils.degRad; - var ry:Float = (rotation + 90 + shearY) * MathUtils.degRad; - a = Math.cos(rx) * scaleX * sx; - b = Math.cos(ry) * scaleY * sx; - c = Math.sin(rx) * scaleX * sy; - d = Math.sin(ry) * scaleY * sy; - worldX = x * sx + skeleton.x; - worldY = y * sy + skeleton.y; - return; - } - - var pa:Float = parent.a, - pb:Float = parent.b, - pc:Float = parent.c, - pd:Float = parent.d; - worldX = pa * x + pb * y + parent.worldX; - worldY = pc * x + pd * y + parent.worldY; - - switch (inherit) { - case Inherit.normal: - var rx:Float = (rotation + shearX) * MathUtils.degRad; - var ry:Float = (rotation + 90 + shearY) * MathUtils.degRad; - la = Math.cos(rx) * scaleX; - lb = Math.cos(ry) * scaleY; - lc = Math.sin(rx) * scaleX; - ld = Math.sin(ry) * scaleY; - a = pa * la + pb * lc; - b = pa * lb + pb * ld; - c = pc * la + pd * lc; - d = pc * lb + pd * ld; - return; - case Inherit.onlyTranslation: - var rx:Float = (rotation + shearX) * MathUtils.degRad; - var ry:Float = (rotation + 90 + shearY) * MathUtils.degRad; - a = Math.cos(rx) * scaleX; - b = Math.cos(ry) * scaleY; - c = Math.sin(rx) * scaleX; - d = Math.sin(ry) * scaleY; - case Inherit.noRotationOrReflection: - var sx:Float = 1 / skeleton.scaleX; - var sy:Float = 1 / skeleton.scaleY; - pa *= sx; - pc *= sy; - s = pa * pa + pc * pc; - var prx:Float = 0; - if (s > 0.0001) { - s = Math.abs(pa * pd * sy - pb * sx * pc) / s; - pb = pc * s; - pd = pa * s; - prx = Math.atan2(pc, pa) * MathUtils.radDeg; - } else { - pa = 0; - pc = 0; - prx = 90 - Math.atan2(pd, pb) * MathUtils.radDeg; - } - var rx:Float = (rotation + shearX - prx) * MathUtils.degRad; - var ry:Float = (rotation + shearY - prx + 90) * MathUtils.degRad; - la = Math.cos(rx) * scaleX; - lb = Math.cos(ry) * scaleY; - lc = Math.sin(rx) * scaleX; - ld = Math.sin(ry) * scaleY; - a = pa * la - pb * lc; - b = pa * lb - pb * ld; - c = pc * la + pd * lc; - d = pc * lb + pd * ld; - case Inherit.noScale, Inherit.noScaleOrReflection: - rotation *= MathUtils.degRad; - cos = Math.cos(rotation); - sin = Math.sin(rotation); - var za:Float = (pa * cos + pb * sin) / sx; - var zc:Float = (pc * cos + pd * sin) / sy; - s = Math.sqrt(za * za + zc * zc); - if (s > 0.00001) - s = 1 / s; - za *= s; - zc *= s; - s = Math.sqrt(za * za + zc * zc); - if (inherit == Inherit.noScale && ((pa * pd - pb * pc < 0) != ((sx < 0) != (sy < 0)))) { - s = -s; - } - rotation = Math.PI / 2 + Math.atan2(zc, za); - var zb:Float = Math.cos(rotation) * s; - var zd:Float = Math.sin(rotation) * s; - shearX *= MathUtils.degRad; - shearY = (90 + shearY) * MathUtils.degRad; - la = Math.cos(shearX) * scaleX; - lb = Math.cos(shearY) * scaleY; - lc = Math.sin(shearX) * scaleX; - ld = Math.sin(shearY) * scaleY; - a = za * la + zb * lc; - b = za * lb + zb * ld; - c = zc * la + zd * lc; - d = zc * lb + zd * ld; - } - a *= sx; - b *= sx; - c *= sy; - d *= sy; - } - - /** Sets this bone's local transform to the setup pose. */ - public function setToSetupPose():Void { - x = data.x; - y = data.y; - rotation = data.rotation; - scaleX = data.scaleX; - scaleY = data.scaleY; - shearX = data.shearX; - shearY = data.shearY; - inherit = data.inherit; - } - - /** Computes the applied transform values from the world transform. - * - * If the world transform is modified (by a constraint, rotateWorld(), etc) then this method should be called so - * the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply another - * constraint). - * - * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after - * calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */ - public function updateAppliedTransform():Void { - var parent:Bone = parent; - if (parent == null) { - ax = worldX - skeleton.x; - ay = worldY - skeleton.y; - arotation = Math.atan2(c, a) * MathUtils.radDeg; - ascaleX = Math.sqrt(a * a + c * c); - ascaleY = Math.sqrt(b * b + d * d); - ashearX = 0; - ashearY = Math.atan2(a * b + c * d, a * d - b * c) * MathUtils.radDeg; - return; - } - var pa:Float = parent.a, - pb:Float = parent.b, - pc:Float = parent.c, - pd:Float = parent.d; - var pid:Float = 1 / (pa * pd - pb * pc); - var ia:Float = pd * pid, - ib:Float = pb * pid, - ic:Float = pc * pid, - id:Float = pa * pid; - var dx:Float = worldX - parent.worldX, - dy:Float = worldY - parent.worldY; - ax = (dx * ia - dy * ib); - ay = (dy * id - dx * ic); - var ra:Float, rb:Float, rc:Float, rd:Float; - if (inherit == Inherit.onlyTranslation) { - ra = a; - rb = b; - rc = c; - rd = d; - } else { - switch (inherit) { - case Inherit.noRotationOrReflection: - var s:Float = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); - pb = -pc * skeleton.scaleX * s / skeleton.scaleY; - pd = pa * skeleton.scaleY * s / skeleton.scaleX; - pid = 1 / (pa * pd - pb * pc); - ia = pd * pid; - ib = pb * pid; - case Inherit.noScale | Inherit.noScaleOrReflection: - var cos:Float = MathUtils.cosDeg(rotation), sin:Float = MathUtils.sinDeg(rotation); - pa = (pa * cos + pb * sin) / skeleton.scaleX; - pc = (pc * cos + pd * sin) / skeleton.scaleY; - var s:Float = Math.sqrt(pa * pa + pc * pc); - if (s > 0.00001) s = 1 / s; - pa *= s; - pc *= s; - s = Math.sqrt(pa * pa + pc * pc); - if (inherit == Inherit.noScale && pid < 0 != ((skeleton.scaleX < 0) != (skeleton.scaleY < 0))) s = -s; - var r:Float = MathUtils.PI / 2 + Math.atan2(pc, pa); - pb = Math.cos(r) * s; - pd = Math.sin(r) * s; - pid = 1 / (pa * pd - pb * pc); - ia = pd * pid; - ib = pb * pid; - ic = pc * pid; - id = pa * pid; - } - ra = ia * a - ib * c; - rb = ia * b - ib * d; - rc = id * c - ic * a; - rd = id * d - ic * b; - } - - ashearX = 0; - ascaleX = Math.sqrt(ra * ra + rc * rc); - if (scaleX > 0.0001) { - var det:Float = ra * rd - rb * rc; - ascaleY = det / ascaleX; - ashearY = -Math.atan2(ra * rb + rc * rd, det) * MathUtils.radDeg; - arotation = Math.atan2(rc, ra) * MathUtils.radDeg; - } else { - ascaleX = 0; - ascaleY = Math.sqrt(rb * rb + rd * rd); - ashearY = 0; - arotation = 90 - Math.atan2(rd, rb) * MathUtils.radDeg; - } - } - - /** The world rotation for the X axis, calculated using a and c. */ - public var worldRotationX(get, never):Float; - - private function get_worldRotationX():Float { - return Math.atan2(c, a) * MathUtils.radDeg; - } - - /** The world rotation for the Y axis, calculated using b and d. */ - public var worldRotationY(get, never):Float; - - private function get_worldRotationY():Float { - return Math.atan2(d, b) * MathUtils.radDeg; - } - - /** The magnitude (always positive) of the world scale X, calculated using a and c. */ - public var worldScaleX(get, never):Float; - - private function get_worldScaleX():Float { - return Math.sqrt(a * a + c * c); - } - - /** The magnitude (always positive) of the world scale Y, calculated using b and d. */ - public var worldScaleY(get, never):Float; - - private function get_worldScaleY():Float { - return Math.sqrt(b * b + d * d); - } - - /** Transforms a point from world coordinates to the parent bone's local coordinates. */ - public function worldToParent(world: Array):Array { - if (world == null) - throw new SpineException("world cannot be null."); - return parent == null ? world : parent.worldToLocal(world); - } - - /** Transforms a point from the parent bone's coordinates to world coordinates. */ - public function parentToWorld(world: Array):Array { - if (world == null) - throw new SpineException("world cannot be null."); - return parent == null ? world : parent.localToWorld(world); - } - - /** Transforms a point from world coordinates to the bone's local coordinates. */ - public function worldToLocal(world:Array):Array { - var a:Float = a, b:Float = b, c:Float = c, d:Float = d; - var invDet:Float = 1 / (a * d - b * c); - var x:Float = world[0] - worldX, y:Float = world[1] - worldY; - world[0] = (x * d * invDet - y * b * invDet); - world[1] = (y * a * invDet - x * c * invDet); - return world; - } - - /** Transforms a point from the bone's local coordinates to world coordinates. */ - public function localToWorld(local:Array):Array { - var localX:Float = local[0], localY:Float = local[1]; - local[0] = localX * a + localY * b + worldX; - local[1] = localX * c + localY * d + worldY; - return local; - } - - /** Transforms a world rotation to a local rotation. */ - public function worldToLocalRotation(worldRotation:Float):Float { - var sin:Float = MathUtils.sinDeg(worldRotation), - cos:Float = MathUtils.cosDeg(worldRotation); - return Math.atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.radDeg + rotation - shearX; - } - - /** Transforms a local rotation to a world rotation. */ - public function localToWorldRotation(localRotation:Float):Float { - localRotation -= rotation - shearX; - var sin:Float = MathUtils.sinDeg(localRotation), - cos:Float = MathUtils.cosDeg(localRotation); - return Math.atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.radDeg; - } - - /** Rotates the world transform the specified amount. - * - * After changes are made to the world transform, updateAppliedTransform() should be called and - * update() will need to be called on any child bones, recursively. */ - public function rotateWorld(degrees:Float):Void { - degrees *= MathUtils.degRad; - var sin:Float = Math.sin(degrees), cos:Float = Math.cos(degrees); - var ra:Float = a, rb:Float = b; - a = cos * ra - sin * c; - b = cos * rb - sin * d; - c = sin * ra + cos * c; - d = sin * rb + cos * d; - } - - public function toString():String { - return data.name; + /** Copy method. Does not copy the children bones. */ + public function copy(bone:Bone, parent:Bone):Bone { + var copy = new Bone(bone.data, parent); + pose.set(bone.pose); + return copy; } } diff --git a/spine-haxe/spine-haxe/spine/BoneData.hx b/spine-haxe/spine-haxe/spine/BoneData.hx index b22a03bf0..5aed0ece7 100644 --- a/spine-haxe/spine-haxe/spine/BoneData.hx +++ b/spine-haxe/spine-haxe/spine/BoneData.hx @@ -29,75 +29,41 @@ package spine; -/** Stores the setup pose for a spine.Bone. */ -class BoneData { - private var _index:Int; - private var _name:String; - private var _parent:BoneData; - - /** The bone's length. */ - public var length:Float = 0; - /** The local x translation. */ - public var x:Float = 0; - /** The local y translation. */ - public var y:Float = 0; - /** The local rotation in degrees, counter clockwise. */ - public var rotation:Float = 0; - /** The local scaleX. */ - public var scaleX:Float = 1; - /** The local scaleY. */ - public var scaleY:Float = 1; - /** The local shearX. */ - public var shearX:Float = 0; - /** The local shearY. */ - public var shearY:Float = 0; - /** Determines how parent world transforms affect this bone. */ - public var inherit:Inherit = Inherit.normal; - /** When true, spine.Skeleton.updateWorldTransform() only updates this bone if the spine.Skeleton.getSkin() contains - * this bone. - * @see spine.Skin.getBones() */ - public var skinRequired:Bool = false; - /** The color of the bone as it was in Spine, or a default color if nonessential data was not exported. Bones are not usually - * rendered at runtime. */ - public var color:Color = new Color(0, 0, 0, 0); - /** The bone icon as it was in Spine, or null if nonessential data was not exported. */ - public var icon:String; - /** False if the bone was hidden in Spine and nonessential data was exported. Does not affect runtime rendering. */ - public var visible:Bool = false; - - /** Copy constructor. */ - public function new(index:Int, name:String, parent:BoneData) { - if (index < 0) - throw new SpineException("index must be >= 0"); - if (name == null) - throw new SpineException("name cannot be null."); - _index = index; - _name = name; - _parent = parent; - } +/** The setup pose for a bone. */ +class BoneData extends PosedData { /** The index of the bone in spine.Skeleton.getBones(). */ - public var index(get, never):Int; + public final index:Int; - private function get_index():Int { - return _index; + public final parent:BoneData = null; + + /** The bone's length. */ + public var length = 0.; + + // Nonessential. + /** The color of the bone as it was in Spine, or a default color if nonessential data was not exported. Bones are not usually + * rendered at runtime. */ + public var color = new Color(0, 0, 0, 0); + + /** The bone icon as it was in Spine, or null if nonessential data was not exported. */ + public var icon:String = null; + + /** False if the bone was hidden in Spine and nonessential data was exported. Does not affect runtime rendering. */ + public var visible = false; + + public function new (index:Int, name:String, parent:BoneData) { + super(name, new BoneLocal()); + if (index < 0) throw new SpineException("index must be >= 0."); + if (name == null) throw new SpineException("name cannot be null."); + this.index = index; + this.parent = parent; } - /** The name of the bone, which is unique across all bones in the skeleton. */ - public var name(get, never):String; - - private function get_name():String { - return _name; - } - - /** @return May be null. */ - public var parent(get, never):BoneData; - - private function get_parent():BoneData { - return _parent; - } - - public function toString():String { - return _name; + /** Copy method. */ + public function copy(data:BoneData, parent:BoneData) { + var copy = new BoneData(data.index, data.name, parent); + length = data.length; + setup.set(data.setup); + return copy; } } diff --git a/spine-haxe/spine-haxe/spine/BoneLocal.hx b/spine-haxe/spine-haxe/spine/BoneLocal.hx new file mode 100644 index 000000000..4dabef224 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/BoneLocal.hx @@ -0,0 +1,79 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +/** Stores a bone's local pose. */ +class BoneLocal implements Pose { + + /** The local x translation. */ + public var x:Float = 0; + + /** The local y translation. */ + public var y:Float = 0; + + /** The local rotation in degrees, counter clockwise. */ + public var rotation:Float = 0; + + /** The local scaleX. */ + public var scaleX:Float = 0; + + /** The local scaleY. */ + public var scaleY:Float = 0; + + /** The local shearX. */ + public var shearX:Float = 0; + + /** The local shearY. */ + public var shearY:Float = 0; + + /** Determines how parent world transforms affect this bone. */ + public var inherit(default, set):Inherit; + function set_inherit (value:Inherit):Inherit { + if (value == null) throw new SpineException("inherit cannot be null."); + inherit = value; + return value; + } + + public function new () { + } + + public function set (pose:BoneLocal):Void { + if (pose == null) throw new SpineException("pose cannot be null."); + x = pose.x; + y = pose.y; + rotation = pose.rotation; + scaleX = pose.scaleX; + scaleY = pose.scaleY; + shearX = pose.shearX; + shearY = pose.shearY; + inherit = pose.inherit; + } + +} diff --git a/spine-haxe/spine-haxe/spine/BonePose.hx b/spine-haxe/spine-haxe/spine/BonePose.hx new file mode 100644 index 000000000..881ce4eb3 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/BonePose.hx @@ -0,0 +1,381 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +/** The applied pose for a bone. This is the {@link Bone} pose with constraints applied and the world transform computed by + * Skeleton.updateWorldTransform(Physics). */ +class BonePose extends BoneLocal implements Update { + + public var bone:Bone; + + /** Part of the world transform matrix for the X axis. If changed, updateAppliedTransform() should be called. */ + public var a:Float = 0; + + /** Part of the world transform matrix for the Y axis. If changed, updateAppliedTransform() should be called. */ + public var b:Float = 0; + + /** Part of the world transform matrix for the X axis. If changed, updateAppliedTransform() should be called. */ + public var c:Float = 0; + + /** Part of the world transform matrix for the Y axis. If changed, updateAppliedTransform() should be called. */ + public var d:Float = 0; + + /** The world X position. If changed, updateAppliedTransform() should be called. */ + public var worldX:Float = 0; + + /** The world Y position. If changed, updateAppliedTransform() should be called. */ + public var worldY:Float = 0; + + public var world:Int; + public var local:Int; + + // public function new () { + // super(); + // } + + /** Called by Skeleton.updateCache() to compute the world transform, if needed. */ + public function update (skeleton:Skeleton, physics:Physics):Void { + if (world != skeleton._update) updateWorldTransform(skeleton); + } + + /** Computes the world transform using the parent bone's applied pose and this pose. Child bones are not updated. + * + * @see https://esotericsoftware.com/spine-runtime-skeletons#World-transforms World transforms in the Spine Runtimes Guide + */ + public function updateWorldTransform(skeleton:Skeleton):Void { + if (local == skeleton._update) + updateLocalTransform(skeleton); + else + world = skeleton._update; + + if (bone.parent == null) { // Root bone. + var sx = skeleton.scaleX, sy = skeleton.scaleY; + var rx = (rotation + shearX) * MathUtils.degRad; + var ry = (rotation + 90 + shearY) * MathUtils.degRad; + a = Math.cos(rx) * scaleX * sx; + b = Math.cos(ry) * scaleY * sx; + c = Math.sin(rx) * scaleX * sy; + d = Math.sin(ry) * scaleY * sy; + worldX = x * sx + skeleton.x; + worldY = y * sy + skeleton.y; + return; + } + + var parent = bone.parent.applied; + var pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; + worldX = pa * x + pb * y + parent.worldX; + worldY = pc * x + pd * y + parent.worldY; + + switch (inherit) { + case Inherit.normal: + var rx = (rotation + shearX) * MathUtils.degRad; + var ry = (rotation + 90 + shearY) * MathUtils.degRad; + var la = Math.cos(rx) * scaleX; + var lb = Math.cos(ry) * scaleY; + var lc = Math.sin(rx) * scaleX; + var ld = Math.sin(ry) * scaleY; + a = pa * la + pb * lc; + b = pa * lb + pb * ld; + c = pc * la + pd * lc; + d = pc * lb + pd * ld; + return; + case Inherit.onlyTranslation: + var rx = (rotation + shearX) * MathUtils.degRad; + var ry = (rotation + 90 + shearY) * MathUtils.degRad; + a = Math.cos(rx) * scaleX; + b = Math.cos(ry) * scaleY; + c = Math.sin(rx) * scaleX; + d = Math.sin(ry) * scaleY; + case Inherit.noRotationOrReflection: + var sx = 1 / skeleton.scaleX, sy = 1 / skeleton.scaleY; + pa *= sx; + pc *= sy; + var s = pa * pa + pc * pc, prx:Float; + if (s > 0.0001) { + s = Math.abs(pa * pd * sy - pb * sx * pc) / s; + pb = pc * s; + pd = pa * s; + prx = MathUtils.atan2Deg(pc, pa); + } else { + pa = 0; + pc = 0; + prx = 90 - MathUtils.atan2Deg(pd, pb); + } + var rx = (rotation + shearX - prx) * MathUtils.degRad; + var ry = (rotation + shearY - prx + 90) * MathUtils.degRad; + var la = Math.cos(rx) * scaleX; + var lb = Math.cos(ry) * scaleY; + var lc = Math.sin(rx) * scaleX; + var ld = Math.sin(ry) * scaleY; + a = pa * la - pb * lc; + b = pa * lb - pb * ld; + c = pc * la + pd * lc; + d = pc * lb + pd * ld; + case Inherit.noScale, Inherit.noScaleOrReflection: + rotation *= MathUtils.degRad; + var cos = Math.cos(rotation); + var sin = Math.sin(rotation); + var za = (pa * cos + pb * sin) / skeleton.scaleX; + var zc = (pc * cos + pd * sin) / skeleton.scaleY; + var s = Math.sqrt(za * za + zc * zc); + if (s > 0.00001) s = 1 / s; + za *= s; + zc *= s; + s = Math.sqrt(za * za + zc * zc); + if (inherit == Inherit.noScale && ((pa * pd - pb * pc < 0) != ((skeleton.scaleX < 0) != (skeleton.scaleY < 0)))) s = -s; + rotation = Math.PI / 2 + Math.atan2(zc, za); + var zb:Float = Math.cos(rotation) * s; + var zd:Float = Math.sin(rotation) * s; + shearX *= MathUtils.degRad; + shearY = (90 + shearY) * MathUtils.degRad; + var la = Math.cos(shearX) * scaleX; + var lb = Math.cos(shearY) * scaleY; + var lc = Math.sin(shearX) * scaleX; + var ld = Math.sin(shearY) * scaleY; + a = za * la + zb * lc; + b = za * lb + zb * ld; + c = zc * la + zd * lc; + d = zc * lb + zd * ld; + } + a *= skeleton.scaleX; + b *= skeleton.scaleX; + c *= skeleton.scaleY; + d *= skeleton.scaleY; + } + + /** Computes the applied transform values from the world transform. + * + * If the world transform is modified (by a constraint, rotateWorld(), etc) then this method should be called so + * the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply another + * constraint). + * + * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after + * calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */ + public function updateLocalTransform(skeleton:Skeleton):Void { + local = 0; + world = skeleton._update; + + if (bone.parent == null) { + x = worldX - skeleton.x; + y = worldY - skeleton.y; + rotation = MathUtils.atan2Deg(c, a); + scaleX = Math.sqrt(a * a + c * c); + scaleY = Math.sqrt(b * b + d * d); + shearX = 0; + shearY = MathUtils.atan2Deg(a * b + c * d, a * d - b * c); + return; + } + + var parent = bone.parent.applied; + var pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; + var pid:Float = 1 / (pa * pd - pb * pc); + var ia = pd * pid, ib = pb * pid, ic = pc * pid, id = pa * pid; + var dx = worldX - parent.worldX, dy = worldY - parent.worldY; + x = (dx * ia - dy * ib); + y = (dy * id - dx * ic); + + var ra:Float, rb:Float, rc:Float, rd:Float; + if (inherit == Inherit.onlyTranslation) { + ra = a; + rb = b; + rc = c; + rd = d; + } else { + switch (inherit) { + case Inherit.noRotationOrReflection: + var s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); + pb = -pc * skeleton.scaleX * s / skeleton.scaleY; + pd = pa * skeleton.scaleY * s / skeleton.scaleX; + pid = 1 / (pa * pd - pb * pc); + ia = pd * pid; + ib = pb * pid; + case Inherit.noScale, Inherit.noScaleOrReflection: + var r = rotation * MathUtils.degRad, cos = Math.cos(rotation), sin = Math.sin(rotation); + pa = (pa * cos + pb * sin) / skeleton.scaleX; + pc = (pc * cos + pd * sin) / skeleton.scaleY; + var s = Math.sqrt(pa * pa + pc * pc); + if (s > 0.00001) s = 1 / s; + pa *= s; + pc *= s; + s = Math.sqrt(pa * pa + pc * pc); + if (inherit == Inherit.noScale && (pid < 0 != ((skeleton.scaleX < 0) != (skeleton.scaleY < 0)))) s = -s; + r = MathUtils.PI / 2 + Math.atan2(pc, pa); + pb = Math.cos(r) * s; + pd = Math.sin(r) * s; + pid = 1 / (pa * pd - pb * pc); + ia = pd * pid; + ib = pb * pid; + ic = pc * pid; + id = pa * pid; + } + ra = ia * a - ib * c; + rb = ia * b - ib * d; + rc = id * c - ic * a; + rd = id * d - ic * b; + } + + shearX = 0; + scaleX = Math.sqrt(ra * ra + rc * rc); + if (scaleX > 0.0001) { + var det = ra * rd - rb * rc; + scaleY = det / scaleX; + shearY = -MathUtils.atan2Deg(ra * rb + rc * rd, det); + rotation = MathUtils.atan2Deg(rc, ra); + } else { + scaleX = 0; + scaleY = Math.sqrt(rb * rb + rd * rd); + shearY = 0; + rotation = 90 - MathUtils.atan2Deg(rd, rb); + } + } + + /** If the world transform has been modified and the local transform no longer matches, {@link #updateLocalTransform(Skeleton)} + * is called. */ + public function validateLocalTransform (skeleton: Skeleton) { + if (local == skeleton._update) updateLocalTransform(skeleton); + } + + public function modifyLocal (skeleton: Skeleton) { + if (local == skeleton._update) updateLocalTransform(skeleton); + world = 0; + resetWorld(skeleton._update); + } + + public function modifyWorld (update:Int) { + local = update; + world = update; + resetWorld(update); + } + + public function resetWorld (update:Int) { + var children = bone.children; + for (i in 0...bone.children.length) { + var child = children[i].applied; + if (child.world == update) { + child.world = 0; + child.local = 0; + child.resetWorld(update); + } + } + } + + /** The world rotation for the X axis, calculated using a and c. */ + public var worldRotationX(get, never):Float; + + private function get_worldRotationX():Float { + return MathUtils.atan2Deg(c, a); + } + + /** The world rotation for the Y axis, calculated using b and d. */ + public var worldRotationY(get, never):Float; + + private function get_worldRotationY():Float { + return MathUtils.atan2Deg(d, b); + } + + /** The magnitude (always positive) of the world scale X, calculated using a and c. */ + public var worldScaleX(get, never):Float; + + private function get_worldScaleX():Float { + return Math.sqrt(a * a + c * c); + } + + /** The magnitude (always positive) of the world scale Y, calculated using b and d. */ + public var worldScaleY(get, never):Float; + + private function get_worldScaleY():Float { + return Math.sqrt(b * b + d * d); + } + + /** Transforms a point from world coordinates to the bone's local coordinates. */ + public function worldToLocal(world:Array):Array { + var a:Float = a, b:Float = b, c:Float = c, d:Float = d; + var invDet:Float = 1 / (a * d - b * c); + var x:Float = world[0] - worldX, y:Float = world[1] - worldY; + world[0] = (x * d * invDet - y * b * invDet); + world[1] = (y * a * invDet - x * c * invDet); + return world; + } + + /** Transforms a point from the bone's local coordinates to world coordinates. */ + public function localToWorld(local:Array):Array { + var localX:Float = local[0], localY:Float = local[1]; + local[0] = localX * a + localY * b + worldX; + local[1] = localX * c + localY * d + worldY; + return local; + } + + /** Transforms a point from world coordinates to the parent bone's local coordinates. */ + public function worldToParent(world: Array):Array { + if (world == null) + throw new SpineException("world cannot be null."); + return bone.parent == null ? world : bone.parent.applied.worldToLocal(world); + } + + /** Transforms a point from the parent bone's coordinates to world coordinates. */ + public function parentToWorld(world: Array):Array { + if (world == null) + throw new SpineException("world cannot be null."); + return bone.parent == null ? world : bone.parent.applied.localToWorld(world); + } + + /** Transforms a world rotation to a local rotation. */ + public function worldToLocalRotation(worldRotation:Float):Float { + var sin:Float = MathUtils.sinDeg(worldRotation), + cos:Float = MathUtils.cosDeg(worldRotation); + return Math.atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.radDeg + rotation - shearX; + } + + /** Transforms a local rotation to a world rotation. */ + public function localToWorldRotation(localRotation:Float):Float { + localRotation -= rotation - shearX; + var sin:Float = MathUtils.sinDeg(localRotation), + cos:Float = MathUtils.cosDeg(localRotation); + return Math.atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.radDeg; + } + + /** Rotates the world transform the specified amount. + * + * After changes are made to the world transform, updateAppliedTransform() should be called and + * update() will need to be called on any child bones, recursively. */ + public function rotateWorld(degrees:Float):Void { + degrees *= MathUtils.degRad; + var sin:Float = Math.sin(degrees), cos:Float = Math.cos(degrees); + var ra:Float = a, rb:Float = b; + a = cos * ra - sin * c; + b = cos * rb - sin * d; + c = sin * ra + cos * c; + d = sin * rb + cos * d; + } + + public function toString ():String { + return bone.data.name; + } +} diff --git a/spine-haxe/spine-haxe/spine/Constraint.hx b/spine-haxe/spine-haxe/spine/Constraint.hx new file mode 100644 index 000000000..d79a66215 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/Constraint.hx @@ -0,0 +1,49 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +abstract class Constraint< // + T:Constraint, // + D:ConstraintData, // + P:Pose> // + extends PosedActive implements Update { + + public function new (data:D, pose:P, constrained:P) { + super(data, pose, constrained); + } + + public abstract function copy (skeleton:Skeleton):T; + + public abstract function sort (skeleton:Skeleton):Void; + + public function isSourceActive ():Bool { + return true; + } +} \ No newline at end of file diff --git a/spine-haxe/spine-haxe/spine/ConstraintData.hx b/spine-haxe/spine-haxe/spine/ConstraintData.hx index 8206b3459..ac7081da3 100644 --- a/spine-haxe/spine-haxe/spine/ConstraintData.hx +++ b/spine-haxe/spine-haxe/spine/ConstraintData.hx @@ -30,24 +30,14 @@ package spine; /** The base class for all constraint datas. */ -class ConstraintData { - /** The constraint's name, which is unique across all constraints in the skeleton of the same type. */ - public var name:String; - /** The ordinal of this constraint for the order a skeleton's constraints will be applied by - * spine.Skeleton.updateWorldTransform(). */ - public var order:Int = 0; - /** When true, spine.Skeleton.updateWorldTransform() only updates this constraint if the spine.Skeleton.getSkin() - * contains this constraint. - * @see spine.Skin.getConstraints() */ - public var skinRequired:Bool = false; +abstract class ConstraintData< // + T:Constraint, // + P:Pose> // + extends PosedData

{ - function new(name:String, order:Int, skinRequired:Bool) { - this.name = name; - this.order = order; - this.skinRequired = skinRequired; + function new(name:String, setup:P) { + super(name, setup); } - public function toString():String { - return name; - } + public abstract function create (skeleton:Skeleton):T; } diff --git a/spine-haxe/spine-haxe/spine/Event.hx b/spine-haxe/spine-haxe/spine/Event.hx index a45ec0ee8..7744abe26 100644 --- a/spine-haxe/spine-haxe/spine/Event.hx +++ b/spine-haxe/spine-haxe/spine/Event.hx @@ -30,38 +30,32 @@ package spine; /** Stores the current pose values for an Event. - * + * * @see spine.Timeline * @see spine.Timeline.apply() * @see spine.AnimationStateListener.event() * @see https://esotericsoftware.com/spine-events Events in the Spine User Guide */ class Event { - private var _data:EventData; + /** The event's setup pose data. */ + public var data:EventData; /** The animation time this event was keyed. */ - public var time:Float = 0; - public var intValue:Int = 0; - public var floatValue:Float = 0; + public var time = 0.; + + public var intValue = 0; + public var floatValue = 0; public var stringValue:String; - public var volume:Float = 1; - public var balance:Float = 0; + public var volume = 1.; + public var balance = 0.; public function new(time:Float, data:EventData) { - if (data == null) - throw new SpineException("data cannot be null."); + if (data == null) throw new SpineException("data cannot be null."); this.time = time; - _data = data; - } - - /** The event's setup pose data. */ - public var data(get, never):EventData; - - private function get_data():EventData { - return _data; + this.data = data; } public function toString():String { - return _data.name != null ? _data.name : "Event?"; + return data.name != null ? data.name : "Event?"; } } diff --git a/spine-haxe/spine-haxe/spine/IkConstraint.hx b/spine-haxe/spine-haxe/spine/IkConstraint.hx index f92f7dc31..7dae6da21 100644 --- a/spine-haxe/spine-haxe/spine/IkConstraint.hx +++ b/spine-haxe/spine-haxe/spine/IkConstraint.hx @@ -33,171 +33,136 @@ package spine; * the last bone is as close to the target bone as possible. * * @see https://esotericsoftware.com/spine-ik-constraints IK constraints in the Spine User Guide */ -class IkConstraint implements Updatable { - private var _data:IkConstraintData; +class IkConstraint extends Constraint { + + /** The 1 or 2 bones that will be modified by this IK constraint. */ + public final bones:Array; - /** The bones that will be modified by this IK constraint. */ - public var bones:Array; /** The bone that is the IK target. */ - public var target:Bone; - /** For two bone IK, controls the bend direction of the IK bones, either 1 or -1. */ - public var bendDirection:Int = 0; - /** For one bone IK, when true and the target is too close, the bone is scaled to reach it. */ - public var compress:Bool = false; - /** When true and the target is out of range, the parent bone is scaled to reach it. - * - * For two bone IK: 1) the child bone's local Y translation is set to 0, 2) stretch is not applied if getSoftness() is - * > 0, and 3) if the parent bone has local nonuniform scale, stretch is not applied. */ - public var stretch:Bool = false; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. - * - * For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0. */ - public var mix:Float = 0; - /** For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones - * will not straighten completely until the target is this far out of range. */ - public var softness:Float = 0; - public var active:Bool = false; + public var target(default, set):Bone; - /** Copy constructor. */ public function new(data:IkConstraintData, skeleton:Skeleton) { - if (data == null) - throw new SpineException("data cannot be null."); - if (skeleton == null) - throw new SpineException("skeleton cannot be null."); - _data = data; + super(data, new IkConstraintPose(), new IkConstraintPose()); + if (skeleton == null) throw new SpineException("skeleton cannot be null."); - bones = new Array(); - for (boneData in data.bones) { - bones.push(skeleton.findBone(boneData.name)); - } - target = skeleton.findBone(data.target.name); - - mix = data.mix; - softness = data.softness; - bendDirection = data.bendDirection; - compress = data.compress; - stretch = data.stretch; + bones = new Array(); + for (boneData in data.bones) + bones.push(skeleton.bones[boneData.index].constrained); + target = skeleton.bones[data.target.index]; } - public function isActive():Bool { - return active; - } - - public function setToSetupPose () { - var data:IkConstraintData = _data; - mix = data.mix; - softness = data.softness; - bendDirection = data.bendDirection; - compress = data.compress; - stretch = data.stretch; + public function copy (skeleton:Skeleton) { + var copy = new IkConstraint(data, skeleton); + copy.pose.set(pose); + return copy; } /** Applies the constraint to the constrained bones. */ - public function update(physics:Physics):Void { - if (mix == 0) - return; + public function update (skeleton:Skeleton, physics:Physics):Void { + var p = applied; + if (p.mix == 0) return; + var target = target.applied; switch (bones.length) { - case 1: - apply1(bones[0], target.worldX, target.worldY, compress, stretch, _data.uniform, mix); - case 2: - apply2(bones[0], bones[1], target.worldX, target.worldY, bendDirection, stretch, _data.uniform, softness, mix); + case 1: apply1(skeleton, bones[0], target.worldX, target.worldY, p.compress, p.stretch, data.uniform, p.mix); + case 2: apply2(skeleton, bones[0], bones[1], target.worldX, target.worldY, p.bendDirection, p.stretch, data.uniform, p.softness, p.mix); } } - /** The IK constraint's setup pose data. */ - public var data(get, never):IkConstraintData; - - private function get_data():IkConstraintData { - return _data; + public function sort (skeleton:Skeleton) { + skeleton.sortBone(target); + var parent = bones[0].bone; + skeleton.sortBone(parent); + skeleton._updateCache.push(this); + parent.sorted = false; + skeleton.sortReset(parent.children); + skeleton.constrained(parent); + if (bones.length > 1) skeleton.constrained(bones[1].bone); } - public function toString():String { - return _data.name != null ? _data.name : "IkConstraint?"; + override public function isSourceActive () { + return target.active; + } + + public function set_target (target:Bone):Bone { + if (target == null) throw new SpineException("target cannot be null."); + this.target = target; + return target; } /** Applies 1 bone IK. The target is specified in the world coordinate system. */ - static public function apply1(bone:Bone, targetX:Float, targetY:Float, compress:Bool, stretch:Bool, uniform:Bool, alpha:Float):Void { - var p:Bone = bone.parent; - var pa:Float = p.a, pb:Float = p.b, pc:Float = p.c, pd:Float = p.d; - var rotationIK:Float = -bone.ashearX - bone.arotation, - tx:Float = 0, - ty:Float = 0; + static public function apply1(skeleton:Skeleton, bone:BonePose, targetX:Float, targetY:Float, compress:Bool, stretch:Bool, + uniform:Bool, mix:Float) { + + if (bone == null) throw new SpineException("bone cannot be null."); + bone.modifyLocal(skeleton); + var p = bone.bone.parent.applied; + var pa = p.a, pb = p.b, pc = p.c, pd = p.d; + var rotationIK = -bone.shearX - bone.rotation, tx = 0., ty = 0.; function switchDefault() { - var x:Float = targetX - p.worldX, y:Float = targetY - p.worldY; - var d:Float = pa * pd - pb * pc; + var x = targetX - p.worldX, y = targetY - p.worldY; + var d = pa * pd - pb * pc; if (Math.abs(d) <= 0.0001) { tx = 0; ty = 0; } else { - tx = (x * pd - y * pb) / d - bone.ax; - ty = (y * pa - x * pc) / d - bone.ay; + tx = (x * pd - y * pb) / d - bone.x; + ty = (y * pa - x * pc) / d - bone.y; } } switch (bone.inherit) { case Inherit.onlyTranslation: - tx = (targetX - bone.worldX) * MathUtils.signum(bone.skeleton.scaleX); - ty = (targetY - bone.worldY) * MathUtils.signum(bone.skeleton.scaleY); + tx = (targetX - bone.worldX) * MathUtils.signum(skeleton.scaleX); + ty = (targetY - bone.worldY) * MathUtils.signum(skeleton.scaleY); case Inherit.noRotationOrReflection: var s = Math.abs(pa * pd - pb * pc) / Math.max(0.0001, pa * pa + pc * pc); - var sa:Float = pa / bone.skeleton.scaleX; - var sc:Float = pc / bone.skeleton.scaleY; - pb = -sc * s * bone.skeleton.scaleX; - pd = sa * s * bone.skeleton.scaleY; - rotationIK += Math.atan2(sc, sa) * MathUtils.radDeg; - var x:Float = targetX - p.worldX, y:Float = targetY - p.worldY; - var d:Float = pa * pd - pb * pc; - tx = (x * pd - y * pb) / d - bone.ax; - ty = (y * pa - x * pc) / d - bone.ay; + var sa:Float = pa / skeleton.scaleX; + var sc:Float = pc / skeleton.scaleY; + pb = -sc * s * skeleton.scaleX; + pd = sa * s * skeleton.scaleY; + rotationIK += MathUtils.atan2Deg(sc, sa); switchDefault(); // Fall through. default: switchDefault(); } - - rotationIK += Math.atan2(ty, tx) * MathUtils.radDeg; - if (bone.ascaleX < 0) - rotationIK += 180; + rotationIK += MathUtils.atan2Deg(ty, tx); + if (bone.scaleX < 0) rotationIK += 180; if (rotationIK > 180) rotationIK -= 360; - else if (rotationIK < -180) + else if (rotationIK < -180) // rotationIK += 360; - var sx:Float = bone.ascaleX; - var sy:Float = bone.ascaleY; + bone.rotation += rotationIK * mix; if (compress || stretch) { switch (bone.inherit) { case Inherit.noScale, Inherit.noScaleOrReflection: tx = targetX - bone.worldX; ty = targetY - bone.worldY; } - var b:Float = bone.data.length * sx; + var b = bone.bone.data.length * bone.scaleX; if (b > 0.0001) { - var dd:Float = tx * tx + ty * ty; + var dd = tx * tx + ty * ty; if ((compress && dd < b * b) || (stretch && dd > b * b)) { - var s:Float = (Math.sqrt(dd) / b - 1) * alpha + 1; - sx *= s; - if (uniform) sy *= s; + var s = (Math.sqrt(dd) / b - 1) * mix + 1; + bone.scaleX *= s; + if (uniform) bone.scaleY *= s; } } } - bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY); } /** Applies 2 bone IK. The target is specified in the world coordinate system. * @param child A direct descendant of the parent bone. */ - static public function apply2(parent:Bone, child:Bone, targetX:Float, targetY:Float, bendDir:Int, stretch:Bool, uniform:Bool, softness:Float, - alpha:Float):Void { + static public function apply2(skeleton:Skeleton, parent:BonePose, child:BonePose, targetX:Float, targetY:Float, bendDir:Int, + stretch:Bool, uniform:Bool, softness:Float, mix:Float):Void { + + if (parent == null) throw new SpineException("parent cannot be null."); + if (child == null) throw new SpineException("child cannot be null."); if (parent.inherit != Inherit.normal || child.inherit != Inherit.normal) return; - var px:Float = parent.ax; - var py:Float = parent.ay; - var psx:Float = parent.ascaleX; - var sx:Float = psx; - var psy:Float = parent.ascaleY; - var sy:Float = psy; - var csx:Float = child.ascaleX; - var os1:Int; - var os2:Int; - var s2:Int; + parent.modifyLocal(skeleton); + child.modifyLocal(skeleton); + var px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX; + var os1 = 0, os2 = 0, s2 = 0; if (psx < 0) { psx = -psx; os1 = 180; @@ -213,43 +178,30 @@ class IkConstraint implements Updatable { if (csx < 0) { csx = -csx; os2 = 180; - } else { + } else os2 = 0; - } - var cx:Float = child.ax; - var cy:Float; - var cwx:Float; - var cwy:Float; - var a:Float = parent.a; - var b:Float = parent.b; - var c:Float = parent.c; - var d:Float = parent.d; - var u:Bool = Math.abs(psx - psy) <= 0.0001; + var cwx = 0., cwy = 0., a = parent.a, b = parent.b, c = parent.c, d = parent.d; + var u = Math.abs(psx - psy) <= 0.0001; if (!u || stretch) { - cy = 0; - cwx = a * cx + parent.worldX; - cwy = c * cx + parent.worldY; + child.y = 0; + cwx = a * child.x + parent.worldX; + cwy = c * child.x + parent.worldY; } else { - cy = child.ay; - cwx = a * cx + b * cy + parent.worldX; - cwy = c * cx + d * cy + parent.worldY; + cwx = a * child.x + b * child.y + parent.worldX; + cwy = c * child.x + d * child.y + parent.worldY; } - var pp:Bone = parent.parent; + var pp = parent.bone.parent.applied; a = pp.a; b = pp.b; c = pp.c; d = pp.d; var id = a * d - b * c, x = cwx - pp.worldX, y = cwy - pp.worldY; id = Math.abs(id) <= 0.0001 ? 0 : 1 / id; - var dx:Float = (x * d - y * b) * id - px, - dy:Float = (y * a - x * c) * id - py; - var l1:Float = Math.sqrt(dx * dx + dy * dy); - var l2:Float = child.data.length * csx; - var a1:Float = 0; - var a2:Float = 0; + var dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; + var l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.bone.data.length * csx, a1 = 0., a2 = 0.; if (l1 < 0.0001) { - apply1(parent, targetX, targetY, false, stretch, false, alpha); - child.updateWorldTransformWith(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); + apply1(skeleton, parent, targetX, targetY, false, stretch, false, mix); + child.rotation = 0; return; } x = targetX - pp.worldX; @@ -279,10 +231,9 @@ class IkConstraint implements Updatable { } else if (cos > 1) { cos = 1; if (stretch) { - a = (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1; - sx *= a; - if (uniform) - sy *= a; + a = (Math.sqrt(dd) / (l1 + l2) - 1) * mix + 1; + parent.scaleX *= a; + if (uniform) parent.scaleY *= a; } } a2 = Math.acos(cos) * bendDir; @@ -352,23 +303,18 @@ class IkConstraint implements Updatable { } } } - - var os:Float = Math.atan2(cy, cx) * s2; - var rotation:Float = parent.arotation; - a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation; - if (a1 > 180) { + var os:Float = Math.atan2(child.y, child.x) * s2; + a1 = (a1 - os) * MathUtils.radDeg + os1 - parent.rotation; + if (a1 > 180) a1 -= 360; - } else if (a1 < -180) { + else if (a1 < -180) // a1 += 360; - } - parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, sx, sy, 0, 0); - rotation = child.arotation; - a2 = ((a2 + os) * MathUtils.radDeg - child.ashearX) * s2 + os2 - rotation; - if (a2 > 180) { + parent.rotation += a1 * mix; + a2 = ((a2 + os) * MathUtils.radDeg - child.shearX) * s2 + os2 - child.rotation; + if (a2 > 180) a2 -= 360; - } else if (a2 < -180) { + else if (a2 < -180) // a2 += 360; - } - child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); + child.rotation += a2 * mix; } } diff --git a/spine-haxe/spine-haxe/spine/IkConstraintData.hx b/spine-haxe/spine-haxe/spine/IkConstraintData.hx index 66500381e..0032826dd 100644 --- a/spine-haxe/spine-haxe/spine/IkConstraintData.hx +++ b/spine-haxe/spine-haxe/spine/IkConstraintData.hx @@ -30,33 +30,32 @@ package spine; /** Stores the setup pose for a spine.IkConstraint. - * + * * @see https://esotericsoftware.com/spine-ik-constraints IK constraints in the Spine User Guide */ -class IkConstraintData extends ConstraintData { +class IkConstraintData extends ConstraintData { + /** The bones that are constrained by this IK constraint. */ - public var bones:Array = new Array(); + public final bones:Array = new Array(); + /** The bone that is the IK target. */ - public var target:BoneData; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. - * - * For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0. */ - public var mix:Float = 0; - /** For two bone IK, controls the bend direction of the IK bones, either 1 or -1. */ - public var bendDirection:Int = 0; - /** For one bone IK, when true and the target is too close, the bone is scaled to reach it. */ - public var compress:Bool = false; - /** When true and the target is out of range, the parent bone is scaled to reach it. - * - * For two bone IK: 1) the child bone's local Y translation is set to 0, 2) stretch is not applied if softness is - * > 0, and 3) if the parent bone has local nonuniform scale, stretch is not applied. */ - public var stretch:Bool = false; - /** When true and compress or stretch is used, the bone is scaled on both the X and Y axes. */ - public var uniform:Bool = false; - /** For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones - * will not straighten completely until the target is this far out of range. */ - public var softness:Float = 0; + public var target(default, set):BoneData; + + /** When true and IkConstraintPose.compress or IkConstraintPose.stretch is used, the bone is scaled + * on both the X and Y axes. */ + public var uniform = false; + public function new(name:String) { - super(name, 0, false); + super(name, new IkConstraintPose()); + } + + public function create (skeleton:Skeleton):IkConstraint { + return new IkConstraint(this, skeleton); + } + + public function set_target (target:BoneData) { + if (target == null) throw new SpineException("target cannot be null."); + this.target = target; + return target; } } diff --git a/spine-haxe/spine-haxe/spine/IkConstraintPose.hx b/spine-haxe/spine-haxe/spine/IkConstraintPose.hx new file mode 100644 index 000000000..ca3d74e77 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/IkConstraintPose.hx @@ -0,0 +1,67 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +/** Stores the current pose for an IK constraint. */ +class IkConstraintPose implements Pose { + + /** For two bone IK, controls the bend direction of the IK bones, either 1 or -1. */ + public var bendDirection = 0; + + /** For one bone IK, when true and the target is too close, the bone is scaled to reach it. */ + public var compress:Bool = false; + + /** When true and the target is out of range, the parent bone is scaled to reach it. + * + * For two bone IK: 1) the child bone's local Y translation is set to 0, 2) stretch is not applied if softness is + * > 0, and 3) if the parent bone has local nonuniform scale, stretch is not applied. */ + public var stretch:Bool = false; + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. + * + * For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0. */ + public var mix = 0.; + + /** For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones + * will not straighten completely until the target is this far out of range. */ + public var softness = 0.; + + public function new () { + } + + public function set (pose:IkConstraintPose) { + mix = pose.mix; + softness = pose.softness; + bendDirection = pose.bendDirection; + compress = pose.compress; + stretch = pose.stretch; + } + +} \ No newline at end of file diff --git a/spine-haxe/spine-haxe/spine/PathConstraint.hx b/spine-haxe/spine-haxe/spine/PathConstraint.hx index 58e700717..0c58822a9 100644 --- a/spine-haxe/spine-haxe/spine/PathConstraint.hx +++ b/spine-haxe/spine-haxe/spine/PathConstraint.hx @@ -29,194 +29,140 @@ package spine; +import spine.Skeleton; +import spine.attachments.Attachment; import spine.attachments.PathAttachment; /** Stores the current pose for a path constraint. A path constraint adjusts the rotation, translation, and scale of the * constrained bones so they follow a PathAttachment. * * @see https://esotericsoftware.com/spine-path-constraints Path constraints in the Spine User Guide */ -class PathConstraint implements Updatable { - private static inline var NONE:Int = -1; - private static inline var BEFORE:Int = -2; - private static inline var AFTER:Int = -3; - private static inline var epsilon:Float = 0.00001; +class PathConstraint extends Constraint { + private static inline var NONE = -1; + private static inline var BEFORE = -2; + private static inline var AFTER = -3; + private static inline var epsilon = 0.00001; - private var _data:PathConstraintData; - private var _bones:Array; + /** The bones that will be modified by this path constraint. */ + public final bones:Array; /** The slot whose path attachment will be used to constrained the bones. */ - public var target:Slot; - /** The position along the path. */ - public var position:Float = 0; - /** The spacing between bones. */ - public var spacing:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */ - public var mixRotate:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */ - public var mixX:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */ - public var mixY:Float = 0; + public var slot:Slot; - private var _spaces(default, never):Array = new Array(); - private var _positions(default, never):Array = new Array(); - private var _world(default, never):Array = new Array(); - private var _curves(default, never):Array = new Array(); - private var _lengths(default, never):Array = new Array(); - private var _segments(default, never):Array = new Array(); + private final spaces = new Array(); + private final positions = new Array(); + private final world = new Array(); + private final curves = new Array(); + private final lengths = new Array(); + private final segments = new Array(); - public var active:Bool = false; + public function new (data:PathConstraintData, skeleton:Skeleton) { + super(data, new PathConstraintPose(), new PathConstraintPose()); + if (skeleton == null) throw new SpineException("skeleton cannot be null."); - public function new(data:PathConstraintData, skeleton:Skeleton) { - if (data == null) - throw new SpineException("data cannot be null."); - if (skeleton == null) - throw new SpineException("skeleton cannot be null."); - _data = data; + bones = new Array(); + for (boneData in data.bones) + bones.push(skeleton.bones[boneData.index].constrained); - _bones = new Array(); - for (boneData in data.bones) { - _bones.push(skeleton.findBone(boneData.name)); - } - target = skeleton.findSlot(data.target.name); - - position = data.position; - spacing = data.spacing; - mixRotate = data.mixRotate; - mixX = data.mixX; - mixY = data.mixY; + slot = skeleton.slots[data.slot.index]; } - public function isActive():Bool { - return active; - } - - public function setToSetupPose () { - var data:PathConstraintData = _data; - position = data.position; - spacing = data.spacing; - mixRotate = data.mixRotate; - mixX = data.mixX; - mixY = data.mixY; + public function copy(skeleton:Skeleton) { + var copy = new PathConstraint(data, skeleton); + copy.pose.set(pose); + return copy; } /** Applies the constraint to the constrained bones. */ - public function update(physics:Physics):Void { - var attachment:PathAttachment = cast(target.attachment, PathAttachment); - if (attachment == null) - return; - if (mixRotate == 0 && mixX == 0 && mixY == 0) - return; + public function update(skeleton:Skeleton, physics:Physics):Void { + var attachment = slot.applied.attachment; + if (!Std.isOfType(attachment, PathAttachment)) return; + var pathAttachment = cast(attachment, PathAttachment); - var data:PathConstraintData = _data; - var fTangents:Bool = data.rotateMode == RotateMode.tangent, - fScale:Bool = data.rotateMode == RotateMode.chainScale; - var boneCount:Int = _bones.length; - var spacesCount:Int = fTangents ? boneCount : boneCount + 1; - ArrayUtils.resize(_spaces, spacesCount, 0); - if (fScale) { - ArrayUtils.resize(_lengths, boneCount, 0); - } + var p = applied; + var mixRotate = p.mixRotate, mixX = p.mixX, mixY = p.mixY; + if (mixRotate == 0 && mixX == 0 && mixY == 0) return; - var bones:Array = _bones; + var data = data; + var fTangents = data.rotateMode == RotateMode.tangent, fScale = data.rotateMode == RotateMode.chainScale; + var boneCount = bones.length, spacesCount = fTangents ? boneCount : boneCount + 1; + ArrayUtils.resize(spaces, spacesCount, 0); + if (fScale) ArrayUtils.resize(lengths, boneCount, 0); + var spacing = p.spacing; - var i:Int, - n:Int, - bone:Bone, - setupLength:Float, - x:Float, - y:Float, - length:Float; + var bones = bones; switch (data.spacingMode) { case SpacingMode.percent: if (fScale) { - n = spacesCount - 1; + var n = spacesCount - 1; for (i in 0...n) { - bone = bones[i]; - setupLength = bone.data.length; - x = setupLength * bone.a; - y = setupLength * bone.c; - _lengths[i] = Math.sqrt(x * x + y * y); + var bone = bones[i]; + var setupLength:Float = bone.bone.data.length; + var x = setupLength * bone.a, y = setupLength * bone.c; + lengths[i] = Math.sqrt(x * x + y * y); } } - for (i in 1...spacesCount) { - _spaces[i] = spacing; - } + for (i in 1...spacesCount) spaces[i] = spacing; case SpacingMode.proportional: - var sum:Float = 0; - i = 0; - n = spacesCount - 1; + var sum = 0.; + var i = 0, n = spacesCount - 1; while (i < n) { - bone = bones[i]; - setupLength = bone.data.length; + var bone = bones[i]; + var setupLength:Float = bone.bone.data.length; if (setupLength < PathConstraint.epsilon) { - if (fScale) - _lengths[i] = 0; - _spaces[++i] = spacing; + if (fScale) lengths[i] = 0; + spaces[++i] = spacing; } else { - x = setupLength * bone.a; - y = setupLength * bone.c; - length = Math.sqrt(x * x + y * y); - if (fScale) - _lengths[i] = length; - _spaces[++i] = length; + var x = setupLength * bone.a, y = setupLength * bone.c; + var length = Math.sqrt(x * x + y * y); + if (fScale) lengths[i] = length; + spaces[++i] = length; sum += length; } } if (sum > 0) { sum = spacesCount / sum * spacing; - for (i in 1...spacesCount) { - _spaces[i] *= sum; - } + for (i in 1...spacesCount) + spaces[i] *= sum; } default: - var lengthSpacing:Bool = data.spacingMode == SpacingMode.length; - i = 0; - n = spacesCount - 1; + var lengthSpacing = data.spacingMode == SpacingMode.length; + var i = 0, n = spacesCount - 1; while (i < n) { - bone = bones[i]; - setupLength = bone.data.length; + var bone = bones[i]; + var setupLength = bone.bone.data.length; if (setupLength < PathConstraint.epsilon) { - if (fScale) - _lengths[i] = 0; - _spaces[++i] = spacing; + if (fScale) lengths[i] = 0; + spaces[++i] = spacing; } else { - x = setupLength * bone.a; - y = setupLength * bone.c; - length = Math.sqrt(x * x + y * y); - if (fScale) - _lengths[i] = length; - _spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength; + var x = setupLength * bone.a, y = setupLength * bone.c; + var length = Math.sqrt(x * x + y * y); + if (fScale) lengths[i] = length; + spaces[++i] = (lengthSpacing ? Math.max(0, setupLength + spacing) : spacing) * length / setupLength; } } } - var positions:Array = computeWorldPositions(attachment, spacesCount, fTangents); - var boneX:Float = positions[0]; - var boneY:Float = positions[1]; - var offsetRotation:Float = data.offsetRotation; - var tip:Bool = false; - if (offsetRotation == 0) { + var positions = computeWorldPositions(skeleton, pathAttachment, spacesCount, fTangents); + var boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; + var tip = false; + if (offsetRotation == 0) tip = data.rotateMode == RotateMode.chain; - } else { + else { tip = false; - var pa:Bone = target.bone; - offsetRotation *= pa.a * pa.d - pa.b * pa.c > 0 ? MathUtils.degRad : -MathUtils.degRad; + var bone = slot.bone.applied; + offsetRotation *= bone.a * bone.d - bone.b * bone.c > 0 ? MathUtils.degRad : -MathUtils.degRad; } - - i = 0; - var p:Int = 3; + var i = 0, ip = 3, u = skeleton._update; while (i < boneCount) { - var bone:Bone = bones[i]; + var bone = bones[i]; bone.worldX += (boneX - bone.worldX) * mixX; bone.worldY += (boneY - bone.worldY) * mixY; - var x:Float = positions[p]; - var y:Float = positions[p + 1]; - var dx:Float = x - boneX; - var dy:Float = y - boneY; + var x = positions[ip], y = positions[ip + 1], dx = x - boneX, dy = y - boneY; if (fScale) { - var length = _lengths[i]; - if (length != 0) { - var s:Float = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1; + var length = lengths[i]; + if (length >= epsilon) { + var s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1; bone.a *= s; bone.c *= s; } @@ -224,35 +170,26 @@ class PathConstraint implements Updatable { boneX = x; boneY = y; if (mixRotate > 0) { - var a:Float = bone.a, - b:Float = bone.b, - c:Float = bone.c, - d:Float = bone.d, - r:Float, - cos:Float, - sin:Float; - if (fTangents) { - r = positions[p - 1]; - } else if (_spaces[i + 1] == 0) { - r = positions[p + 2]; - } else { + var a = bone.a, b = bone.b, c = bone.c, d = bone.d, r:Float, cos:Float, sin:Float; + if (fTangents) + r = positions[ip - 1]; + else if (spaces[i + 1] < epsilon) + r = positions[ip + 2]; + else r = Math.atan2(dy, dx); - } r -= Math.atan2(c, a); if (tip) { cos = Math.cos(r); sin = Math.sin(r); - var length:Float = bone.data.length; + var length = bone.bone.data.length; boneX += (length * (cos * a - sin * c) - dx) * mixRotate; boneY += (length * (sin * a + cos * c) - dy) * mixRotate; - } else { + } else r += offsetRotation; - } - if (r > Math.PI) { + if (r > Math.PI) r -= (Math.PI * 2); - } else if (r < -Math.PI) { + else if (r < -Math.PI) // r += (Math.PI * 2); - } r *= mixRotate; cos = Math.cos(r); sin = Math.sin(r); @@ -261,64 +198,55 @@ class PathConstraint implements Updatable { bone.c = sin * a + cos * c; bone.d = sin * b + cos * d; } - bone.updateAppliedTransform(); - + bone.modifyWorld(u); i++; - p += 3; + ip += 3; } } - private function computeWorldPositions(path:PathAttachment, spacesCount:Int, tangents:Bool):Array { - var position:Float = this.position; - ArrayUtils.resize(_positions, spacesCount * 3 + 2, 0); - var out:Array = _positions, world:Array; - var closed:Bool = path.closed; - var verticesLength:Int = path.worldVerticesLength; - var curveCount:Int = Std.int(verticesLength / 6); - var prevCurve:Int = NONE; - var multiplier:Float, i:Int; + private function computeWorldPositions(skeleton:Skeleton, path:PathAttachment, spacesCount:Int, tangents:Bool):Array { + var position = applied.position; + ArrayUtils.resize(positions, spacesCount * 3 + 2, 0); + var out:Array = positions, world:Array; + var closed = path.closed; + var verticesLength = path.worldVerticesLength, curveCount = Std.int(verticesLength / 6), prevCurve = NONE; if (!path.constantSpeed) { - var lengths:Array = path.lengths; + var lengths = path.lengths; curveCount -= closed ? 1 : 2; - var pathLength:Float = lengths[curveCount]; - if (data.positionMode == PositionMode.percent) - position *= pathLength; + var pathLength = lengths[curveCount]; + + if (data.positionMode == PositionMode.percent) position *= pathLength; + + var multiplier: Float; switch (data.spacingMode) { - case SpacingMode.percent: - multiplier = pathLength; - case SpacingMode.proportional: - multiplier = pathLength / spacesCount; - default: - multiplier = 1; + case SpacingMode.percent: multiplier = pathLength; + case SpacingMode.proportional: multiplier = pathLength / spacesCount; + default: multiplier = 1; } - ArrayUtils.resize(_world, 8, 0); - world = _world; - var i:Int = 0; - var o:Int = 0; - var curve:Int = 0; + ArrayUtils.resize(world, 8, 0); + var i = 0, o = 0, curve = 0; while (i < spacesCount) { - var space:Float = _spaces[i] * multiplier; + var space = spaces[i] * multiplier; position += space; - var p:Float = position; + var p = position; if (closed) { p %= pathLength; - if (p < 0) - p += pathLength; + if (p < 0) p += pathLength; curve = 0; } else if (p < 0) { if (prevCurve != BEFORE) { prevCurve = BEFORE; - path.computeWorldVertices(target, 2, 4, world, 0, 2); + path.computeWorldVertices(skeleton, slot, 2, 4, world, 0, 2); } addBeforePosition(p, world, 0, out, o); continue; } else if (p > pathLength) { if (prevCurve != AFTER) { prevCurve = AFTER; - path.computeWorldVertices(target, verticesLength - 6, 4, world, 0, 2); + path.computeWorldVertices(skeleton, slot, verticesLength - 6, 4, world, 0, 2); } addAfterPosition(p - pathLength, world, 0, out, o); continue; @@ -326,15 +254,15 @@ class PathConstraint implements Updatable { // Determine curve containing position. while (true) { - var length:Float = lengths[curve]; + var length = lengths[curve]; if (p > length) { curve++; continue; } - if (curve == 0) { + if (curve == 0) p /= length; - } else { - var prev:Float = lengths[curve - 1]; + else { + var prev = lengths[curve - 1]; p = (p - prev) / (length - prev); } break; @@ -342,14 +270,14 @@ class PathConstraint implements Updatable { if (curve != prevCurve) { prevCurve = curve; if (closed && curve == curveCount) { - path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2); - path.computeWorldVertices(target, 0, 4, world, 4, 2); + path.computeWorldVertices(skeleton, slot, verticesLength - 4, 4, world, 0, 2); + path.computeWorldVertices(skeleton, slot, 0, 4, world, 4, 2); } else { - path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); + path.computeWorldVertices(skeleton, slot, curve * 6 + 2, 8, world, 0, 2); } } - addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space == 0)); - + addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, + tangents || (i > 0 && space == 0)); i++; o += 3; } @@ -359,35 +287,25 @@ class PathConstraint implements Updatable { // World vertices. if (closed) { verticesLength += 2; - ArrayUtils.resize(_world, verticesLength, 0); - world = _world; - path.computeWorldVertices(target, 2, verticesLength - 4, world, 0, 2); - path.computeWorldVertices(target, 0, 2, world, verticesLength - 4, 2); + ArrayUtils.resize(world, verticesLength, 0); + path.computeWorldVertices(skeleton, slot, 2, verticesLength - 4, world, 0, 2); + path.computeWorldVertices(skeleton, slot, 0, 2, world, verticesLength - 4, 2); world[verticesLength - 2] = world[0]; world[verticesLength - 1] = world[1]; } else { curveCount--; verticesLength -= 4; - ArrayUtils.resize(_world, verticesLength, 0); - world = _world; - path.computeWorldVertices(target, 2, verticesLength, world, 0, 2); + ArrayUtils.resize(world, verticesLength, 0); + path.computeWorldVertices(skeleton, slot, 2, verticesLength, world, 0, 2); } // Curve lengths. - ArrayUtils.resize(_curves, curveCount, 0); - var curves:Array = _curves; + ArrayUtils.resize(curves, curveCount, 0); + var curves:Array = curves; var pathLength:Float = 0; - var x1:Float = world[0], - y1:Float = world[1], - cx1:Float = 0, - cy1:Float = 0, - cx2:Float = 0, - cy2:Float = 0, - x2:Float = 0, - y2:Float = 0; + var x1 = world[0], y1 = world[1], cx1 = 0., cy1 = 0., cx2 = 0., cy2 = 0., x2 = 0., y2 = 0.; var tmpx:Float, tmpy:Float, dddfx:Float, dddfy:Float, ddfx:Float, ddfy:Float, dfx:Float, dfy:Float; - var i:Int = 0; - var w:Int = 2; + var i = 0, w = 2; while (i < curveCount) { cx1 = world[w]; cy1 = world[w + 1]; @@ -423,33 +341,28 @@ class PathConstraint implements Updatable { w += 6; } - if (data.positionMode == PositionMode.percent) - position *= pathLength; + if (data.positionMode == PositionMode.percent) position *= pathLength; + var multiplier:Float; switch (data.spacingMode) { - case SpacingMode.percent: - multiplier = pathLength; - case SpacingMode.proportional: - multiplier = pathLength / spacesCount; - default: - multiplier = 1; + case SpacingMode.percent: multiplier = pathLength; + case SpacingMode.proportional: multiplier = pathLength / spacesCount; + default: multiplier = 1; } - var segments:Array = _segments; - var curveLength:Float = 0; - var segment:Int; - i = 0; - var o:Int = 0; - var segment:Int = 0; + var segments = segments; + var curveLength = 0.; + var i = 0, o = 0, curve = 0, segment = 0; while (i < spacesCount) { - var space = _spaces[i] * multiplier; + var space = spaces[i] * multiplier; position += space; var p = position; if (closed) { p %= pathLength; - if (p < 0) - p += pathLength; + if (p < 0) p += pathLength; + curve = 0; + segment = 0; } else if (p < 0) { addBeforePosition(p, world, 0, out, o); i++; @@ -528,9 +441,9 @@ class PathConstraint implements Updatable { segment++; continue; } - if (segment == 0) { + if (segment == 0) p /= length; - } else { + else { var prev = segments[segment - 1]; p = segment + (p - prev) / (length - prev); } @@ -596,21 +509,51 @@ class PathConstraint implements Updatable { } } - /** The bones that will be modified by this path constraint. */ - public var bones(get, never):Array; - - private function get_bones():Array { - return _bones; + public function sort (skeleton:Skeleton) { + var slotIndex = slot.data.index; + var slotBone = slot.bone; + if (skeleton.skin != null) sortPathSlot(skeleton, skeleton.skin, slotIndex, slotBone); + if (skeleton.data.defaultSkin != null && skeleton.data.defaultSkin != skeleton.skin) + sortPathSlot(skeleton, skeleton.data.defaultSkin, slotIndex, slotBone); + sortPath(skeleton, slot.pose.attachment, slotBone); + var boneCount = bones.length; + for (i in 0...boneCount) { + var bone = bones[i].bone; + skeleton.sortBone(bone); + skeleton.constrained(bone); + } + skeleton._updateCache.push(this); + for (i in 0...boneCount) + skeleton.sortReset(bones[i].bone.children); + for (i in 0...boneCount) + bones[i].bone.sorted = true; } - /** The path constraint's setup pose data. */ - public var data(get, never):PathConstraintData; - - private function get_data():PathConstraintData { - return _data; + public function sortPathSlot (skeleton:Skeleton, skin:Skin, slotIndex:Int, slotBone:Bone) { + var entries = skin.getAttachments(); + for (entry in entries) { + if (entry.slotIndex == slotIndex) sortPath(skeleton, entry.attachment, slotBone); + } } - public function toString():String { - return _data.name != null ? _data.name : "PathConstraint?"; + private function sortPath (skeleton:Skeleton, attachment:Attachment, slotBone:Bone) { + if (!(Std.isOfType(attachment, PathAttachment))) return; + var pathBones = cast(attachment, PathAttachment).bones; + if (pathBones == null) + skeleton.sortBone(slotBone); + else { + var bones = skeleton.bones; + var i = 0, n = pathBones.length; + while (i < n) { + var nn = pathBones[i++]; + nn += i; + while (i < nn) + skeleton.sortBone(bones[pathBones[i++]]); + } + } + } + + override public function isSourceActive (): Bool { + return slot.bone.active; } } diff --git a/spine-haxe/spine-haxe/spine/PathConstraintData.hx b/spine-haxe/spine-haxe/spine/PathConstraintData.hx index 333efcfa5..41b324a14 100644 --- a/spine-haxe/spine-haxe/spine/PathConstraintData.hx +++ b/spine-haxe/spine-haxe/spine/PathConstraintData.hx @@ -30,41 +30,56 @@ package spine; /** Stores the setup pose for a spine.PathConstraint. - * + * * @see https://esotericsoftware.com/spine-path-constraints Path constraints in the Spine User Guide */ -class PathConstraintData extends ConstraintData { +class PathConstraintData extends ConstraintData { /** The bones that will be modified by this path constraint. */ - private var _bones:Array = new Array(); + public final bones:Array = new Array(); /** The slot whose path attachment will be used to constrain the bones. */ - public var target:SlotData; + public var slot(default, set):SlotData; + /** The mode for positioning the first bone on the path. */ - public var positionMode:PositionMode = PositionMode.fixed; + public var positionMode(default, set):PositionMode = PositionMode.fixed; + /** The mode for positioning the bones after the first bone on the path. */ - public var spacingMode:SpacingMode = SpacingMode.fixed; + public var spacingMode(default, set):SpacingMode = SpacingMode.fixed; + /** The mode for adjusting the rotation of the bones. */ - public var rotateMode:RotateMode = RotateMode.chain; + public var rotateMode(default, set):RotateMode = RotateMode.chain; + /** An offset added to the constrained bone rotation. */ public var offsetRotation:Float = 0; - /** The position along the path. */ - public var position:Float = 0; - /** The spacing between bones. */ - public var spacing:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */ - public var mixRotate:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */ - public var mixX:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */ - public var mixY:Float = 0; - public function new(name:String) { - super(name, 0, false); + public function new (name:String) { + super(name, new PathConstraintPose()); } - /** The bones that will be modified by this path constraint. */ - public var bones(get, never):Array; + public function create (skeleton:Skeleton) { + return new PathConstraint(this, skeleton); + } - private function get_bones():Array { - return _bones; + public function set_slot (slot:SlotData):SlotData { + if (slot == null) throw new SpineException("slot cannot be null."); + this.slot = slot; + return slot; + } + + public function set_positionMode (positionMode:PositionMode):PositionMode { + if (positionMode == null) throw new SpineException("positionMode cannot be null."); + this.positionMode = positionMode; + return positionMode; + } + + public function set_spacingMode (spacingMode:SpacingMode):SpacingMode { + if (spacingMode == null) throw new SpineException("spacingMode cannot be null."); + this.spacingMode = spacingMode; + return spacingMode; + } + + public function set_rotateMode (rotateMode:RotateMode):RotateMode { + if (rotateMode == null) throw new SpineException("rotateMode cannot be null."); + this.rotateMode = rotateMode; + return rotateMode; } } diff --git a/spine-haxe/spine-haxe/spine/PathConstraintPose.hx b/spine-haxe/spine-haxe/spine/PathConstraintPose.hx new file mode 100644 index 000000000..3f0b726da --- /dev/null +++ b/spine-haxe/spine-haxe/spine/PathConstraintPose.hx @@ -0,0 +1,59 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +class PathConstraintPose implements Pose { + + /** The position along the path. */ + public var position = 0.; + + /** The spacing between bones. */ + public var spacing = 0.; + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */ + public var mixRotate = 0.; + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */ + public var mixX = 0.; + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */ + public var mixY = 0.; + + public function new () { + } + + public function set(pose:PathConstraintPose) { + position = pose.position; + spacing = pose.spacing; + mixRotate = pose.mixRotate; + mixX = pose.mixX; + mixY = pose.mixY; + } +} diff --git a/spine-haxe/spine-haxe/spine/PhysicsConstraint.hx b/spine-haxe/spine-haxe/spine/PhysicsConstraint.hx index 789418a8e..012d2391e 100644 --- a/spine-haxe/spine-haxe/spine/PhysicsConstraint.hx +++ b/spine-haxe/spine-haxe/spine/PhysicsConstraint.hx @@ -30,277 +30,72 @@ package spine; /** Stores the current pose for a physics constraint. A physics constraint applies physics to bones. - * - * + * + * * @see https://esotericsoftware.com/spine-physics-constraints Physics constraints in the Spine User Guide */ -class PhysicsConstraint implements Updatable { - private var _data:PhysicsConstraintData; - private var _bone:Bone = null; +class PhysicsConstraint extends Constraint { - public var inertia:Float = 0; - public var strength:Float = 0; - public var damping:Float = 0; - public var massInverse:Float = 0; - public var wind:Float = 0; - public var gravity:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */ - public var mix:Float = 0; + /** The bone constrained by this physics constraint. */ + public var bone:BonePose = null; - private var _reset:Bool = true; + public var _reset = true; - public var ux:Float = 0; - public var uy:Float = 0; - public var cx:Float = 0; - public var cy:Float = 0; - public var tx:Float = 0; - public var ty:Float = 0; - public var xOffset:Float = 0; - public var xVelocity:Float = 0; - public var yOffset:Float = 0; - public var yVelocity:Float = 0; - public var rotateOffset:Float = 0; - public var rotateVelocity:Float = 0; - public var scaleOffset:Float = 0; - public var scaleVelocity:Float = 0; - - public var active:Bool = false; - - private var _skeleton:Skeleton; + public var ux = 0.; + public var uy = 0.; + public var cx = 0.; + public var cy = 0.; + public var tx = 0.; + public var ty = 0.; + public var xOffset = 0.; + public var xLag = 0.; + public var xVelocity = 0.; + public var yOffset = 0.; + public var yLag = 0.; + public var yVelocity = 0.; + public var rotateOffset = 0.; + public var rotateLag = 0.; + public var rotateVelocity = 0.; + public var scaleOffset = 0.; + public var scaleLag = 0.; + public var scaleVelocity = 0.; public var remaining:Float = 0; public var lastTime:Float = 0; public function new(data: PhysicsConstraintData, skeleton: Skeleton) { - _data = data; - _skeleton = skeleton; + super(data, new PhysicsConstraintPose(), new PhysicsConstraintPose()); + if (skeleton == null) throw new SpineException("skeleton cannot be null."); - _bone = skeleton.bones[data.bone.index]; - - inertia = data.inertia; - strength = data.strength; - damping = data.damping; - massInverse = data.massInverse; - wind = data.wind; - gravity = data.gravity; - mix = data.mix; + bone = skeleton.bones[data.bone.index].constrained; } - public function reset () { + public function copy(skeleton:Skeleton) { + var copy = new PhysicsConstraint(data, skeleton); + copy.pose.set(pose); + return copy; + } + + public function reset (skeleton:Skeleton) { remaining = 0; lastTime = skeleton.time; _reset = true; xOffset = 0; + xLag = 0; xVelocity = 0; yOffset = 0; + yLag = 0; yVelocity = 0; rotateOffset = 0; + rotateLag = 0; rotateVelocity = 0; scaleOffset = 0; + scaleLag = 0; scaleVelocity = 0; } - public function setToSetupPose () { - var data:PhysicsConstraintData = _data; - inertia = data.inertia; - strength = data.strength; - damping = data.damping; - massInverse = data.massInverse; - wind = data.wind; - gravity = data.gravity; - mix = data.mix; - } - - public function isActive():Bool { - return active; - } - - /** Applies the constraint to the constrained bones. */ - public function update(physics:Physics):Void { - var mix:Float = this.mix; - if (mix == 0) return; - - var x:Bool = _data.x > 0, y:Bool = _data.y > 0, - rotateOrShearX:Bool = _data.rotate > 0 || _data.shearX > 0, - scaleX:Bool = _data.scaleX > 0; - var bone:Bone = _bone; - var l:Float = bone.data.length; - - switch (physics) { - case Physics.none: - return; - case Physics.reset, Physics.update: - if (physics == Physics.reset) reset(); - - var delta:Float = Math.max(skeleton.time - lastTime, 0); - remaining += delta; - lastTime = _skeleton.time; - - var bx:Float = bone.worldX, by:Float = bone.worldY; - if (_reset) { - _reset = false; - ux = bx; - uy = by; - } else { - var a:Float = remaining, - i:Float = inertia, - t:Float = _data.step, - f:Float = skeleton.data.referenceScale, - d:Float = -1; - - var qx:Float = _data.limit * delta, - qy:Float = qx * Math.abs(skeleton.scaleY); - qx *= Math.abs(skeleton.scaleX); - if (x || y) { - if (x) { - var u:Float = (ux - bx) * i; - xOffset += u > qx ? qx : u < -qx ? -qx : u; - ux = bx; - } - if (y) { - var u:Float = (uy - by) * i; - yOffset += u > qy ? qy : u < -qy ? -qy : u; - uy = by; - } - if (a >= t) { - d = Math.pow(damping, 60 * t); - var m:Float = massInverse * t, - e:Float = strength, - w:Float = wind * f * skeleton.scaleX, - g:Float = gravity * f * skeleton.scaleY; - do { - if (x) { - xVelocity += (w - xOffset * e) * m; - xOffset += xVelocity * t; - xVelocity *= d; - } - if (y) { - yVelocity -= (g + yOffset * e) * m; - yOffset += yVelocity * t; - yVelocity *= d; - } - a -= t; - } while (a >= t); - } - if (x) bone.worldX += xOffset * mix * data.x; - if (y) bone.worldY += yOffset * mix * data.y; - } - if (rotateOrShearX || scaleX) { - var ca:Float = Math.atan2(bone.c, bone.a), - c:Float = 0, - s:Float = 0, - mr:Float = 0; - var dx:Float = cx - bone.worldX, - dy:Float = cy - bone.worldY; - if (dx > qx) - dx = qx; - else if (dx < -qx) // - dx = -qx; - if (dy > qy) - dy = qy; - else if (dy < -qy) // - dy = -qy; - if (rotateOrShearX) { - mr = (_data.rotate + _data.shearX) * mix; - var r:Float = Math.atan2(dy + ty, dx + tx) - ca - rotateOffset * mr; - rotateOffset += (r - Math.ceil(r * MathUtils.invPI2 - 0.5) * MathUtils.PI2) * i; - r = rotateOffset * mr + ca; - c = Math.cos(r); - s = Math.sin(r); - if (scaleX) { - r = l * bone.worldScaleX; - if (r > 0) scaleOffset += (dx * c + dy * s) * i / r; - } - } else { - c = Math.cos(ca); - s = Math.sin(ca); - var r:Float = l * bone.worldScaleX; - if (r > 0) scaleOffset += (dx * c + dy * s) * i / r; - } - a = remaining; - if (a >= t) { - if (d == -1) d = Math.pow(damping, 60 * t); - var m:Float = massInverse * t, - e:Float = strength, - w:Float = wind, - g:Float = (Bone.yDown ? -gravity : gravity), - h:Float = l / f; - while (true) { - a -= t; - if (scaleX) { - scaleVelocity += (w * c - g * s - scaleOffset * e) * m; - scaleOffset += scaleVelocity * t; - scaleVelocity *= d; - } - if (rotateOrShearX) { - rotateVelocity -= ((w * s + g * c) * h + rotateOffset * e) * m; - rotateOffset += rotateVelocity * t; - rotateVelocity *= d; - if (a < t) break; - var r:Float = rotateOffset * mr + ca; - c = Math.cos(r); - s = Math.sin(r); - } else if (a < t) // - break; - } - } - } - remaining = a; - } - cx = bone.worldX; - cy = bone.worldY; - case Physics.pose: - if (x) bone.worldX += xOffset * mix * data.x; - if (y) bone.worldY += yOffset * mix * data.y; - } - - if (rotateOrShearX) { - var o:Float = rotateOffset * mix, - s:Float = 0, - c:Float = 0, - a:Float = 0; - if (_data.shearX > 0) { - var r:Float = 0; - if (_data.rotate > 0) { - r = o * _data.rotate; - s = Math.sin(r); - c = Math.cos(r); - a = bone.b; - bone.b = c * a - s * bone.d; - bone.d = s * a + c * bone.d; - } - r += o * _data.shearX; - s = Math.sin(r); - c = Math.cos(r); - a = bone.a; - bone.a = c * a - s * bone.c; - bone.c = s * a + c * bone.c; - } else { - o *= _data.rotate; - s = Math.sin(o); - c = Math.cos(o); - a = bone.a; - bone.a = c * a - s * bone.c; - bone.c = s * a + c * bone.c; - a = bone.b; - bone.b = c * a - s * bone.d; - bone.d = s * a + c * bone.d; - } - } - if (scaleX) { - var s:Float = 1 + scaleOffset * mix * data.scaleX; - bone.a *= s; - bone.c *= s; - } - if (physics != Physics.pose) { - tx = l * bone.a; - ty = l * bone.c; - } - bone.updateAppliedTransform(); - } - /** Translates the physics constraint so next update(Physics) forces are applied as if the bone moved an additional * amount in world space. */ - public function translate (x:Float, y:Float):Void { + public function translate (x:Float, y:Float):Void { ux -= x; uy -= y; cx -= x; @@ -315,26 +110,199 @@ class PhysicsConstraint implements Updatable { translate(dx * cos - dy * sin - dx, dx * sin + dy * cos - dy); } - /** The bone constrained by this physics constraint. */ - public var bone(get, never):Bone; + /** Applies the constraint to the constrained bones. */ + public function update(skeleton:Skeleton, physics:Physics):Void { + var p = applied; + var mix = p.mix; + if (mix == 0) return; - private function get_bone():Bone { - if (_bone == null) - throw new SpineException("Bone not set.") - else return _bone; + var x = data.x > 0, y = data.y > 0, rotateOrShearX = data.rotate > 0 || data.shearX > 0, scaleX = data.scaleX > 0; + var l = bone.bone.data.length, t = data.step, z = 0.; + + switch (physics) { + case Physics.none: + return; + case Physics.reset, Physics.update: + if (physics == Physics.reset) reset(skeleton); + + var delta = Math.max(skeleton.time - lastTime, 0), aa = remaining; + remaining += delta; + lastTime = skeleton.time; + + var bx = bone.worldX, by = bone.worldY; + if (_reset) { + _reset = false; + ux = bx; + uy = by; + } else { + var a = remaining, i = p.inertia, f = skeleton.data.referenceScale, d = -1., m = 0., e = 0., qx = data.limit * delta, + qy = qx * Math.abs(skeleton.scaleY); + qx *= Math.abs(skeleton.scaleX); + if (x || y) { + if (x) { + var u = (ux - bx) * i; + xOffset += u > qx ? qx : u < -qx ? -qx : u; + ux = bx; + } + if (y) { + var u = (uy - by) * i; + yOffset += u > qy ? qy : u < -qy ? -qy : u; + uy = by; + } + if (a >= t) { + var xs = xOffset, ys = yOffset; + d = Math.pow(p.damping, 60 * t); + m = t * p.massInverse; + e = p.strength; + var w = f * p.wind * skeleton.scaleX, g = f * p.gravity * skeleton.scaleY, + ax = w * skeleton.windX + g * skeleton.gravityX, ay = w * skeleton.windY + g * skeleton.gravityY; + do { + if (x) { + xVelocity += (ax - xOffset * e) * m; + xOffset += xVelocity * t; + xVelocity *= d; + } + if (y) { + yVelocity -= (ay + yOffset * e) * m; + yOffset += yVelocity * t; + yVelocity *= d; + } + a -= t; + } while (a >= t); + xLag = xOffset - xs; + yLag = yOffset - ys; + } + z = Math.max(0, 1 - a / t); + if (x) bone.worldX += xOffset * mix * this.data.x; + if (y) bone.worldY += yOffset * mix * this.data.y; + } + if (rotateOrShearX || scaleX) { + var ca = Math.atan2(bone.c, bone.a), c = 0., s = 0., mr = 0., dx = cx - bone.worldX, dy = cy - bone.worldY; + if (dx > qx) + dx = qx; + else if (dx < -qx) // + dx = -qx; + if (dy > qy) + dy = qy; + else if (dy < -qy) // + dy = -qy; + a = remaining; + if (rotateOrShearX) { + mr = (data.rotate + data.shearX) * mix; + z = rotateLag * Math.max(0, 1 - aa / t); + var r = Math.atan2(dy + ty, dx + tx) - ca - (rotateOffset - z) * mr; + rotateOffset += (r - Math.ceil(r * MathUtils.invPI2 - 0.5) * MathUtils.PI2) * i; + r = (rotateOffset - z) * mr + ca; + c = Math.cos(r); + s = Math.sin(r); + if (scaleX) { + r = l * bone.worldScaleX; + if (r > 0) scaleOffset += (dx * c + dy * s) * i / r; + } + } else { + c = Math.cos(ca); + s = Math.sin(ca); + var r = l * bone.worldScaleX - scaleLag * Math.max(0, 1 - aa / t); + if (r > 0) scaleOffset += (dx * c + dy * s) * i / r; + } + a = remaining; + if (a >= t) { + if (d == -1) { + d = Math.pow(p.damping, 60 * t); + m = t * p.massInverse; + e = p.strength; + } + var g = Bone.yDown ? -p.gravity : p.gravity; + var rs = rotateOffset, ss = scaleLag, h = l / f, + ax = p.wind * skeleton.windX + g * skeleton.gravityX, + ay = p.wind * skeleton.windY + g * skeleton.gravityY; + while (true) { + a -= t; + if (scaleX) { + scaleVelocity += (ax * c - ay * s - scaleOffset * e) * m; + scaleOffset += scaleVelocity * t; + scaleVelocity *= d; + } + if (rotateOrShearX) { + rotateVelocity -= ((ax * s + ay * c) * h + rotateOffset * e) * m; + rotateOffset += rotateVelocity * t; + rotateVelocity *= d; + if (a < t) break; + var r:Float = rotateOffset * mr + ca; + c = Math.cos(r); + s = Math.sin(r); + } else if (a < t) // + break; + } + rotateLag = rotateOffset - rs; + scaleLag = scaleOffset - ss; + } + z = Math.max(0, 1 - a / t); + } + remaining = a; + } + cx = bone.worldX; + cy = bone.worldY; + case Physics.pose: + z = Math.max(0, 1 - remaining / t); + if (x) bone.worldX += (xOffset - xLag * z) * mix * data.x; + if (y) bone.worldY += (yOffset - yLag * z) * mix * data.y; + } + + if (rotateOrShearX) { + var o:Float = (rotateOffset - rotateLag * z) * mix, + s:Float = 0, + c:Float = 0, + a:Float = 0; + if (data.shearX > 0) { + var r:Float = 0; + if (data.rotate > 0) { + r = o * data.rotate; + s = Math.sin(r); + c = Math.cos(r); + a = bone.b; + bone.b = c * a - s * bone.d; + bone.d = s * a + c * bone.d; + } + r += o * data.shearX; + s = Math.sin(r); + c = Math.cos(r); + a = bone.a; + bone.a = c * a - s * bone.c; + bone.c = s * a + c * bone.c; + } else { + o *= data.rotate; + s = Math.sin(o); + c = Math.cos(o); + a = bone.a; + bone.a = c * a - s * bone.c; + bone.c = s * a + c * bone.c; + a = bone.b; + bone.b = c * a - s * bone.d; + bone.d = s * a + c * bone.d; + } + } + if (scaleX) { + var s:Float = 1 + (scaleOffset - scaleLag * z) * mix * this.data.scaleX; + bone.a *= s; + bone.c *= s; + } + if (physics != Physics.pose) { + tx = l * bone.a; + ty = l * bone.c; + } + bone.modifyWorld(skeleton._update); } - /** The physics constraint's setup pose data. */ - public var data(get, never):PhysicsConstraintData; - - private function get_data():PhysicsConstraintData { - return _data; + public function sort (skeleton: Skeleton) { + var bone = bone.bone; + skeleton.sortBone(bone); + skeleton._updateCache.push(this); + skeleton.sortReset(bone.children); + skeleton.constrained(bone); } - public var skeleton(get, never):Skeleton; - - private function get_skeleton():Skeleton { - return _skeleton; + override public function isSourceActive () { + return bone.bone.active; } - } diff --git a/spine-haxe/spine-haxe/spine/PhysicsConstraintData.hx b/spine-haxe/spine-haxe/spine/PhysicsConstraintData.hx index 55c2d2232..8d2917b69 100644 --- a/spine-haxe/spine-haxe/spine/PhysicsConstraintData.hx +++ b/spine-haxe/spine-haxe/spine/PhysicsConstraintData.hx @@ -30,36 +30,42 @@ package spine; /** Stores the setup pose for a PhysicsConstraint. - * + * * @see https://esotericsoftware.com/spine-physics-constraints Physics constraints in the Spine User Guide */ -class PhysicsConstraintData extends ConstraintData { +class PhysicsConstraintData extends ConstraintData { + /** The bone constrained by this physics constraint. */ public var bone:BoneData; - public var x:Float = 0; - public var y:Float = 0; - public var rotate:Float = 0; - public var scaleX:Float = 0; - public var shearX:Float = 0; - public var limit:Float = 0; - public var step:Float = 0; - public var inertia:Float = 0; - public var strength:Float = 0; - public var damping:Float = 0; - public var massInverse:Float = 0; - public var wind:Float = 0; - public var gravity:Float = 0; + + public var x = 0.; + public var y = 0.; + public var rotate = 0.; + public var scaleX = 0.; + public var shearX = 0.; + public var limit = 0.; + public var step = 0.; + public var inertia = 0.; + public var strength = 0.; + public var damping = 0.; + public var massInverse = 0.; + public var wind = 0.; + public var gravity = 0.; /** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */ - public var mix:Float = 0; - public var inertiaGlobal:Bool = false; - public var strengthGlobal:Bool = false; - public var dampingGlobal:Bool = false; - public var massGlobal:Bool = false; - public var windGlobal:Bool = false; - public var gravityGlobal:Bool = false; - public var mixGlobal:Bool = false; - + public var mix = 0.; + public var inertiaGlobal = false; + public var strengthGlobal = false; + public var dampingGlobal = false; + public var massGlobal = false; + public var windGlobal = false; + public var gravityGlobal = false; + public var mixGlobal = false; + public function new(name:String) { - super(name, 0, false); + super(name, new PhysicsConstraintPose()); + } + + public function create (skeleton:Skeleton) { + return new PhysicsConstraint(this, skeleton); } } diff --git a/spine-haxe/spine-haxe/spine/PhysicsConstraintPose.hx b/spine-haxe/spine-haxe/spine/PhysicsConstraintPose.hx new file mode 100644 index 000000000..9b30f51ed --- /dev/null +++ b/spine-haxe/spine-haxe/spine/PhysicsConstraintPose.hx @@ -0,0 +1,56 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +/** Stores a pose for a physics constraint. */ +class PhysicsConstraintPose implements Pose { + + public var inertia = 0.; + public var strength = 0.; + public var damping = 0.; + public var massInverse = 0.; + public var wind = 0.; + public var gravity = 0.; + /** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */ + public var mix = 0.; + + public function new () { + } + + public function set (pose:PhysicsConstraintPose) { + inertia = pose.inertia; + strength = pose.strength; + damping = pose.damping; + massInverse = pose.massInverse; + wind = pose.wind; + gravity = pose.gravity; + mix = pose.mix; + } +} diff --git a/spine-haxe/spine-haxe/spine/Pose.hx b/spine-haxe/spine-haxe/spine/Pose.hx new file mode 100644 index 000000000..88da2ad71 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/Pose.hx @@ -0,0 +1,34 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +interface Pose

{ + public function set (pose:P):Void; +} \ No newline at end of file diff --git a/spine-haxe/spine-haxe/spine/Posed.hx b/spine-haxe/spine-haxe/spine/Posed.hx new file mode 100644 index 000000000..c7d247b3d --- /dev/null +++ b/spine-haxe/spine-haxe/spine/Posed.hx @@ -0,0 +1,59 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +abstract class Posed< // + D:PosedData

, // + P:Pose, // + A:P> { + + /** The constraint's setup pose data. */ + public final data:D; + + public final pose:P; + public final constrained:A; + public var applied:A; + + public function new (data:D, pose:P, constrained:A) { + if (data == null) throw new SpineException("data cannot be null."); + this.data = data; + this.pose = pose; + this.constrained = constrained; + applied = cast pose; + } + + public function setupPose ():Void { + pose.set(data.setup); + } + + public function toString ():String { + return data.name; + } +} diff --git a/spine-haxe/spine-haxe/spine/PosedActive.hx b/spine-haxe/spine-haxe/spine/PosedActive.hx new file mode 100644 index 000000000..9f6b3d7ff --- /dev/null +++ b/spine-haxe/spine-haxe/spine/PosedActive.hx @@ -0,0 +1,55 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +abstract class PosedActive< // + D:PosedData

, // + P:Pose, // + A:P> // + extends Posed { + + public var active:Bool; + + public function new (data:D, pose:P, constrained:A) { + super(data, pose, constrained); + setupPose(); + } + + /** Returns false when this constraint won't be updated by + * Skeleton.updateWorldTransform(Physics) because a skin is required and the + * Skeleton.getSkin() (active skin) does not contain this item. + * @see Skin.getBones() + * @see Skin.getConstraints() + * @see PosedData.getSkinRequired() + * @see Skeleton.updateCache() */ + public function isActive ():Bool { + return active; + } +} diff --git a/spine-haxe/spine-haxe/spine/PosedData.hx b/spine-haxe/spine-haxe/spine/PosedData.hx new file mode 100644 index 000000000..d42878b41 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/PosedData.hx @@ -0,0 +1,54 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +/** The base class for all constrained datas. */ +abstract class PosedData> { + /** The constraint's name, which is unique across all constraints in the skeleton of the same type. */ + public var name:String; + + public final setup:P; + + /** When true, `Skeleton.updateWorldTransform(Physics)` only updates this constraint if the `Skeleton.getSkin()` + * contains this constraint. + * + * See `Skin.getConstraints()`. */ + public var skinRequired:Bool; + + public function new (name:String, setup:P) { + if (name == null) throw new SpineException("name cannot be null."); + this.name = name; + this.setup = setup; + } + + public function toString ():String { + return name; + } +} diff --git a/spine-haxe/spine-haxe/spine/Sequence.hx b/spine-haxe/spine-haxe/spine/Sequence.hx index 4646c16a4..3974d00fc 100644 --- a/spine-haxe/spine-haxe/spine/Sequence.hx +++ b/spine-haxe/spine-haxe/spine/Sequence.hx @@ -59,7 +59,7 @@ class Sequence { return copy; } - public function apply(slot:Slot, attachment:HasTextureRegion) { + public function apply(slot:SlotPose, attachment:HasTextureRegion) { var index:Int = slot.sequenceIndex; if (index == -1) index = this.setupIndex; diff --git a/spine-haxe/spine-haxe/spine/Skeleton.hx b/spine-haxe/spine-haxe/spine/Skeleton.hx index 2f543143d..2e5fc3366 100644 --- a/spine-haxe/spine-haxe/spine/Skeleton.hx +++ b/spine-haxe/spine-haxe/spine/Skeleton.hx @@ -43,69 +43,84 @@ import spine.attachments.RegionAttachment; */ class Skeleton { private static var quadTriangles:Array = [0, 1, 2, 2, 3, 0]; - private var _data:SkeletonData; + + /** The skeleton's setup pose data. */ + public final data:SkeletonData; /** The skeleton's bones, sorted parent first. The root bone is always the first bone. */ - public var bones:Array; + public final bones:Array; + /** The skeleton's slots. */ - public var slots:Array; // Setup pose draw order. + public final slots:Array; // Setup pose draw order. + /** The skeleton's slots in the order they should be drawn. The returned array may be modified to change the draw order. */ public var drawOrder:Array; - /** The skeleton's IK constraints. */ - public var ikConstraints:Array; - /** The skeleton's transform constraints. */ - public var transformConstraints:Array; - /** The skeleton's path constraints. */ - public var pathConstraints:Array; - /** The skeleton's physics constraints. */ - public var physicsConstraints:Array; - private var _updateCache:Array = new Array(); - private var _skin:Skin; + /** The skeleton's constraints. */ + public final constraints:Array>; + + /** The skeleton's physics constraints. */ + public final physics:Array; + + /** The list of bones and constraints, sorted in the order they should be updated, as computed by Skeleton.updateCache(). */ + public final _updateCache = new Array(); + + private final resetCache = new Array> (); + + /** The skeleton's current skin. */ + public var skin(default, set):Skin = null; /** The color to tint all the skeleton's attachments. */ - public var color:Color = new Color(1, 1, 1, 1); - /** Scales the entire skeleton on the X axis. - * - * Bones that do not inherit scale are still affected by this property. */ - public var scaleX:Float = 1; - - /** Scales the entire skeleton on the Y axis. - * - * Bones that do not inherit scale are still affected by this property. */ - public var scaleY(get, default):Float = 1; - function get_scaleY() { - return Bone.yDown ? -scaleY : scaleY; - } + public final color:Color; /** Sets the skeleton X position, which is added to the root bone worldX position. * * Bones that do not inherit translation are still affected by this property. */ public var x:Float = 0; + /** Sets the skeleton Y position, which is added to the root bone worldY position. * * Bones that do not inherit translation are still affected by this property. */ public var y:Float = 0; + + /** Scales the entire skeleton on the X axis. + * + * Bones that do not inherit scale are still affected by this property. */ + public var scaleX:Float = 1; + + /** Scales the entire skeleton on the Y axis. + * + * Bones that do not inherit scale are still affected by this property. */ + public var scaleY(get, default):Float = 1; + function get_scaleY() { + return Bone.yDown ? -scaleY : scaleY; + } + /** Returns the skeleton's time. This is used for time-based manipulations, such as spine.PhysicsConstraint. * * See Skeleton.update(). */ public var time:Float = 0; + public var windX:Float = 1; + public var windY:Float = 0; + public var gravityX:Float = 0; + public var gravityY:Float = 1; + + public var _update = 0; + /** Creates a new skeleton with the specified skeleton data. */ public function new(data:SkeletonData) { - if (data == null) { - throw new SpineException("data cannot be null."); - } - _data = data; + if (data == null) throw new SpineException("data cannot be null."); + this.data = data; bones = new Array(); for (boneData in data.bones) { var bone:Bone; if (boneData.parent == null) { - bone = new Bone(boneData, this, null); + bone = new Bone(boneData, null); } else { var parent:Bone = bones[boneData.parent.index]; - bone = new Bone(boneData, this, parent); + bone = new Bone(boneData, parent); parent.children.push(bone); } bones.push(bone); @@ -114,31 +129,20 @@ class Skeleton { slots = new Array(); drawOrder = new Array(); for (slotData in data.slots) { - var bone = bones[slotData.boneData.index]; - var slot:Slot = new Slot(slotData, bone); + var slot = new Slot(slotData, this); slots.push(slot); drawOrder.push(slot); } - ikConstraints = new Array(); - for (ikConstraintData in data.ikConstraints) { - ikConstraints.push(new IkConstraint(ikConstraintData, this)); + physics = new Array(); + constraints = new Array>(); + for (constraintData in data.constraints) { + var constraint = constraintData.create(this); + if (Std.isOfType(constraint, PhysicsConstraint)) physics.push(cast(constraint, PhysicsConstraint)); + constraints.push(constraint); } - transformConstraints = new Array(); - for (transformConstraintData in data.transformConstraints) { - transformConstraints.push(new TransformConstraint(transformConstraintData, this)); - } - - pathConstraints = new Array(); - for (pathConstraintData in data.pathConstraints) { - pathConstraints.push(new PathConstraint(pathConstraintData, this)); - } - - physicsConstraints = new Array(); - for (physicConstraintData in data.physicsConstraints) { - physicsConstraints.push(new PhysicsConstraint(physicConstraintData, this)); - } + color = new Color(1, 1, 1, 1); updateCache(); } @@ -147,14 +151,19 @@ class Skeleton { * constraints, or weighted path attachments are added or removed. */ public function updateCache():Void { _updateCache.resize(0); + resetCache.resize(0); + + for (slot in slots) + slot.applied = slot.pose; for (bone in bones) { bone.sorted = bone.data.skinRequired; bone.active = !bone.sorted; + bone.applied = cast(bone.pose, BonePose); } if (skin != null) { - var skinBones:Array = skin.bones; + var skinBones = skin.bones; for (i in 0...skin.bones.length) { var bone:Bone = bones[skinBones[i].index]; do { @@ -165,216 +174,54 @@ class Skeleton { } } - // IK first, lowest hierarchy depth first. - var ikCount:Int = ikConstraints.length; - var transformCount:Int = transformConstraints.length; - var pathCount:Int = pathConstraints.length; - var physicCount:Int = physicsConstraints.length; - var constraintCount:Int = ikCount + transformCount + pathCount + physicCount; - - var continueOuter:Bool; - for (i in 0...constraintCount) { - continueOuter = false; - for (ikConstraint in ikConstraints) { - if (ikConstraint.data.order == i) { - sortIkConstraint(ikConstraint); - continueOuter = true; - break; - } - } - if (continueOuter) - continue; - for (transformConstraint in transformConstraints) { - if (transformConstraint.data.order == i) { - sortTransformConstraint(transformConstraint); - continueOuter = true; - break; - } - } - if (continueOuter) - continue; - for (pathConstraint in pathConstraints) { - if (pathConstraint.data.order == i) { - sortPathConstraint(pathConstraint); - break; - } - } - if (continueOuter) - continue; - for (physicConstraint in physicsConstraints) { - if (physicConstraint.data.order == i) { - sortPhysicsConstraint(physicConstraint); - break; - } - } + for (constraint in constraints) + constraint.applied = constraint.pose; + for (c in constraints) { + var constraint:Constraint = c; + constraint.active = constraint.isSourceActive() + && (!constraint.data.skinRequired || (skin != null && contains(skin.constraints, constraint.data))); + if (constraint.active) constraint.sort(this); } - for (bone in bones) { + for (bone in bones) sortBone(bone); + + var updateCache = this._updateCache; + var n = updateCache.length; + for (i in 0...n) { + var updatable = updateCache[i]; + if (Std.isOfType(updatable, Bone)) { + var b:Bone = cast updatable; + updateCache[i] = b.applied; + } } } - private static function contains(list:Array, element:ConstraintData):Bool { + private static function contains(list:Array>, element:ConstraintData):Bool { return list.indexOf(element) != -1; } - private function sortIkConstraint(constraint:IkConstraint):Void { - constraint.active = constraint.target.isActive() - && (!constraint.data.skinRequired || (this.skin != null && contains(this.skin.constraints, constraint.data))); - if (!constraint.active) - return; - - var target:Bone = constraint.target; - sortBone(target); - - var constrained:Array = constraint.bones; - var parent:Bone = constrained[0]; - sortBone(parent); - - if (constrained.length == 1) { - _updateCache.push(constraint); - sortReset(parent.children); - } else { - var child:Bone = constrained[constrained.length - 1]; - sortBone(child); - - _updateCache.push(constraint); - - sortReset(parent.children); - child.sorted = true; - } - - _updateCache.push(constraint); - - sortReset(parent.children); - constrained[constrained.length - 1].sorted = true; - } - - private function sortPathConstraint(constraint:PathConstraint):Void { - constraint.active = constraint.target.bone.isActive() - && (!constraint.data.skinRequired || (this.skin != null && contains(this.skin.constraints, constraint.data))); - if (!constraint.active) - return; - - var slot:Slot = constraint.target; - var slotIndex:Int = slot.data.index; - var slotBone:Bone = slot.bone; - if (skin != null) - sortPathConstraintAttachment(skin, slotIndex, slotBone); - if (data.defaultSkin != null && data.defaultSkin != skin) { - sortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone); - } - for (i in 0...data.skins.length) { - sortPathConstraintAttachment(data.skins[i], slotIndex, slotBone); - } - - var attachment:Attachment = slot.attachment; - if (Std.isOfType(attachment, PathAttachment)) - sortPathConstraintAttachment2(attachment, slotBone); - - var constrainedBones:Array = constraint.bones; - for (bone in constrainedBones) { - sortBone(bone); - } - - _updateCache.push(constraint); - - for (bone in constrainedBones) { - sortReset(bone.children); - } - for (bone in constrainedBones) { - bone.sorted = true; + public function constrained (object:Posed) { + if (object.pose == object.applied) { + object.applied = object.constrained; + resetCache.push(object); } } - private function sortTransformConstraint(constraint:TransformConstraint):Void { - constraint.active = constraint.target.isActive() - && (!constraint.data.skinRequired || (this.skin != null && contains(this.skin.constraints, constraint.data))); - if (!constraint.active) - return; - - sortBone(constraint.target); - - var constrainedBones:Array = constraint.bones; - if (constraint.data.local) { - for (bone in constrainedBones) { - sortBone(bone.parent); - sortBone(bone); - } - } else { - for (bone in constrainedBones) { - sortBone(bone); - } - } - - _updateCache.push(constraint); - for (bone in constrainedBones) { - sortReset(bone.children); - } - for (bone in constrainedBones) { - bone.sorted = true; - } - } - - private function sortPathConstraintAttachment(skin:Skin, slotIndex:Int, slotBone:Bone):Void { - var dict:StringMap = skin.attachments[slotIndex]; - if (dict != null) { - for (attachment in dict.keyValueIterator()) { - sortPathConstraintAttachment2(attachment.value, slotBone); - } - } - } - - private function sortPathConstraintAttachment2(attachment:Attachment, slotBone:Bone):Void { - var pathAttachment:PathAttachment = cast(attachment, PathAttachment); - if (pathAttachment == null) - return; - var pathBones:Array = pathAttachment.bones; - if (pathBones == null) { - sortBone(slotBone); - } else { - var i:Int = 0; - var n:Int = pathBones.length; - while (i < n) { - var nn:Int = pathBones[i++]; - nn += i; - while (i < nn) { - sortBone(bones[pathBones[i++]]); - } - } - } - } - - private function sortPhysicsConstraint (constraint: PhysicsConstraint) { - var bone:Bone = constraint.bone; - constraint.active = bone.active && (!constraint.data.skinRequired || (skin != null && contains(skin.constraints, constraint.data))); - if (!constraint.active) return; - - sortBone(bone); - - _updateCache.push(constraint); - - sortReset(bone.children); - bone.sorted = true; - } - - private function sortBone(bone:Bone):Void { - if (bone.sorted) - return; - var parent:Bone = bone.parent; - if (parent != null) - sortBone(parent); + public function sortBone(bone:Bone):Void { + if (bone.sorted || !bone.active) return; + var parent = bone.parent; + if (parent != null) sortBone(parent); bone.sorted = true; _updateCache.push(bone); } - private function sortReset(bones:Array):Void { + public function sortReset(bones:Array):Void { for (bone in bones) { - if (!bone.active) - continue; - if (bone.sorted) - sortReset(bone.children); - bone.sorted = false; + if (bone.active) { + if (bone.sorted) sortReset(bone.children); + bone.sorted = false; + } } } @@ -383,124 +230,58 @@ class Skeleton { * @see https://esotericsoftware.com/spine-runtime-skeletons#World-transforms World transforms in the Spine Runtimes Guide */ public function updateWorldTransform(physics:Physics):Void { - if (physics == null) throw new SpineException("physics is undefined"); - for (bone in bones) { - 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; - } + _update++; - for (updatable in _updateCache) { - updatable.update(physics); - } - } + for (object in resetCache) + object.applied.set(object.pose); - /** Temporarily sets the root bone as a child of the specified bone, then updates the world transform for each bone and applies - * all constraints. - * - * @see https://esotericsoftware.com/spine-runtime-skeletons#World-transforms World transforms in the Spine Runtimes Guide - */ - public function updateWorldTransformWith(physics:Physics, 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:Float = parent.a, - pb:Float = parent.b, - pc:Float = parent.c, - pd:Float = parent.d; - rootBone.worldX = pa * x + pb * y + parent.worldX; - rootBone.worldY = pc * x + pd * y + parent.worldY; - - var rx:Float = (rootBone.rotation + rootBone.shearX) * MathUtils.degRad; - var ry:Float = (rootBone.rotation + 90 + rootBone.shearY) * MathUtils.degRad; - var la:Float = Math.cos(rx) * rootBone.scaleX; - var lb:Float = Math.cos(ry) * rootBone.scaleY; - var lc:Float = Math.sin(rx) * rootBone.scaleX; - var ld:Float = Math.sin(ry) * 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. - for (updatable in _updateCache) { - if (updatable != rootBone) - updatable.update(physics); - } + for (updatable in _updateCache) + updatable.update(this, physics); } /** Sets the bones, constraints, slots, and draw order to their setup pose values. */ - public function setToSetupPose():Void { - setBonesToSetupPose(); - setSlotsToSetupPose(); + public function setupPose():Void { + setupPoseBones(); + setupPoseSlots(); } /** Sets the bones and constraints to their setup pose values. */ - public function setBonesToSetupPose():Void { - for (bone in this.bones) bone.setToSetupPose(); - for (constraint in this.ikConstraints) constraint.setToSetupPose(); - for (constraint in this.transformConstraints) constraint.setToSetupPose(); - for (constraint in this.pathConstraints) constraint.setToSetupPose(); - for (constraint in this.physicsConstraints) constraint.setToSetupPose(); + public function setupPoseBones():Void { + for (bone in this.bones) bone.setupPose(); + for (constraint in this.constraints) constraint.setupPose(); } /** Sets the slots and draw order to their setup pose values. */ - public function setSlotsToSetupPose():Void { + public function setupPoseSlots():Void { var i:Int = 0; for (slot in slots) { drawOrder[i++] = slot; - slot.setToSetupPose(); + slot.setupPose(); } } - /** The skeleton's setup pose data. */ - public var data(get, never):SkeletonData; - - private function get_data():SkeletonData { - return _data; - } - - /** The list of bones and constraints, sorted in the order they should be updated, as computed by Skeleton.updateCache(). */ - public var getUpdateCache(get, never):Array; - - private function get_getUpdateCache():Array { - return _updateCache; - } - /** Returns the root bone, or null if the skeleton has no bones. */ public var rootBone(get, never):Bone; private function get_rootBone():Bone { - if (bones.length == 0) - return null; - return bones[0]; + return bones.length == 0 ? null : bones[0]; } /** Finds a bone by comparing each bone's name. It is more efficient to cache the results of this method than to call it * repeatedly. */ public function findBone(boneName:String):Bone { - if (boneName == null) { - throw new SpineException("boneName cannot be null."); - } - for (bone in bones) { - if (bone.data.name == boneName) - return bone; - } + if (boneName == null) throw new SpineException("boneName cannot be null."); + for (bone in bones) + if (bone.data.name == boneName) return bone; return null; } /** @return -1 if the bone was not found. */ public function findBoneIndex(boneName:String):Int { - if (boneName == null) { - throw new SpineException("boneName cannot be null."); - } + if (boneName == null) throw new SpineException("boneName cannot be null."); var i:Int = 0; for (bone in bones) { - if (bone.data.name == boneName) - return i; + if (bone.data.name == boneName) return i; i++; } return -1; @@ -509,13 +290,9 @@ class Skeleton { /** Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it * repeatedly. */ public function findSlot(slotName:String):Slot { - if (slotName == null) { - throw new SpineException("slotName cannot be null."); - } - for (slot in slots) { - if (slot.data.name == slotName) - return slot; - } + if (slotName == null) throw new SpineException("slotName cannot be null."); + for (slot in slots) + if (slot.data.name == slotName) return slot; return null; } @@ -535,14 +312,7 @@ class Skeleton { /** @return May be null. */ private function get_skinName():String { - return _skin == null ? null : _skin.name; - } - - /** The skeleton's current skin. */ - public var skin(get, set):Skin; - - private function get_skin():Skin { - return _skin; + return skin == null ? null : skin.name; } /** Sets the skin used to look up attachments before looking in the spine.SkeletonData default skin. If the @@ -556,7 +326,7 @@ class Skeleton { * skeleton is rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new * skin. */ private function set_skin(newSkin:Skin):Skin { - if (newSkin == _skin) + if (newSkin == skin) return null; if (newSkin != null) { if (skin != null) { @@ -567,16 +337,15 @@ class Skeleton { var name:String = slot.data.attachmentName; if (name != null) { var attachment:Attachment = newSkin.getAttachment(i, name); - if (attachment != null) - slot.attachment = attachment; + if (attachment != null) slot.pose.attachment = attachment; } i++; } } } - _skin = newSkin; + skin = newSkin; updateCache(); - return _skin; + return skin; } /** Finds an attachment by looking in the Skeleton.skin and spine.SkeletonData defaultSkin using the slot name and attachment @@ -621,7 +390,7 @@ class Skeleton { throw new SpineException("Attachment not found: " + attachmentName + ", for slot: " + slotName); } } - slot.attachment = attachment; + slot.pose.attachment = attachment; return; } i++; @@ -629,110 +398,66 @@ class Skeleton { throw new SpineException("Slot not found: " + slotName); } - /** Finds an IK constraint by comparing each IK constraint's name. It is more efficient to cache the results of this method - * than to call it repeatedly. */ - public function findIkConstraint(constraintName:String):IkConstraint { - if (constraintName == null) - throw new SpineException("constraintName cannot be null."); - for (ikConstraint in ikConstraints) { - if (ikConstraint.data.name == constraintName) - return ikConstraint; - } + public function findConstraint>(constraintName:String, type:Class):Null { + if (constraintName == null) throw new SpineException("constraintName cannot be null."); + if (type == null) throw new SpineException("type cannot be null."); + for (constraint in constraints) + if (Std.isOfType(constraint, type) && constraint.data.name == constraintName) return Std.downcast(constraint, type); return null; } - /** Finds a transform constraint by comparing each transform constraint's name. It is more efficient to cache the results of - * this method than to call it repeatedly. */ - public function findTransformConstraint(constraintName:String):TransformConstraint { - if (constraintName == null) - throw new SpineException("constraintName cannot be null."); - for (transformConstraint in transformConstraints) { - if (transformConstraint.data.name == constraintName) - return transformConstraint; - } - return null; - } - - /** Finds a path constraint by comparing each path constraint's name. It is more efficient to cache the results of this method - * than to call it repeatedly. */ - public function findPathConstraint(constraintName:String):PathConstraint { - if (constraintName == null) - throw new SpineException("constraintName cannot be null."); - for (pathConstraint in pathConstraints) { - if (pathConstraint.data.name == constraintName) - return pathConstraint; - } - return null; - } - - /** Finds a physics constraint by comparing each physics constraint's name. It is more efficient to cache the results of this - * method than to call it repeatedly. */ - public function findPhysicsConstraint(constraintName:String):PhysicsConstraint { - if (constraintName == null) - throw new SpineException("constraintName cannot be null."); - for (physicsConstraint in physicsConstraints) { - if (physicsConstraint.data.name == constraintName) - return physicsConstraint; - } - return null; - } - - public function toString():String { - return _data.name != null ? _data.name : "Skeleton?"; - } - private var _tempVertices = new Array(); private var _bounds = new Rectangle(); /** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose. Optionally applies * clipping. */ public function getBounds(clipper: SkeletonClipping = null):Rectangle { - var minX:Float = Math.POSITIVE_INFINITY; - var minY:Float = Math.POSITIVE_INFINITY; - var maxX:Float = Math.NEGATIVE_INFINITY; - var maxY:Float = Math.NEGATIVE_INFINITY; + var minX = Math.POSITIVE_INFINITY; + var minY = Math.POSITIVE_INFINITY; + var maxX = Math.NEGATIVE_INFINITY; + var maxY = Math.NEGATIVE_INFINITY; for (slot in drawOrder) { var verticesLength:Int = 0; var vertices:Array = null; var triangles:Array = null; - var attachment:Attachment = slot.attachment; - - if (Std.isOfType(attachment, RegionAttachment)) { - verticesLength = 8; - _tempVertices.resize(verticesLength); - vertices = _tempVertices; - cast(attachment, RegionAttachment).computeWorldVertices(slot, vertices, 0, 2); - triangles = Skeleton.quadTriangles; - } else if (Std.isOfType(attachment, MeshAttachment)) { - var mesh:MeshAttachment = cast(attachment, MeshAttachment); - verticesLength = mesh.worldVerticesLength; - _tempVertices.resize(verticesLength); - vertices = _tempVertices; - mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2); - triangles = mesh.triangles; - } else if (Std.isOfType(attachment, ClippingAttachment) && clipper != null) { - clipper.clipStart(slot, cast(attachment, ClippingAttachment)); - continue; - } - - if (vertices != null) { - if (clipper != null && clipper.isClipping()) { - clipper.clipTriangles(vertices, triangles, triangles.length); - vertices = clipper.clippedVertices; - verticesLength = clipper.clippedVertices.length; + var attachment:Attachment = slot.pose.attachment; + if (attachment != null) { + if (Std.isOfType(attachment, RegionAttachment)) { + verticesLength = 8; + _tempVertices.resize(verticesLength); + vertices = _tempVertices; + cast(attachment, RegionAttachment).computeWorldVertices(slot, vertices, 0, 2); + triangles = Skeleton.quadTriangles; + } else if (Std.isOfType(attachment, MeshAttachment)) { + var mesh:MeshAttachment = cast(attachment, MeshAttachment); + verticesLength = mesh.worldVerticesLength; + _tempVertices.resize(verticesLength); + vertices = _tempVertices; + mesh.computeWorldVertices(this, slot, 0, verticesLength, vertices, 0, 2); + triangles = mesh.triangles; + } else if (Std.isOfType(attachment, ClippingAttachment) && clipper != null) { + clipper.clipEnd(slot); + clipper.clipStart(this, slot, cast(attachment, ClippingAttachment)); + continue; } - var ii:Int = 0; - var nn:Int = vertices.length; - while (ii < nn) { - var x:Float = vertices[ii], y:Float = vertices[ii + 1]; - minX = Math.min(minX, x); - minY = Math.min(minY, y); - maxX = Math.max(maxX, x); - maxY = Math.max(maxY, y); - ii += 2; + if (vertices != null) { + if (clipper != null && clipper.isClipping() && clipper.clipTriangles(vertices, triangles, triangles.length)) { + vertices = clipper.clippedVertices; + verticesLength = clipper.clippedVertices.length; + } + var ii:Int = 0; + var nn:Int = vertices.length; + while (ii < nn) { + var x:Float = vertices[ii], y:Float = vertices[ii + 1]; + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + ii += 2; + } } + if (clipper != null) clipper.clipEnd(slot); } - if (clipper != null) clipper.clipEndWithSlot(slot); } if (clipper != null) clipper.clipEnd(); _bounds.x = minX; @@ -749,13 +474,17 @@ class Skeleton { /** Calls spine.PhysicsConstraint.translate() for each physics constraint. */ public function physicsTranslate (x:Float, y:Float):Void { - for (physicsConstraint in physicsConstraints) + for (physicsConstraint in physics) physicsConstraint.translate(x, y); } /** Calls spine.PhysicsConstraint.rotate() for each physics constraint. */ public function physicsRotate (x:Float, y:Float, degrees:Float):Void { - for (physicsConstraint in physicsConstraints) + for (physicsConstraint in physics) physicsConstraint.rotate(x, y, degrees); } + + public function toString():String { + return data.name != null ? data.name : "Skeleton?"; + } } diff --git a/spine-haxe/spine-haxe/spine/SkeletonClipping.hx b/spine-haxe/spine-haxe/spine/SkeletonClipping.hx index dc259c923..cf86599c3 100644 --- a/spine-haxe/spine-haxe/spine/SkeletonClipping.hx +++ b/spine-haxe/spine-haxe/spine/SkeletonClipping.hx @@ -32,27 +32,29 @@ package spine; import spine.attachments.ClippingAttachment; class SkeletonClipping { - private var triangulator:Triangulator = new Triangulator(); - private var clippingPolygon:Array = new Array(); - private var clipOutput:Array = new Array(); + private var triangulator = new Triangulator(); + private var clippingPolygon = new Array(); + private var clipOutput = new Array(); - public var clippedVertices:Array = new Array(); - public var clippedUvs:Array = new Array(); - public var clippedTriangles:Array = new Array(); + public var clippedVertices = new Array(); + public var clippedUvs = new Array(); + public var clippedTriangles = new Array(); - private var scratch:Array = new Array(); + private var scratch = new Array(); private var clipAttachment:ClippingAttachment; private var clippingPolygons:Array>; public function new() {} - public function clipStart(slot:Slot, clip:ClippingAttachment):Int { - if (clipAttachment != null) - return 0; + public function clipStart(skeleton:Skeleton, slot:Slot, clip:ClippingAttachment):Int { + if (clipAttachment != null) return 0; + var n = clip.worldVerticesLength; + if (n < 6) return 0; clipAttachment = clip; - clippingPolygon.resize(clip.worldVerticesLength); - clip.computeWorldVertices(slot, 0, clippingPolygon.length, clippingPolygon, 0, 2); + + clippingPolygon.resize(n); + clip.computeWorldVertices(skeleton, slot, 0, n, clippingPolygon, 0, 2); SkeletonClipping.makeClockwise(clippingPolygon); clippingPolygons = triangulator.decompose(clippingPolygon, triangulator.triangulate(clippingPolygon)); for (polygon in clippingPolygons) { @@ -63,14 +65,8 @@ class SkeletonClipping { return clippingPolygons.length; } - public function clipEndWithSlot(slot:Slot):Void { - if (clipAttachment != null && clipAttachment.endSlot == slot.data) - clipEnd(); - } - - public function clipEnd():Void { - if (clipAttachment == null) - return; + public function clipEnd(?slot:Slot):Void { + if (clipAttachment == null || (slot != null && clipAttachment.endSlot != slot.data)) return; clipAttachment = null; clippingPolygons = null; clippedVertices.resize(0); @@ -84,36 +80,34 @@ class SkeletonClipping { return clipAttachment != null; } - private function clipTrianglesNoRender(vertices:Array, triangles:Array, trianglesLength:Float):Void { + private function clipTrianglesNoRender(vertices:Array, triangles:Array, trianglesLength:Float):Bool { var polygonsCount:Int = clippingPolygons.length; var index:Int = 0; clippedVertices.resize(0); clippedTriangles.resize(0); var i:Int = 0; + var clipOutputItems:Array = null; while (i < trianglesLength) { - var vertexOffset:Int = triangles[i] << 1; - var x1:Float = vertices[vertexOffset], - y1:Float = vertices[vertexOffset + 1]; + var v:Int = triangles[i] << 1; + var x1:Float = vertices[v], y1:Float = vertices[v + 1]; - vertexOffset = triangles[i + 1] << 1; - var x2:Float = vertices[vertexOffset], - y2:Float = vertices[vertexOffset + 1]; + v = triangles[i + 1] << 1; + var x2:Float = vertices[v], y2:Float = vertices[v + 1]; - vertexOffset = triangles[i + 2] << 1; - var x3:Float = vertices[vertexOffset], - y3:Float = vertices[vertexOffset + 1]; + v = triangles[i + 2] << 1; + var x3:Float = vertices[v], y3:Float = vertices[v + 1]; for (p in 0...polygonsCount) { var s:Int = clippedVertices.length; var clippedVerticesItems:Array; var clippedTrianglesItems:Array; if (this.clip(x1, y1, x2, y2, x3, y3, clippingPolygons[p], clipOutput)) { + clipOutputItems = clipOutput; var clipOutputLength:Int = clipOutput.length; if (clipOutputLength == 0) continue; var clipOutputCount:Int = clipOutputLength >> 1; - var clipOutputItems:Array = clipOutput; clippedVerticesItems = clippedVertices; clippedVerticesItems.resize(s + clipOutputLength); var ii:Int = 0; @@ -160,12 +154,12 @@ class SkeletonClipping { i += 3; } + return clipOutputItems != null; } - public function clipTriangles(vertices:Array, triangles:Array, trianglesLength:Float, uvs:Array = null):Void { + public function clipTriangles(vertices:Array, triangles:Array, trianglesLength:Float, uvs:Array = null, stride:Int = 0):Bool { if (uvs == null) { - clipTrianglesNoRender(vertices, triangles, trianglesLength); - return; + return clipTrianglesNoRender(vertices, triangles, trianglesLength); } var polygonsCount:Int = clippingPolygons.length; @@ -174,21 +168,19 @@ class SkeletonClipping { clippedUvs.resize(0); clippedTriangles.resize(0); var i:Int = 0; + var clipOutputItems:Array = null; while (i < trianglesLength) { - var vertexOffset:Int = triangles[i] << 1; - var x1:Float = vertices[vertexOffset], - y1:Float = vertices[vertexOffset + 1]; - var u1:Float = uvs[vertexOffset], v1:Float = uvs[vertexOffset + 1]; + var t:Int = triangles[i]; + var u1:Float = uvs[t << 1], v1:Float = uvs[(t << 1) + 1]; + var x1:Float = vertices[t * stride], y1:Float = vertices[t * stride + 1]; - vertexOffset = triangles[i + 1] << 1; - var x2:Float = vertices[vertexOffset], - y2:Float = vertices[vertexOffset + 1]; - var u2:Float = uvs[vertexOffset], v2:Float = uvs[vertexOffset + 1]; + t = triangles[i + 1]; + var u2:Float = uvs[t << 1], v2:Float = uvs[(t << 1) + 1]; + var x2:Float = vertices[t * stride], y2:Float = vertices[t * stride + 1]; - vertexOffset = triangles[i + 2] << 1; - var x3:Float = vertices[vertexOffset], - y3:Float = vertices[vertexOffset + 1]; - var u3:Float = uvs[vertexOffset], v3:Float = uvs[vertexOffset + 1]; + t = triangles[i + 2]; + var u3:Float = uvs[t << 1], v3:Float = uvs[(t << 1) + 1]; + var x3:Float = vertices[t * stride], y3:Float = vertices[t * stride + 1]; for (p in 0...polygonsCount) { var s:Int = clippedVertices.length; @@ -196,6 +188,7 @@ class SkeletonClipping { var clippedUvsItems:Array; var clippedTrianglesItems:Array; if (this.clip(x1, y1, x2, y2, x3, y3, clippingPolygons[p], clipOutput)) { + clipOutputItems = clipOutput; var clipOutputLength:Int = clipOutput.length; if (clipOutputLength == 0) continue; @@ -206,9 +199,8 @@ class SkeletonClipping { var d:Float = 1 / (d0 * d2 + d1 * (y1 - y3)); var clipOutputCount:Int = clipOutputLength >> 1; - var clipOutputItems:Array = clipOutput; clippedVerticesItems = clippedVertices; - clippedVerticesItems.resize(s + clipOutputLength); + clippedVerticesItems.resize(s + clipOutputLength * stride); clippedUvsItems = clippedUvs; clippedUvsItems.resize(s + clipOutputLength); @@ -242,7 +234,7 @@ class SkeletonClipping { index += clipOutputCount + 1; } else { clippedVerticesItems = clippedVertices; - clippedVerticesItems.resize(s + 3 * 2); + clippedVerticesItems.resize(s + 3 * stride); clippedVerticesItems[s] = x1; clippedVerticesItems[s + 1] = y1; clippedVerticesItems[s + 2] = x2; @@ -272,13 +264,14 @@ class SkeletonClipping { i += 3; } + return clipOutputItems != null; } /** * Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping * area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */ - public function clip(x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float, clippingArea:Array, output:Array):Bool { + private function clip(x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float, clippingArea:Array, output:Array):Bool { var originalOutput:Array = output; var clipped:Bool = false; diff --git a/spine-haxe/spine-haxe/spine/SkeletonData.hx b/spine-haxe/spine-haxe/spine/SkeletonData.hx index f92ebe4a2..a02fa8bdf 100644 --- a/spine-haxe/spine-haxe/spine/SkeletonData.hx +++ b/spine-haxe/spine-haxe/spine/SkeletonData.hx @@ -37,55 +37,66 @@ import spine.attachments.AtlasAttachmentLoader; /** Stores the setup pose and all of the stateless data for a skeleton. * - * + * * @see https://esotericsoftware.com/spine-runtime-architecture#Data-objects Data objects in the Spine Runtimes * Guide. */ class SkeletonData { /** The skeleton's name, which by default is the name of the skeleton data file when possible, or null when a name hasn't been * set. */ - public var name:String; + public var name:String = null; /** The skeleton's bones, sorted parent first. The root bone is always the first bone. */ - public var bones:Array = new Array(); // Ordered parents first. + public final bones = new Array(); // Ordered parents first. + /** The skeleton's slots in the setup pose draw order. */ - public var slots:Array = new Array(); // Setup pose draw order. + public final slots = new Array(); // Setup pose draw order. + /** All skins, including the default skin. */ - public var skins:Array = new Array(); + public final skins = new Array(); + /** The skeleton's default skin. By default this skin contains all attachments that were not in a skin in Spine. * * See Skeleton#getAttachment(int, String). */ - public var defaultSkin:Skin; + public var defaultSkin:Skin = null; + /** The skeleton's events. */ - public var events:Array = new Array(); + public var events = new Array(); + /** The skeleton's animations. */ - public var animations:Array = new Array(); - /** The skeleton's IK constraints. */ - public var ikConstraints:Array = new Array(); - /** The skeleton's transform constraints. */ - public var transformConstraints:Array = new Array(); - /** The skeleton's path constraints. */ - public var pathConstraints:Array = new Array(); - /** The skeleton's physics constraints. */ - public var physicsConstraints:Array = new Array(); + public var animations = new Array(); + + /** The skeleton's constraints. */ + public var constraints = new Array>(); + /** The X coordinate of the skeleton's axis aligned bounding box in the setup pose. */ public var x:Float = 0; + /** The Y coordinate of the skeleton's axis aligned bounding box in the setup pose. */ public var y:Float = 0; + /** The width of the skeleton's axis aligned bounding box in the setup pose. */ public var width:Float = 0; + /** The height of the skeleton's axis aligned bounding box in the setup pose. */ public var height:Float = 0; + /** Baseline scale factor for applying physics and other effects based on distance to non-scalable properties, such as angle or * scale. Default is 100. */ public var referenceScale:Float = 100; + /** The Spine version used to export the skeleton data, or null. */ public var version:String; + /** The skeleton data hash. This value will change if any of the skeleton data has changed. */ public var hash:String; + + // Nonessential. /** The dopesheet FPS in Spine, or zero if nonessential data was not exported. */ public var fps:Float = 0; + /** The path to the images directory as defined in Spine, or null if nonessential data was not exported. */ public var imagesPath:String; + /** The path to the audio directory as defined in Spine, or null if nonessential data was not exported. */ public var audioPath:String; @@ -112,13 +123,9 @@ class SkeletonData { * @param boneName The name of the bone to find. * @return May be null. */ public function findBone(boneName:String):BoneData { - if (boneName == null) - throw new SpineException("boneName cannot be null."); - for (i in 0...bones.length) { - var bone:BoneData = bones[i]; - if (bone.name == boneName) - return bone; - } + if (boneName == null) throw new SpineException("boneName cannot be null."); + for (bone in bones) + if (bone.name == boneName) return bone; return null; } @@ -142,13 +149,9 @@ class SkeletonData { * @param slotName The name of the slot to find. * @return May be null. */ public function findSlot(slotName:String):SlotData { - if (slotName == null) - throw new SpineException("slotName cannot be null."); - for (i in 0...slots.length) { - var slot:SlotData = slots[i]; - if (slot.name == slotName) - return slot; - } + if (slotName == null) throw new SpineException("slotName cannot be null."); + for (slot in slots) + if (slot.name == slotName) return slot; return null; } @@ -159,12 +162,9 @@ class SkeletonData { * @param skinName The name of the skin to find. * @return May be null. */ public function findSkin(skinName:String):Skin { - if (skinName == null) - throw new SpineException("skinName cannot be null."); - for (skin in skins) { - if (skin.name == skinName) - return skin; - } + if (skinName == null) throw new SpineException("skinName cannot be null."); + for (skin in skins) + if (skin.name == skinName) return skin; return null; } @@ -175,12 +175,9 @@ class SkeletonData { * @param eventName The name of the event to find. * @return May be null. */ public function findEvent(eventName:String):EventData { - if (eventName == null) - throw new SpineException("eventName cannot be null."); - for (eventData in events) { - if (eventData.name == eventName) - return eventData; - } + if (eventName == null) throw new SpineException("eventName cannot be null."); + for (eventData in events) + if (eventData.name == eventName) return eventData; return null; } @@ -191,120 +188,25 @@ class SkeletonData { * @param animationName The name of the animation to find. * @return May be null. */ public function findAnimation(animationName:String):Animation { - if (animationName == null) - throw new SpineException("animationName cannot be null."); - for (animation in animations) { - if (animation.name == animationName) - return animation; - } + if (animationName == null) throw new SpineException("animationName cannot be null."); + for (animation in animations) + if (animation.name == animationName) return animation; return null; } - // --- IK constraints. + // --- Constraints. - /** Finds an IK constraint by comparing each IK constraint's name. It is more efficient to cache the results of this method - * than to call it multiple times. - * @param constraintName The name of the IK constraint to find. - * @return May be null. */ - public function findIkConstraint(constraintName:String):IkConstraintData { - if (constraintName == null) - throw new SpineException("constraintName cannot be null."); - for (ikConstraintData in ikConstraints) { - if (ikConstraintData.name == constraintName) - return ikConstraintData; + public function findConstraint>(constraintName:String, type:Class):Null { + if (constraintName == null) throw new SpineException("constraintName cannot be null."); + if (type == null) throw new SpineException("type cannot be null."); + + for (constraint in constraints) { + if (Std.is(constraint, type) && constraint.name == constraintName) + return Std.downcast(constraint, type); } return null; } - // --- Transform constraints. - - /** Finds a transform constraint by comparing each transform constraint's name. It is more efficient to cache the results of - * this method than to call it multiple times. - * @param constraintName The name of the transform constraint to find. - * @return May be null. */ - public function findTransformConstraint(constraintName:String):TransformConstraintData { - if (constraintName == null) - throw new SpineException("constraintName cannot be null."); - for (transformConstraintData in transformConstraints) { - if (transformConstraintData.name == constraintName) - return transformConstraintData; - } - return null; - } - - /** Finds the index of a transform constraint by comparing each transform constraint's name. - * @param transformConstraintName The name of the transform constraint to find. - * @return -1 if the transform constraint was not found. */ - public function findTransformConstraintIndex(transformConstraintName:String):Int { - if (transformConstraintName == null) - throw new SpineException("transformConstraintName cannot be null."); - for (i in 0...transformConstraints.length) { - if (transformConstraints[i].name == transformConstraintName) - return i; - } - return -1; - } - - // --- Path constraints. - - /** Finds a path constraint by comparing each path constraint's name. It is more efficient to cache the results of this method - * than to call it multiple times. - * @param constraintName The name of the path constraint to find. - * @return May be null. */ - public function findPathConstraint(constraintName:String):PathConstraintData { - if (constraintName == null) - throw new SpineException("constraintName cannot be null."); - for (i in 0...pathConstraints.length) { - var constraint:PathConstraintData = pathConstraints[i]; - if (constraint.name == constraintName) - return constraint; - } - return null; - } - - /** Finds the index of a path constraint by comparing each path constraint's name. - * @param pathConstraintName The name of the path constraint to find. - * @return -1 if the path constraint was not found. */ - public function findPathConstraintIndex(pathConstraintName:String):Int { - if (pathConstraintName == null) - throw new SpineException("pathConstraintName cannot be null."); - for (i in 0...pathConstraints.length) { - if (pathConstraints[i].name == pathConstraintName) - return i; - } - return -1; - } - - // --- Physics constraints. - - /** Finds a physics constraint by comparing each physics constraint's name. It is more efficient to cache the results of this - * method than to call it multiple times. - * @param constraintName The name of the physics constraint to find. - * @return May be null. */ - public function findPhysicsConstraint(constraintName:String):PhysicsConstraintData { - if (constraintName == null) - throw new SpineException("constraintName cannot be null."); - for (i in 0...physicsConstraints.length) { - var constraint:PhysicsConstraintData = physicsConstraints[i]; - if (constraint.name == constraintName) - return constraint; - } - return null; - } - - /** Finds the index of a physics constraint by comparing each physics constraint's name. - * @param constraintName The name of the physics constraint to find. - * @return -1 if the physics constraint was not found. */ - public function findPhysicsConstraintIndex(constraintName:String):Int { - if (constraintName == null) - throw new SpineException("constraintName cannot be null."); - for (i in 0...physicsConstraints.length) { - if (physicsConstraints[i].name == constraintName) - return i; - } - return -1; - } - public function toString():String { return name; } diff --git a/spine-haxe/spine-haxe/spine/Skin.hx b/spine-haxe/spine-haxe/spine/Skin.hx index d04f31729..1785b725a 100644 --- a/spine-haxe/spine-haxe/spine/Skin.hx +++ b/spine-haxe/spine-haxe/spine/Skin.hx @@ -34,31 +34,35 @@ import spine.attachments.Attachment; import spine.attachments.MeshAttachment; /** Stores attachments by slot index and attachment name. - * + * * See spine.SkeletonData.defaultSkin, spine.Skeleton.skin, and * Runtime skins at https://esotericsoftware.com/spine-runtime-skins in the Spine Runtimes Guide. */ class Skin { - private var _name:String; - private var _attachments:Array> = new Array>(); - private var _bones:Array = new Array(); - private var _constraints:Array = new Array(); - private var _color:Color = new Color(0.99607843, 0.61960787, 0.30980393, 1); // fe9e4fff + /** The skin's name, which is unique across all skins in the skeleton. */ + public final name:String; + + /** Returns the attachment for the specified slot index and name, or null. */ + public final attachments:Array> = new Array>(); + + public final bones:Array = new Array(); + public final constraints = new Array>>(); + + /** The color of the skin as it was in Spine, or a default color if nonessential data was not exported. */ + public final color:Color = new Color(0.99607843, 0.61960787, 0.30980393, 1); // fe9e4fff public function new(name:String) { - if (name == null) - throw new SpineException("name cannot be null."); - _name = name; + if (name == null) throw new SpineException("name cannot be null."); + this.name = name; } /** Adds an attachment to the skin for the specified slot index and name. */ public function setAttachment(slotIndex:Int, name:String, attachment:Attachment):Void { - if (attachment == null) - throw new SpineException("attachment cannot be null."); - if (slotIndex >= _attachments.length) - _attachments.resize(slotIndex + 1); - if (_attachments[slotIndex] == null) - _attachments[slotIndex] = new StringMap(); - _attachments[slotIndex].set(name, attachment); + if (attachment == null) throw new SpineException("attachment cannot be null."); + if (slotIndex >= attachments.length) + attachments.resize(slotIndex + 1); + if (attachments[slotIndex] == null) + attachments[slotIndex] = new StringMap(); + attachments[slotIndex].set(name, attachment); } /** Adds all attachments, bones, and constraints from the specified skin to this skin. */ @@ -68,26 +72,26 @@ class Skin { var bone:BoneData = skin.bones[i]; contained = false; for (j in 0...bones.length) { - if (_bones[j] == bone) { + if (bones[j] == bone) { contained = true; break; } } if (!contained) - _bones.push(bone); + bones.push(bone); } for (i in 0...skin.constraints.length) { - var constraint:ConstraintData = skin.constraints[i]; + var constraint = skin.constraints[i]; contained = false; - for (j in 0..._constraints.length) { - if (_constraints[j] == constraint) { + for (j in 0...constraints.length) { + if (constraints[j] == constraint) { contained = true; break; } } if (!contained) - _constraints.push(constraint); + constraints.push(constraint); } var attachments:Array = skin.getAttachments(); @@ -106,27 +110,27 @@ class Skin { for (i in 0...skin.bones.length) { var bone:BoneData = skin.bones[i]; contained = false; - for (j in 0..._bones.length) { - if (_bones[j] == bone) { + for (j in 0...bones.length) { + if (bones[j] == bone) { contained = true; break; } } if (!contained) - _bones.push(bone); + bones.push(bone); } for (i in 0...skin.constraints.length) { - var constraint:ConstraintData = skin.constraints[i]; + var constraint = skin.constraints[i]; contained = false; - for (j in 0..._constraints.length) { - if (_constraints[j] == constraint) { + for (j in 0...constraints.length) { + if (constraints[j] == constraint) { contained = true; break; } } if (!contained) - _constraints.push(constraint); + constraints.push(constraint); } var attachments:Array = skin.getAttachments(); @@ -147,15 +151,15 @@ class Skin { /** Returns the attachment for the specified slot index and name, or null. */ public function getAttachment(slotIndex:Int, name:String):Attachment { - if (slotIndex >= _attachments.length) + if (slotIndex >= attachments.length) return null; - var dictionary:StringMap = _attachments[slotIndex]; + var dictionary:StringMap = attachments[slotIndex]; return dictionary != null ? dictionary.get(name) : null; } /** Removes the attachment in the skin for the specified slot index and name, if any. */ public function removeAttachment(slotIndex:Int, name:String):Void { - var dictionary:StringMap = _attachments[slotIndex]; + var dictionary:StringMap = attachments[slotIndex]; if (dictionary != null) dictionary.remove(name); } @@ -163,8 +167,8 @@ class Skin { /** Returns all attachments in this skin. */ public function getAttachments():Array { var entries:Array = new Array(); - for (slotIndex in 0..._attachments.length) { - var attachments:StringMap = _attachments[slotIndex]; + for (slotIndex in 0...attachments.length) { + var attachments:StringMap = attachments[slotIndex]; if (attachments != null) { for (name in attachments.keys()) { var attachment:Attachment = attachments.get(name); @@ -179,7 +183,7 @@ class Skin { /** Returns all attachments in this skin for the specified slot index. */ public function getAttachmentsForSlot(slotIndex:Int):Array { var entries:Array = new Array(); - var attachments:StringMap = _attachments[slotIndex]; + var attachments:StringMap = attachments[slotIndex]; if (attachments != null) { for (name in attachments.keys()) { var attachment:Attachment = attachments.get(name); @@ -192,54 +196,21 @@ class Skin { /** Clears all attachments, bones, and constraints. */ public function clear():Void { - _attachments.resize(0); - _bones.resize(0); - _constraints.resize(0); + attachments.resize(0); + bones.resize(0); + constraints.resize(0); } - public var attachments(get, never):Array>; - - private function get_attachments():Array> { - return _attachments; + public function toString():String { + return name; } - public var bones(get, never):Array; - - private function get_bones():Array { - return _bones; - } - - public var constraints(get, never):Array; - - private function get_constraints():Array { - return _constraints; - } - - /** The skin's name, which is unique across all skins in the skeleton. */ - public var name(get, never):String; - - private function get_name():String { - return _name; - } - - /** The color of the skin as it was in Spine, or a default color if nonessential data was not exported. */ - public var color(get, never):Color; - - private function get_color():Color { - return _color; - } - - /* - public function toString():String - { - return _name; - } - */ /** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */ public function attachAll(skeleton:Skeleton, oldSkin:Skin):Void { var slotIndex:Int = 0; - for (slot in skeleton.slots) { - var slotAttachment:Attachment = slot.attachment; + for (element in skeleton.slots) { + var slot = element.pose; + var slotAttachment = slot.attachment; if (slotAttachment != null && slotIndex < oldSkin.attachments.length) { var dictionary:StringMap = oldSkin.attachments[slotIndex]; if (null != dictionary) { @@ -247,8 +218,7 @@ class Skin { var skinAttachment:Attachment = dictionary.get(name); if (slotAttachment == skinAttachment) { var attachment:Attachment = getAttachment(slotIndex, name); - if (attachment != null) - slot.attachment = attachment; + if (attachment != null) slot.attachment = attachment; break; } } diff --git a/spine-haxe/spine-haxe/spine/Slider.hx b/spine-haxe/spine-haxe/spine/Slider.hx new file mode 100644 index 000000000..b946408dd --- /dev/null +++ b/spine-haxe/spine-haxe/spine/Slider.hx @@ -0,0 +1,112 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +/** Stores the setup pose for a {@link PhysicsConstraint}. +*

+* See Physics constraints in the Spine User Guide. */ +class Slider extends Constraint { + static private final offsets:Array = [for (i in 0...6) .0]; + + public var bone:Bone; + + public function new (data:SliderData, skeleton:Skeleton) { + super(data, new SliderPose(), new SliderPose()); + if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); + + if (data.bone != null) bone = skeleton.bones.items[data.bone.index]; + } + + public function copy (skeleton:Skeleton) { + var copy = new Slider(data, skeleton); + copy.pose.set(pose); + return copy; + } + + public function update (skeleton:Skeleton, physics:Physics) { + var p = applied; + if (p.mix == 0) return; + + var animation = data.animation; + if (bone != null) { + if (!bone.active) return; + if (data.local) bone.applied.validateLocalTransform(skeleton); + p.time = (data.property.value(bone.applied, data.local, offsets) - data.property.offset) * data.scale; + if (data.loop) + p.time = animation.duration + (p.time % animation.duration); + else + p.time = Math.max(0, p.time); + } + + var bones = skeleton.bones; + var indices = animation.bones; + var i = 0, n = animation.bones.size; + while (i < n) + bones[indices[i++]].applied.modifyLocal(skeleton); + + animation.apply(skeleton, p.time, p.time, data.loop, null, p.mix, data.additive ? MixBlend.add : MixBlend.replace, + MixDirection.mixIn, true); + } + + function sort (skeleton:Skeleton) { + if (bone != null && !data.local) skeleton.sortBone(bone); + skeleton.updateCache.add(this); + + var bones = skeleton.bones; + var indices = data.animation.bones; + var i = 0, n = data.animation.bones.length; + while (i < n) { + var bone = bones[indices[i++]]; + bone.sorted = false; + skeleton.sortReset(bone.children); + skeleton.constrained(bone); + } + + var timelines = data.animation.timelines; + var slots = skeleton.slots; + var constraints = skeleton.constraints; + var physics = skeleton.physics; + var physicsCount = skeleton.physics.length; + var i = 0, n = data.animation.timelines.length; + while (i < n) { + var t = timelines[i++]; + if (std.isOfType(t, SlotTimeline)) + skeleton.constrained(slots[cast(t, SlotTimeline).getSlotIndex()]); + else if (std.isOfType(t, PhysicsConstraintTimeline)) { + if (cast(t, PhysicsConstraintTimeline).constraintIndex == -1) { + for (ii in 0...physicsCount) + skeleton.constrained(physics[ii]); + } else + skeleton.constrained(constraints[timeline.constraintIndex]); + } else if (std.isOfType(t, ConstraintTimeline)) // + skeleton.constrained(constraints[cast(t, ConstraintTimeline).getConstraintIndex()]); + } + } +} diff --git a/spine-haxe/spine-haxe/spine/SliderData.hx b/spine-haxe/spine-haxe/spine/SliderData.hx new file mode 100644 index 000000000..5627babe9 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/SliderData.hx @@ -0,0 +1,53 @@ + + +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +/** Stores the setup pose for a PhysicsConstraint. +* +* See Physics constraints in the Spine User Guide. */ +class SliderData extends ConstraintData { + public var animation:Animation; + public var additive = false; + public var loop = false; + public var bone:BoneData = null; + public var property:FromProperty = null; + public var scale = 1.; + public var local = false; + + public function new (name:String) { + super(name, new SliderPose()); + } + + public function create (skeleton:Skeleton) { + return new Slider(this, skeleton); + } +} diff --git a/spine-haxe/spine-haxe/spine/SliderPose.hx b/spine-haxe/spine-haxe/spine/SliderPose.hx new file mode 100644 index 000000000..1eda08fc2 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/SliderPose.hx @@ -0,0 +1,41 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +/** Stores a pose for a slider. */ +class SliderPose implements Pose { + public var time = 0.; + public var mix = 0.; + + public function set (pose:SliderPose) { + time = pose.time; + mix = pose.mix; + } +} \ No newline at end of file diff --git a/spine-haxe/spine-haxe/spine/Slot.hx b/spine-haxe/spine-haxe/spine/Slot.hx index 3f4e0eee5..38be3f925 100644 --- a/spine-haxe/spine-haxe/spine/Slot.hx +++ b/spine-haxe/spine-haxe/spine/Slot.hx @@ -35,101 +35,51 @@ import spine.attachments.VertexAttachment; /** Stores a slot's current pose. Slots organize attachments for Skeleton.drawOrder purposes and provide a place to store * state for an attachment. State cannot be stored in an attachment itself because attachments are stateless and may be shared * across multiple skeletons. */ -class Slot { - private var _data:SlotData; - private var _bone:Bone; +class Slot extends Posed { - /** The color used to tint the slot's attachment. If darkColor is set, this is used as the light color for two - * color tinting. */ - public var color:Color; - /** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark - * color's alpha is not used. */ - public var darkColor:Color; - - private var _attachment:Attachment; - - /** The index of the texture region to display when the slot's attachment has a spine.attachments.Sequence. -1 represents the - * Sequence.getSetupIndex(). */ - public var sequenceIndex = -1; - - public var attachmentState:Int = 0; - /** Values to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a - * weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions. - * @see spine.attachments.VertexAttachment.computeWorldVertices() - * @see spine.animation.DeformTimeline */ - public var deform:Array = new Array(); - - /** Copy constructor. */ - public function new(data:SlotData, bone:Bone) { - if (data == null) - throw new SpineException("data cannot be null."); - if (bone == null) - throw new SpineException("bone cannot be null."); - _data = data; - _bone = bone; - this.color = new Color(1, 1, 1, 1); - this.darkColor = data.darkColor == null ? null : new Color(1, 1, 1, 1); - setToSetupPose(); - } - - /** The slot's setup pose data. */ - public var data(get, never):SlotData; - - private function get_data():SlotData { - return _data; - } + public var skeleton:Skeleton; /** The bone this slot belongs to. */ - public var bone(get, never):Bone; + public var bone:Bone; - private function get_bone():Bone { - return _bone; - } + public var attachmentState:Int ; - /** The skeleton this slot belongs to. */ - public var skeleton(get, never):Skeleton; - - private function get_skeleton():Skeleton { - return _bone.skeleton; - } - - /** The current attachment for the slot, or null if the slot has no attachment. */ - public var attachment(get, set):Attachment; - - private function get_attachment():Attachment { - return _attachment; - } - - /** Sets the slot's attachment and, if the attachment changed, resets sequenceIndex and clears the deform. - * The deform is not cleared if the old attachment has the same spine.attachments.VertexAttachment.timelineAttachment as the - * specified attachment. */ - public function set_attachment(attachmentNew:Attachment):Attachment { - if (attachment == attachmentNew) - return attachmentNew; - if (!Std.isOfType(attachmentNew, VertexAttachment) - || !Std.isOfType(attachment, VertexAttachment) - || cast(attachmentNew, VertexAttachment).timelineAttachment != cast(attachment, VertexAttachment).timelineAttachment) { - deform = new Array(); + public function new(data:SlotData, skeleton:Skeleton) { + super(data, new SlotPose(), new SlotPose()); + if (skeleton == null) throw new SpineException("skeleton cannot be null."); + this.skeleton = skeleton; + bone = skeleton.bones[data.boneData.index]; + if (data.setup.darkColor != null) { + pose.darkColor = new Color(1, 1, 1, 1); + constrained.darkColor = new Color(1, 1, 1, 1); } - _attachment = attachmentNew; - sequenceIndex = -1; - return attachmentNew; + setupPose(); + } + + /** Copy method. */ + public function copy(slot:Slot, bone:Bone, skeleton:Skeleton):Slot { + var copy = new Slot(slot.data, skeleton); + if (bone == null) throw new SpineException("bone cannot be null."); + if (skeleton == null) throw new SpineException("skeleton cannot be null."); + this.bone = bone; + if (data.setup.darkColor != null) { + pose.darkColor = new Color(1, 1, 1, 1); + constrained.darkColor = new Color(1, 1, 1, 1); + } + copy.pose.set(slot.pose); + return copy; } /** Sets this slot to the setup pose. */ - public function setToSetupPose():Void { - color.setFromColor(data.color); - if (darkColor != null) - darkColor.setFromColor(data.darkColor); - if (_data.attachmentName == null) { - attachment = null; + override public function setupPose():Void { + pose.color.setFromColor(data.setup.color); + if (pose.darkColor != null) pose.darkColor.setFromColor(data.setup.darkColor); + pose.sequenceIndex = data.setup.sequenceIndex; + if (data.attachmentName == null) { + pose.attachment = null; } else { - _attachment = null; - attachment = skeleton.getAttachmentForSlotIndex(data.index, data.attachmentName); + pose.attachment = null; + pose.attachment = skeleton.getAttachmentForSlotIndex(data.index, data.attachmentName); } } - - public function toString():String { - return _data.name != null ? _data.name : "Slot?"; - } } diff --git a/spine-haxe/spine-haxe/spine/SlotData.hx b/spine-haxe/spine-haxe/spine/SlotData.hx index d968564bd..fab240ac8 100644 --- a/spine-haxe/spine-haxe/spine/SlotData.hx +++ b/spine-haxe/spine-haxe/spine/SlotData.hx @@ -30,58 +30,29 @@ package spine; /** Stores the setup pose for a spine.Slot. */ -class SlotData { - private var _index:Int; - private var _name:String; - private var _boneData:BoneData; +class SlotData extends PosedData { + + /** The index of the slot in spine.Skeleton.getSlots(). */ + public final index:Int; + + /** The bone this slot belongs to. */ + public final boneData:BoneData; - /** The color used to tint the slot's attachment. If SlotData.darkColor is set, this is used as the light color for two - * color tinting. */ - public var color:Color = new Color(1, 1, 1, 1); - /** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark - * color's alpha is not used. */ - public var darkColor:Color = null; /** The name of the attachment that is visible for this slot in the setup pose, or null if no attachment is visible. */ - public var attachmentName:String; + public var attachmentName:String = null; + /** The blend mode for drawing the slot's attachment. */ public var blendMode:BlendMode = BlendMode.normal; + + // Nonessential. /** False if the slot was hidden in Spine and nonessential data was exported. Does not affect runtime rendering. */ public var visible:Bool = true; public function new(index:Int, name:String, boneData:BoneData) { - if (index < 0) - throw new SpineException("index must be >= 0."); - if (name == null) - throw new SpineException("name cannot be null."); - if (boneData == null) - throw new SpineException("boneData cannot be null."); - _index = index; - _name = name; - _boneData = boneData; - } - - /** The index of the slot in spine.Skeleton.getSlots(). */ - public var index(get, never):Int; - - private function get_index():Int { - return _index; - } - - /** The name of the slot, which is unique across all slots in the skeleton. */ - public var name(get, never):String; - - private function get_name():String { - return _name; - } - - /** The bone this slot belongs to. */ - public var boneData(get, never):BoneData; - - private function get_boneData():BoneData { - return _boneData; - } - - public function toString():String { - return _name; + super(name, new SlotPose()); + if (index < 0) throw new SpineException("index must be >= 0."); + if (boneData == null) throw new SpineException("boneData cannot be null."); + this.index = index; + this.boneData = boneData; } } diff --git a/spine-haxe/spine-haxe/spine/SlotPose.hx b/spine-haxe/spine-haxe/spine/SlotPose.hx new file mode 100644 index 000000000..86397df4b --- /dev/null +++ b/spine-haxe/spine-haxe/spine/SlotPose.hx @@ -0,0 +1,87 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +import spine.attachments.Attachment; +import spine.attachments.VertexAttachment; + +/** Stores a slot's pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store state +* for an attachment. State cannot be stored in an attachment itself because attachments are stateless and may be shared across +* multiple skeletons. */ +class SlotPose implements Pose { + + /** The color used to tint the slot's attachment. If SlotData.darkColor is set, this is used as the light color for two + * color tinting. */ + public final color:Color = new Color(1, 1, 1, 1); + + /** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark + * color's alpha is not used. */ + public var darkColor:Color = null; + + public var attachment(default, set):Attachment; // Not used in setup pose. + + /** The index of the texture region to display when the slot's attachment has a spine.attachments.Sequence. -1 represents the + * Sequence.getSetupIndex(). */ + public var sequenceIndex = -1; + + /** Values to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a + * weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions. + * @see spine.attachments.VertexAttachment.computeWorldVertices() + * @see spine.animation.DeformTimeline */ + public var deform:Array = new Array(); + + public function new () { + } + + public function set (pose:SlotPose):Void { + if (pose == null) throw new SpineException("pose cannot be null."); + color.setFromColor(pose.color); + if (darkColor != null) darkColor.setFromColor(pose.darkColor); + attachment = pose.attachment; + sequenceIndex = pose.sequenceIndex; + deform.resize(0); + for (e in pose.deform) deform.push(e); + } + + /** Sets the slot's attachment and, if the attachment changed, resets sequenceIndex and clears the deform. + * The deform is not cleared if the old attachment has the same spine.attachments.VertexAttachment.timelineAttachment as the + * specified attachment. */ + public function set_attachment(attachmentNew:Attachment):Attachment { + if (attachment == attachmentNew) return attachment; + if (!Std.isOfType(attachmentNew, VertexAttachment) || !Std.isOfType(attachment, VertexAttachment) + || cast(attachmentNew, VertexAttachment).timelineAttachment != cast(attachment, VertexAttachment).timelineAttachment) { + deform = new Array(); + } + attachment = attachmentNew; + sequenceIndex = -1; + return attachment; + } + +} \ No newline at end of file diff --git a/spine-haxe/spine-haxe/spine/TransformConstraint.hx b/spine-haxe/spine-haxe/spine/TransformConstraint.hx index c8c3edaa0..a5c8acb37 100644 --- a/spine-haxe/spine-haxe/spine/TransformConstraint.hx +++ b/spine-haxe/spine-haxe/spine/TransformConstraint.hx @@ -33,272 +33,96 @@ package spine; * bones to match that of the target bone. * * @see https://esotericsoftware.com/spine-transform-constraints Transform constraints in the Spine User Guide */ -class TransformConstraint implements Updatable { - private var _data:TransformConstraintData; - private var _bones:Array; +class TransformConstraint extends Constraint { + + /** The bones that will be modified by this transform constraint. */ + public final bones:Array; /** The target bone whose world transform will be copied to the constrained bones. */ - public var target:Bone; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */ - public var mixRotate:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */ - public var mixX:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */ - public var mixY:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained scale X. */ - public var mixScaleX:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained scale Y. */ - public var mixScaleY:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y. */ - public var mixShearY:Float = 0; + public var source:Bone; - private var _temp:Array = new Array(); - - public var active:Bool = false; - - /** Copy constructor. */ public function new(data:TransformConstraintData, skeleton:Skeleton) { - if (data == null) - throw new SpineException("data cannot be null."); - if (skeleton == null) - throw new SpineException("skeleton cannot be null."); - _data = data; + super(data, new TransformConstraintPose(), new TransformConstraintPose()); + if (skeleton == null) throw new SpineException("skeleton cannot be null."); - _bones = new Array(); - for (boneData in data.bones) { - _bones.push(skeleton.findBone(boneData.name)); - } - target = skeleton.findBone(data.target.name); - - mixRotate = data.mixRotate; - mixX = data.mixX; - mixY = data.mixY; - mixScaleX = data.mixScaleX; - mixScaleY = data.mixScaleY; - mixShearY = data.mixShearY; + bones = new Array(); + for (boneData in data.bones) + bones.push(skeleton.bones[boneData.index].constrained); + source = skeleton.bones[data.source.index]; } - public function isActive():Bool { - return active; - } - - public function setToSetupPose () { - var data:TransformConstraintData = _data; - mixRotate = data.mixRotate; - mixX = data.mixX; - mixY = data.mixY; - mixScaleX = data.mixScaleX; - mixScaleY = data.mixScaleY; - mixShearY = data.mixShearY; + public function copy(skeleton:Skeleton) { + var copy = new TransformConstraint(data, skeleton); + copy.pose.set(pose); + return copy; } /** Applies the constraint to the constrained bones. */ - public function update(physics:Physics):Void { - if (mixRotate == 0 && mixX == 0 && mixY == 0 && mixScaleX == 0 && mixScaleY == 0 && mixShearY == 0) - return; + public function update(skeleton:Skeleton, physics:Physics):Void { + var p = applied; + if (p.mixRotate == 0 && p.mixX == 0 && p.mixY == 0 && p.mixScaleX == 0 && p.mixScaleY == 0 && p.mixShearY == 0) return; - if (data.local) { - if (data.relative) { - applyRelativeLocal(); - } else { - applyAbsoluteLocal(); - } - } else { - if (data.relative) { - applyRelativeWorld(); - } else { - applyAbsoluteWorld(); + var localSource = data.localSource, localTarget = data.localTarget, additive = data.additive, clamp = data.clamp; + var offsets = data.offsets; + var source = this.source.applied; + if (localSource) source.validateLocalTransform(skeleton); + var fromItems = data.properties; + var fn = data.properties.length, update = skeleton.update; + var bones = this.bones; + var i = 0, n = this.bones.length; + var update = skeleton._update; + while (i < n) { + var bone = bones[i]; + if (localTarget) + bone.modifyLocal(skeleton); + else + bone.modifyWorld(update); + var f = 0; + while (f < fn) { + var from = fromItems[f]; + var value = from.value(source, localSource, offsets) - from.offset; + var toItems = from.to; + var t = 0, tn = from.to.length; + while (t < tn) { + var to = toItems[t]; + if (to.mix(p) != 0) { + var clamped = to.offset + value * to.scale; + if (clamp) { + if (to.offset < to.max) + clamped = MathUtils.clamp(clamped, to.offset, to.max); + else + clamped = MathUtils.clamp(clamped, to.max, to.offset); + } + to.apply(p, bone, clamped, localTarget, additive); + } + t++; + } + f++; } + i++; } } - private function applyAbsoluteWorld():Void { - var translate:Bool = mixX != 0 || mixY != 0; - var ta:Float = target.a, - tb:Float = target.b, - tc:Float = target.c, - td:Float = target.d; - var degRadReflect:Float = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; - var offsetRotation:Float = data.offsetRotation * degRadReflect; - var offsetShearY:Float = data.offsetShearY * degRadReflect; - for (bone in bones) { - if (mixRotate != 0) { - var a:Float = bone.a, - b:Float = bone.b, - c:Float = bone.c, - d:Float = bone.d; - var r:Float = 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 *= mixRotate; - var cos:Float = Math.cos(r), sin:Float = 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; - } - - if (translate) { - _temp[0] = data.offsetX; - _temp[1] = data.offsetY; - target.localToWorld(_temp); - bone.worldX += (_temp[0] - bone.worldX) * mixX; - bone.worldY += (_temp[1] - bone.worldY) * mixY; - } - - if (mixScaleX != 0) { - var s:Float = 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) { - var s:Float = Math.sqrt(bone.b * bone.b + bone.d * bone.d); - if (s != 0) - s = (s + (Math.sqrt(tb * tb + td * td) - s + _data.offsetScaleY) * mixScaleY) / s; - bone.b *= s; - bone.d *= s; - } - - if (mixShearY > 0) { - var by:Float = Math.atan2(bone.d, bone.b); - var r:Float = 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) * mixShearY; - var s:Float = Math.sqrt(bone.b * bone.b + bone.d * bone.d); - bone.b = Math.cos(r) * s; - bone.d = Math.sin(r) * s; - } - - bone.updateAppliedTransform(); + public function sort (skeleton:Skeleton) { + if (!data.localSource) skeleton.sortBone(source); + var bones = this.bones; + var boneCount = this.bones.length; + var worldTarget = !data.localTarget; + if (worldTarget) { + for (i in 0...boneCount) + skeleton.sortBone(bones[i].bone); } - } - - public function applyRelativeWorld():Void { - var translate:Bool = mixX != 0 || mixY != 0; - var ta:Float = target.a, - tb:Float = target.b, - tc:Float = target.c, - td:Float = target.d; - var degRadReflect:Float = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; - var offsetRotation:Float = _data.offsetRotation * degRadReflect, - offsetShearY:Float = _data.offsetShearY * degRadReflect; - for (bone in bones) { - if (mixRotate != 0) { - var a:Float = bone.a, - b:Float = bone.b, - c:Float = bone.c, - d:Float = bone.d; - var r:Float = Math.atan2(tc, ta) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) - r += MathUtils.PI2; - r *= mixRotate; - var cos:Float = Math.cos(r), sin:Float = 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; - } - - if (translate) { - var temp:Array = _temp; - temp[0] = _data.offsetX; - temp[1] = _data.offsetY; - target.localToWorld(temp); - bone.worldX += temp[0] * mixX; - bone.worldY += temp[1] * mixY; - } - - if (mixScaleX != 0) { - var s:Float = (Math.sqrt(ta * ta + tc * tc) - 1 + _data.offsetScaleX) * mixScaleX + 1; - bone.a *= s; - bone.c *= s; - } - - if (mixScaleY != 0) { - var s:Float = (Math.sqrt(tb * tb + td * td) - 1 + _data.offsetScaleY) * mixScaleY + 1; - bone.b *= s; - bone.d *= s; - } - - if (mixShearY > 0) { - var r = Math.atan2(td, tb) - Math.atan2(tc, ta); - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) - r += MathUtils.PI2; - var b = bone.b; - var d = bone.d; - r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY; - var s = Math.sqrt(b * b + d * d); - bone.b = Math.cos(r) * s; - bone.d = Math.sin(r) * s; - } - - bone.updateAppliedTransform(); + skeleton._updateCache.push(this); + for (i in 0...boneCount) { + var bone = bones[i].bone; + skeleton.sortReset(bone.children); + skeleton.constrained(bone); } + for (i in 0...boneCount) + bones[i].bone.sorted = worldTarget; } - public function applyAbsoluteLocal():Void { - for (bone in bones) { - var rotation:Float = bone.arotation; - if (mixRotate != 0) rotation += (target.arotation - rotation + _data.offsetRotation) * mixRotate; - - var x:Float = bone.ax, y:Float = bone.ay; - x += (target.ax - x + _data.offsetX) * mixX; - y += (target.ay - y + _data.offsetY) * mixY; - - var scaleX:Float = bone.ascaleX, scaleY:Float = bone.ascaleY; - 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) * mixScaleX) / scaleY; - } - - var shearY:Float = bone.ashearY; - if (mixShearY != 0) shearY += (target.ashearY - shearY + _data.offsetShearY) * mixShearY; - - bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); - } - } - - public function applyRelativeLocal():Void { - for (bone in bones) { - var rotation:Float = bone.arotation + (target.arotation + _data.offsetRotation) * mixRotate; - var x:Float = bone.ax + (target.ax + _data.offsetX) * mixX; - var y:Float = bone.ay + (target.ay + _data.offsetY) * mixY; - var scaleX:Float = bone.ascaleX * (((target.ascaleX - 1 + _data.offsetScaleX) * mixScaleX) + 1); - var scaleY:Float = bone.ascaleY * (((target.ascaleY - 1 + _data.offsetScaleY) * mixScaleY) + 1); - var shearY:Float = bone.ashearY + (target.ashearY + _data.offsetShearY) * mixShearY; - bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); - } - } - - /** The transform constraint's setup pose data. */ - public var data(get, never):TransformConstraintData; - - private function get_data():TransformConstraintData { - return _data; - } - - /** The bones that will be modified by this transform constraint. */ - public var bones(get, never):Array; - - private function get_bones():Array { - return _bones; - } - - public function toString():String { - return _data.name != null ? _data.name : "TransformConstraint?"; + override public function isSourceActive () { + return source.active; } } diff --git a/spine-haxe/spine-haxe/spine/TransformConstraintData.hx b/spine-haxe/spine-haxe/spine/TransformConstraintData.hx index 48d61addf..ee3ce6699 100644 --- a/spine-haxe/spine-haxe/spine/TransformConstraintData.hx +++ b/spine-haxe/spine-haxe/spine/TransformConstraintData.hx @@ -31,27 +31,34 @@ package spine; /** * Stores the setup pose for a spine.TransformConstraint. - * - * + * + * * @see https://esotericsoftware.com/spine-transform-constraints Transform constraints in the Spine User Guide */ -class TransformConstraintData extends ConstraintData { - private var _bones:Array = new Array(); +class TransformConstraintData extends ConstraintData { + /** The bones that will be modified by this transform constraint. */ + public final bones:Array = new Array(); + + /** The bone whose world transform will be copied to the constrained bones. */ + public var source(default, set):BoneData; + + public var offsets:Array = [for (_ in 0...6) 0.0]; + + /** Reads the source bone's local transform instead of its world transform. */ + public var localSource = false; + + /** Sets the constrained bones' local transforms instead of their world transforms. */ + public var localTarget = false; + + /** Adds the source bone transform to the constrained bones instead of setting it absolutely. */ + public var additive = false; + + /** Prevents constrained bones from exceeding the ranged defined by ToProperty.offset and ToProperty.max. */ + public var clamp = false; + + /** The mapping of transform properties to other transform properties. */ + public final properties = new Array(); - /** The target bone whose world transform will be copied to the constrained bones. */ - public var target:BoneData; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */ - public var mixRotate:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */ - public var mixX:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */ - public var mixY:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained scale X. */ - public var mixScaleX:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained scale Y. */ - public var mixScaleY:Float = 0; - /** A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y. */ - public var mixShearY:Float = 0; /** An offset added to the constrained bone rotation. */ public var offsetRotation:Float = 0; /** An offset added to the constrained bone X translation. */ @@ -67,16 +74,286 @@ class TransformConstraintData extends ConstraintData { public var relative:Bool = false; public var local:Bool = false; - public function new(name:String) { - super(name, 0, false); + public function new (name:String) { + super(name, new TransformConstraintPose()); } - /** - * The bones that will be modified by this transform constraint. - */ - public var bones(get, never):Array; + public function create (skeleton:Skeleton) { + return new TransformConstraint(this, skeleton); + } - private function get_bones():Array { - return _bones; + public function set_source (source:BoneData):BoneData { + if (source == null) throw new SpineException("source cannot be null."); + this.source = source; + return source; + } + + /** An offset added to the constrained bone rotation. */ + public function getOffsetRotation ():Float { + return offsets[0]; + } + + public function setOffsetRotation (offsetRotation:Float):Float { + offsets[0] = offsetRotation; + return offsetRotation; + } + + /** An offset added to the constrained bone X translation. */ + public function getOffsetX ():Float { + return offsets[1]; + } + + public function setOffsetX (offsetX:Float):Float { + offsets[1] = offsetX; + return offsetX; + } + + /** An offset added to the constrained bone Y translation. */ + public function getOffsetY ():Float { + return offsets[2]; + } + + public function setOffsetY (offsetY:Float):Float { + offsets[2] = offsetY; + return offsetY; + } + + /** An offset added to the constrained bone scaleX. */ + public function getOffsetScaleX ():Float { + return offsets[3]; + } + + public function setOffsetScaleX (offsetScaleX:Float):Float { + offsets[3] = offsetScaleX; + return offsetScaleX; + } + + /** An offset added to the constrained bone scaleY. */ + public function getOffsetScaleY ():Float { + return offsets[4]; + } + + public function setOffsetScaleY (offsetScaleY:Float):Float { + offsets[4] = offsetScaleY; + return offsetScaleY; + } + + /** An offset added to the constrained bone shearY. */ + public function getOffsetShearY ():Float { + return offsets[5]; + } + + public function setOffsetShearY (offsetShearY:Float):Float { + offsets[5] = offsetShearY; + return offsetShearY; + } + +} + +/** Source property for a {@link TransformConstraint}. */ +abstract class FromProperty { + /** The value of this property that corresponds to ToProperty.offset. */ + public var offset:Float; + + /** Constrained properties. */ + public final to = new Array(); + + /** Reads this property from the specified bone. */ + abstract public function value (source:BonePose, local:Bool, offsets:Array):Float; +} + +/** Constrained property for a TransformConstraint. */ +abstract class ToProperty { + /** The value of this property that corresponds to FromProperty.offset. */ + public var offset:Float; + + /** The maximum value of this property when is clamped (TransformConstraintData.clamp). */ + public var max:Float; + + /** The scale of the FromProperty value in relation to this property. */ + public var scale:Float; + + /** Reads the mix for this property from the specified pose. */ + abstract public function mix (pose:TransformConstraintPose):Float; + + /** Applies the value to this property. */ + abstract public function apply (pose:TransformConstraintPose, bone:BonePose, value:Float, local:Bool, additive:Bool):Void; +} + +class FromRotate extends FromProperty { + public function value (source:BonePose, local:Bool, offsets:Array):Float { + if (local) return source.rotation + offsets[0]; + var value = Math.atan2(source.c, source.a) * MathUtils.radDeg + + (source.a * source.d - source.b * source.c > 0 ? offsets[0] : -offsets[0]); + if (value < 0) value += 360; + return value; + } +} + +class ToRotate extends ToProperty { + public function mix (pose:TransformConstraintPose):Float { + return pose.mixRotate; + } + + public function apply (pose:TransformConstraintPose, bone:BonePose, value:Float, local:Bool, additive:Bool):Void { + if (local) { + if (!additive) value -= bone.rotation; + bone.rotation += value * pose.mixRotate; + } else { + var a = bone.a, b = bone.b, c = bone.c, d = bone.d; + value *= MathUtils.degRad; + if (!additive) value -= Math.atan2(c, a); + if (value > Math.PI) + value -= MathUtils.PI2; + else if (value < -Math.PI) // + value += MathUtils.PI2; + value *= pose.mixRotate; + var cos = Math.cos(value), sin = Math.sin(value); + bone.a = cos * a - sin * c; + bone.b = cos * b - sin * d; + bone.c = sin * a + cos * c; + bone.d = sin * b + cos * d; + } + } +} + +class FromX extends FromProperty { + public function value (source:BonePose, local:Bool, offsets:Array):Float { + return local ? source.x + offsets[1] : offsets[1] * source.a + offsets[2] * source.b + source.worldX; + } +} + +class ToX extends ToProperty { + public function mix (pose:TransformConstraintPose):Float { + return pose.mixX; + } + + public function apply (pose:TransformConstraintPose, bone:BonePose, value:Float, local:Bool, additive:Bool):Void { + if (local) { + if (!additive) value -= bone.x; + bone.x += value * pose.mixX; + } else { + if (!additive) value -= bone.worldX; + bone.worldX += value * pose.mixX; + } + } +} + +class FromY extends FromProperty { + public function value (source:BonePose, local:Bool, offsets:Array):Float { + return local ? source.y + offsets[2] : offsets[1] * source.c + offsets[2] * source.d + source.worldY; + } +} + +class ToY extends ToProperty { + public function mix (pose:TransformConstraintPose):Float { + return pose.mixY; + } + + public function apply (pose:TransformConstraintPose, bone:BonePose, value:Float, local:Bool, additive:Bool):Void { + if (local) { + if (!additive) value -= bone.y; + bone.y += value * pose.mixY; + } else { + if (!additive) value -= bone.worldY; + bone.worldY += value * pose.mixY; + } + } +} + +class FromScaleX extends FromProperty { + public function value (source:BonePose, local:Bool, offsets:Array):Float { + return (local ? source.scaleX : Math.sqrt(source.a * source.a + source.c * source.c)) + offsets[3]; + } +} + +class ToScaleX extends ToProperty { + public function mix (pose:TransformConstraintPose):Float { + return pose.mixScaleX; + } + + public function apply (pose:TransformConstraintPose, bone:BonePose, value:Float, local:Bool, additive:Bool):Void { + if (local) { + if (additive) + bone.scaleX *= 1 + ((value - 1) * pose.mixScaleX); + else if (bone.scaleX != 0) // + bone.scaleX = 1 + (value / bone.scaleX - 1) * pose.mixScaleX; + } else { + var s:Float; + if (additive) + s = 1 + (value - 1) * pose.mixScaleX; + else { + s = Math.sqrt(bone.a * bone.a + bone.c * bone.c); + if (s != 0) s = 1 + (value / s - 1) * pose.mixScaleX; + } + bone.a *= s; + bone.c *= s; + } + } +} + +class FromScaleY extends FromProperty { + public function value (source:BonePose, local:Bool, offsets:Array):Float { + return (local ? source.scaleY : Math.sqrt(source.b * source.b + source.d * source.d)) + offsets[4]; + } +} + +class ToScaleY extends ToProperty { + public function mix (pose:TransformConstraintPose):Float { + return pose.mixScaleY; + } + + public function apply (pose:TransformConstraintPose, bone:BonePose, value:Float, local:Bool, additive:Bool):Void { + if (local) { + if (additive) + bone.scaleY *= 1 + ((value - 1) * pose.mixScaleY); + else if (bone.scaleY != 0) // + bone.scaleY = 1 + (value / bone.scaleY - 1) * pose.mixScaleY; + } else { + var s:Float; + if (additive) + s = 1 + (value - 1) * pose.mixScaleY; + else { + s = Math.sqrt(bone.b * bone.b + bone.d * bone.d); + if (s != 0) s = 1 + (value / s - 1) * pose.mixScaleY; + } + bone.b *= s; + bone.d *= s; + } + } +} + +class FromShearY extends FromProperty { + public function value (source:BonePose, local:Bool, offsets:Array):Float { + return (local ? source.shearY : (Math.atan2(source.d, source.b) - Math.atan2(source.c, source.a)) * MathUtils.radDeg - 90) + offsets[5]; + } +} + +class ToShearY extends ToProperty { + public function mix (pose:TransformConstraintPose):Float { + return pose.mixShearY; + } + + public function apply (pose:TransformConstraintPose, bone:BonePose, value:Float, local:Bool, additive:Bool):Void { + if (local) { + if (!additive) value -= bone.shearY; + bone.shearY += value * pose.mixShearY; + } else { + var b = bone.b, d = bone.d, by = Math.atan2(d, b); + value = (value + 90) * MathUtils.degRad; + if (additive) + value -= Math.PI / 2; + else { + value -= by - Math.atan2(bone.c, bone.a); + if (value > Math.PI) + value -= MathUtils.PI2; + else if (value < -Math.PI) // + value += MathUtils.PI2; + } + value = by + value * pose.mixShearY; + var s = Math.sqrt(b * b + d * d); + bone.b = Math.cos(value) * s; + bone.d = Math.sin(value) * s; + } } } diff --git a/spine-haxe/spine-haxe/spine/TransformConstraintPose.hx b/spine-haxe/spine-haxe/spine/TransformConstraintPose.hx new file mode 100644 index 000000000..ad56a544e --- /dev/null +++ b/spine-haxe/spine-haxe/spine/TransformConstraintPose.hx @@ -0,0 +1,65 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +/** Stores a pose for a transform constraint. */ +class TransformConstraintPose implements Pose { + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */ + public var mixRotate = 0.; + /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */ + + public var mixX = 0.; + /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */ + + public var mixY = 0.; + /** A percentage (0-1) that controls the mix between the constrained and unconstrained scale X. */ + + public var mixScaleX = 0.; + /** A percentage (0-1) that controls the mix between the constrained and unconstrained scale Y. */ + + public var mixScaleY = 0.; + /** A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y. */ + + public var mixShearY = 0.; + + public function new () { + } + + public function set (pose:TransformConstraintPose) { + mixRotate = pose.mixRotate; + mixX = pose.mixX; + mixY = pose.mixY; + mixScaleX = pose.mixScaleX; + mixScaleY = pose.mixScaleY; + mixShearY = pose.mixShearY; + } + +} \ No newline at end of file diff --git a/spine-haxe/spine-haxe/spine/Updatable.hx b/spine-haxe/spine-haxe/spine/Update.hx similarity index 81% rename from spine-haxe/spine-haxe/spine/Updatable.hx rename to spine-haxe/spine-haxe/spine/Update.hx index c33a7ea0e..adf2a3c38 100644 --- a/spine-haxe/spine-haxe/spine/Updatable.hx +++ b/spine-haxe/spine-haxe/spine/Update.hx @@ -30,16 +30,7 @@ package spine; /** The interface for items updated by spine.Skeleton.updateWorldTransform(). */ -interface Updatable { +interface Update { /** @param physics Determines how physics and other non-deterministic updates are applied. */ - function update(physics:Physics):Void; - - /** Returns false when this item won't be updated by - * spine.Skeleton.updateWorldTransform() because a skin is required and the - * active skin does not contain this item. - * @see spine.Skin.getBones() - * @see spine.Skin.getConstraints() - * @see spine.BoneData.getSkinRequired() - * @see spine.ConstraintData.getSkinRequired() */ - function isActive():Bool; + function update(skeleton:Skeleton, physics:Physics):Void; } diff --git a/spine-haxe/spine-haxe/spine/animation/AlphaTimeline.hx b/spine-haxe/spine-haxe/spine/animation/AlphaTimeline.hx index 699a487fc..31593ee48 100644 --- a/spine-haxe/spine-haxe/spine/animation/AlphaTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/AlphaTimeline.hx @@ -43,7 +43,7 @@ class AlphaTimeline extends CurveTimeline1 implements SlotTimeline { private var slotIndex:Int = 0; public function new(frameCount:Int, bezierCount:Int, slotIndex:Int) { - super(frameCount, bezierCount, [Property.alpha + "|" + slotIndex]); + super(frameCount, bezierCount, Property.alpha + "|" + slotIndex); this.slotIndex = slotIndex; } @@ -55,15 +55,15 @@ class AlphaTimeline extends CurveTimeline1 implements SlotTimeline { return slotIndex; } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var slot:Slot = skeleton.slots[slotIndex]; - if (!slot.bone.active) - return; + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { - var color:Color = slot.color; + var slot = skeleton.slots[slotIndex]; + if (!slot.bone.active) return; + + var color = (appliedPose ? slot.applied : slot.pose).color; if (time < frames[0]) { - var setup:Color = slot.data.color; + var setup:Color = slot.data.setup.color; switch (blend) { case MixBlend.setup: color.a = setup.a; @@ -78,7 +78,7 @@ class AlphaTimeline extends CurveTimeline1 implements SlotTimeline { color.a = a; } else { if (blend == MixBlend.setup) - color.a = slot.data.color.a; + color.a = slot.data.setup.color.a; color.a += (a - color.a) * alpha; } } diff --git a/spine-haxe/spine-haxe/spine/animation/Animation.hx b/spine-haxe/spine-haxe/spine/animation/Animation.hx index 0d43f9a5d..55645e04b 100644 --- a/spine-haxe/spine-haxe/spine/animation/Animation.hx +++ b/spine-haxe/spine-haxe/spine/animation/Animation.hx @@ -29,37 +29,52 @@ package spine.animation; +import haxe.ds.IntMap; import haxe.ds.StringMap; import spine.Event; import spine.Skeleton; /** Stores a list of timelines to animate a skeleton's pose over time. */ class Animation { - private var _name:String; - private var _timelines:Array; - private var _timelineIds:StringMap = new StringMap(); + + /** The animation's name, which is unique across all animations in the skeleton. */ + public final name:String; /** The duration of the animation in seconds, which is usually the highest time of all frames in the timeline. The duration is - * used to know when it has completed and when it should loop back to the start. */ + * used to know when it has completed and when it should loop back to the start. */ public var duration:Float = 0; + public var timelines:Array; + public final timelineIds:StringMap = new StringMap(); + public final bones:Array; + public function new(name:String, timelines:Array, duration:Float) { - if (name == null) - throw new SpineException("name cannot be null."); - _name = name; - setTimelines(timelines); + if (name == null) throw new SpineException("name cannot be null."); + this.name = name; this.duration = duration; + var n = timelines.length << 1; + timelineIds = new StringMap(); + bones = new Array(); + setTimelines(timelines); } public function setTimelines(timelines:Array) { - if (timelines == null) - throw new SpineException("timelines cannot be null."); - _timelines = timelines; - _timelineIds = new StringMap(); + if (timelines == null) throw new SpineException("timelines cannot be null."); + this.timelines = timelines; + + timelineIds.clear(); + bones.resize(0); + var boneSet = new IntMap(); for (timeline in timelines) { var ids:Array = timeline.propertyIds; - for (id in ids) { - _timelineIds.set(id, true); + for (id in ids) timelineIds.set(id, true); + if (Std.isOfType(timeline, BoneTimeline)) { + var boneTimeline = cast(timeline, BoneTimeline); + var boneIndex = boneTimeline.getBoneIndex(); + if (!boneSet.exists(boneIndex)) { + boneSet.set(boneIndex, true); + bones.push(boneIndex); + } } } } @@ -67,14 +82,14 @@ class Animation { /** Returns true if this animation contains a timeline with any of the specified property IDs. */ public function hasTimeline(ids:Array):Bool { for (id in ids) { - if (_timelineIds.exists(id)) + if (timelineIds.exists(id)) return true; } return false; } /** Applies the animation's timelines to the specified skeleton. - * + * * See Timeline.apply(). * @param skeleton The skeleton the animation is being applied to. This provides access to the bones, slots, and other skeleton * components the timelines may change. @@ -94,36 +109,16 @@ class Animation { * @param direction Indicates whether the timelines are mixing in or out. Used by timelines which perform instant transitions, * such as DrawOrderTimeline or AttachmentTimeline. */ public function apply(skeleton:Skeleton, lastTime:Float, time:Float, loop:Bool, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - if (skeleton == null) - throw new SpineException("skeleton cannot be null."); + direction:MixDirection, appliedPose:Bool):Void { + if (skeleton == null) throw new SpineException("skeleton cannot be null."); if (loop && duration != 0) { time %= duration; - if (lastTime > 0) - lastTime %= duration; + if (lastTime > 0) lastTime %= duration; } for (timeline in timelines) { - timeline.apply(skeleton, lastTime, time, events, alpha, blend, direction); + timeline.apply(skeleton, lastTime, time, events, alpha, blend, direction, appliedPose); } } - - /** The animation's name, which is unique across all animations in the skeleton. */ - public var name(get, never):String; - - private function get_name():String { - return _name; - } - - public function toString():String { - return _name; - } - - /** If the returned array or the timelines it contains are modified, setTimelines() must be called. */ - public var timelines(get, never):Array; - - private function get_timelines():Array { - return _timelines; - } } diff --git a/spine-haxe/spine-haxe/spine/animation/AnimationState.hx b/spine-haxe/spine-haxe/spine/animation/AnimationState.hx index b37757450..b25e96f70 100644 --- a/spine-haxe/spine-haxe/spine/animation/AnimationState.hx +++ b/spine-haxe/spine-haxe/spine/animation/AnimationState.hx @@ -298,7 +298,7 @@ class AnimationState { for (slot in skeleton.slots) { if (slot.attachmentState == setupState) { var attachmentName:String = slot.data.attachmentName; - slot.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slot.data.index, attachmentName); + slot.pose.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slot.data.index, attachmentName); } } unkeyedState += 2; // Increasing after each use avoids the need to reset attachmentState for every slot. @@ -437,28 +437,29 @@ class AnimationState { timelinesRotation[i] = 0; if (alpha == 1) { - timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn); + timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn, false); return; } var bone = skeleton.bones[timeline.boneIndex]; if (!bone.active) return; + var pose = bone.pose, setup = bone.data.setup; var frames = timeline.frames; var r1:Float = 0, r2:Float = 0; if (time < frames[0]) { switch (blend) { case MixBlend.setup: - bone.rotation = bone.data.rotation; + pose.rotation = setup.rotation; default: return; case MixBlend.first: - r1 = bone.rotation; - r2 = bone.data.rotation; + r1 = pose.rotation; + r2 = setup.rotation; } } else { - r1 = blend == MixBlend.setup ? bone.data.rotation : bone.rotation; - r2 = bone.data.rotation + timeline.getCurveValue(time); + r1 = blend == MixBlend.setup ? setup.rotation : pose.rotation; + r2 = setup.rotation + timeline.getCurveValue(time); } // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. @@ -492,11 +493,11 @@ class AnimationState { timelinesRotation[i] = total; } timelinesRotation[i + 1] = diff; - bone.rotation = r1 + total * alpha; + pose.rotation = r1 + total * alpha; } private function setAttachment(skeleton:Skeleton, slot:Slot, attachmentName:String, attachments:Bool):Void { - slot.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slot.data.index, attachmentName); + slot.pose.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slot.data.index, attachmentName); if (attachments) slot.attachmentState = unkeyedState + CURRENT; } @@ -629,9 +630,9 @@ class AnimationState { return setAnimation(trackIndex, animation, loop); } - /** - * Sets the current animation for a track, discarding any queued animations. If the formerly current track entry was never - * applied to a skeleton, it is replaced (not mixed from). + /** Sets the current animation for a track, discarding any queued animations. + * If the formerly current track entry is for the same animation and was never applied to a skeleton, it is replaced (not mixed + * from). * @param loop If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its * duration. In either case spine.animation.TrackEntry.getTrackEnd() determines when the track is cleared. * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept @@ -643,7 +644,7 @@ class AnimationState { var interrupt:Bool = true; var current:TrackEntry = expandToIndex(trackIndex); if (current != null) { - if (current.nextTrackLast == -1) { + if (current.nextTrackLast == -1 && current.animation == animation) { // Don't mix from an entry that was never applied. tracks[trackIndex] = current.mixingFrom; queue.interrupt(current); @@ -673,8 +674,7 @@ class AnimationState { return addAnimation(trackIndex, animation, loop, delay); } - /** - * Adds an animation to be played after the current or last queued animation for a track. If the track is empty, it is + /** Adds an animation to be played after the current or last queued animation for a track. If the track has no entries, this is * equivalent to calling spine.animation.AnimationState.setAnimation(). * @param delay If > 0, sets spine.animation.TrackEntry.getDelay(). If <= 0, the delay set is the duration of the previous track entry * minus any mix duration (from the spine.animation.AnimationStateData) plus the specified delay (ie the mix @@ -726,7 +726,9 @@ class AnimationState { * animation to be applied more and more over the mix duration. Properties keyed in the new animation transition from the value * from lower tracks or from the setup pose value if no lower tracks key the property to the value keyed in the new * animation. - */ + * + * See Empty animations in the Spine + * Runtimes Guide. */ public function setEmptyAnimation(trackIndex:Int, mixDuration:Float):TrackEntry { var entry:TrackEntry = setAnimation(trackIndex, emptyAnimation, false); entry.mixDuration = mixDuration; @@ -736,10 +738,12 @@ class AnimationState { /** * Adds an empty animation to be played after the current or last queued animation for a track, and sets the track entry's - * spine.animation.TrackEntry.getMixDuration(). If the track is empty, it is equivalent to calling + * spine.animation.TrackEntry.getMixDuration(). If the track has no entries,, it is equivalent to calling * spine.animation.AnimationState.setEmptyAnimation(). * - * See spine.animation.AnimationState.setEmptyAnimation(). + * See spine.animation.AnimationState.setEmptyAnimation() and + * Empty animations in the Spine + * Runtimes Guide. * @param delay If > 0, sets spine.animation.TrackEntry.getDelay(). If <= 0, the delay set is the duration of the previous track entry * minus any mix duration plus the specified delay (ie the mix ends at (delay = 0) or * before (delay < 0) the previous track entry duration). If the previous entry is looping, its next @@ -755,10 +759,10 @@ class AnimationState { return entry; } - /** - * Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix - * duration. - */ + /** Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix duration. + * + * See Empty animations in the Spine + * Runtimes Guide. */ public function setEmptyAnimations(mixDuration:Float):Void { var oldDrainDisabled:Bool = queue.drainDisabled; queue.drainDisabled = true; diff --git a/spine-haxe/spine-haxe/spine/animation/AttachmentTimeline.hx b/spine-haxe/spine-haxe/spine/animation/AttachmentTimeline.hx index dd5642f07..3e726a3d0 100644 --- a/spine-haxe/spine-haxe/spine/animation/AttachmentTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/AttachmentTimeline.hx @@ -41,7 +41,7 @@ class AttachmentTimeline extends Timeline implements SlotTimeline { public var attachmentNames:Array; public function new(frameCount:Int, slotIndex:Int) { - super(frameCount, [Property.attachment + "|" + slotIndex]); + super(frameCount, Property.attachment + "|" + slotIndex); this.slotIndex = slotIndex; attachmentNames = new Array(); attachmentNames.resize(frameCount); @@ -63,30 +63,22 @@ class AttachmentTimeline extends Timeline implements SlotTimeline { attachmentNames[frame] = attachmentName; } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var slot:Slot = skeleton.slots[slotIndex]; - if (!slot.bone.active) - return; + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + + var slot = skeleton.slots[slotIndex]; + if (!slot.bone.active) return; + var pose = appliedPose ? slot.applied : slot.pose; if (direction == MixDirection.mixOut) { - if (blend == MixBlend.setup) { - setAttachment(skeleton, slot, slot.data.attachmentName); - } - return; - } - - if (time < frames[0]) { - if (blend == MixBlend.setup || blend == MixBlend.first) { - setAttachment(skeleton, slot, slot.data.attachmentName); - } - return; - } - - setAttachment(skeleton, slot, attachmentNames[Timeline.search1(frames, time)]); + if (blend == MixBlend.setup) setAttachment(skeleton, pose, slot.data.attachmentName); + } else if (time < frames[0]) { + if (blend == MixBlend.setup || blend == MixBlend.first) setAttachment(skeleton, pose, slot.data.attachmentName); + } else + setAttachment(skeleton, pose, attachmentNames[Timeline.search1(frames, time)]); } - private function setAttachment(skeleton:Skeleton, slot:Slot, attachmentName:String):Void { - slot.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slotIndex, attachmentName); + private function setAttachment(skeleton:Skeleton, pose:SlotPose, attachmentName:String):Void { + pose.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slotIndex, attachmentName); } } diff --git a/spine-haxe/spine-haxe/spine/animation/BoneTimeline.hx b/spine-haxe/spine-haxe/spine/animation/BoneTimeline.hx index 7c6380012..4fe099978 100644 --- a/spine-haxe/spine-haxe/spine/animation/BoneTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/BoneTimeline.hx @@ -32,5 +32,5 @@ package spine.animation; /** An interface for timelines which change the property of a bone. */ interface BoneTimeline { /** The index of the bone in spine.Skeleton.getBones() that will be changed when this timeline is applied. */ - function getBoneIndex():Int; + public function getBoneIndex():Int; } diff --git a/spine-haxe/spine-haxe/spine/animation/BoneTimeline1.hx b/spine-haxe/spine-haxe/spine/animation/BoneTimeline1.hx new file mode 100644 index 000000000..fdb52bf33 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/animation/BoneTimeline1.hx @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + + abstract class BoneTimeline1 extends CurveTimeline1 implements BoneTimeline { + public final boneIndex:Int; + + public function new(frameCount:Int, bezierCount:Int, boneIndex:Int, property:Property) { + super(frameCount, bezierCount, property + "|" + boneIndex); + this.boneIndex = boneIndex; + } + + public function getBoneIndex () { + return boneIndex; + } + + public function apply (skeleton: Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, + direction:MixDirection, appliedPose:Bool) { + + var bone = skeleton.bones[boneIndex]; + if (bone.active) apply1(appliedPose ? bone.applied : bone.pose, bone.data.setup, time, alpha, blend, direction); + } + + abstract function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection):Void; +} diff --git a/spine-haxe/spine-haxe/spine/animation/CurveTimeline2.hx b/spine-haxe/spine-haxe/spine/animation/BoneTimeline2.hx similarity index 67% rename from spine-haxe/spine-haxe/spine/animation/CurveTimeline2.hx rename to spine-haxe/spine-haxe/spine/animation/BoneTimeline2.hx index fafbdcce1..62450d0ef 100644 --- a/spine-haxe/spine-haxe/spine/animation/CurveTimeline2.hx +++ b/spine-haxe/spine-haxe/spine/animation/BoneTimeline2.hx @@ -29,17 +29,18 @@ package spine.animation; -/** The base class for a spine.animation.CurveTimeline which sets two properties. */ -class CurveTimeline2 extends CurveTimeline { +/** The base class for a spine.animation.CurveTimeline that is a spine.animation.BoneTimeline and sets two properties. */ +abstract class BoneTimeline2 extends CurveTimeline implements BoneTimeline { private static inline var ENTRIES:Int = 3; private static inline var VALUE1:Int = 1; private static inline var VALUE2:Int = 2; - /** @param frameCount The number of frames in the timeline. - * @param bezierCount The maximum number of Bezier curves. See spine.animation.CurveTimeline.shrink(). - * @param propertyIds Array of unique identifiers for the properties the timeline modifies. */ - public function new(frameCount:Int, bezierCount:Int, propertyIds:Array) { - super(frameCount, bezierCount, propertyIds); + public final boneIndex:Int; + + /** @param bezierCount The maximum number of Bezier curves. See spine.animation.CurveTimeline.shrink(). */ + public function new(frameCount:Int, bezierCount:Int, boneIndex:Int, property1:Property, property2:Property) { + super(frameCount, bezierCount, property1 + "|" + boneIndex, property2 + "|" + boneIndex); + this.boneIndex = boneIndex; } public override function getFrameEntries():Int { @@ -55,4 +56,17 @@ class CurveTimeline2 extends CurveTimeline { frames[frame + VALUE1] = value1; frames[frame + VALUE2] = value2; } + + public function getBoneIndex () { + return boneIndex; + } + + public function apply (skeleton: Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, + direction:MixDirection, appliedPose:Bool) { + + var bone = skeleton.bones[boneIndex]; + if (bone.active) apply1(appliedPose ? bone.applied : bone.pose, bone.data.setup, time, alpha, blend, direction); + } + + abstract function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection):Void; } diff --git a/spine-haxe/spine-haxe/spine/animation/ConstraintTimeline.hx b/spine-haxe/spine-haxe/spine/animation/ConstraintTimeline.hx new file mode 100644 index 000000000..4b5882c89 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/animation/ConstraintTimeline.hx @@ -0,0 +1,35 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +interface ConstraintTimeline { + /** The index of the constraint in spine.Skeleton.constraints that will be changed when this timeline is applied. */ + public function getConstraintIndex():Int; +} diff --git a/spine-haxe/spine-haxe/spine/animation/ConstraintTimeline1.hx b/spine-haxe/spine-haxe/spine/animation/ConstraintTimeline1.hx new file mode 100644 index 000000000..bebb46cd7 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/animation/ConstraintTimeline1.hx @@ -0,0 +1,43 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +abstract class ConstraintTimeline1 extends CurveTimeline1 implements ConstraintTimeline { + public final constraintIndex:Int; + + public function new (frameCount:Int, bezierCount:Int, constraintIndex:Int, property:Property) { + super(frameCount, bezierCount, property + "|" + constraintIndex); + this.constraintIndex = constraintIndex; + } + + public function getConstraintIndex () { + return constraintIndex; + } +} diff --git a/spine-haxe/spine-haxe/spine/animation/CurveTimeline.hx b/spine-haxe/spine-haxe/spine/animation/CurveTimeline.hx index 54885d104..88631937d 100644 --- a/spine-haxe/spine-haxe/spine/animation/CurveTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/CurveTimeline.hx @@ -30,7 +30,7 @@ package spine.animation; /** The base class for timelines that interpolate between frame values using stepped, linear, or a Bezier curve. */ -class CurveTimeline extends Timeline { +abstract class CurveTimeline extends Timeline { private static inline var LINEAR:Int = 0; private static inline var STEPPED:Int = 1; private static inline var BEZIER:Int = 2; @@ -40,8 +40,8 @@ class CurveTimeline extends Timeline { /** @param bezierCount The maximum number of Bezier curves. See CurveTimeline.shrink(). * @param propertyIds Unique identifiers for the properties the timeline modifies. */ - public function new(frameCount:Int, bezierCount:Int, propertyIds:Array) { - super(frameCount, propertyIds); + public function new(frameCount:Int, bezierCount:Int, propertyIds:...String) { + super(frameCount, ...propertyIds); curves = new Array(); curves.resize(frameCount + bezierCount * BEZIER_SIZE); curves[frameCount - 1] = STEPPED; diff --git a/spine-haxe/spine-haxe/spine/animation/CurveTimeline1.hx b/spine-haxe/spine-haxe/spine/animation/CurveTimeline1.hx index a79a1c4dc..deea375f0 100644 --- a/spine-haxe/spine-haxe/spine/animation/CurveTimeline1.hx +++ b/spine-haxe/spine-haxe/spine/animation/CurveTimeline1.hx @@ -30,15 +30,15 @@ package spine.animation; /** The base class for a spine.animation.CurveTimeline that sets one property. */ -class CurveTimeline1 extends CurveTimeline { +abstract class CurveTimeline1 extends CurveTimeline { private static inline var ENTRIES:Int = 2; private static inline var VALUE:Int = 1; /** @param frameCount The number of frames in the timeline. * @param bezierCount The maximum number of Bezier curves. See spine.animation.CurveTimeline.shrink(). * @param propertyIds Unique identifiers for the properties the timeline modifies. */ - public function new(frameCount:Int, bezierCount:Int, propertyIds:Array) { - super(frameCount, bezierCount, propertyIds); + public function new(frameCount:Int, bezierCount:Int, propertyId:String) { + super(frameCount, bezierCount, propertyId); } public override function getFrameEntries():Int { @@ -73,75 +73,69 @@ class CurveTimeline1 extends CurveTimeline { return value + (time - before) / (frames[i + ENTRIES] - before) * (frames[i + ENTRIES + VALUE] - value); case CurveTimeline.STEPPED: return frames[i + VALUE]; + default: + return getBezierValue(time, i, VALUE, curveType - CurveTimeline.BEZIER); } - return getBezierValue(time, i, VALUE, curveType - CurveTimeline.BEZIER); } public function getRelativeValue (time:Float, alpha:Float, blend: MixBlend, current:Float, setup:Float):Float { if (time < frames[0]) { switch (blend) { - case MixBlend.setup: - return setup; - case MixBlend.first: - return current + (setup - current) * alpha; + case MixBlend.setup: return setup; + case MixBlend.first: return current + (setup - current) * alpha; + default: return current; } - return current; } var value:Float = getCurveValue(time); switch (blend) { - case MixBlend.setup: - return setup + value * alpha; - case MixBlend.first, MixBlend.replace: - value += setup - current; + case MixBlend.setup: return setup + value * alpha; + case MixBlend.first, MixBlend.replace: return current + (value + setup - current) * alpha; + default: return current + value * alpha; // MixBlend.add } - return current + value * alpha; } public function getAbsoluteValue (time:Float, alpha:Float, blend: MixBlend, current:Float, setup:Float):Float { if (time < frames[0]) { switch (blend) { - case MixBlend.setup: - return setup; - case MixBlend.first: - return current + (setup - current) * alpha; + case MixBlend.setup: return setup; + case MixBlend.first: return current + (setup - current) * alpha; + default: return current; } - return current; } var value:Float = getCurveValue(time); - if (blend == MixBlend.setup) return setup + (value - setup) * alpha; - return current + (value - current) * alpha; + switch (blend) { + case MixBlend.setup: return setup + (value - setup) * alpha; + case MixBlend.first, MixBlend.replace: return current + (value - current) * alpha; + default: return current + value * alpha; // MixBlend.add + } } public function getAbsoluteValue2 (time:Float, alpha:Float, blend: MixBlend, current:Float, setup:Float, value:Float):Float { if (time < frames[0]) { switch (blend) { - case MixBlend.setup: - return setup; - case MixBlend.first: - return current + (setup - current) * alpha; + case MixBlend.setup: return setup; + case MixBlend.first: return current + (setup - current) * alpha; + default: current; } - return current; } - if (blend == MixBlend.setup) return setup + (value - setup) * alpha; - return current + (value - current) * alpha; + switch (blend) { + case MixBlend.setup: return setup + (value - setup) * alpha; + case MixBlend.first, MixBlend.replace: return current + (value - current) * alpha; + default: return current + value * alpha; // MixBlend.add + } } public function getScaleValue (time:Float, alpha:Float, blend: MixBlend, direction: MixDirection, current:Float, setup:Float):Float { var frames:Array = frames; if (time < frames[0]) { switch (blend) { - case MixBlend.setup: - return setup; - case MixBlend.first: - return current + (setup - current) * alpha; + case MixBlend.setup: return setup; + case MixBlend.first: return current + (setup - current) * alpha; + default: return current; } - return current; } var value:Float = getCurveValue(time) * setup; - if (alpha == 1) { - if (blend == MixBlend.add) return current + value - setup; - return value; - } + if (alpha == 1) return blend == MixBlend.add ? current + value - setup : value; // Mixing out uses sign of setup or current pose, else use sign of key. if (direction == MixDirection.mixOut) { switch (blend) { diff --git a/spine-haxe/spine-haxe/spine/animation/DeformTimeline.hx b/spine-haxe/spine-haxe/spine/animation/DeformTimeline.hx index 0f2706bba..f669307b1 100644 --- a/spine-haxe/spine-haxe/spine/animation/DeformTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/DeformTimeline.hx @@ -37,19 +37,17 @@ import spine.Skeleton; import spine.Slot; /** Changes a slot's spine.Slot.deform to deform a spine.attachments.VertexAttachment. */ -class DeformTimeline extends CurveTimeline implements SlotTimeline { - public var slotIndex:Int = 0; - +class DeformTimeline extends SlotCurveTimeline { /** The attachment that will be deformed. - * + * * @see spine.attachments.VertexAttachment.getTimelineAttachment() */ - public var attachment:VertexAttachment; + public final attachment:VertexAttachment; /** The vertices for each frame. */ - public var vertices:Array>; + public final vertices:Array>; public function new(frameCount:Int, bezierCount:Int, slotIndex:Int, attachment:VertexAttachment) { - super(frameCount, bezierCount, [Property.deform + "|" + slotIndex + "|" + attachment.id]); + super(frameCount, bezierCount, slotIndex, Property.deform + "|" + slotIndex + "|" + attachment.id); this.slotIndex = slotIndex; this.attachment = attachment; vertices = new Array>(); @@ -60,10 +58,6 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { return frames.length; } - public function getSlotIndex():Int { - return slotIndex; - } - /** Sets the time and vertices for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. @@ -145,110 +139,79 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { return y + (1 - y) * (time - x) / (frames[frame + getFrameEntries()] - x); } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var slot:Slot = skeleton.slots[slotIndex]; - if (!slot.bone.active) - return; - var slotAttachment:Attachment = slot.attachment; - if (slotAttachment == null) - return; - if (!Std.isOfType(slotAttachment, VertexAttachment) || cast(slotAttachment, VertexAttachment).timelineAttachment != attachment) - return; + public function apply1 (slot:Slot, pose:SlotPose, time:Float, alpha:Float, blend:MixBlend) { + if (!Std.isOfType(pose.attachment, VertexAttachment)) return; + var vertexAttachment = cast(pose.attachment, VertexAttachment); + if (vertexAttachment.timelineAttachment != attachment) return; - var deform:Array = slot.deform; - if (deform.length == 0) - blend = MixBlend.setup; + var deform = pose.deform; + if (deform.length == 0) blend = MixBlend.setup; - var vertexCount:Int = vertices[0].length; - var i:Int, setupVertices:Array; + var vertexCount = vertices[0].length; if (time < frames[0]) { switch (blend) { - case MixBlend.setup: - deform.resize(0); + case MixBlend.setup: deform.resize(0); case MixBlend.first: if (alpha == 1) { deform.resize(0); return; } ArrayUtils.resize(deform, vertexCount, 0); - var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); - if (vertexAttachment.bones == null) { - // Unweighted vertex positions. - setupVertices = vertexAttachment.vertices; - for (i in 0...vertexCount) { + if (vertexAttachment.bones == null) { // Unweighted vertex positions. + var setupVertices = vertexAttachment.vertices; + for (i in 0...vertexCount) deform[i] += (setupVertices[i] - deform[i]) * alpha; - } - } else { - // Weighted deform offsets. + } else { // Weighted deform offsets. alpha = 1 - alpha; - for (i in 0...vertexCount) { + for (i in 0...vertexCount) deform[i] *= alpha; - } } } return; } ArrayUtils.resize(deform, vertexCount, 0); - var setup:Float; - if (time >= frames[frames.length - 1]) { + + if (time >= frames[frames.length - 1]) { // Time is after last frame. var lastVertices:Array = vertices[frames.length - 1]; if (alpha == 1) { if (blend == MixBlend.add) { - var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); - if (vertexAttachment.bones == null) { - // Unweighted vertex positions, with alpha. - setupVertices = vertexAttachment.vertices; - for (i in 0...vertexCount) { + if (vertexAttachment.bones == null) { // Unweighted vertex positions, no alpha. + var setupVertices = vertexAttachment.vertices; + for (i in 0...vertexCount) deform[i] += lastVertices[i] - setupVertices[i]; - } - } else { - // Weighted deform offsets, with alpha. - for (i in 0...vertexCount) { + } else { // Weighted deform offsets, with alpha. + for (i in 0...vertexCount) deform[i] += lastVertices[i]; - } } - } else { - for (i in 0...vertexCount) { + } else + for (i in 0...vertexCount) deform[i] = lastVertices[i]; - } - } } else { switch (blend) { case MixBlend.setup: - var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); - if (vertexAttachment.bones == null) { - // Unweighted vertex positions, with alpha. - setupVertices = vertexAttachment.vertices; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. + var setupVertices = vertexAttachment.vertices; for (i in 0...vertexCount) { - setup = setupVertices[i]; + var setup = setupVertices[i]; deform[i] = setup + (lastVertices[i] - setup) * alpha; } - } else { - // Weighted deform offsets, with alpha. - for (i in 0...vertexCount) { + } else { // Weighted deform offsets, with alpha. + for (i in 0...vertexCount) deform[i] = lastVertices[i] * alpha; - } } - case MixBlend.first, MixBlend.replace: - for (i in 0...vertexCount) { + case MixBlend.first, MixBlend.replace: // Vertex positions or deform offsets, with alpha. + for (i in 0...vertexCount) deform[i] += (lastVertices[i] - deform[i]) * alpha; - } case MixBlend.add: - var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); - if (vertexAttachment.bones == null) { - // Unweighted vertex positions, with alpha. - setupVertices = vertexAttachment.vertices; - for (i in 0...vertexCount) { + if (vertexAttachment.bones == null) { // Unweighted vertex positions, no alpha. + var setupVertices = vertexAttachment.vertices; + for (i in 0...vertexCount) deform[i] += (lastVertices[i] - setupVertices[i]) * alpha; - } - } else { - // Weighted deform offsets, with alpha. - for (i in 0...vertexCount) { + } else { // Weighted deform offsets, alpha. + for (i in 0...vertexCount) deform[i] += lastVertices[i] * alpha; - } } } } @@ -258,69 +221,63 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { // Interpolate between the previous frame and the current frame. var frame:Int = Timeline.search1(frames, time); var percent:Float = getCurvePercent(time, frame); - var prevVertices:Array = vertices[frame], prev:Float; + var prevVertices:Array = vertices[frame]; var nextVertices:Array = vertices[frame + 1]; if (alpha == 1) { if (blend == MixBlend.add) { - var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); - if (vertexAttachment.bones == null) { - // Unweighted vertex positions, with alpha. - setupVertices = vertexAttachment.vertices; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, no alpha. + var setupVertices = vertexAttachment.vertices; for (i in 0...vertexCount) { - prev = prevVertices[i]; + var prev = prevVertices[i]; deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i]; } - } else { - // Weighted deform offsets, with alpha. + } else { // Weighted deform offsets, no alpha. for (i in 0...vertexCount) { - prev = prevVertices[i]; + var prev = prevVertices[i]; deform[i] += prev + (nextVertices[i] - prev) * percent; } } + } else if (percent == 0) { + for (i in 0...vertexCount) + deform[i] = prevVertices[i]; } else { for (i in 0...vertexCount) { - prev = prevVertices[i]; + var prev = prevVertices[i]; deform[i] = prev + (nextVertices[i] - prev) * percent; } } } else { switch (blend) { case MixBlend.setup: - var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); - if (vertexAttachment.bones == null) { - // Unweighted vertex positions, with alpha. - setupVertices = vertexAttachment.vertices; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. + var setupVertices = vertexAttachment.vertices; for (i in 0...vertexCount) { - prev = prevVertices[i]; - setup = setupVertices[i]; + var prev = prevVertices[i], setup = setupVertices[i]; deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha; } - } else { - // Weighted deform offsets, with alpha. + } else { // Weighted deform offsets, with alpha. for (i in 0...vertexCount) { - prev = prevVertices[i]; + var prev = prevVertices[i]; deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha; } } - case MixBlend.first, MixBlend.replace: + case MixBlend.first, MixBlend.replace: // Vertex positions or deform offsets, with alpha. for (i in 0...vertexCount) { - prev = prevVertices[i]; + var prev = prevVertices[i]; deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha; } case MixBlend.add: - var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); - if (vertexAttachment.bones == null) { + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. // Unweighted vertex positions, with alpha. - setupVertices = vertexAttachment.vertices; + var setupVertices = vertexAttachment.vertices; for (i in 0...vertexCount) { - prev = prevVertices[i]; + var prev = prevVertices[i]; deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha; } - } else { - // Weighted deform offsets, with alpha. + } else { // Weighted deform offsets, with alpha. for (i in 0...vertexCount) { - prev = prevVertices[i]; + var prev = prevVertices[i]; deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha; } } diff --git a/spine-haxe/spine-haxe/spine/animation/DrawOrderTimeline.hx b/spine-haxe/spine-haxe/spine/animation/DrawOrderTimeline.hx index c312fb1ea..69e1ef84a 100644 --- a/spine-haxe/spine-haxe/spine/animation/DrawOrderTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/DrawOrderTimeline.hx @@ -39,7 +39,7 @@ class DrawOrderTimeline extends Timeline { public var drawOrders:Array>; public function new(frameCount:Int) { - super(frameCount, [Std.string(Property.drawOrder)]); + super(frameCount, Property.drawOrder); drawOrders = new Array>(); drawOrders.resize(frameCount); } @@ -59,8 +59,9 @@ class DrawOrderTimeline extends Timeline { drawOrders[frame] = drawOrder; } - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + var drawOrder:Array = skeleton.drawOrder; var slots:Array = skeleton.slots; var i:Int = 0, n:Int = slots.length; diff --git a/spine-haxe/spine-haxe/spine/animation/EventQueue.hx b/spine-haxe/spine-haxe/spine/animation/EventQueue.hx index 3c48e644a..e6912baf7 100644 --- a/spine-haxe/spine-haxe/spine/animation/EventQueue.hx +++ b/spine-haxe/spine-haxe/spine/animation/EventQueue.hx @@ -79,8 +79,7 @@ class EventQueue { } public function drain():Void { - if (drainDisabled) - return; // Not reentrant. + if (drainDisabled) return; // Not reentrant. drainDisabled = true; var i:Int = 0; diff --git a/spine-haxe/spine-haxe/spine/animation/EventTimeline.hx b/spine-haxe/spine-haxe/spine/animation/EventTimeline.hx index 7fe8f1a75..0011a681e 100644 --- a/spine-haxe/spine-haxe/spine/animation/EventTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/EventTimeline.hx @@ -39,7 +39,7 @@ class EventTimeline extends Timeline { public var events:Array; public function new(frameCount:Int) { - super(frameCount, [Std.string(Property.event)]); + super(frameCount, Property.event); events = new Array(); events.resize(frameCount); } @@ -56,22 +56,17 @@ class EventTimeline extends Timeline { } /** Fires events for frames > lastTime and <= time. */ - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - if (events == null) - return; + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, firedEvents:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + if (firedEvents == null) return; var frameCount:Int = frames.length; - if (lastTime > time) // Apply events after lastTime for looped animations. - { - apply(skeleton, lastTime, 2147483647, events, alpha, blend, direction); + if (lastTime > time) { // Apply events after lastTime for looped animations. + apply(skeleton, lastTime, 2147483647, firedEvents, alpha, blend, direction, appliedPose); lastTime = -1; } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. - { return; - } - if (time < frames[0]) return; var frame:Int; @@ -87,7 +82,7 @@ class EventTimeline extends Timeline { } } while (i < frameCount && time >= frames[i]) { - events.push(this.events[i]); + firedEvents.push(this.events[i]); i++; } } diff --git a/spine-haxe/spine-haxe/spine/animation/IkConstraintTimeline.hx b/spine-haxe/spine-haxe/spine/animation/IkConstraintTimeline.hx index 037c224d2..3899fa3c6 100644 --- a/spine-haxe/spine-haxe/spine/animation/IkConstraintTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/IkConstraintTimeline.hx @@ -29,13 +29,9 @@ package spine.animation; -import spine.Event; -import spine.IkConstraint; -import spine.Skeleton; - -/** Changes an IK constraint's spine.IkConstraint.mix, spine.IkConstraint.softness, - * spine.IkConstraint.bendDirection, spine.IkConstraint.stretch, and spine.IkConstraint.compress. */ -class IkConstraintTimeline extends CurveTimeline { +/** Changes an IK constraint's spine.IkConstraintPose.mix, spine.IkConstraintPose.softness, + * spine.IkConstraintPose.bendDirection, spine.IkConstraintPose.stretch, and spine.IkConstraintPose.compress. */ +class IkConstraintTimeline extends CurveTimeline implements ConstraintTimeline { private static inline var ENTRIES:Int = 6; private static inline var MIX:Int = 1; private static inline var SOFTNESS:Int = 2; @@ -47,15 +43,19 @@ class IkConstraintTimeline extends CurveTimeline { * applied. */ public var constraintIndex:Int = 0; - public function new(frameCount:Int, bezierCount:Int, ikConstraintIndex:Int) { - super(frameCount, bezierCount, [Property.ikConstraint + "|" + ikConstraintIndex]); - this.constraintIndex = ikConstraintIndex; + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, Property.ikConstraint + "|" + constraintIndex); + this.constraintIndex = constraintIndex; } public override function getFrameEntries():Int { return ENTRIES; } + public function getConstraintIndex () { + return constraintIndex; + } + /** Sets the time, mix, softness, bend direction, compress, and stretch for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. @@ -70,26 +70,28 @@ class IkConstraintTimeline extends CurveTimeline { frames[frame + STRETCH] = stretch ? 1 : 0; } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var constraint:IkConstraint = skeleton.ikConstraints[constraintIndex]; - if (!constraint.active) - return; + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + + var constraint = cast(skeleton.constraints[constraintIndex], IkConstraint); + if (!constraint.active) return; + var pose = appliedPose ? constraint.applied : constraint.pose; if (time < frames[0]) { + var setup = constraint.data.setup; switch (blend) { case MixBlend.setup: - constraint.mix = constraint.data.mix; - constraint.softness = constraint.data.softness; - constraint.bendDirection = constraint.data.bendDirection; - constraint.compress = constraint.data.compress; - constraint.stretch = constraint.data.stretch; + pose.mix = setup.mix; + pose.softness = setup.softness; + pose.bendDirection = setup.bendDirection; + pose.compress = setup.compress; + pose.stretch = setup.stretch; case MixBlend.first: - constraint.mix += (constraint.data.mix - constraint.mix) * alpha; - constraint.softness += (constraint.data.softness - constraint.softness) * alpha; - constraint.bendDirection = constraint.data.bendDirection; - constraint.compress = constraint.data.compress; - constraint.stretch = constraint.data.stretch; + pose.mix += (setup.mix - pose.mix) * alpha; + pose.softness += (setup.softness - pose.softness) * alpha; + pose.bendDirection = setup.bendDirection; + pose.compress = setup.compress; + pose.stretch = setup.stretch; } return; } @@ -113,26 +115,29 @@ class IkConstraintTimeline extends CurveTimeline { softness = getBezierValue(time, i, SOFTNESS, curveType + CurveTimeline.BEZIER_SIZE - CurveTimeline.BEZIER); } - if (blend == MixBlend.setup) { - 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 = Std.int(frames[i + BEND_DIRECTION]); - constraint.compress = frames[i + COMPRESS] != 0; - constraint.stretch = frames[i + STRETCH] != 0; - } - } else { - constraint.mix += (mix - constraint.mix) * alpha; - constraint.softness += (softness - constraint.softness) * alpha; - if (direction == MixDirection.mixIn) { - constraint.bendDirection = Std.int(frames[i + BEND_DIRECTION]); - constraint.compress = frames[i + COMPRESS] != 0; - constraint.stretch = frames[i + STRETCH] != 0; - } + switch (blend) { + case MixBlend.setup: + var setup = constraint.data.setup; + pose.mix = setup.mix + (mix - setup.mix) * alpha; + pose.softness = setup.softness + (softness - setup.softness) * alpha; + if (direction == MixDirection.mixOut) { + pose.bendDirection = setup.bendDirection; + pose.compress = setup.compress; + pose.stretch = setup.stretch; + return; + } + case MixBlend.first, MixBlend.replace: + pose.mix += (mix - pose.mix) * alpha; + pose.softness += (softness - pose.softness) * alpha; + if (direction == MixDirection.mixOut) return; + case MixBlend.add: + pose.mix += mix * alpha; + pose.softness += softness * alpha; + if (direction == MixDirection.mixOut) return; + } + pose.bendDirection = Std.int(frames[i + BEND_DIRECTION]); + pose.compress = frames[i + COMPRESS] != 0; + pose.stretch = frames[i + STRETCH] != 0; } } diff --git a/spine-haxe/spine-haxe/spine/animation/InheritTimeline.hx b/spine-haxe/spine-haxe/spine/animation/InheritTimeline.hx index 087ba5156..35e77ca47 100644 --- a/spine-haxe/spine-haxe/spine/animation/InheritTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/InheritTimeline.hx @@ -41,18 +41,18 @@ class InheritTimeline extends Timeline implements BoneTimeline { private var boneIndex:Int = 0; public function new(frameCount:Int, boneIndex:Int) { - super(frameCount, [Property.inherit + "|" + boneIndex]); + super(frameCount, Property.inherit + "|" + boneIndex); this.boneIndex = boneIndex; } + public function getBoneIndex () { + return boneIndex; + } + public override function getFrameEntries():Int { return ENTRIES; } - public function getBoneIndex():Int { - return boneIndex; - } - /** Sets the transform mode for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ @@ -62,21 +62,22 @@ class InheritTimeline extends Timeline implements BoneTimeline { frames[frame + INHERIT] = inherit.ordinal; } - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool):Void { + var bone:Bone = skeleton.bones[boneIndex]; if (!bone.active) return; + var pose = appliedPose ? bone.applied : bone.pose; if (direction == MixDirection.mixOut) { - if (blend == MixBlend.setup) bone.inherit = bone.data.inherit; + if (blend == MixBlend.setup) pose.inherit = bone.data.setup.inherit; return; } var frames:Array = frames; if (time < frames[0]) { - if (blend == MixBlend.setup || blend == MixBlend.first) bone.inherit = bone.data.inherit; - return; - } - bone.inherit = Inherit.values[Std.int(frames[Timeline.search(frames, time, ENTRIES) + INHERIT])]; + if (blend == MixBlend.setup || blend == MixBlend.first) pose.inherit = bone.data.setup.inherit; + } else + pose.inherit = Inherit.values[Std.int(frames[Timeline.search(frames, time, ENTRIES) + INHERIT])]; } } diff --git a/spine-haxe/spine-haxe/spine/animation/MixBlend.hx b/spine-haxe/spine-haxe/spine/animation/MixBlend.hx index 7f6c1761c..1d2e250ed 100644 --- a/spine-haxe/spine-haxe/spine/animation/MixBlend.hx +++ b/spine-haxe/spine-haxe/spine/animation/MixBlend.hx @@ -31,7 +31,7 @@ package spine.animation; /** Controls how timeline values are mixed with setup pose values or current pose values when a timeline is applied with * alpha < 1. - * + * * @see spine.animation.Timeline.apply() */ class MixBlend { public var ordinal:Int = 0; @@ -40,25 +40,29 @@ class MixBlend { this.ordinal = ordinal; } - /** Transitions from the setup value to the timeline value (the current value is not used). Before the first frame, the - * setup value is set. */ + /** Transitions between the setup and timeline values (the current value is not used). Before the first frame, the setup + * value is used. + * + * `setup` is intended to transition to or from the setup pose, not for animations layered on top of others. */ public static var setup(default, never):MixBlend = new MixBlend(0); - /** Transitions from the current value to the timeline value. Before the first frame, transitions from the current value to - * the setup value. Timelines which perform instant transitions, such as spine.animation.DrawOrderTimeline or - * spine.animation.AttachmentTimeline, use the setup value before the first frame. - * - * first is intended for the first animations applied, not for animations layered on top of those. */ + + /** Transitions between the current and timeline values. Before the first frame, transitions between the current and setup + * values. Timelines which perform instant transitions, such as {@link DrawOrderTimeline} or {@link AttachmentTimeline}, use + * the setup value before the first frame. + * + * `first` is intended for the first animations applied, not for animations layered on top of others. */ public static var first(default, never):MixBlend = new MixBlend(1); - /** Transitions from the current value to the timeline value. No change is made before the first frame (the current value is - * kept until the first frame). - * - * replace is intended for animations layered on top of others, not for the first animations applied. */ + + /** Transitions between the current and timeline values. No change is made before the first frame. + * + * `replace` is intended for animations layered on top of others, not for the first animations applied. */ public static var replace(default, never):MixBlend = new MixBlend(2); - /** Transitions from the current value to the current value plus the timeline value. No change is made before the first - * frame (the current value is kept until the first frame). - * - * add is intended for animations layered on top of others, not for the first animations applied. Properties - * set by additive animations must be set manually or by another animation before applying the additive animations, else the - * property values will increase each time the additive animations are applied. */ + + /** Transitions between the current value and the current plus timeline values. No change is made before the first frame. + * + * `add` is intended for animations layered on top of others, not for the first animations applied. + * + * Properties set by additive animations must be set manually or by another animation before applying the additive + * animations, else the property values will increase each time the additive animations are applied. */ public static var add(default, never):MixBlend = new MixBlend(3); } diff --git a/spine-haxe/spine-haxe/spine/animation/MixDirection.hx b/spine-haxe/spine-haxe/spine/animation/MixDirection.hx index 6d827b27f..5f87b7919 100644 --- a/spine-haxe/spine-haxe/spine/animation/MixDirection.hx +++ b/spine-haxe/spine-haxe/spine/animation/MixDirection.hx @@ -31,7 +31,7 @@ package spine.animation; /** Indicates whether a timeline's alpha is mixing out over time toward 0 (the setup or current pose value) or * mixing in toward 1 (the timeline's value). Some timelines use this to decide how values are applied. - * + * * @see spine.animation.Timeline.apply() */ class MixDirection { diff --git a/spine-haxe/spine-haxe/spine/animation/PathConstraintMixTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PathConstraintMixTimeline.hx index b53f86c6e..fcc437755 100644 --- a/spine-haxe/spine-haxe/spine/animation/PathConstraintMixTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PathConstraintMixTimeline.hx @@ -35,7 +35,7 @@ import spine.Skeleton; /** Changes a path constraint's PathConstraint.mixRotate, PathConstraint.mixX, and * PathConstraint.mixY. */ -class PathConstraintMixTimeline extends CurveTimeline { +class PathConstraintMixTimeline extends CurveTimeline implements ConstraintTimeline { private static inline var ENTRIES:Int = 4; private static inline var ROTATE:Int = 1; private static inline var X:Int = 2; @@ -45,15 +45,19 @@ class PathConstraintMixTimeline extends CurveTimeline { * applied. */ public var constraintIndex:Int = 0; - public function new(frameCount:Int, bezierCount:Int, pathConstraintIndex:Int) { - super(frameCount, bezierCount, [Property.pathConstraintMix + "|" + pathConstraintIndex]); - this.constraintIndex = pathConstraintIndex; + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, Property.pathConstraintMix + "|" + constraintIndex); + this.constraintIndex = constraintIndex; } public override function getFrameEntries():Int { return ENTRIES; } + public function getConstraintIndex () { + return constraintIndex; + } + /** Sets the time and color for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ @@ -65,24 +69,24 @@ class PathConstraintMixTimeline extends CurveTimeline { frames[frame + Y] = mixY; } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var constraint:PathConstraint = skeleton.pathConstraints[constraintIndex]; - if (!constraint.active) - return; + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + + var constraint = cast(skeleton.constraints[constraintIndex], PathConstraint); + if (!constraint.active) return; + var pose = appliedPose ? constraint.applied : constraint.pose; - var data:PathConstraintData; if (time < frames[0]) { - data = constraint.data; + var setup = constraint.data.setup; switch (blend) { case MixBlend.setup: - constraint.mixRotate = data.mixRotate; - constraint.mixX = data.mixX; - constraint.mixY = data.mixY; + pose.mixRotate = setup.mixRotate; + pose.mixX = setup.mixX; + pose.mixY = setup.mixY; case MixBlend.first: - constraint.mixRotate += (data.mixRotate - constraint.mixRotate) * alpha; - constraint.mixX += (data.mixX - constraint.mixX) * alpha; - constraint.mixY += (data.mixY - constraint.mixY) * alpha; + pose.mixRotate += (setup.mixRotate - pose.mixRotate) * alpha; + pose.mixX += (setup.mixX - pose.mixX) * alpha; + pose.mixY += (setup.mixY - pose.mixY) * alpha; } return; } @@ -110,15 +114,20 @@ class PathConstraintMixTimeline extends CurveTimeline { y = getBezierValue(time, i, Y, curveType + CurveTimeline.BEZIER_SIZE * 2 - CurveTimeline.BEZIER); } - if (blend == MixBlend.setup) { - 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.mixRotate += (rotate - constraint.mixRotate) * alpha; - constraint.mixX += (x - constraint.mixX) * alpha; - constraint.mixY += (y - constraint.mixY) * alpha; + switch (blend) { + case MixBlend.setup: + var setup = constraint.data.setup; + pose.mixRotate = setup.mixRotate + (rotate - setup.mixRotate) * alpha; + pose.mixX = setup.mixX + (x - setup.mixX) * alpha; + pose.mixY = setup.mixY + (y - setup.mixY) * alpha; + case MixBlend.first, MixBlend.replace: + pose.mixRotate += (rotate - pose.mixRotate) * alpha; + pose.mixX += (x - pose.mixX) * alpha; + pose.mixY += (y - pose.mixY) * alpha; + case MixBlend.add: + pose.mixRotate += rotate * alpha; + pose.mixX += x * alpha; + pose.mixY += y * alpha; } } } diff --git a/spine-haxe/spine-haxe/spine/animation/PathConstraintPositionTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PathConstraintPositionTimeline.hx index ae22dab44..699e439b4 100644 --- a/spine-haxe/spine-haxe/spine/animation/PathConstraintPositionTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PathConstraintPositionTimeline.hx @@ -29,25 +29,20 @@ package spine.animation; -import spine.Event; -import spine.PathConstraint; -import spine.Skeleton; - /** Changes a path constraint's spine.PathConstraint.position. */ -class PathConstraintPositionTimeline extends CurveTimeline1 { - /** The index of the path constraint in spine.Skeleton.pathConstraints that will be changed when this timeline is - * applied. */ - public var constraintIndex:Int = 0; - - public function new(frameCount:Int, bezierCount:Int, pathConstraintIndex:Int) { - super(frameCount, bezierCount, [Property.pathConstraintPosition + "|" + pathConstraintIndex]); - this.constraintIndex = pathConstraintIndex; +class PathConstraintPositionTimeline extends ConstraintTimeline1 { + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.pathConstraintPosition); } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var constraint:PathConstraint = skeleton.pathConstraints[constraintIndex]; - if (constraint.active) - constraint.position = getAbsoluteValue(time, alpha, blend, constraint.position, constraint.data.position); + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + + var constraint = cast(skeleton.constraints[constraintIndex], PathConstraint); + if (constraint.active) { + var pose = appliedPose ? constraint.applied : constraint.pose; + pose.position = getAbsoluteValue(time, alpha, blend, pose.position, constraint.data.setup.position); + } + } } diff --git a/spine-haxe/spine-haxe/spine/animation/PathConstraintSpacingTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PathConstraintSpacingTimeline.hx index faddac0aa..4b5ab5ba8 100644 --- a/spine-haxe/spine-haxe/spine/animation/PathConstraintSpacingTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PathConstraintSpacingTimeline.hx @@ -34,19 +34,18 @@ import spine.PathConstraint; import spine.Skeleton; /** Changes a path constraint's PathConstraint#spacing. */ -class PathConstraintSpacingTimeline extends CurveTimeline1 { - /** The index of the path constraint in Skeleton#pathConstraints that will be changed when this timeline is - * applied. */ - public var constraintIndex:Int = 0; - - public function new(frameCount:Int, bezierCount:Int, pathConstraintIndex:Int) { - super(frameCount, bezierCount, [Property.pathConstraintSpacing + "|" + pathConstraintIndex]); - this.constraintIndex = pathConstraintIndex; +class PathConstraintSpacingTimeline extends ConstraintTimeline1 { + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.pathConstraintSpacing); } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var constraint:PathConstraint = skeleton.pathConstraints[constraintIndex]; - if (constraint.active) constraint.spacing = getAbsoluteValue(time, alpha, blend, constraint.spacing, constraint.data.spacing); + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + + var constraint = cast(skeleton.constraints[constraintIndex], PathConstraint); + if (constraint.active) { + var pose = appliedPose ? constraint.applied : constraint.pose; + pose.spacing = getAbsoluteValue(time, alpha, blend, pose.spacing, constraint.data.setup.spacing); + } } } diff --git a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintDampingTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintDampingTimeline.hx index a4dcbee3f..172a65de1 100644 --- a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintDampingTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintDampingTimeline.hx @@ -29,22 +29,18 @@ package spine.animation; -/** Changes a physics constraint's spine.PhysicsConstraint.damping. */ +/** Changes a physics constraint's spine.PhysicsConstraintPose.damping. */ class PhysicsConstraintDampingTimeline extends PhysicsConstraintTimeline { - public function new(frameCount:Int, bezierCount:Int, physicsConstraintIndex:Int) { - super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintDamping); + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.physicsConstraintDamping); } - public function setup (constraint: PhysicsConstraint):Float { - return constraint.data.damping; + public function get (pose: PhysicsConstraintPose):Float { + return pose.damping; } - public function get (constraint: PhysicsConstraint):Float { - return constraint.damping; - } - - public function set (constraint: PhysicsConstraint, value:Float):Void { - constraint.damping = value; + public function set (pose: PhysicsConstraintPose, value:Float):Void { + pose.damping = value; } public function global (constraint: PhysicsConstraintData):Bool { diff --git a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintGravityTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintGravityTimeline.hx index 5ecf42076..c6399926b 100644 --- a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintGravityTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintGravityTimeline.hx @@ -29,22 +29,18 @@ package spine.animation; -/** Changes a physics constraint's spine.PhysicsConstraint.gravity. */ +/** Changes a physics constraint's spine.PhysicsConstraintPose.gravity. */ class PhysicsConstraintGravityTimeline extends PhysicsConstraintTimeline { - public function new(frameCount:Int, bezierCount:Int, physicsConstraintIndex:Int) { - super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintGravity); + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.physicsConstraintGravity); } - public function setup (constraint: PhysicsConstraint):Float { - return constraint.data.gravity; + public function get (pose: PhysicsConstraintPose):Float { + return pose.gravity; } - public function get (constraint: PhysicsConstraint):Float { - return constraint.gravity; - } - - public function set (constraint: PhysicsConstraint, value:Float):Void { - constraint.gravity = value; + public function set (pose: PhysicsConstraintPose, value:Float):Void { + pose.gravity = value; } public function global (constraint: PhysicsConstraintData):Bool { diff --git a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintInertiaTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintInertiaTimeline.hx index 45d490081..32054adf8 100644 --- a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintInertiaTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintInertiaTimeline.hx @@ -29,22 +29,18 @@ package spine.animation; -/** Changes a physics constraint's spine.PhysicsConstraint.inertia. */ +/** Changes a physics constraint's spine.PhysicsConstraintPose.inertia. */ class PhysicsConstraintInertiaTimeline extends PhysicsConstraintTimeline { - public function new(frameCount:Int, bezierCount:Int, physicsConstraintIndex:Int) { - super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintInertia); + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.physicsConstraintInertia); } - public function setup (constraint: PhysicsConstraint):Float { - return constraint.data.inertia; + public function get (pose: PhysicsConstraintPose):Float { + return pose.inertia; } - public function get (constraint: PhysicsConstraint):Float { - return constraint.inertia; - } - - public function set (constraint: PhysicsConstraint, value:Float):Void { - constraint.inertia = value; + public function set (pose: PhysicsConstraintPose, value:Float):Void { + pose.inertia = value; } public function global (constraint: PhysicsConstraintData):Bool { diff --git a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintMassTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintMassTimeline.hx index abc1e695e..853ca25dc 100644 --- a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintMassTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintMassTimeline.hx @@ -29,22 +29,18 @@ package spine.animation; -/** Changes a physics constraint's spine.PhysicsConstraint.massInverse. The timeline values are not inverted. */ +/** Changes a physics constraint's spine.PhysicsConstraintPose.massInverse. The timeline values are not inverted. */ class PhysicsConstraintMassTimeline extends PhysicsConstraintTimeline { - public function new(frameCount:Int, bezierCount:Int, physicsConstraintIndex:Int) { - super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintMass); + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.physicsConstraintMass); } - public function setup (constraint: PhysicsConstraint):Float { - return constraint.data.massInverse; + public function get (pose: PhysicsConstraintPose):Float { + return pose.massInverse; } - public function get (constraint: PhysicsConstraint):Float { - return constraint.massInverse; - } - - public function set (constraint: PhysicsConstraint, value:Float):Void { - constraint.massInverse = value; + public function set (pose: PhysicsConstraintPose, value:Float):Void { + pose.massInverse = value; } public function global (constraint: PhysicsConstraintData):Bool { diff --git a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintMixTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintMixTimeline.hx index 9c080a03c..21e3211a3 100644 --- a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintMixTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintMixTimeline.hx @@ -29,22 +29,18 @@ package spine.animation; -/** Changes a physics constraint's spine.PhysicsConstraint.mix. */ +/** Changes a physics constraint's spine.PhysicsConstraintPose.mix. */ class PhysicsConstraintMixTimeline extends PhysicsConstraintTimeline { - public function new(frameCount:Int, bezierCount:Int, physicsConstraintIndex:Int) { - super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintMix); + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.physicsConstraintMix); } - public function setup (constraint: PhysicsConstraint):Float { - return constraint.data.mix; + public function get (pose: PhysicsConstraintPose):Float { + return pose.mix; } - public function get (constraint: PhysicsConstraint):Float { - return constraint.mix; - } - - public function set (constraint: PhysicsConstraint, value:Float):Void { - constraint.mix = value; + public function set (pose: PhysicsConstraintPose, value:Float):Void { + pose.mix = value; } public function global (constraint: PhysicsConstraintData):Bool { diff --git a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintResetTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintResetTimeline.hx index f6679531d..73ea745c4 100644 --- a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintResetTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintResetTimeline.hx @@ -34,16 +34,17 @@ import spine.Event; import spine.Skeleton; /** Resets a physics constraint when specific animation times are reached. */ -class PhysicsConstraintResetTimeline extends Timeline { - /** The index of the physics constraint in Skeleton#physicsConstraints that will be reset when this timeline is - * applied, or -1 if all physics constraints in the skeleton will be reset. */ - public var constraintIndex:Int = 0; +class PhysicsConstraintResetTimeline extends Timeline implements ConstraintTimeline { + public var constraintIndex:Int; - /** @param physicsConstraintIndex -1 for all physics constraints in the skeleton. */ - public function new(frameCount:Int, physicsConstraintIndex:Int) { - propertyIds = [Std.string(Property.physicsConstraintReset)]; - super(frameCount, propertyIds); - constraintIndex = physicsConstraintIndex; + /** @param constraintIndex -1 for all physics constraints in the skeleton. */ + public function new(frameCount:Int, constraintIndex:Int) { + super(frameCount, Property.physicsConstraintReset); + this.constraintIndex = constraintIndex; + } + + public function getConstraintIndex () { + return constraintIndex; } public override function getFrameCount():Int { @@ -57,31 +58,30 @@ class PhysicsConstraintResetTimeline extends Timeline { } /** Resets the physics constraint when frames > lastTime and <= time. */ - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, firedEvents:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + var constraint:PhysicsConstraint = null; - if (this.constraintIndex != -1) { - constraint = skeleton.physicsConstraints[constraintIndex]; + if (constraintIndex != -1) { + constraint = cast(skeleton.constraints[constraintIndex], PhysicsConstraint); if (!constraint.active) return; } var frames:Array = this.frames; - if (lastTime > time) // Apply events after lastTime for looped animations. - { - apply(skeleton, lastTime, 2147483647, [], alpha, blend, direction); + + if (lastTime > time) { // Apply events after lastTime for looped animations. + apply(skeleton, lastTime, 2147483647, [], alpha, blend, direction, appliedPose); lastTime = -1; } else if (lastTime >= frames[frames.length - 1]) // Last time is after last frame. - { return; - } if (time < frames[0]) return; if (lastTime < frames[0] || time >= frames[Timeline.search1(frames, lastTime) + 1]) { if (constraint != null) - constraint.reset(); + constraint.reset(skeleton); else { - for (constraint in skeleton.physicsConstraints) { - if (constraint.active) constraint.reset(); + for (constraint in skeleton.physics) { + if (constraint.active) constraint.reset(skeleton); } } } diff --git a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintStrengthTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintStrengthTimeline.hx index 08092c48f..e1ddd9e2a 100644 --- a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintStrengthTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintStrengthTimeline.hx @@ -29,22 +29,18 @@ package spine.animation; -/** Changes a physics constraint's spine.PhysicsConstraint.strength. */ +/** Changes a physics constraint's spine.PhysicsConstraintPose.strength. */ class PhysicsConstraintStrengthTimeline extends PhysicsConstraintTimeline { - public function new(frameCount:Int, bezierCount:Int, physicsConstraintIndex:Int) { - super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintStrength); + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.physicsConstraintStrength); } - public function setup (constraint: PhysicsConstraint):Float { - return constraint.data.strength; + public function get (pose: PhysicsConstraintPose):Float { + return pose.strength; } - public function get (constraint: PhysicsConstraint):Float { - return constraint.strength; - } - - public function set (constraint: PhysicsConstraint, value:Float):Void { - constraint.strength = value; + public function set (pose: PhysicsConstraintPose, value:Float):Void { + pose.strength = value; } public function global (constraint: PhysicsConstraintData):Bool { diff --git a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintTimeline.hx index 5bf15d4fb..d61c20422 100644 --- a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintTimeline.hx @@ -34,39 +34,37 @@ import spine.PathConstraint; import spine.Skeleton; /** The base class for most spine.PhysicsConstraint timelines. */ -abstract class PhysicsConstraintTimeline extends CurveTimeline1 { - /** The index of the physics constraint in Skeleton.physicsConstraints that will be changed when this timeline - * is applied, or -1 if all physics constraints in the skeleton will be changed. */ - public var constraintIndex:Int = 0; - +abstract class PhysicsConstraintTimeline extends ConstraintTimeline1 { /** - * @param physicsConstraintIndex -1 for all physics constraints in the skeleton. + * @param constraintIndex -1 for all physics constraints in the skeleton. */ - public function new(frameCount:Int, bezierCount:Int, physicsConstraintIndex:Int, property:Int) { - super(frameCount, bezierCount, [property + "|" + physicsConstraintIndex]); - constraintIndex = physicsConstraintIndex; + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int, property:Property) { + super(frameCount, bezierCount, constraintIndex, property); } - public override function apply (skeleton:Skeleton, lastTime:Float, time:Float, firedEvents:Array, alpha:Float, blend:MixBlend, direction:MixDirection):Void { - var constraint:PhysicsConstraint; + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + if (constraintIndex == -1) { var value:Float = time >= frames[0] ? getCurveValue(time) : 0; - - for (constraint in skeleton.physicsConstraints) { - if (constraint.active && global(constraint.data)) - set(constraint, getAbsoluteValue2(time, alpha, blend, get(constraint), setup(constraint), value)); + for (constraint in skeleton.physics) { + if (constraint.active && global(constraint.data)) { + var pose = appliedPose ? constraint.applied : constraint.pose; + set(pose, getAbsoluteValue2(time, alpha, blend, get(pose), get(constraint.data.setup), value)); + } } } else { - constraint = skeleton.physicsConstraints[constraintIndex]; - if (constraint.active) set(constraint, getAbsoluteValue(time, alpha, blend, get(constraint), setup(constraint))); + var constraint = cast(skeleton.constraints[constraintIndex], PhysicsConstraint); + if (constraint.active) { + var pose = appliedPose ? constraint.applied : constraint.pose; + set(pose, getAbsoluteValue(time, alpha, blend, get(pose), get(constraint.data.setup))); + } } } - abstract public function setup (constraint: PhysicsConstraint):Float; + abstract public function get (pose: PhysicsConstraintPose):Float; - abstract public function get (constraint: PhysicsConstraint):Float; - - abstract public function set (constraint: PhysicsConstraint, value:Float):Void; + abstract public function set (pose: PhysicsConstraintPose, value:Float):Void; abstract public function global (constraint: PhysicsConstraintData):Bool; } diff --git a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintWindTimeline.hx b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintWindTimeline.hx index aed4b21b5..37590d5bb 100644 --- a/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintWindTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/PhysicsConstraintWindTimeline.hx @@ -29,22 +29,18 @@ package spine.animation; -/** Changes a physics constraint's spine.PhysicsConstraint.wind. */ +/** Changes a physics constraint's spine.PhysicsConstraintPose.wind. */ class PhysicsConstraintWindTimeline extends PhysicsConstraintTimeline { - public function new(frameCount:Int, bezierCount:Int, physicsConstraintIndex:Int) { - super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintWind); + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.physicsConstraintWind); } - public function setup (constraint: PhysicsConstraint):Float { - return constraint.data.wind; + public function get (pose: PhysicsConstraintPose):Float { + return pose.wind; } - public function get (constraint: PhysicsConstraint):Float { - return constraint.wind; - } - - public function set (constraint: PhysicsConstraint, value:Float):Void { - constraint.wind = value; + public function set (pose: PhysicsConstraintPose, value:Float):Void { + pose.wind = value; } public function global (constraint: PhysicsConstraintData):Bool { diff --git a/spine-haxe/spine-haxe/spine/animation/Property.hx b/spine-haxe/spine-haxe/spine/animation/Property.hx index 743600ff8..3a99febf8 100644 --- a/spine-haxe/spine-haxe/spine/animation/Property.hx +++ b/spine-haxe/spine-haxe/spine/animation/Property.hx @@ -32,43 +32,44 @@ package spine.animation; /** * Constants for animation property types. */ -class Property { - public static inline var rotate:Int = 0; - public static inline var x:Int = 1; - public static inline var y:Int = 2; - public static inline var scaleX:Int = 3; - public static inline var scaleY:Int = 4; - public static inline var shearX:Int = 5; - public static inline var shearY:Int = 6; - public static inline var inherit:Int = 7; +enum abstract Property(String) from String to String { + var rotate = "0"; + var x = "1"; + var y = "2"; + var scaleX = "3"; + var scaleY = "4"; + var shearX = "5"; + var shearY = "6"; + var inherit = "7"; - public static inline var rgb:Int = 8; - public static inline var alpha:Int = 9; - public static inline var rgb2:Int = 10; + var rgb = "8"; + var alpha = "9"; + var rgb2 = "10"; - public static inline var attachment:Int = 11; - public static inline var deform:Int = 12; + var attachment = "11"; + var deform = "12"; - public static inline var event:Int = 13; - public static inline var drawOrder:Int = 14; + var event = "13"; + var drawOrder = "14"; - public static inline var ikConstraint:Int = 15; - public static inline var transformConstraint:Int = 16; + var ikConstraint = "15"; + var transformConstraint = "16"; - public static inline var pathConstraintPosition:Int = 17; - public static inline var pathConstraintSpacing:Int = 18; - public static inline var pathConstraintMix:Int = 19; + var pathConstraintPosition = "17"; + var pathConstraintSpacing = "18"; + var pathConstraintMix = "19"; - public static inline var physicsConstraintInertia:Int = 20; - public static inline var physicsConstraintStrength:Int = 21; - public static inline var physicsConstraintDamping:Int = 22; - public static inline var physicsConstraintMass:Int = 23; - public static inline var physicsConstraintWind:Int = 24; - public static inline var physicsConstraintGravity:Int = 25; - public static inline var physicsConstraintMix:Int = 26; - public static inline var physicsConstraintReset:Int = 27; + var physicsConstraintInertia = "20"; + var physicsConstraintStrength = "21"; + var physicsConstraintDamping = "22"; + var physicsConstraintMass = "23"; + var physicsConstraintWind = "24"; + var physicsConstraintGravity = "25"; + var physicsConstraintMix = "26"; + var physicsConstraintReset = "27"; - public static inline var sequence:Int = 28; + var sequence = "28"; - public function new() {} -} + var sliderTime = "29"; + var sliderMix = "30"; +} \ No newline at end of file diff --git a/spine-haxe/spine-haxe/spine/animation/RGB2Timeline.hx b/spine-haxe/spine-haxe/spine/animation/RGB2Timeline.hx index eadbff7b1..6cba8d6e2 100644 --- a/spine-haxe/spine-haxe/spine/animation/RGB2Timeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/RGB2Timeline.hx @@ -30,7 +30,7 @@ package spine.animation; /** Changes the RGB for a slot's spine.Slot.color and spine.Slot.darkColor for two color tinting. */ -class RGB2Timeline extends CurveTimeline implements SlotTimeline { +class RGB2Timeline extends SlotCurveTimeline { private static inline var ENTRIES:Int = 7; private static inline var R:Int = 1; private static inline var G:Int = 2; @@ -39,23 +39,16 @@ class RGB2Timeline extends CurveTimeline implements SlotTimeline { private static inline var G2:Int = 5; private static inline var B2:Int = 6; - private var slotIndex:Int = 0; - public function new(frameCount:Int, bezierCount:Int, slotIndex:Int) { - super(frameCount, bezierCount, [Property.rgb + "|" + slotIndex, Property.rgb2 + "|" + slotIndex]); - this.slotIndex = slotIndex; + super(frameCount, bezierCount, slotIndex, + Property.rgb + "|" + slotIndex, + Property.rgb2 + "|" + slotIndex); } public override function getFrameEntries():Int { return ENTRIES; } - /** The index of the slot in spine.Skeleton.slots that will be changed when this timeline is applied. The - * spine.Slot.darkColor must not be null. */ - public function getSlotIndex():Int { - return slotIndex; - } - /** Sets the time, light color, and dark color for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ @@ -70,17 +63,11 @@ class RGB2Timeline extends CurveTimeline implements SlotTimeline { frames[frame + B2] = b2; } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var slot:Slot = skeleton.slots[slotIndex]; - if (!slot.bone.active) - return; - - var light:Color = slot.color, dark:Color = slot.darkColor; - var setupLight:Color, setupDark:Color; + public function apply1 (slot:Slot, pose:SlotPose, time:Float, alpha:Float, blend:MixBlend) { + var light:Color = pose.color, dark:Color = pose.darkColor; if (time < frames[0]) { - setupLight = slot.data.color; - setupDark = slot.data.darkColor; + var setup = slot.data.setup; + var setupLight = setup.color, setupDark = setup.darkColor; switch (blend) { case MixBlend.setup: light.r = setupLight.r; @@ -144,8 +131,8 @@ class RGB2Timeline extends CurveTimeline implements SlotTimeline { dark.b = b2; } else { if (blend == MixBlend.setup) { - setupLight = slot.data.color; - setupDark = slot.data.darkColor; + var setup = slot.data.setup; + var setupLight = setup.color, setupDark = setup.darkColor; light.r = setupLight.r; light.g = setupLight.g; light.b = setupLight.b; diff --git a/spine-haxe/spine-haxe/spine/animation/RGBA2Timeline.hx b/spine-haxe/spine-haxe/spine/animation/RGBA2Timeline.hx index 1e45b2f63..26f5d37f1 100644 --- a/spine-haxe/spine-haxe/spine/animation/RGBA2Timeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/RGBA2Timeline.hx @@ -30,7 +30,7 @@ package spine.animation; /** Changes a slot's spine.Slot.getColor() and spine.Slot.getDarkColor() for two color tinting. */ -class RGBA2Timeline extends CurveTimeline implements SlotTimeline { +class RGBA2Timeline extends SlotCurveTimeline { private static inline var ENTRIES:Int = 8; private static inline var R:Int = 1; private static inline var G:Int = 2; @@ -40,27 +40,18 @@ class RGBA2Timeline extends CurveTimeline implements SlotTimeline { private static inline var G2:Int = 6; private static inline var B2:Int = 7; - private var slotIndex:Int = 0; - public function new(frameCount:Int, bezierCount:Int, slotIndex:Int) { - super(frameCount, bezierCount, [ + super(frameCount, bezierCount, slotIndex, Property.rgb + "|" + slotIndex, Property.alpha + "|" + slotIndex, Property.rgb2 + "|" + slotIndex - ]); - this.slotIndex = slotIndex; + ); } public override function getFrameEntries():Int { return ENTRIES; } - /** The index of the slot in spine.Skeleton.getSlots() that will be changed when this timeline is applied. The - * spine.Slot.getDarkColor() must not be null. */ - public function getSlotIndex():Int { - return slotIndex; - } - /** Sets the time, light color, and dark color for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ @@ -76,16 +67,11 @@ class RGBA2Timeline extends CurveTimeline implements SlotTimeline { frames[frame + B2] = b2; } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var slot:Slot = skeleton.slots[slotIndex]; - if (!slot.bone.active) - return; - - var light:Color = slot.color, dark:Color = slot.darkColor; + public function apply1 (slot:Slot, pose:SlotPose, time:Float, alpha:Float, blend:MixBlend) { + var light = pose.color, dark = pose.darkColor; if (time < frames[0]) { - var setupLight:Color = slot.data.color, - setupDark:Color = slot.data.darkColor; + var setup = slot.data.setup; + var setupLight = setup.color, setupDark = setup.darkColor; switch (blend) { case MixBlend.setup: light.setFromColor(setupLight); @@ -148,8 +134,9 @@ class RGBA2Timeline extends CurveTimeline implements SlotTimeline { dark.b = b2; } else { if (blend == MixBlend.setup) { - light.setFromColor(slot.data.color); - dark.setFromColor(slot.data.darkColor); + var setup = slot.data.setup; + light.setFromColor(setup.color); + dark.setFromColor(setup.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; diff --git a/spine-haxe/spine-haxe/spine/animation/RGBATimeline.hx b/spine-haxe/spine-haxe/spine/animation/RGBATimeline.hx index 8e7a751d5..f51ac1039 100644 --- a/spine-haxe/spine-haxe/spine/animation/RGBATimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/RGBATimeline.hx @@ -30,28 +30,23 @@ package spine.animation; /** Changes a slot's spine.Slot.color. */ -class RGBATimeline extends CurveTimeline implements SlotTimeline { +class RGBATimeline extends SlotCurveTimeline { private static inline var ENTRIES:Int = 5; private static inline var R:Int = 1; private static inline var G:Int = 2; private static inline var B:Int = 3; private static inline var A:Int = 4; - private var slotIndex:Int = 0; - public function new(frameCount:Int, bezierCount:Int, slotIndex:Int) { - super(frameCount, bezierCount, [Property.rgb + "|" + slotIndex, Property.alpha + "|" + slotIndex]); - this.slotIndex = slotIndex; + super(frameCount, bezierCount, slotIndex, + Property.rgb + "|" + slotIndex, + Property.alpha + "|" + slotIndex); } public override function getFrameEntries():Int { return ENTRIES; } - public function getSlotIndex():Int { - return slotIndex; - } - /** Sets the time and color for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ @@ -64,15 +59,10 @@ class RGBATimeline extends CurveTimeline implements SlotTimeline { frames[frame + A] = a; } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var slot:Slot = skeleton.slots[slotIndex]; - if (!slot.bone.active) - return; - - var color:Color = slot.color; + public function apply1 (slot:Slot, pose:SlotPose, time:Float, alpha:Float, blend:MixBlend) { + var color = pose.color; if (time < frames[0]) { - var setup:Color = slot.data.color; + var setup:Color = slot.data.setup.color; switch (blend) { case MixBlend.setup: color.setFromColor(setup); @@ -113,7 +103,7 @@ class RGBATimeline extends CurveTimeline implements SlotTimeline { color.set(r, g, b, a); } else { if (blend == MixBlend.setup) - color.setFromColor(slot.data.color); + color.setFromColor(slot.data.setup.color); color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); } } diff --git a/spine-haxe/spine-haxe/spine/animation/RGBTimeline.hx b/spine-haxe/spine-haxe/spine/animation/RGBTimeline.hx index 885b3f768..e4a9d7fbc 100644 --- a/spine-haxe/spine-haxe/spine/animation/RGBTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/RGBTimeline.hx @@ -30,27 +30,20 @@ package spine.animation; /** Changes the RGB for a slot's spine.Slot.color. */ -class RGBTimeline extends CurveTimeline implements SlotTimeline { +class RGBTimeline extends SlotCurveTimeline { private static inline var ENTRIES:Int = 4; private static inline var R:Int = 1; private static inline var G:Int = 2; private static inline var B:Int = 3; - private var slotIndex:Int = 0; - public function new(frameCount:Int, bezierCount:Int, slotIndex:Int) { - super(frameCount, bezierCount, [Property.rgb + "|" + slotIndex]); - this.slotIndex = slotIndex; + super(frameCount, bezierCount, slotIndex, Property.rgb + "|" + slotIndex); } public override function getFrameEntries():Int { return ENTRIES; } - public function getSlotIndex():Int { - return slotIndex; - } - /** Sets the time and color for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ @@ -62,15 +55,10 @@ class RGBTimeline extends CurveTimeline implements SlotTimeline { frames[frame + B] = b; } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var slot:Slot = skeleton.slots[slotIndex]; - if (!slot.bone.active) - return; - - var color:Color = slot.color, setup:Color; + public function apply1 (slot:Slot, pose:SlotPose, time:Float, alpha:Float, blend:MixBlend) { + var color = pose.color; if (time < frames[0]) { - setup = slot.data.color; + var setup = slot.data.setup.color; switch (blend) { case MixBlend.setup: color.r = setup.r; @@ -112,7 +100,7 @@ class RGBTimeline extends CurveTimeline implements SlotTimeline { color.b = b; } else { if (blend == MixBlend.setup) { - setup = slot.data.color; + var setup = slot.data.setup.color; color.r = setup.r; color.g = setup.g; color.b = setup.b; diff --git a/spine-haxe/spine-haxe/spine/animation/RotateTimeline.hx b/spine-haxe/spine-haxe/spine/animation/RotateTimeline.hx index 9d47c7a2b..6ab957401 100644 --- a/spine-haxe/spine-haxe/spine/animation/RotateTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/RotateTimeline.hx @@ -29,27 +29,14 @@ package spine.animation; -import spine.Bone; -import spine.Event; -import spine.Skeleton; - /** Changes a bone's local rotation. */ -class RotateTimeline extends CurveTimeline1 implements BoneTimeline { - public var boneIndex:Int = 0; - +class RotateTimeline extends BoneTimeline1 { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { - super(frameCount, bezierCount, [Property.rotate + "|" + boneIndex]); + super(frameCount, bezierCount, boneIndex, Property.rotate); this.boneIndex = boneIndex; } - public function getBoneIndex():Int { - return boneIndex; - } - - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var bone:Bone = skeleton.bones[boneIndex]; - if (bone.active) - bone.rotation = getRelativeValue(time, alpha, blend, bone.rotation, bone.data.rotation); + public function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection):Void { + pose.rotation = getRelativeValue(time, alpha, blend, pose.rotation, setup.rotation); } } diff --git a/spine-haxe/spine-haxe/spine/animation/ScaleTimeline.hx b/spine-haxe/spine-haxe/spine/animation/ScaleTimeline.hx index 5aaaf6022..381d94f1c 100644 --- a/spine-haxe/spine-haxe/spine/animation/ScaleTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/ScaleTimeline.hx @@ -35,97 +35,86 @@ import spine.MathUtils; import spine.Skeleton; /** Changes a bone's local spine.Bone.scaleX and spine.Bone.scaleY. */ -class ScaleTimeline extends CurveTimeline2 implements BoneTimeline { - private var boneIndex:Int = 0; - +class ScaleTimeline extends BoneTimeline2 { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { - super(frameCount, bezierCount, [Property.scaleX + "|" + boneIndex, Property.scaleY + "|" + boneIndex]); - this.boneIndex = boneIndex; + super(frameCount, bezierCount, boneIndex, Property.scaleX, Property.scaleY); } - public function getBoneIndex():Int { - return boneIndex; - } - - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var bone:Bone = skeleton.bones[boneIndex]; - if (!bone.active) - return; + public function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection) { if (time < frames[0]) { switch (blend) { case MixBlend.setup: - bone.scaleX = bone.data.scaleX; - bone.scaleY = bone.data.scaleY; + pose.scaleX = setup.scaleX; + pose.scaleY = setup.scaleY; case MixBlend.first: - bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; - bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; + pose.scaleX += (setup.scaleX - pose.scaleX) * alpha; + pose.scaleY += (setup.scaleY - pose.scaleY) * alpha; } return; } var x:Float = 0, y:Float = 0; - var i:Int = Timeline.search(frames, time, CurveTimeline2.ENTRIES); - var curveType:Int = Std.int(curves[Std.int(i / CurveTimeline2.ENTRIES)]); + var i:Int = Timeline.search(frames, time, BoneTimeline2.ENTRIES); + var curveType:Int = Std.int(curves[Std.int(i / BoneTimeline2.ENTRIES)]); switch (curveType) { case CurveTimeline.LINEAR: var before:Float = frames[i]; - x = frames[i + CurveTimeline2.VALUE1]; - y = frames[i + CurveTimeline2.VALUE2]; - var t:Float = (time - before) / (frames[i + CurveTimeline2.ENTRIES] - before); - x += (frames[i + CurveTimeline2.ENTRIES + CurveTimeline2.VALUE1] - x) * t; - y += (frames[i + CurveTimeline2.ENTRIES + CurveTimeline2.VALUE2] - y) * t; + x = frames[i + BoneTimeline2.VALUE1]; + y = frames[i + BoneTimeline2.VALUE2]; + var t:Float = (time - before) / (frames[i + BoneTimeline2.ENTRIES] - before); + x += (frames[i + BoneTimeline2.ENTRIES + BoneTimeline2.VALUE1] - x) * t; + y += (frames[i + BoneTimeline2.ENTRIES + BoneTimeline2.VALUE2] - y) * t; case CurveTimeline.STEPPED: - x = frames[i + CurveTimeline2.VALUE1]; - y = frames[i + CurveTimeline2.VALUE2]; + x = frames[i + BoneTimeline2.VALUE1]; + y = frames[i + BoneTimeline2.VALUE2]; default: - x = getBezierValue(time, i, CurveTimeline2.VALUE1, curveType - CurveTimeline.BEZIER); - y = getBezierValue(time, i, CurveTimeline2.VALUE2, curveType + CurveTimeline.BEZIER_SIZE - CurveTimeline.BEZIER); + x = getBezierValue(time, i, BoneTimeline2.VALUE1, curveType - CurveTimeline.BEZIER); + y = getBezierValue(time, i, BoneTimeline2.VALUE2, curveType + CurveTimeline.BEZIER_SIZE - CurveTimeline.BEZIER); } - x *= bone.data.scaleX; - y *= bone.data.scaleY; + x *= setup.scaleX; + y *= setup.scaleY; if (alpha == 1) { if (blend == MixBlend.add) { - bone.scaleX += x - bone.data.scaleX; - bone.scaleY += y - bone.data.scaleY; + pose.scaleX += x - setup.scaleX; + pose.scaleY += y - setup.scaleY; } else { - bone.scaleX = x; - bone.scaleY = y; + pose.scaleX = x; + pose.scaleY = y; } } else { var bx:Float = 0, by:Float = 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; + bx = setup.scaleX; + by = setup.scaleY; + pose.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha; + pose.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha; case MixBlend.first, 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; + bx = pose.scaleX; + by = pose.scaleY; + pose.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha; + pose.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha; case MixBlend.add: - bone.scaleX = (x - bone.data.scaleX) * alpha; - bone.scaleY = (y - bone.data.scaleY) * alpha; + pose.scaleX = (x - setup.scaleX) * alpha; + pose.scaleY = (y - setup.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; + bx = Math.abs(setup.scaleX) * MathUtils.signum(x); + by = Math.abs(setup.scaleY) * MathUtils.signum(y); + pose.scaleX = bx + (x - bx) * alpha; + pose.scaleY = by + (y - by) * alpha; case MixBlend.first, 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; + bx = Math.abs(pose.scaleX) * MathUtils.signum(x); + by = Math.abs(pose.scaleY) * MathUtils.signum(y); + pose.scaleX = bx + (x - bx) * alpha; + pose.scaleY = by + (y - by) * alpha; case MixBlend.add: - bone.scaleX += (x - bone.data.scaleX) * alpha; - bone.scaleY += (y - bone.data.scaleY) * alpha; + pose.scaleX += (x - setup.scaleX) * alpha; + pose.scaleY += (y - setup.scaleY) * alpha; } } } diff --git a/spine-haxe/spine-haxe/spine/animation/ScaleXTimeline.hx b/spine-haxe/spine-haxe/spine/animation/ScaleXTimeline.hx index 4c7c06357..6352e2182 100644 --- a/spine-haxe/spine-haxe/spine/animation/ScaleXTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/ScaleXTimeline.hx @@ -29,27 +29,13 @@ package spine.animation; -import spine.Bone; -import spine.Event; -import spine.MathUtils; -import spine.Skeleton; - /** Changes a bone's local spine.Bone.scaleX. */ -class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline { - private var boneIndex:Int = 0; - +class ScaleXTimeline extends BoneTimeline1 { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { - super(frameCount, bezierCount, [Property.scaleX + "|" + boneIndex]); - this.boneIndex = boneIndex; + super(frameCount, bezierCount, boneIndex, Property.scaleX); } - public function getBoneIndex():Int { - return boneIndex; - } - - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var bone:Bone = skeleton.bones[boneIndex]; - if (bone.active) bone.scaleX = getScaleValue(time, alpha, blend, direction, bone.scaleX, bone.data.scaleX); + public function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection) { + pose.scaleX = getScaleValue(time, alpha, blend, direction, pose.scaleX, setup.scaleX); } } diff --git a/spine-haxe/spine-haxe/spine/animation/ScaleYTimeline.hx b/spine-haxe/spine-haxe/spine/animation/ScaleYTimeline.hx index d1ea7abe5..63b384a7a 100644 --- a/spine-haxe/spine-haxe/spine/animation/ScaleYTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/ScaleYTimeline.hx @@ -29,28 +29,13 @@ package spine.animation; -import spine.Bone; -import spine.Event; -import spine.MathUtils; -import spine.Skeleton; - /** Changes a bone's local spine.Bone.scaleY. */ -class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline { - private var boneIndex:Int = 0; - +class ScaleYTimeline extends BoneTimeline1 { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { - super(frameCount, bezierCount, [Property.scaleY + "|" + boneIndex]); - this.boneIndex = boneIndex; + super(frameCount, bezierCount, boneIndex, Property.scaleY); } - public function getBoneIndex():Int { - return boneIndex; - } - - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var bone:Bone = skeleton.bones[boneIndex]; - if (bone.active) bone.scaleY = getScaleValue(time, alpha, blend, direction, bone.scaleY, bone.data.scaleY); - + public function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection) { + pose.scaleY = getScaleValue(time, alpha, blend, direction, pose.scaleY, setup.scaleY); } } diff --git a/spine-haxe/spine-haxe/spine/animation/SequenceTimeline.hx b/spine-haxe/spine-haxe/spine/animation/SequenceTimeline.hx index fa0ebd320..fca8b29f6 100644 --- a/spine-haxe/spine-haxe/spine/animation/SequenceTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/SequenceTimeline.hx @@ -42,9 +42,8 @@ class SequenceTimeline extends Timeline implements SlotTimeline { var attachment:HasTextureRegion; public function new(frameCount:Int, slotIndex:Int, attachment:HasTextureRegion) { - super(frameCount, [ - Std.string(Property.sequence) + "|" + Std.string(slotIndex) + "|" + Std.string(attachment.sequence.id) - ]); + super(frameCount, + Std.string(Property.sequence) + "|" + Std.string(slotIndex) + "|" + Std.string(attachment.sequence.id)); this.slotIndex = slotIndex; this.attachment = attachment; } @@ -71,12 +70,14 @@ class SequenceTimeline extends Timeline implements SlotTimeline { frames[frame + SequenceTimeline.DELAY] = delay; } - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + var slot = skeleton.slots[this.slotIndex]; - if (!slot.bone.active) - return; - var slotAttachment = slot.attachment; + if (!slot.bone.active) return; + var pose = appliedPose ? slot.applied : slot.pose; + + var slotAttachment = pose.attachment; var attachment = cast(this.attachment, Attachment); if (slotAttachment != attachment) { if (!Std.isOfType(slotAttachment, VertexAttachment) || cast(slotAttachment, VertexAttachment).timelineAttachment != attachment) @@ -84,13 +85,12 @@ class SequenceTimeline extends Timeline implements SlotTimeline { } if (direction == MixDirection.mixOut) { - if (blend == MixBlend.setup) slot.sequenceIndex = -1; + if (blend == MixBlend.setup) pose.sequenceIndex = -1; return; } if (time < frames[0]) { - if (blend == MixBlend.setup || blend == MixBlend.first) - slot.sequenceIndex = -1; + if (blend == MixBlend.setup || blend == MixBlend.first) pose.sequenceIndex = -1; return; } @@ -127,6 +127,6 @@ class SequenceTimeline extends Timeline implements SlotTimeline { index = n - index; } } - slot.sequenceIndex = index; + pose.sequenceIndex = index; } } diff --git a/spine-haxe/spine-haxe/spine/animation/ShearTimeline.hx b/spine-haxe/spine-haxe/spine/animation/ShearTimeline.hx index ad39f8043..a2fb42c5c 100644 --- a/spine-haxe/spine-haxe/spine/animation/ShearTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/ShearTimeline.hx @@ -29,70 +29,54 @@ package spine.animation; -import spine.Bone; -import spine.Event; -import spine.Skeleton; - /** Changes a bone's local spine.Bone.shearX and spine.Bone.shearY. */ -class ShearTimeline extends CurveTimeline2 implements BoneTimeline { - private var boneIndex:Int = 0; - +class ShearTimeline extends BoneTimeline2 { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { - super(frameCount, bezierCount, [Property.shearX + "|" + boneIndex, Property.shearY + "|" + boneIndex]); - this.boneIndex = boneIndex; + super(frameCount, bezierCount, boneIndex, Property.shearX, Property.shearY); } - public function getBoneIndex():Int { - return boneIndex; - } - - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var bone:Bone = skeleton.bones[boneIndex]; - if (!bone.active) - return; - + public function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection) { if (time < frames[0]) { switch (blend) { case MixBlend.setup: - bone.shearX = bone.data.shearX; - bone.shearY = bone.data.shearY; + pose.shearX = setup.shearX; + pose.shearY = setup.shearY; case MixBlend.first: - bone.shearX += (bone.data.shearX - bone.shearX) * alpha; - bone.shearY += (bone.data.shearY - bone.shearY) * alpha; + pose.shearX += (setup.shearX - pose.shearX) * alpha; + pose.shearY += (setup.shearY - pose.shearY) * alpha; } return; } var x:Float = 0, y:Float = 0; - var i:Int = Timeline.search(frames, time, CurveTimeline2.ENTRIES); - var curveType:Int = Std.int(curves[Std.int(i / CurveTimeline2.ENTRIES)]); + var i:Int = Timeline.search(frames, time, BoneTimeline2.ENTRIES); + var curveType:Int = Std.int(curves[Std.int(i / BoneTimeline2.ENTRIES)]); switch (curveType) { case CurveTimeline.LINEAR: var before:Float = frames[i]; - x = frames[i + CurveTimeline2.VALUE1]; - y = frames[i + CurveTimeline2.VALUE2]; - var t:Float = (time - before) / (frames[i + CurveTimeline2.ENTRIES] - before); - x += (frames[i + CurveTimeline2.ENTRIES + CurveTimeline2.VALUE1] - x) * t; - y += (frames[i + CurveTimeline2.ENTRIES + CurveTimeline2.VALUE2] - y) * t; + x = frames[i + BoneTimeline2.VALUE1]; + y = frames[i + BoneTimeline2.VALUE2]; + var t:Float = (time - before) / (frames[i + BoneTimeline2.ENTRIES] - before); + x += (frames[i + BoneTimeline2.ENTRIES + BoneTimeline2.VALUE1] - x) * t; + y += (frames[i + BoneTimeline2.ENTRIES + BoneTimeline2.VALUE2] - y) * t; case CurveTimeline.STEPPED: - x = frames[i + CurveTimeline2.VALUE1]; - y = frames[i + CurveTimeline2.VALUE2]; + x = frames[i + BoneTimeline2.VALUE1]; + y = frames[i + BoneTimeline2.VALUE2]; default: - x = getBezierValue(time, i, CurveTimeline2.VALUE1, curveType - CurveTimeline.BEZIER); - y = getBezierValue(time, i, CurveTimeline2.VALUE2, curveType + CurveTimeline.BEZIER_SIZE - CurveTimeline.BEZIER); + x = getBezierValue(time, i, BoneTimeline2.VALUE1, curveType - CurveTimeline.BEZIER); + y = getBezierValue(time, i, BoneTimeline2.VALUE2, curveType + CurveTimeline.BEZIER_SIZE - CurveTimeline.BEZIER); } switch (blend) { case MixBlend.setup: - bone.shearX = bone.data.shearX + x * alpha; - bone.shearY = bone.data.shearY + y * alpha; + pose.shearX = setup.shearX + x * alpha; + pose.shearY = setup.shearY + y * alpha; case MixBlend.first, MixBlend.replace: - bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha; - bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha; + pose.shearX += (setup.shearX + x - pose.shearX) * alpha; + pose.shearY += (setup.shearY + y - pose.shearY) * alpha; case MixBlend.add: - bone.shearX += x * alpha; - bone.shearY += y * alpha; + pose.shearX += x * alpha; + pose.shearY += y * alpha; } } } diff --git a/spine-haxe/spine-haxe/spine/animation/ShearXTimeline.hx b/spine-haxe/spine-haxe/spine/animation/ShearXTimeline.hx index aeb8ac9de..1d37533b5 100644 --- a/spine-haxe/spine-haxe/spine/animation/ShearXTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/ShearXTimeline.hx @@ -29,26 +29,13 @@ package spine.animation; -import spine.Bone; -import spine.Event; -import spine.Skeleton; - /** Changes a bone's local spine.Bone.shearX. */ -class ShearXTimeline extends CurveTimeline1 implements BoneTimeline { - private var boneIndex:Int = 0; - +class ShearXTimeline extends BoneTimeline1 { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { - super(frameCount, bezierCount, [Property.shearX + "|" + boneIndex]); - this.boneIndex = boneIndex; + super(frameCount, bezierCount, boneIndex, Property.shearX); } - public function getBoneIndex():Int { - return boneIndex; - } - - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var bone:Bone = skeleton.bones[boneIndex]; - if (bone.active) bone.shearX = getRelativeValue(time, alpha, blend, bone.shearX, bone.data.shearX); + public function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection) { + pose.shearX = getRelativeValue(time, alpha, blend, pose.shearX, setup.shearX); } } diff --git a/spine-haxe/spine-haxe/spine/animation/ShearYTimeline.hx b/spine-haxe/spine-haxe/spine/animation/ShearYTimeline.hx index ccf6e3400..ea046c054 100644 --- a/spine-haxe/spine-haxe/spine/animation/ShearYTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/ShearYTimeline.hx @@ -29,26 +29,13 @@ package spine.animation; -import spine.Bone; -import spine.Event; -import spine.Skeleton; - /** Changes a bone's local Bone.shearY. */ -class ShearYTimeline extends CurveTimeline1 implements BoneTimeline { - private var boneIndex:Int = 0; - +class ShearYTimeline extends BoneTimeline1 { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { - super(frameCount, bezierCount, [Property.shearY + "|" + boneIndex]); - this.boneIndex = boneIndex; + super(frameCount, bezierCount, boneIndex, Property.shearY); } - public function getBoneIndex():Int { - return boneIndex; - } - - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var bone:Bone = skeleton.bones[boneIndex]; - if (bone.active) bone.shearY = getRelativeValue(time, alpha, blend, bone.shearY, bone.data.shearY); + public function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection) { + pose.shearY = getRelativeValue(time, alpha, blend, pose.shearY, setup.shearY); } } diff --git a/spine-haxe/spine-haxe/spine/animation/SliderMixTimeline.hx b/spine-haxe/spine-haxe/spine/animation/SliderMixTimeline.hx new file mode 100644 index 000000000..5468e0532 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/animation/SliderMixTimeline.hx @@ -0,0 +1,49 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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 haxe.macro.Type.VarAccess; + +/** Changes a slider's spine.SliderPose.mix. */ +class SliderMixTimeline extends ConstraintTimeline1 { + public function new (frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.sliderMix); + } + + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + + var constraint = cast(skeleton.constraints[constraintIndex], Slider); + if (constraint.active) { + var pose = appliedPose ? constraint.applied : constraint.pose; + pose.mix = getAbsoluteValue(time, alpha, blend, pose.mix, constraint.data.setup.mix); + } + } +} diff --git a/spine-haxe/spine-haxe/spine/animation/SliderTimeline.hx b/spine-haxe/spine-haxe/spine/animation/SliderTimeline.hx new file mode 100644 index 000000000..eb80803b5 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/animation/SliderTimeline.hx @@ -0,0 +1,47 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +/** Changes a slider's spine.SliderPose.time. */ +class SliderTimeline extends ConstraintTimeline1 { + public function new (frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, constraintIndex, Property.sliderTime); + } + + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + + var constraint = cast(skeleton.constraints[constraintIndex], Slider); + if (constraint.active) { + var pose = appliedPose ? constraint.applied : constraint.pose; + pose.time = getAbsoluteValue(time, alpha, blend, pose.time, constraint.data.setup.time); + } + } +} diff --git a/spine-haxe/spine-haxe/spine/animation/SlotCurveTimeline.hx b/spine-haxe/spine-haxe/spine/animation/SlotCurveTimeline.hx new file mode 100644 index 000000000..226a901e9 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/animation/SlotCurveTimeline.hx @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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; + +abstract class SlotCurveTimeline extends CurveTimeline implements SlotTimeline { + public final slotIndex:Int; + + public function new (frameCount:Int, bezierCount:Int, slotIndex:Int, propertyIds:...String) { + super(frameCount, bezierCount, ...propertyIds); + this.slotIndex = slotIndex; + } + + public function getSlotIndex () { + return slotIndex; + } + + public function apply (skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, + direction:MixDirection, appliedPose:Bool) { + + var slot = skeleton.slots[slotIndex]; + if (slot.bone.active) apply1(slot, appliedPose ? slot.applied : slot.pose, time, alpha, blend); + } + + abstract function apply1 (slot:Slot, pose:SlotPose, time:Float, alpha:Float, blend:MixBlend):Void; +} diff --git a/spine-haxe/spine-haxe/spine/animation/Timeline.hx b/spine-haxe/spine-haxe/spine/animation/Timeline.hx index 46eb76ed9..10f02a60e 100644 --- a/spine-haxe/spine-haxe/spine/animation/Timeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/Timeline.hx @@ -33,7 +33,7 @@ import spine.Event; import spine.Skeleton; /** The base class for all timelines. */ -class Timeline { +abstract class Timeline { /** Uniquely encodes both the type of this timeline and the skeleton properties that it affects. */ public var propertyIds:Array; /** The time in seconds and any other values for each frame. */ @@ -42,7 +42,7 @@ class Timeline { /** * @param propertyIds Unique identifiers for the properties the timeline modifies. */ - public function new(frameCount:Int, propertyIds:Array) { + public function new(frameCount:Int, propertyIds:...String) { this.propertyIds = propertyIds; frames = new Array(); frames.resize(frameCount * getFrameEntries()); @@ -82,10 +82,9 @@ class Timeline { * @param blend Controls how mixing is applied when alpha < 1. * @param direction Indicates whether the timeline is mixing in or out. Used by timelines which perform instant transitions, * such as spine.animation.DrawOrderTimeline or spine.animation.AttachmentTimeline, and others such as spine.animation.ScaleTimeline. - */ - public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, direction:MixDirection):Void { - throw new SpineException("Timeline implementations must override apply()"); - } + * @param appliedPose True to to modify the applied pose. */ + abstract public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool):Void; /** Linear search using a stride of 1. * @param time Must be >= the first value in frames. diff --git a/spine-haxe/spine-haxe/spine/animation/TransformConstraintTimeline.hx b/spine-haxe/spine-haxe/spine/animation/TransformConstraintTimeline.hx index 4c2893a8b..37b085555 100644 --- a/spine-haxe/spine-haxe/spine/animation/TransformConstraintTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/TransformConstraintTimeline.hx @@ -29,15 +29,10 @@ package spine.animation; -import spine.Event; -import spine.Skeleton; -import spine.TransformConstraint; -import spine.TransformConstraintData; - /** Changes a transform constraint's spine.TransformConstraint.mixRotate, spine.TransformConstraint.mixX, * spine.TransformConstraint.mixY, spine.TransformConstraint.mixScaleX, * spine.TransformConstraint.mixScaleY, and spine.TransformConstraint.mixShearY. */ -class TransformConstraintTimeline extends CurveTimeline { +class TransformConstraintTimeline extends CurveTimeline implements ConstraintTimeline { static public inline var ENTRIES:Int = 7; private static inline var ROTATE:Int = 1; private static inline var X:Int = 2; @@ -50,15 +45,19 @@ class TransformConstraintTimeline extends CurveTimeline { * timeline is applied. */ public var constraintIndex:Int = 0; - public function new(frameCount:Int, bezierCount:Int, transformConstraintIndex:Int) { - super(frameCount, bezierCount, [Property.transformConstraint + "|" + transformConstraintIndex]); - this.constraintIndex = transformConstraintIndex; + public function new(frameCount:Int, bezierCount:Int, constraintIndex:Int) { + super(frameCount, bezierCount, Property.transformConstraint + "|" + constraintIndex); + this.constraintIndex = constraintIndex; } public override function getFrameEntries():Int { return ENTRIES; } + public function getConstraintIndex () { + return constraintIndex; + } + /** Sets the time, rotate mix, translate mix, scale mix, and shear mix for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ @@ -73,30 +72,30 @@ class TransformConstraintTimeline extends CurveTimeline { frames[frame + SHEARY] = mixShearY; } - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var constraint:TransformConstraint = skeleton.transformConstraints[constraintIndex]; - if (!constraint.active) - return; + public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, + blend:MixBlend, direction:MixDirection, appliedPose:Bool) { + + var constraint = cast(skeleton.constraints[constraintIndex], TransformConstraint); + if (!constraint.active) return; + var pose = appliedPose ? constraint.applied : constraint.pose; - var data:TransformConstraintData; if (time < frames[0]) { - data = constraint.data; + var setup = constraint.data.setup; switch (blend) { case MixBlend.setup: - constraint.mixRotate = data.mixRotate; - constraint.mixX = data.mixX; - constraint.mixY = data.mixY; - constraint.mixScaleX = data.mixScaleX; - constraint.mixScaleY = data.mixScaleY; - constraint.mixShearY = data.mixShearY; + pose.mixRotate = setup.mixRotate; + pose.mixX = setup.mixX; + pose.mixY = setup.mixY; + pose.mixScaleX = setup.mixScaleX; + pose.mixScaleY = setup.mixScaleY; + pose.mixShearY = setup.mixShearY; case MixBlend.first: - 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; + pose.mixRotate += (setup.mixRotate - pose.mixRotate) * alpha; + pose.mixX += (setup.mixX - pose.mixX) * alpha; + pose.mixY += (setup.mixY - pose.mixY) * alpha; + pose.mixScaleX += (setup.mixScaleX - pose.mixScaleX) * alpha; + pose.mixScaleY += (setup.mixScaleY - pose.mixScaleY) * alpha; + pose.mixShearY += (setup.mixShearY - pose.mixShearY) * alpha; } return; } @@ -136,21 +135,29 @@ class TransformConstraintTimeline extends CurveTimeline { shearY = getBezierValue(time, i, SHEARY, curveType + CurveTimeline.BEZIER_SIZE * 5 - CurveTimeline.BEZIER); } - if (blend == MixBlend.setup) { - 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; - 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.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; + switch (blend) { + case MixBlend.setup: + var setup = constraint.data.setup; + pose.mixRotate = setup.mixRotate + (rotate - setup.mixRotate) * alpha; + pose.mixX = setup.mixX + (x - setup.mixX) * alpha; + pose.mixY = setup.mixY + (y - setup.mixY) * alpha; + pose.mixScaleX = setup.mixScaleX + (scaleX - setup.mixScaleX) * alpha; + pose.mixScaleY = setup.mixScaleY + (scaleY - setup.mixScaleY) * alpha; + pose.mixShearY = setup.mixShearY + (shearY - setup.mixShearY) * alpha; + case MixBlend.first, MixBlend.replace: + pose.mixRotate += (rotate - pose.mixRotate) * alpha; + pose.mixX += (x - pose.mixX) * alpha; + pose.mixY += (y - pose.mixY) * alpha; + pose.mixScaleX += (scaleX - pose.mixScaleX) * alpha; + pose.mixScaleY += (scaleY - pose.mixScaleY) * alpha; + pose.mixShearY += (shearY - pose.mixShearY) * alpha; + case MixBlend.add: + pose.mixRotate += rotate * alpha; + pose.mixX += x * alpha; + pose.mixY += y * alpha; + pose.mixScaleX += scaleX * alpha; + pose.mixScaleY += scaleY * alpha; + pose.mixShearY += shearY * alpha; } } } diff --git a/spine-haxe/spine-haxe/spine/animation/TranslateTimeline.hx b/spine-haxe/spine-haxe/spine/animation/TranslateTimeline.hx index e49dbaaa0..14f9afc32 100644 --- a/spine-haxe/spine-haxe/spine/animation/TranslateTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/TranslateTimeline.hx @@ -34,66 +34,54 @@ import spine.Event; import spine.Skeleton; /** Changes a bone's local spine.Bone.x and spine.Bone.y. */ -class TranslateTimeline extends CurveTimeline2 implements BoneTimeline { - public var boneIndex:Int = 0; - +class TranslateTimeline extends BoneTimeline2 { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { - super(frameCount, bezierCount, [Property.x + "|" + boneIndex, Property.y + "|" + boneIndex]); - this.boneIndex = boneIndex; + super(frameCount, bezierCount, boneIndex, Property.x, Property.y); } - public function getBoneIndex():Int { - return boneIndex; - } - - override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var bone:Bone = skeleton.bones[boneIndex]; - if (!bone.active) - return; - + public function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection):Void { if (time < frames[0]) { switch (blend) { case MixBlend.setup: - bone.x = bone.data.x; - bone.y = bone.data.y; + pose.x = setup.x; + pose.y = setup.y; case MixBlend.first: - bone.x += (bone.data.x - bone.x) * alpha; - bone.y += (bone.data.y - bone.y) * alpha; + pose.x += (setup.x - pose.x) * alpha; + pose.y += (setup.y - pose.y) * alpha; } return; } var x:Float = 0, y:Float = 0; - var i:Int = Timeline.search(frames, time, CurveTimeline2.ENTRIES); - var curveType:Int = Std.int(curves[Std.int(i / CurveTimeline2.ENTRIES)]); + var i:Int = Timeline.search(frames, time, BoneTimeline2.ENTRIES); + var curveType:Int = Std.int(curves[Std.int(i / BoneTimeline2.ENTRIES)]); switch (curveType) { case CurveTimeline.LINEAR: var before:Float = frames[i]; - x = frames[i + CurveTimeline2.VALUE1]; - y = frames[i + CurveTimeline2.VALUE2]; - var t:Float = (time - before) / (frames[i + CurveTimeline2.ENTRIES] - before); - x += (frames[i + CurveTimeline2.ENTRIES + CurveTimeline2.VALUE1] - x) * t; - y += (frames[i + CurveTimeline2.ENTRIES + CurveTimeline2.VALUE2] - y) * t; + x = frames[i + BoneTimeline2.VALUE1]; + y = frames[i + BoneTimeline2.VALUE2]; + var t:Float = (time - before) / (frames[i + BoneTimeline2.ENTRIES] - before); + x += (frames[i + BoneTimeline2.ENTRIES + BoneTimeline2.VALUE1] - x) * t; + y += (frames[i + BoneTimeline2.ENTRIES + BoneTimeline2.VALUE2] - y) * t; case CurveTimeline.STEPPED: - x = frames[i + CurveTimeline2.VALUE1]; - y = frames[i + CurveTimeline2.VALUE2]; + x = frames[i + BoneTimeline2.VALUE1]; + y = frames[i + BoneTimeline2.VALUE2]; default: - x = getBezierValue(time, i, CurveTimeline2.VALUE1, curveType - CurveTimeline.BEZIER); - y = getBezierValue(time, i, CurveTimeline2.VALUE2, curveType + CurveTimeline.BEZIER_SIZE - CurveTimeline.BEZIER); + x = getBezierValue(time, i, BoneTimeline2.VALUE1, curveType - CurveTimeline.BEZIER); + y = getBezierValue(time, i, BoneTimeline2.VALUE2, curveType + CurveTimeline.BEZIER_SIZE - CurveTimeline.BEZIER); } switch (blend) { case MixBlend.setup: - bone.x = bone.data.x + x * alpha; - bone.y = bone.data.y + y * alpha; + pose.x = setup.x + x * alpha; + pose.y = setup.y + y * alpha; case MixBlend.first, MixBlend.replace: - bone.x += (bone.data.x + x - bone.x) * alpha; - bone.y += (bone.data.y + y - bone.y) * alpha; + pose.x += (setup.x + x - pose.x) * alpha; + pose.y += (setup.y + y - pose.y) * alpha; case MixBlend.add: - bone.x += x * alpha; - bone.y += y * alpha; + pose.x += x * alpha; + pose.y += y * alpha; } } } diff --git a/spine-haxe/spine-haxe/spine/animation/TranslateXTimeline.hx b/spine-haxe/spine-haxe/spine/animation/TranslateXTimeline.hx index d09afac27..898d30423 100644 --- a/spine-haxe/spine-haxe/spine/animation/TranslateXTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/TranslateXTimeline.hx @@ -34,21 +34,13 @@ import spine.Event; import spine.Skeleton; /** Changes a bone's local spine.Bone.x. */ -class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline { - public var boneIndex:Int = 0; - +class TranslateXTimeline extends BoneTimeline1 { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { - super(frameCount, bezierCount, [Property.x + "|" + boneIndex]); + super(frameCount, bezierCount, boneIndex, Property.x); this.boneIndex = boneIndex; } - public function getBoneIndex():Int { - return boneIndex; - } - - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var bone:Bone = skeleton.bones[boneIndex]; - if (bone.active) bone.x = getRelativeValue(time, alpha, blend, bone.x, bone.data.x); + public function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection) { + pose.x = getRelativeValue(time, alpha, blend, pose.x, setup.x); } } diff --git a/spine-haxe/spine-haxe/spine/animation/TranslateYTimeline.hx b/spine-haxe/spine-haxe/spine/animation/TranslateYTimeline.hx index d2ee9e757..c85fe9e53 100644 --- a/spine-haxe/spine-haxe/spine/animation/TranslateYTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/TranslateYTimeline.hx @@ -29,26 +29,13 @@ package spine.animation; -import spine.Bone; -import spine.Event; -import spine.Skeleton; - /** Changes a bone's local y translation. */ -class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline { - public var boneIndex:Int = 0; - +class TranslateYTimeline extends BoneTimeline1 { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { - super(frameCount, bezierCount, [Property.y + "|" + boneIndex]); - this.boneIndex = boneIndex; + super(frameCount, bezierCount, boneIndex, Property.y); } - public function getBoneIndex():Int { - return boneIndex; - } - - public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Array, alpha:Float, blend:MixBlend, - direction:MixDirection):Void { - var bone:Bone = skeleton.bones[boneIndex]; - if (bone.active) bone.y = getRelativeValue(time, alpha, blend, bone.y, bone.data.y); + public function apply1 (pose:BoneLocal, setup:BoneLocal, time:Float, alpha:Float, blend:MixBlend, direction:MixDirection) { + pose.y = getRelativeValue(time, alpha, blend, pose.y, setup.y); } } diff --git a/spine-haxe/spine-haxe/spine/attachments/MeshAttachment.hx b/spine-haxe/spine-haxe/spine/attachments/MeshAttachment.hx index d5ac32222..14e483d83 100644 --- a/spine-haxe/spine-haxe/spine/attachments/MeshAttachment.hx +++ b/spine-haxe/spine-haxe/spine/attachments/MeshAttachment.hx @@ -125,11 +125,12 @@ class MeshAttachment extends VertexAttachment implements HasTextureRegion { i += 2; } return; + default: + u -= region.offsetX / textureWidth; + v -= (region.originalHeight - region.offsetY - region.height) / textureHeight; + width = region.originalWidth / textureWidth; + height = region.originalHeight / textureHeight; } - u -= region.offsetX / textureWidth; - v -= (region.originalHeight - region.offsetY - region.height) / textureHeight; - width = region.originalWidth / textureWidth; - height = region.originalHeight / textureHeight; } else if (region == null) { u = v = 0; width = height = 1; @@ -194,10 +195,10 @@ class MeshAttachment extends VertexAttachment implements HasTextureRegion { } /** If the attachment has a sequence, the region may be changed. */ - public override function computeWorldVertices(slot:Slot, start:Int, count:Int, worldVertices:Array, offset:Int, stride:Int):Void { + public override function computeWorldVertices(skeleton:Skeleton, slot:Slot, start:Int, count:Int, worldVertices:Array, offset:Int, stride:Int):Void { if (sequence != null) - sequence.apply(slot, this); - super.computeWorldVertices(slot, start, count, worldVertices, offset, stride); + sequence.apply(slot.applied, this); + super.computeWorldVertices(skeleton, slot, start, count, worldVertices, offset, stride); } /** Returns a new mesh with the parentMesh set to this mesh's parent mesh, if any, else to this mesh. */ diff --git a/spine-haxe/spine-haxe/spine/attachments/PointAttachment.hx b/spine-haxe/spine-haxe/spine/attachments/PointAttachment.hx index 6cf369690..6e5872e66 100644 --- a/spine-haxe/spine-haxe/spine/attachments/PointAttachment.hx +++ b/spine-haxe/spine-haxe/spine/attachments/PointAttachment.hx @@ -29,7 +29,7 @@ package spine.attachments; -import spine.Bone; +import spine.BonePose; import spine.Color; import spine.MathUtils; @@ -51,13 +51,13 @@ class PointAttachment extends VertexAttachment { super(name); } - public function computeWorldPosition(bone:Bone, point:Array):Array { + public function computeWorldPosition(bone:BonePose, point:Array):Array { point[0] = x * bone.a + y * bone.b + bone.worldX; point[1] = x * bone.c + y * bone.d + bone.worldY; return point; } - public function computeWorldRotation(bone:Bone):Float { + public function computeWorldRotation(bone:BonePose):Float { var r:Float = this.rotation * MathUtils.degRad, cos:Float = Math.cos(r), sin:Float = Math.sin(r); var x:Float = cos * bone.a + sin * bone.b; var y:Float = cos * bone.c + sin * bone.d; diff --git a/spine-haxe/spine-haxe/spine/attachments/RegionAttachment.hx b/spine-haxe/spine-haxe/spine/attachments/RegionAttachment.hx index b4a8d9c44..efe6e103a 100644 --- a/spine-haxe/spine-haxe/spine/attachments/RegionAttachment.hx +++ b/spine-haxe/spine-haxe/spine/attachments/RegionAttachment.hx @@ -66,13 +66,13 @@ class RegionAttachment extends Attachment implements HasTextureRegion { public var sequence:Sequence; /** For each of the 4 vertices, a pair of x,y values that is the local position of the vertex. - * + * * See RegionAttachment.updateRegion(). */ private var offsets:Array = new Array(); public var uvs:Array = new Array(); - /** + /** * @param name The attachment name. * @param path The path used to find the region for the attachment. */ @@ -148,17 +148,16 @@ class RegionAttachment extends Attachment implements HasTextureRegion { /** Transforms the attachment's four vertices to world coordinates. If the attachment has a RegionAttachment.sequence, the region may * be changed. - * + * * @see https://esotericsoftware.com/spine-runtime-skeletons#World-transforms World transforms in the Spine Runtimes Guide * @param worldVertices The output world vertices. Must have a length >= offset + 8. * @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, worldVertices:Array, offset:Int, stride:Int):Void { - if (sequence != null) - sequence.apply(slot, this); + if (sequence != null) sequence.apply(slot.applied, this); - var bone = slot.bone; var vertexOffset = this.offsets; + var bone = slot.bone.applied; var x = bone.worldX, y = bone.worldY; var a = bone.a, b = bone.b, c = bone.c, d = bone.d; var offsetX:Float = 0, offsetY:Float = 0; diff --git a/spine-haxe/spine-haxe/spine/attachments/VertexAttachment.hx b/spine-haxe/spine-haxe/spine/attachments/VertexAttachment.hx index d4e5a8601..ffca2f6a9 100644 --- a/spine-haxe/spine-haxe/spine/attachments/VertexAttachment.hx +++ b/spine-haxe/spine-haxe/spine/attachments/VertexAttachment.hx @@ -34,7 +34,7 @@ import spine.Skeleton; import spine.Slot; /** Base class for an attachment with vertices that are transformed by one or more bones and can be deformed by a slot's - * spine.Slot.deform. */ + * spine.SlotPose.deform. */ class VertexAttachment extends Attachment { private static var nextID:Int = 0; @@ -42,15 +42,19 @@ class VertexAttachment extends Attachment { * the vertex followed by that many bone indices, which is the index of the bone in spine.Skeleton.bones. Will be null * if this attachment has no weights. */ public var bones:Array; + /** The vertex positions in the bone's coordinate system. For a non-weighted attachment, the values are `x,y` * entries for each vertex. For a weighted attachment, the values are `x,y,weight` entries for each bone affecting * each vertex. */ public var vertices = new Array(); + /** The maximum number of world vertex values that can be output by * computeWorldVertices() using the `count` parameter. */ public var worldVerticesLength:Int = 0; + /** Returns a unique ID for this attachment. */ public var id:Int = nextID++; + /** Timelines for the timeline attachment are also applied to this attachment. * May be null if no attachment-specific timelines should be applied. */ public var timelineAttachment:VertexAttachment; @@ -60,7 +64,7 @@ class VertexAttachment extends Attachment { timelineAttachment = this; } - /** Transforms the attachment's local vertices to world coordinates. If the slot's spine.Slot.deform is + /** Transforms the attachment's local vertices to world coordinates. If the slot's spine.SlotPose.deform is * not empty, it is used to deform the vertices. * * @see https://esotericsoftware.com/spine-runtime-skeletons#World-transforms World transforms in the Spine Runtimes Guide @@ -70,61 +74,42 @@ class VertexAttachment extends Attachment { * `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:Array, offset:Int, stride:Int):Void { + public function computeWorldVertices(skeleton:Skeleton, slot:Slot, start:Int, count:Int, worldVertices:Array, offset:Int, stride:Int):Void { count = offset + (count >> 1) * stride; - var skeleton:Skeleton = slot.skeleton; - var deform:Array = slot.deform; - - var v:Int, w:Int, n:Int, i:Int, skip:Int, b:Int, f:Int; - var vx:Float, vy:Float; - var wx:Float, wy:Float; - var bone:Bone; - + var deform:Array = slot.applied.deform; + var vertices = vertices; if (bones == null) { - if (deform.length > 0) - vertices = deform; - bone = slot.bone; - var x:Float = bone.worldX; - var y:Float = bone.worldY; - var a:Float = bone.a, - bb:Float = bone.b, - c:Float = bone.c, - d:Float = bone.d; - v = start; - w = offset; + if (deform.length > 0) vertices = deform; + var bone = slot.bone.applied; + var x = bone.worldX, y = bone.worldY; + var a = bone.a, b = bone.b, c = bone.c, d = bone.d; + var v = start, w = offset; while (w < count) { - vx = vertices[v]; - vy = vertices[v + 1]; - worldVertices[w] = vx * a + vy * bb + x; + var vx = vertices[v], vy = vertices[v + 1]; + worldVertices[w] = vx * a + vy * b + x; worldVertices[w + 1] = vx * c + vy * d + y; v += 2; w += stride; } return; } - v = 0; - skip = 0; - i = 0; + var v = 0, skip = 0, i = 0; while (i < start) { - n = bones[v]; + var n = bones[v]; v += n + 1; skip += n; i += 2; } - var skeletonBones:Array = skeleton.bones; + var skeletonBones = skeleton.bones; if (deform.length == 0) { - w = offset; - b = skip * 3; + var w = offset, b = skip * 3; while (w < count) { - wx = 0; - wy = 0; - n = bones[v++]; + var wx = 0., wy = 0.; + var n = bones[v++]; n += v; while (v < n) { - bone = skeletonBones[bones[v]]; - vx = vertices[b]; - vy = vertices[b + 1]; - var weight:Float = vertices[b + 2]; + var bone = skeletonBones[bones[v]].applied; + var vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight; wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight; v++; @@ -135,19 +120,14 @@ class VertexAttachment extends Attachment { w += stride; } } else { - w = offset; - b = skip * 3; - f = skip << 1; + var w = offset, b = skip * 3, f = skip << 1; while (w < count) { - wx = 0; - wy = 0; - n = bones[v++]; + var wx = 0., wy = 0.; + var n = bones[v++]; n += v; while (v < n) { - bone = skeletonBones[bones[v]]; - vx = vertices[b] + deform[f]; - vy = vertices[b + 1] + deform[f + 1]; - var weight = vertices[b + 2]; + var bone = skeletonBones[bones[v]].applied; + var vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2]; wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight; wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight; v++; diff --git a/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx b/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx index 1730b2c29..366ccd684 100644 --- a/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx +++ b/spine-haxe/spine-haxe/spine/flixel/SkeletonSprite.hx @@ -201,7 +201,7 @@ class SkeletonSprite extends FlxObject for (slot in drawOrder) { var clippedVertexSize:Int = clipper.isClipping() ? 2 : vertexSize; if (!slot.bone.active) { - clipper.clipEndWithSlot(slot); + clipper.clipEnd(slot); continue; } @@ -239,7 +239,7 @@ class SkeletonSprite extends FlxObject clipper.clipStart(slot, clip); continue; } else { - clipper.clipEndWithSlot(slot); + clipper.clipEnd(slot); continue; } @@ -312,7 +312,7 @@ class SkeletonSprite extends FlxObject mesh.draw(); } - clipper.clipEndWithSlot(slot); + clipper.clipEnd(slot); } clipper.clipEnd(); } diff --git a/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx b/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx index 8a60d8534..c429302e8 100644 --- a/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx +++ b/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx @@ -111,7 +111,7 @@ class SkeletonSprite extends DisplayObject implements IAnimatable { for (slot in drawOrder) { if (!slot.bone.active) { - clipper.clipEndWithSlot(slot); + clipper.clipEnd(slot); continue; } @@ -178,13 +178,13 @@ class SkeletonSprite extends DisplayObject implements IAnimatable { clipper.clipStart(slot, clip); continue; } else { - clipper.clipEndWithSlot(slot); + clipper.clipEnd(slot); continue; } a = slot.color.a * attachmentColor.a; if (a == 0) { - clipper.clipEndWithSlot(slot); + clipper.clipEnd(slot); continue; } rgb = Color.rgb(Std.int(r * slot.color.r * attachmentColor.r), Std.int(g * slot.color.g * attachmentColor.g), @@ -230,7 +230,7 @@ class SkeletonSprite extends DisplayObject implements IAnimatable { painter.batchMesh(mesh); } - clipper.clipEndWithSlot(slot); + clipper.clipEnd(slot); } painter.state.blendMode = originalBlendMode; clipper.clipEnd();