From fafb58588208c2e29a15efcf5cb62ddfa7f2c0c1 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Tue, 12 Sep 2023 16:21:32 +0200 Subject: [PATCH] =?UTF-8?q?[haxe]=C2=A0Initial=20port=20complete.=20WIP.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spine-haxe/spine-haxe/spine/SequenceMode.hx | 24 +- spine-haxe/spine-haxe/spine/SkeletonBinary.hx | 169 +++++++----- spine-haxe/spine-haxe/spine/SkeletonJson.hx | 259 +++++++++++------- spine-haxe/spine-haxe/spine/Skin.hx | 3 +- spine-haxe/spine-haxe/spine/SpineException.hx | 7 + .../spine-haxe/spine/animation/Animation.hx | 13 +- .../spine/animation/AnimationState.hx | 110 +++++++- .../spine/animation/DeformTimeline.hx | 13 +- .../spine-haxe/spine/animation/Property.hx | 2 + .../spine/animation/RotateTimeline.hx | 2 +- .../spine/animation/SequenceTimeline.hx | 98 +++++++ .../spine-haxe/spine/animation/Timeline.hx | 2 +- .../spine-haxe/spine/animation/TrackEntry.hx | 1 + .../attachments/AtlasAttachmentLoader.hx | 63 ++--- .../spine/attachments/MeshAttachment.hx | 2 +- .../spine/attachments/RegionAttachment.hx | 2 +- .../spine/starling/SkeletonAnimation.hx | 1 - .../spine/starling/SkeletonSprite.hx | 4 +- 18 files changed, 533 insertions(+), 242 deletions(-) create mode 100644 spine-haxe/spine-haxe/spine/SpineException.hx create mode 100644 spine-haxe/spine-haxe/spine/animation/SequenceTimeline.hx diff --git a/spine-haxe/spine-haxe/spine/SequenceMode.hx b/spine-haxe/spine-haxe/spine/SequenceMode.hx index bfee3b4ba..f3225f84e 100644 --- a/spine-haxe/spine-haxe/spine/SequenceMode.hx +++ b/spine-haxe/spine-haxe/spine/SequenceMode.hx @@ -1,20 +1,24 @@ package spine; -class SequenceMode { - public static var hold(default, never):RotateMode = new SequenceMode("hold"); - public static var once(default, never):RotateMode = new SequenceMode("once"); - public static var loop(default, never):RotateMode = new SequenceMode("loop"); - public static var pingpong(default, never):RotateMode = new SequenceMode("pingpong"); - public static var onceReverse(default, never):RotateMode = new SequenceMode("onceReverse"); - public static var loopReverse(default, never):RotateMode = new SequenceMode("loopReverse"); - public static var pingpongReverse(default, never):RotateMode = new SequenceMode("pingpongReverse"); +import openfl.Vector; - public static var values(default, never):Vector = Vector.ofArray([tangent, chain, chainScale]); +class SequenceMode { + public static var hold(default, never):SequenceMode = new SequenceMode("hold", 0); + public static var once(default, never):SequenceMode = new SequenceMode("once", 1); + public static var loop(default, never):SequenceMode = new SequenceMode("loop", 2); + public static var pingpong(default, never):SequenceMode = new SequenceMode("pingpong", 3); + public static var onceReverse(default, never):SequenceMode = new SequenceMode("onceReverse", 4); + public static var loopReverse(default, never):SequenceMode = new SequenceMode("loopReverse", 5); + public static var pingpongReverse(default, never):SequenceMode = new SequenceMode("pingpongReverse", 6); + + public static var values(default, never):Vector = Vector.ofArray([hold, once, loop, pingpong, onceReverse, loopReverse, pingpongReverse]); public var name(default, null):String; + public var value:Int; - public function new(name:String) { + public function new(name:String, value:Int) { this.name = name; + this.value = value; } public static function fromName(name:String):SequenceMode { diff --git a/spine-haxe/spine-haxe/spine/SkeletonBinary.hx b/spine-haxe/spine-haxe/spine/SkeletonBinary.hx index 565dcf765..75ddd81b8 100644 --- a/spine-haxe/spine-haxe/spine/SkeletonBinary.hx +++ b/spine-haxe/spine-haxe/spine/SkeletonBinary.hx @@ -1,5 +1,6 @@ package spine; +import spine.animation.SequenceTimeline; import openfl.errors.ArgumentError; import openfl.errors.Error; import openfl.utils.ByteArray; @@ -69,6 +70,9 @@ class SkeletonBinary { private static inline var SLOT_RGB2:Int = 4; private static inline var SLOT_ALPHA:Int = 5; + private static inline var ATTACHMENT_DEFORM = 0; + private static inline var ATTACHMENT_SEQUENCE = 1; + private static inline var PATH_POSITION:Int = 0; private static inline var PATH_SPACING:Int = 1; private static inline var PATH_MIX:Int = 2; @@ -253,13 +257,14 @@ class SkeletonBinary { for (linkedMesh in linkedMeshes) { var skin:Skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); if (skin == null) - throw new Error("Skin not found: " + linkedMesh.skin); + throw new SpineException("Skin not found: " + linkedMesh.skin); var parent:Attachment = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); if (parent == null) - throw new Error("Parent mesh not found: " + linkedMesh.parent); - linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? cast(parent, VertexAttachment) : linkedMesh.mesh; + throw new SpineException("Parent mesh not found: " + linkedMesh.parent); + linkedMesh.mesh.timelineAttachment = linkedMesh.inheritTimeline ? cast(parent, VertexAttachment) : linkedMesh.mesh; linkedMesh.mesh.parentMesh = cast(parent, MeshAttachment); - linkedMesh.mesh.updateUVs(); + if (linkedMesh.mesh.region != null) + linkedMesh.mesh.updateRegion(); } linkedMeshes.length = 0; @@ -327,6 +332,16 @@ class SkeletonBinary { return skin; } + private function readSequence(input:BinaryInput):Sequence { + if (!input.readBoolean()) + return null; + var sequence = new Sequence(input.readInt(true)); + sequence.start = input.readInt(true); + sequence.digits = input.readInt(true); + sequence.setupIndex = input.readInt(true); + return sequence; + } + private function readAttachment(input:BinaryInput, skeletonData:SkeletonData, skin:Skin, slotIndex:Int, attachmentName:String, nonessential:Bool):Attachment { var vertexCount:Int; @@ -357,10 +372,11 @@ class SkeletonBinary { width = input.readFloat(); height = input.readFloat(); color = input.readInt32(); + var sequence = readSequence(input); if (path == null) path = name; - var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, path); + var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, path, sequence); if (region == null) return null; region.path = path; @@ -372,7 +388,9 @@ class SkeletonBinary { region.width = width * scale; region.height = height * scale; region.color.setFromRgba8888(color); - region.updateOffset(); + region.sequence = sequence; + if (sequence == null) + region.updateRegion(); return region; case AttachmentType.boundingbox: vertexCount = input.readInt(true); @@ -397,6 +415,7 @@ class SkeletonBinary { var triangles:Vector = readShortArray(input); vertices = readVertices(input, vertexCount); var hullLength:Int = input.readInt(true); + var sequence = readSequence(input); var edges:Vector = null; if (nonessential) { edges = readShortArray(input); @@ -406,7 +425,7 @@ class SkeletonBinary { if (path == null) path = name; - mesh = attachmentLoader.newMeshAttachment(skin, name, path); + mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence); if (mesh == null) return null; mesh.path = path; @@ -417,8 +436,10 @@ class SkeletonBinary { mesh.worldVerticesLength = vertexCount << 1; mesh.triangles = triangles; mesh.regionUVs = uvs; - mesh.updateUVs(); + if (sequence == null) + mesh.updateRegion(); mesh.hullLength = hullLength << 1; + mesh.sequence = sequence; if (nonessential) { mesh.edges = edges; mesh.width = width * scale; @@ -430,7 +451,8 @@ class SkeletonBinary { color = input.readInt32(); var skinName:String = input.readStringRef(); var parent:String = input.readStringRef(); - var inheritDeform:Bool = input.readBoolean(); + var inheritTimelines:Bool = input.readBoolean(); + var sequence = readSequence(input); if (nonessential) { width = input.readFloat(); height = input.readFloat(); @@ -438,16 +460,17 @@ class SkeletonBinary { if (path == null) path = name; - mesh = attachmentLoader.newMeshAttachment(skin, name, path); + mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence); if (mesh == null) return null; mesh.path = path; mesh.color.setFromRgba8888(color); + mesh.sequence = sequence; if (nonessential) { mesh.width = width * scale; mesh.height = height * scale; } - this.linkedMeshes.push(new LinkedMeshBinary(mesh, skinName, slotIndex, parent, inheritDeform)); + this.linkedMeshes.push(new LinkedMeshBinary(mesh, skinName, slotIndex, parent, inheritTimelines)); return mesh; case AttachmentType.path: var closed:Bool = input.readBoolean(); @@ -985,64 +1008,78 @@ class SkeletonBinary { var attachmentName:String = input.readStringRef(); var attachment:VertexAttachment = cast(skin.getAttachment(slotIndex, attachmentName), VertexAttachment); if (attachment == null) - throw new Error("Vertex attachment not found: " + attachmentName); - var weighted:Bool = attachment.bones != null; - var vertices:Vector = attachment.vertices; - var deformLength:Int = weighted ? Std.int(vertices.length / 3 * 2) : vertices.length; - + throw new SpineException("Vertex attachment not found: " + attachmentName); + var timelineType = input.readByte(); frameCount = input.readInt(true); frameLast = frameCount - 1; - bezierCount = input.readInt(true); - var deformTimeline:DeformTimeline = new DeformTimeline(frameCount, bezierCount, slotIndex, attachment); - time = input.readFloat(); - frame = 0; - bezier = 0; - while (true) { - var deform:Vector; - var end:Int = input.readInt(true); - if (end == 0) { - if (weighted) { - deform = new Vector(deformLength, true); - } else { - deform = vertices; - } - } else { - var v:Int, vn:Int; - deform = new Vector(deformLength, true); - var start:Int = input.readInt(true); - end += start; - if (scale == 1) { - for (v in start...end) { - deform[v] = input.readFloat(); - } - } else { - for (v in start...end) { - deform[v] = input.readFloat() * scale; - } - } - if (!weighted) { - for (v in 0...deform.length) { - deform[v] += vertices[v]; - } - } - } + switch (timelineType) { + case ATTACHMENT_DEFORM: + var weighted:Bool = attachment.bones != null; + var vertices:Vector = attachment.vertices; + var deformLength:Int = weighted ? Std.int(vertices.length / 3 * 2) : vertices.length; - deformTimeline.setFrame(frame, time, deform); - if (frame == frameLast) + bezierCount = input.readInt(true); + var deformTimeline:DeformTimeline = new DeformTimeline(frameCount, bezierCount, slotIndex, attachment); + + time = input.readFloat(); + frame = 0; + bezier = 0; + while (true) { + var deform:Vector; + var end:Int = input.readInt(true); + if (end == 0) { + if (weighted) { + deform = new Vector(deformLength, true); + } else { + deform = vertices; + } + } else { + var v:Int, vn:Int; + deform = new Vector(deformLength, true); + var start:Int = input.readInt(true); + end += start; + if (scale == 1) { + for (v in start...end) { + deform[v] = input.readFloat(); + } + } else { + for (v in start...end) { + deform[v] = input.readFloat() * scale; + } + } + if (!weighted) { + for (v in 0...deform.length) { + deform[v] += vertices[v]; + } + } + } + + deformTimeline.setFrame(frame, time, deform); + if (frame == frameLast) + break; + time2 = input.readFloat(); + switch (input.readByte()) { + case CURVE_STEPPED: + deformTimeline.setStepped(frame); + case CURVE_BEZIER: + SkeletonBinary.setBezier(input, deformTimeline, bezier++, frame, 0, time, time2, 0, 1, 1); + } + time = time2; + + frame++; + } + timelines.push(deformTimeline); + case ATTACHMENT_SEQUENCE: + var timeline = new SequenceTimeline(frameCount, slotIndex, cast(attachment, HasTextureRegion)); + for (frame in 0...frameCount) { + var time = input.readFloat(); + var modeAndIndex = input.readInt32(); + timeline.setFrame(frame, time, SequenceMode.values[modeAndIndex & 0xf], modeAndIndex >> 4, input.readFloat()); + } + timelines.push(timeline); break; - time2 = input.readFloat(); - switch (input.readByte()) { - case CURVE_STEPPED: - deformTimeline.setStepped(frame); - case CURVE_BEZIER: - SkeletonBinary.setBezier(input, deformTimeline, bezier++, frame, 0, time, time2, 0, 1, 1); - } - time = time2; - - frame++; } - timelines.push(deformTimeline); } } } @@ -1193,13 +1230,13 @@ class LinkedMeshBinary { public var skin(default, null):String; public var slotIndex(default, null):Int; public var mesh(default, null):MeshAttachment; - public var inheritDeform(default, null):Bool; + public var inheritTimeline(default, null):Bool; - public function new(mesh:MeshAttachment, skin:String, slotIndex:Int, parent:String, inheritDeform:Bool) { + public function new(mesh:MeshAttachment, skin:String, slotIndex:Int, parent:String, inheritTimeline:Bool) { this.mesh = mesh; this.skin = skin; this.slotIndex = slotIndex; this.parent = parent; - this.inheritDeform = inheritDeform; + this.inheritTimeline = inheritTimeline; } } diff --git a/spine-haxe/spine-haxe/spine/SkeletonJson.hx b/spine-haxe/spine-haxe/spine/SkeletonJson.hx index 3344a95c9..b17d3f30f 100644 --- a/spine-haxe/spine-haxe/spine/SkeletonJson.hx +++ b/spine-haxe/spine-haxe/spine/SkeletonJson.hx @@ -1,5 +1,6 @@ package spine; +import spine.animation.SequenceTimeline; import haxe.Json; import openfl.errors.ArgumentError; import openfl.errors.Error; @@ -97,7 +98,7 @@ class SkeletonJson { if (parentName != null) { parent = skeletonData.findBone(parentName); if (parent == null) - throw new Error("Parent bone not found: " + parentName); + throw new SpineException("Parent bone not found: " + parentName); } boneData = new BoneData(skeletonData.bones.length, Reflect.getProperty(boneMap, "name"), parent); boneData.length = getFloat(Reflect.getProperty(boneMap, "length")) * scale; @@ -126,7 +127,7 @@ class SkeletonJson { var boneName:String = Reflect.getProperty(slotMap, "bone"); boneData = skeletonData.findBone(boneName); if (boneData == null) - throw new Error("Slot bone not found: " + boneName); + throw new SpineException("Slot bone not found: " + boneName); var slotData:SlotData = new SlotData(skeletonData.slots.length, slotName, boneData); var color:String = Reflect.getProperty(slotMap, "color"); @@ -155,22 +156,22 @@ class SkeletonJson { for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array)) { var bone:BoneData = skeletonData.findBone(boneName); if (bone == null) - throw new Error("IK constraint bone not found: " + boneName); + throw new SpineException("IK constraint bone not found: " + boneName); ikData.bones.push(bone); } ikData.target = skeletonData.findBone(Reflect.getProperty(constraintMap, "target")); if (ikData.target == null) - throw new Error("Target bone not found: " + Reflect.getProperty(constraintMap, "target")); + throw new SpineException("Target bone not found: " + Reflect.getProperty(constraintMap, "target")); + ikData.mix = getFloat(Reflect.getProperty(constraintMap, "mix"), 1); + ikData.softness = getFloat(Reflect.getProperty(constraintMap, "softness"), 0) * scale; ikData.bendDirection = (!Reflect.hasField(constraintMap, "bendPositive") || cast(Reflect.getProperty(constraintMap, "bendPositive"), Bool)) ? 1 : -1; ikData.compress = (Reflect.hasField(constraintMap, "compress") && cast(Reflect.getProperty(constraintMap, "compress"), Bool)); ikData.stretch = (Reflect.hasField(constraintMap, "stretch") && cast(Reflect.getProperty(constraintMap, "stretch"), Bool)); ikData.uniform = (Reflect.hasField(constraintMap, "uniform") && cast(Reflect.getProperty(constraintMap, "uniform"), Bool)); - ikData.softness = getFloat(Reflect.getProperty(constraintMap, "softness")) * scale; - ikData.mix = getFloat(Reflect.getProperty(constraintMap, "mix"), 1); skeletonData.ikConstraints.push(ikData); } @@ -186,13 +187,13 @@ class SkeletonJson { for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array)) { var bone = skeletonData.findBone(boneName); if (bone == null) - throw new Error("Transform constraint bone not found: " + boneName); + throw new SpineException("Transform constraint bone not found: " + boneName); transformData.bones.push(bone); } transformData.target = skeletonData.findBone(Reflect.getProperty(constraintMap, "target")); if (transformData.target == null) - throw new Error("Target bone not found: " + Reflect.getProperty(constraintMap, "target")); + throw new SpineException("Target bone not found: " + Reflect.getProperty(constraintMap, "target")); transformData.local = Reflect.hasField(constraintMap, "local") ? cast(Reflect.getProperty(constraintMap, "local"), Bool) : false; transformData.relative = Reflect.hasField(constraintMap, "relative") ? cast(Reflect.getProperty(constraintMap, "relative"), Bool) : false; @@ -225,13 +226,13 @@ class SkeletonJson { for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array)) { var bone = skeletonData.findBone(boneName); if (bone == null) - throw new Error("Path constraint bone not found: " + boneName); + throw new SpineException("Path constraint bone not found: " + boneName); pathData.bones.push(bone); } pathData.target = skeletonData.findSlot(Reflect.getProperty(constraintMap, "target")); if (pathData.target == null) - throw new Error("Path target slot not found: " + Reflect.getProperty(constraintMap, "target")); + throw new SpineException("Path target slot not found: " + Reflect.getProperty(constraintMap, "target")); pathData.positionMode = Reflect.hasField(constraintMap, "positionMode") ? PositionMode.fromName(Reflect.getProperty(constraintMap, "positionMode")) : PositionMode.percent; @@ -264,7 +265,7 @@ class SkeletonJson { for (ii in 0...bones.length) { var boneData:BoneData = skeletonData.findBone(bones[ii]); if (boneData == null) - throw new Error("Skin bone not found: " + bones[ii]); + throw new SpineException("Skin bone not found: " + bones[ii]); skin.bones.push(boneData); } } @@ -274,7 +275,7 @@ class SkeletonJson { for (ii in 0...ik.length) { var constraint:ConstraintData = skeletonData.findIkConstraint(ik[ii]); if (constraint == null) - throw new Error("Skin IK constraint not found: " + ik[ii]); + throw new SpineException("Skin IK constraint not found: " + ik[ii]); skin.constraints.push(constraint); } } @@ -284,7 +285,7 @@ class SkeletonJson { for (ii in 0...transform.length) { var constraint:ConstraintData = skeletonData.findTransformConstraint(transform[ii]); if (constraint == null) - throw new Error("Skin transform constraint not found: " + transform[ii]); + throw new SpineException("Skin transform constraint not found: " + transform[ii]); skin.constraints.push(constraint); } } @@ -294,7 +295,7 @@ class SkeletonJson { for (ii in 0...path.length) { var constraint:ConstraintData = skeletonData.findPathConstraint(path[ii]); if (constraint == null) - throw new Error("Skin path constraint not found: " + path[ii]); + throw new SpineException("Skin path constraint not found: " + path[ii]); skin.constraints.push(constraint); } } @@ -325,13 +326,14 @@ class SkeletonJson { for (linkedMesh in linkedMeshes) { var parentSkin:Skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); if (parentSkin == null) - throw new Error("Skin not found: " + linkedMesh.skin); + throw new SpineException("Skin not found: " + linkedMesh.skin); var parentMesh:Attachment = parentSkin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); if (parentMesh == null) - throw new Error("Parent mesh not found: " + linkedMesh.parent); - linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? cast(parentMesh, VertexAttachment) : linkedMesh.mesh; + throw new SpineException("Parent mesh not found: " + linkedMesh.parent); + linkedMesh.mesh.timelineAttachment = linkedMesh.inheritTimeline ? cast(parentMesh, VertexAttachment) : linkedMesh.mesh; linkedMesh.mesh.parentMesh = cast(parentMesh, MeshAttachment); - linkedMesh.mesh.updateUVs(); + if (linkedMesh.mesh.region != null) + linkedMesh.mesh.updateRegion(); } linkedMeshes.length = 0; @@ -359,6 +361,16 @@ class SkeletonJson { return skeletonData; } + private function readSequence(map:Object) { + if (map == null) + return null; + var sequence = new Sequence(getInt(map["count"], 0)); + sequence.start = getInt(map["start"], 1); + sequence.digits = getInt(map["digits"], 0); + sequence.setupIndex = getInt(map["setup"], 0); + return sequence; + } + private function readAttachment(map:Object, skin:Skin, slotIndex:Int, name:String, skeletonData:SkeletonData):Attachment { if (map["name"] != null) name = map["name"]; @@ -366,10 +378,12 @@ class SkeletonJson { var color:String; switch (AttachmentType.fromName(Reflect.hasField(map, "type") ? Reflect.getProperty(map, "type") : "region")) { case AttachmentType.region: - var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, map["path"] != null ? map["path"] : name); + var path = getString(map, "path", name); + var sequence = readSequence(map["sequence"]); + var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, path, sequence); if (region == null) return null; - region.path = map["path"] != null ? map["path"] : name; + region.path = path; region.x = getFloat(map["x"]) * scale; region.y = getFloat(map["y"]) * scale; region.scaleX = getFloat(map["scaleX"], 1); @@ -377,36 +391,48 @@ class SkeletonJson { region.rotation = getFloat(map["rotation"]); region.width = getFloat(map["width"]) * scale; region.height = getFloat(map["height"]) * scale; + region.sequence = sequence; + color = Reflect.getProperty(map, "color"); if (color != null) { region.color.setFromString(color); } - region.updateOffset(); + if (region.region != null) + region.updateRegion(); return region; case AttachmentType.mesh, AttachmentType.linkedmesh: - var mesh:MeshAttachment = attachmentLoader.newMeshAttachment(skin, name, map["path"] != null ? map["path"] : name); + var path = getString(map, "path", name); + var sequence = readSequence(map["sequence"]); + var mesh:MeshAttachment = attachmentLoader.newMeshAttachment(skin, name, path, sequence); if (mesh == null) return null; - mesh.path = map["path"] != null ? map["path"] : name; + mesh.path = path; + color = Reflect.getProperty(map, "color"); if (color != null) { mesh.color.setFromString(color); } + mesh.width = getFloat(map["width"]) * scale; mesh.height = getFloat(map["height"]) * scale; + mesh.sequence = sequence; + if (map["parent"] != null) { - var inheritDeform:Bool = map.hasOwnProperty("deform") ? cast(map["deform"], Bool) : true; - linkedMeshes.push(new LinkedMesh(mesh, map["skin"], slotIndex, map["parent"], inheritDeform)); + var inheritTimelines:Bool = map.hasOwnProperty("timelines") ? cast(map["timelines"], Bool) : true; + linkedMeshes.push(new LinkedMesh(mesh, map["skin"], slotIndex, map["parent"], inheritTimelines)); return mesh; } + var uvs:Vector = getFloatArray(map, "uvs"); readVertices(map, mesh, uvs.length); mesh.triangles = getIntArray(map, "triangles"); mesh.regionUVs = uvs; - mesh.updateUVs(); - mesh.hullLength = (getInt(map["hull"])) * 2; + if (mesh.region != null) + mesh.updateRegion(); + if (map["edges"] != null) mesh.edges = getIntArray(map, "edges"); + mesh.hullLength = (getInt(map["hull"])) * 2; return mesh; case AttachmentType.boundingbox: var box:BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name); @@ -448,7 +474,7 @@ class SkeletonJson { if (end != null) { var slot:SlotData = skeletonData.findSlot(end); if (slot == null) - throw new Error("Clipping end slot not found: " + end); + throw new SpineException("Clipping end slot not found: " + end); clip.endSlot = slot; } var vertexCount:Int = Std.parseInt(map["vertexCount"]); @@ -524,7 +550,7 @@ class SkeletonJson { var attachmentTimeline:AttachmentTimeline = new AttachmentTimeline(timelineMap.length, slotIndex); for (frame in 0...timelineMap.length) { keyMap = timelineMap[frame]; - attachmentTimeline.setFrame(frame, getFloat(Reflect.getProperty(keyMap, "time")), keyMap.name); + attachmentTimeline.setFrame(frame, getFloat(Reflect.getProperty(keyMap, "time")), getString(keyMap, "name", null)); } timelines.push(attachmentTimeline); } else if (timelineName == "rgba") { @@ -672,7 +698,7 @@ class SkeletonJson { timelines.push(rgb2Timeline); } else { - throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); + throw new SpineException("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); } } } @@ -682,7 +708,7 @@ class SkeletonJson { for (boneName in bones) { var boneIndex:Int = skeletonData.findBoneIndex(boneName); if (boneIndex == -1) - throw new Error("Bone not found: " + boneName); + throw new SpineException("Bone not found: " + boneName); var boneMap:Object = bones[boneName]; for (timelineName in boneMap) { timelineMap = boneMap[timelineName]; @@ -719,7 +745,7 @@ class SkeletonJson { var shearYTimeline:ShearYTimeline = new ShearYTimeline(timelineMap.length, timelineMap.length, boneIndex); timelines.push(readTimeline(timelineMap, shearYTimeline, 0, 1)); } else { - throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); + throw new SpineException("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); } } } @@ -839,7 +865,7 @@ class SkeletonJson { for (pathName in paths) { var index:Int = skeletonData.findPathConstraintIndex(pathName); if (index == -1) - throw new Error("Path constraint not found: " + pathName); + throw new SpineException("Path constraint not found: " + pathName); var pathData:PathConstraintData = skeletonData.pathConstraints[index]; var pathMap:Object = paths[pathName]; @@ -896,78 +922,99 @@ class SkeletonJson { } } - // Deform timelines. - var deforms:Object = Reflect.getProperty(map, "deform"); - for (deformName in deforms) { - var deformMap:Object = deforms[deformName]; - var skin:Skin = skeletonData.findSkin(deformName); + // Attachment timelines. + var attachments:Object = Reflect.getProperty(map, "attachments"); + for (attachmentsName in attachments) { + var attachmentsMap:Object = attachments[attachmentsName]; + var skin:Skin = skeletonData.findSkin(attachmentsName); if (skin == null) - throw new Error("Skin not found: " + deformName); + throw new SpineException("Skin not found: " + attachmentsName); - for (slotName in deformMap) { - slotMap = deformMap[slotName]; + for (slotMapName in attachmentsMap) { + slotMap = attachmentsMap[slotMapName]; slotIndex = skeletonData.findSlot(slotName).index; if (slotIndex == -1) - throw new Error("Slot not found: " + slotName); - for (timelineName in slotMap) { - timelineMap = slotMap[timelineName]; - keyMap = timelineMap[0]; - if (keyMap == null) - continue; - - var attachment:VertexAttachment = cast(skin.getAttachment(slotIndex, timelineName), VertexAttachment); + throw new SpineException("Slot not found: " + slotName); + for (attachmentMapName in slotMap) { + var attachmentMap = slotMap[attachmentMapName]; + var attachment:VertexAttachment = cast(skin.getAttachment(slotIndex, attachmentMapName), VertexAttachment); if (attachment == null) - throw new Error("Deform attachment not found: " + timelineName); - var weighted:Bool = attachment.bones != null; - var vertices:Vector = attachment.vertices; - var deformLength:Int = weighted ? Std.int(vertices.length / 3 * 2) : vertices.length; + throw new SpineException("Timeline attachment not found: " + timelineName); - var deformTimeline:DeformTimeline = new DeformTimeline(timelineMap.length, timelineMap.length, slotIndex, attachment); - time = getFloat(Reflect.getProperty(keyMap, "time")); - frame = 0; - bezier = 0; - while (true) { - var deform:Vector; - var verticesValue:Vector = Reflect.getProperty(keyMap, "vertices"); - if (verticesValue == null) { - deform = weighted ? new Vector(deformLength, true) : vertices; - } else { - deform = new Vector(deformLength, true); - var start:Int = getInt(Reflect.getProperty(keyMap, "offset")); - var temp:Vector = getFloatArray(keyMap, "vertices"); - for (i in 0...temp.length) { - deform[start + i] = temp[i]; - } - if (scale != 1) { - for (i in start...start + temp.length) { - deform[i] *= scale; + for (timelineMapName in attachmentMap) { + var timelineMap = attachmentMap[timelineMapName]; + var keyMap = timelineMap[0]; + if (keyMap == null) + continue; + + if (timelineMapName == "deform") { + var weighted:Bool = attachment.bones != null; + var vertices:Vector = attachment.vertices; + var deformLength:Int = weighted ? Std.int(vertices.length / 3 * 2) : vertices.length; + + var deformTimeline:DeformTimeline = new DeformTimeline(timelineMap.length, timelineMap.length, slotIndex, attachment); + time = getFloat(Reflect.getProperty(keyMap, "time")); + frame = 0; + bezier = 0; + while (true) { + var deform:Vector; + var verticesValue:Vector = Reflect.getProperty(keyMap, "vertices"); + if (verticesValue == null) { + deform = weighted ? new Vector(deformLength, true) : vertices; + } else { + deform = new Vector(deformLength, true); + var start:Int = getInt(Reflect.getProperty(keyMap, "offset")); + var temp:Vector = getFloatArray(keyMap, "vertices"); + for (i in 0...temp.length) { + deform[start + i] = temp[i]; + } + if (scale != 1) { + for (i in start...start + temp.length) { + deform[i] *= scale; + } + } + if (!weighted) { + for (i in 0...deformLength) { + deform[i] += vertices[i]; + } + } } - } - if (!weighted) { - for (i in 0...deformLength) { - deform[i] += vertices[i]; + + deformTimeline.setFrame(frame, time, deform); + nextMap = timelineMap[frame + 1]; + if (nextMap == null) { + deformTimeline.shrink(bezier); + break; } + time2 = getFloat(Reflect.getProperty(nextMap, "time")); + curve = keyMap.curve; + if (curve != null) { + bezier = readCurve(curve, deformTimeline, bezier, frame, 0, time, time2, 0, 1, 1); + } + time = time2; + keyMap = nextMap; + + frame++; } - } - deformTimeline.setFrame(frame, time, deform); - nextMap = timelineMap[frame + 1]; - if (nextMap == null) { - deformTimeline.shrink(bezier); - break; + timelines.push(deformTimeline); + } else if (timelineMapName == "sequence") { + var timeline = new SequenceTimeline(timelineMap.length, slotIndex, cast(attachment, HasTextureRegion)); + var lastDelay:Float = 0; + var frame:Int = 0; + while (frame < timelineMap.length) { + var delay = getFloat(keyMap["delay"], lastDelay); + var time = getFloat(keyMap["time"], 0); + var mode = SequenceMode.fromName(getString(keyMap, "mode", "hold")); + var index = getInt(keyMap["index"], 0); + timeline.setFrame(frame, time, mode, index, delay); + lastDelay = delay; + keyMap = timelineMap[frame + 1]; + frame++; + } + timelines.push(timeline); } - time2 = getFloat(Reflect.getProperty(nextMap, "time")); - curve = keyMap.curve; - if (curve != null) { - bezier = readCurve(curve, deformTimeline, bezier, frame, 0, time, time2, 0, 1, 1); - } - time = time2; - keyMap = nextMap; - - frame++; } - - timelines.push(deformTimeline); } } } @@ -993,7 +1040,7 @@ class SkeletonJson { for (offsetMap in offsets) { slotIndex = skeletonData.findSlot(Reflect.getProperty(offsetMap, "slot")).index; if (slotIndex == -1) - throw new Error("Slot not found: " + Reflect.getProperty(offsetMap, "slot")); + throw new SpineException("Slot not found: " + Reflect.getProperty(offsetMap, "slot")); // Collect unchanged items. while (originalIndex != slotIndex) { unchanged[unchangedIndex++] = originalIndex++; @@ -1028,7 +1075,7 @@ class SkeletonJson { for (eventMap in eventsMap) { var eventData:EventData = skeletonData.findEvent(Reflect.getProperty(eventMap, "name")); if (eventData == null) - throw new Error("Event not found: " + Reflect.getProperty(eventMap, "name")); + throw new SpineException("Event not found: " + Reflect.getProperty(eventMap, "name")); var event:Event = new Event(getFloat(Reflect.getProperty(eventMap, "time")), eventData); event.intValue = Reflect.hasField(eventMap, "int") ? getInt(Reflect.getProperty(eventMap, "int")) : eventData.intValue; event.floatValue = Reflect.hasField(eventMap, "float") ? getFloat(Reflect.getProperty(eventMap, "float")) : eventData.floatValue; @@ -1128,6 +1175,18 @@ class SkeletonJson { return bezier + 1; } + static private function getValue(map:Object, name:String, defaultValue:Dynamic):Dynamic { + if (map.hasOwnProperty(name)) + return map[name]; + return defaultValue; + } + + static private function getString(value:Object, name:String, defaultValue:String):String { + if (Std.isOfType(value[name], String)) + return cast(value[name], String); + return defaultValue; + } + static private function getFloat(value:Object, defaultValue:Float = 0):Float { if (Std.isOfType(value, Float)) return cast(value, Float); @@ -1146,12 +1205,12 @@ class SkeletonJson { return values; } - static private function getInt(value:Object):Int { + static private function getInt(value:Object, defaultValue:Int = 0):Int { if (Std.isOfType(value, Int)) return cast(value, Int); var intValue:Null = Std.parseInt(value); if (intValue == null) - intValue = 0; + intValue = defaultValue; return intValue; } @@ -1170,13 +1229,13 @@ class LinkedMesh { public var skin(default, null):String; public var slotIndex(default, null):Int; public var mesh(default, null):MeshAttachment; - public var inheritDeform(default, null):Bool; + public var inheritTimeline(default, null):Bool; - public function new(mesh:MeshAttachment, skin:String, slotIndex:Int, parent:String, inheritDeform:Bool) { + public function new(mesh:MeshAttachment, skin:String, slotIndex:Int, parent:String, inheritTimeline:Bool) { this.mesh = mesh; this.skin = skin; this.slotIndex = slotIndex; this.parent = parent; - this.inheritDeform = inheritDeform; + this.inheritTimeline = inheritTimeline; } } diff --git a/spine-haxe/spine-haxe/spine/Skin.hx b/spine-haxe/spine-haxe/spine/Skin.hx index 63f40ec94..fb5869203 100644 --- a/spine-haxe/spine-haxe/spine/Skin.hx +++ b/spine-haxe/spine-haxe/spine/Skin.hx @@ -100,7 +100,8 @@ class Skin { if (attachment.attachment == null) continue; if (Std.isOfType(attachment.attachment, MeshAttachment)) { - attachment.attachment = new MeshAttachment(attachment.attachment.name).newLinkedMesh(); + var mesh = cast(attachment.attachment, MeshAttachment); + attachment.attachment = new MeshAttachment(mesh.name, mesh.path).newLinkedMesh(); setAttachment(attachment.slotIndex, attachment.name, attachment.attachment); } else { attachment.attachment = attachment.attachment.copy(); diff --git a/spine-haxe/spine-haxe/spine/SpineException.hx b/spine-haxe/spine-haxe/spine/SpineException.hx new file mode 100644 index 000000000..7f2344cfa --- /dev/null +++ b/spine-haxe/spine-haxe/spine/SpineException.hx @@ -0,0 +1,7 @@ +package spine; + +class SpineException extends haxe.Exception { + public function new(message:String) { + super(message); + } +} diff --git a/spine-haxe/spine-haxe/spine/animation/Animation.hx b/spine-haxe/spine-haxe/spine/animation/Animation.hx index 47bd508be..aa3dd0338 100644 --- a/spine-haxe/spine-haxe/spine/animation/Animation.hx +++ b/spine-haxe/spine-haxe/spine/animation/Animation.hx @@ -15,18 +15,23 @@ class Animation { public function new(name:String, timelines:Vector, duration:Float) { if (name == null) - throw new ArgumentError("name cannot be null."); - if (timelines == null) - throw new ArgumentError("timelines cannot be null."); + throw new SpineException("name cannot be null."); _name = name; + setTimelines(timelines); + this.duration = duration; + } + + public function setTimelines(timelines:Vector) { + if (timelines == null) + throw new SpineException("timelines cannot be null."); _timelines = timelines; + _timelineIds = new Dictionary(); for (timeline in timelines) { var ids:Vector = timeline.propertyIds; for (id in ids) { _timelineIds[id] = true; } } - this.duration = duration; } public function hasTimeline(ids:Vector):Bool { diff --git a/spine-haxe/spine-haxe/spine/animation/AnimationState.hx b/spine-haxe/spine-haxe/spine/animation/AnimationState.hx index 06ba4f1c4..c0c21b386 100644 --- a/spine-haxe/spine-haxe/spine/animation/AnimationState.hx +++ b/spine-haxe/spine-haxe/spine/animation/AnimationState.hx @@ -181,14 +181,22 @@ class AnimationState { } else { var timelineMode:Vector = current.timelineMode; - var firstFrame:Bool = current.timelinesRotation.length == 0; + var shortestRotation = current.shortestRotation; + var firstFrame:Bool = !shortestRotation && current.timelinesRotation.length != timelineCount << 1; if (firstFrame) current.timelinesRotation.length = timelineCount << 1; for (ii in 0...timelineCount) { var timeline:Timeline = timelines[ii]; var timelineBlend:MixBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup; - timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend, MixDirection.mixIn); + if (!shortestRotation && Std.isOfType(timeline, RotateTimeline)) { + this.applyRotateTimeline(cast(timeline, RotateTimeline), skeleton, applyTime, mix, timelineBlend, current.timelinesRotation, ii << 1, + firstFrame); + } else if (Std.isOfType(timeline, AttachmentTimeline)) { + this.applyAttachmentTimeline(cast(timeline, AttachmentTimeline), skeleton, applyTime, blend, true); + } else { + timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend, MixDirection.mixIn); + } } } queueEvents(current, animationTime); @@ -255,8 +263,9 @@ class AnimationState { } else { var timelineMode:Vector = from.timelineMode; var timelineHoldMix:Vector = from.timelineHoldMix; + var shortestRotation = from.shortestRotation; - var firstFrame:Bool = from.timelinesRotation.length != timelineCount << 1; + var firstFrame:Bool = !shortestRotation && from.timelinesRotation.length != timelineCount << 1; if (firstFrame) from.timelinesRotation.length = timelineCount << 1; var timelinesRotation:Vector = from.timelinesRotation; @@ -290,9 +299,15 @@ class AnimationState { from.totalAlpha += alpha; - if (drawOrder && Std.isOfType(timeline, DrawOrderTimeline) && timelineBlend == MixBlend.setup) - direction = MixDirection.mixIn; - timeline.apply(skeleton, animationLast, applyTime, applyEvents, alpha, timelineBlend, direction); + if (!shortestRotation && Std.isOfType(timeline, RotateTimeline)) { + applyRotateTimeline(cast(timeline, RotateTimeline), skeleton, applyTime, alpha, timelineBlend, from.timelinesRotation, i << 1, firstFrame); + } else if (Std.isOfType(timeline, AttachmentTimeline)) { + applyAttachmentTimeline(cast(timeline, AttachmentTimeline), skeleton, applyTime, timelineBlend, attachments); + } else { + if (drawOrder && Std.isOfType(timeline, DrawOrderTimeline) && timelineBlend == MixBlend.setup) + direction = MixDirection.mixIn; + timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction); + } } } @@ -305,6 +320,83 @@ class AnimationState { return mix; } + public function applyAttachmentTimeline(timeline:AttachmentTimeline, skeleton:Skeleton, time:Float, blend:MixBlend, attachments:Bool) { + var slot = skeleton.slots[timeline.slotIndex]; + if (!slot.bone.active) + return; + + if (time < timeline.frames[0]) { // Time is before first frame. + if (blend == MixBlend.setup || blend == MixBlend.first) + this.setAttachment(skeleton, slot, slot.data.attachmentName, attachments); + } else + this.setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search1(timeline.frames, time)], attachments); + + // If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later. + if (slot.attachmentState <= this.unkeyedState) + slot.attachmentState = this.unkeyedState + SETUP; + } + + public function applyRotateTimeline(timeline:RotateTimeline, skeleton:Skeleton, time:Float, alpha:Float, blend:MixBlend, timelinesRotation:Vector, + i:Int, firstFrame:Bool) { + if (firstFrame) + timelinesRotation[i] = 0; + + if (alpha == 1) { + timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn); + return; + } + + var bone = skeleton.bones[timeline.boneIndex]; + if (!bone.active) + return; + 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; + default: + return; + case MixBlend.first: + r1 = bone.rotation; + r2 = bone.data.rotation; + } + } else { + r1 = blend == MixBlend.setup ? bone.data.rotation : bone.rotation; + r2 = bone.data.rotation + timeline.getCurveValue(time); + } + + // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. + var total:Float = 0, diff:Float = r2 - r1; + diff -= (16384.0 - Std.int((16384.499999999996 - diff / 360.0))) * 360.0; + if (diff == 0) { + total = timelinesRotation[i]; + } else { + var lastTotal:Float = 0, lastDiff:Float = 0; + if (firstFrame) { + lastTotal = 0; + lastDiff = diff; + } else { + lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops. + lastDiff = timelinesRotation[i + 1]; // Difference between bones. + } + var current = diff > 0, dir = lastTotal >= 0; + // Detect cross at 0 (not 180). + if (MathUtils.signum(lastDiff) != MathUtils.signum(diff) && Math.abs(lastDiff) <= 90) { + // A cross after a 360 rotation is a loop. + if (Math.abs(lastTotal) > 180) + lastTotal += 360 * MathUtils.signum(lastTotal); + dir = current; + } + total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal. + if (dir != current) + total += 360 * MathUtils.signum(lastTotal); + timelinesRotation[i] = total; + } + timelinesRotation[i + 1] = diff; + bone.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); if (attachments) @@ -519,6 +611,9 @@ class AnimationState { entry.loop = loop; entry.holdPrevious = false; + entry.reverse = false; + entry.shortestRotation = false; + entry.eventThreshold = 0; entry.attachmentThreshold = 0; entry.drawOrderThreshold = 0; @@ -536,9 +631,10 @@ class AnimationState { entry.timeScale = 1; entry.alpha = 1; - entry.interruptAlpha = 1; entry.mixTime = 0; entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation); + entry.interruptAlpha = 1; + entry.totalAlpha = 0; entry.mixBlend = MixBlend.replace; return entry; } diff --git a/spine-haxe/spine-haxe/spine/animation/DeformTimeline.hx b/spine-haxe/spine-haxe/spine/animation/DeformTimeline.hx index 5f3378d3f..8cb0db0cc 100644 --- a/spine-haxe/spine-haxe/spine/animation/DeformTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/DeformTimeline.hx @@ -106,10 +106,10 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { if (!slot.bone.active) return; var slotAttachment:Attachment = slot.attachment; - - if (!Std.isOfType(slotAttachment, VertexAttachment) || cast(slotAttachment, VertexAttachment).deformAttachment != attachment) + if (slotAttachment == null) + return; + if (!Std.isOfType(slotAttachment, VertexAttachment) || cast(slotAttachment, VertexAttachment).timelineAttachment != attachment) return; - var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); var deform:Vector = slot.deform; if (deform.length == 0) @@ -128,6 +128,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { return; } deform.length = vertexCount; + var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); if (vertexAttachment.bones == null) { // Unweighted vertex positions. setupVertices = vertexAttachment.vertices; @@ -152,6 +153,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { var lastVertices:Vector = 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; @@ -172,6 +174,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { } else { switch (blend) { case MixBlend.setup: + var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. setupVertices = vertexAttachment.vertices; @@ -190,6 +193,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { 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; @@ -215,6 +219,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { 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; @@ -238,6 +243,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { } else { switch (blend) { case MixBlend.setup: + var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. setupVertices = vertexAttachment.vertices; @@ -259,6 +265,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline { deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha; } case MixBlend.add: + var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment); if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. setupVertices = vertexAttachment.vertices; diff --git a/spine-haxe/spine-haxe/spine/animation/Property.hx b/spine-haxe/spine-haxe/spine/animation/Property.hx index 7ff8440c8..28d78e85b 100644 --- a/spine-haxe/spine-haxe/spine/animation/Property.hx +++ b/spine-haxe/spine-haxe/spine/animation/Property.hx @@ -26,5 +26,7 @@ class Property { public static inline var pathConstraintSpacing:Int = 17; public static inline var pathConstraintMix:Int = 18; + public static inline var sequence:Int = 19; + public function new() {} } diff --git a/spine-haxe/spine-haxe/spine/animation/RotateTimeline.hx b/spine-haxe/spine-haxe/spine/animation/RotateTimeline.hx index ce7554e90..c5840b13c 100644 --- a/spine-haxe/spine-haxe/spine/animation/RotateTimeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/RotateTimeline.hx @@ -6,7 +6,7 @@ import spine.Event; import spine.Skeleton; class RotateTimeline extends CurveTimeline1 implements BoneTimeline { - private var boneIndex:Int = 0; + public var boneIndex:Int = 0; public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) { super(frameCount, bezierCount, Vector.ofArray([Property.rotate + "|" + boneIndex])); diff --git a/spine-haxe/spine-haxe/spine/animation/SequenceTimeline.hx b/spine-haxe/spine-haxe/spine/animation/SequenceTimeline.hx new file mode 100644 index 000000000..1e89688e2 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/animation/SequenceTimeline.hx @@ -0,0 +1,98 @@ +package spine.animation; + +import openfl.Vector; +import spine.attachments.VertexAttachment; +import spine.attachments.Attachment; + +class SequenceTimeline extends Timeline implements SlotTimeline { + static var ENTRIES = 3; + static var MODE = 1; + static var DELAY = 2; + + var slotIndex:Int; + var attachment:HasTextureRegion; + + public function new(frameCount:Int, slotIndex:Int, attachment:HasTextureRegion) { + super(frameCount, Vector.ofArray([ + Std.string(Property.sequence) + "|" + Std.string(slotIndex) + "|" + Std.string(attachment.sequence.id) + ])); + this.slotIndex = slotIndex; + this.attachment = attachment; + } + + public override function getFrameEntries():Int { + return SequenceTimeline.ENTRIES; + } + + public function getSlotIndex():Int { + return this.slotIndex; + } + + public function getAttachment():Attachment { + return cast(attachment, Attachment); + } + + /** Sets the time, mode, index, and frame time for the specified frame. + * @param frame Between 0 and frameCount, inclusive. + * @param time Seconds between frames. */ + public function setFrame(frame:Int, time:Float, mode:SequenceMode, index:Int, delay:Float) { + frame *= SequenceTimeline.ENTRIES; + frames[frame] = time; + frames[frame + SequenceTimeline.MODE] = mode.value | (index << 4); + frames[frame + SequenceTimeline.DELAY] = delay; + } + + public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Vector, alpha:Float, blend:MixBlend, + direction:MixDirection):Void { + var slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) + return; + var slotAttachment = slot.attachment; + var attachment = cast(this.attachment, Attachment); + if (slotAttachment != attachment) { + if (!Std.isOfType(slotAttachment, VertexAttachment) || cast(slotAttachment, VertexAttachment).timelineAttachment != attachment) + return; + } + + if (time < frames[0]) { // Time is before first frame. + if (blend == MixBlend.setup || blend == MixBlend.first) + slot.sequenceIndex = -1; + return; + } + + var i = Timeline.search(frames, time, SequenceTimeline.ENTRIES); + var before = frames[i]; + var modeAndIndex = Std.int(frames[i + SequenceTimeline.MODE]); + var delay = frames[i + SequenceTimeline.DELAY]; + + if (this.attachment.sequence == null) + return; + var index = modeAndIndex >> 4, + count = this.attachment.sequence.regions.length; + var mode = SequenceMode.values[modeAndIndex & 0xf]; + if (mode != SequenceMode.hold) { + index += Std.int(((time - before) / delay + 0.00001)); + switch (mode) { + case SequenceMode.once: + index = Std.int(Math.min(count - 1, index)); + case SequenceMode.loop: + index %= count; + case SequenceMode.pingpong: + var n = (count << 1) - 2; + index = n == 0 ? 0 : index % n; + if (index >= count) + index = n - index; + case SequenceMode.onceReverse: + index = Std.int(Math.max(count - 1 - index, 0)); + case SequenceMode.loopReverse: + index = count - 1 - (index % count); + case SequenceMode.pingpongReverse: + var n = (count << 1) - 2; + index = n == 0 ? 0 : (index + count - 1) % n; + if (index >= count) + index = n - index; + } + } + slot.sequenceIndex = index; + } +} diff --git a/spine-haxe/spine-haxe/spine/animation/Timeline.hx b/spine-haxe/spine-haxe/spine/animation/Timeline.hx index ca230f9ab..fd6eb6a70 100644 --- a/spine-haxe/spine-haxe/spine/animation/Timeline.hx +++ b/spine-haxe/spine-haxe/spine/animation/Timeline.hx @@ -26,7 +26,7 @@ class Timeline { } public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Vector, alpha:Float, blend:MixBlend, direction:MixDirection):Void { - trace("Timeline implementations must override apply()"); + throw new SpineException("Timeline implementations must override apply()"); } public static function search1(frames:Vector, time:Float):Int { diff --git a/spine-haxe/spine-haxe/spine/animation/TrackEntry.hx b/spine-haxe/spine-haxe/spine/animation/TrackEntry.hx index da9806f51..f64483bec 100644 --- a/spine-haxe/spine-haxe/spine/animation/TrackEntry.hx +++ b/spine-haxe/spine-haxe/spine/animation/TrackEntry.hx @@ -42,6 +42,7 @@ class TrackEntry implements Poolable { public var timelineMode:Vector = new Vector(); public var timelineHoldMix:Vector = new Vector(); public var timelinesRotation:Vector = new Vector(); + public var shortestRotation = false; public function new() {} diff --git a/spine-haxe/spine-haxe/spine/attachments/AtlasAttachmentLoader.hx b/spine-haxe/spine-haxe/spine/attachments/AtlasAttachmentLoader.hx index 2d6f8c1f5..d2bbe2b90 100644 --- a/spine-haxe/spine-haxe/spine/attachments/AtlasAttachmentLoader.hx +++ b/spine-haxe/spine-haxe/spine/attachments/AtlasAttachmentLoader.hx @@ -20,49 +20,34 @@ class AtlasAttachmentLoader implements AttachmentLoader { var path = sequence.getPath(basePath, i); var region = this.atlas.findRegion(path); if (region == null) - trace("Region not found in atlas: " + path + " (sequence: " + name + ")"); + throw new SpineException("Region not found in atlas: " + path + " (sequence: " + name + ")"); regions[i] = region; } } - public function newRegionAttachment(skin:Skin, name:String, path:String):RegionAttachment { - var region = atlas.findRegion(path); - if (region == null) { - trace("Region not found in atlas: " + path + " (region attachment: " + name + ")"); - return null; + public function newRegionAttachment(skin:Skin, name:String, path:String, sequence:Sequence):RegionAttachment { + var attachment = new RegionAttachment(name, path); + if (sequence != null) { + this.loadSequence(name, path, sequence); + } else { + var region = this.atlas.findRegion(path); + if (region == null) + throw new SpineException("Region not found in atlas: " + path + " (region attachment: " + name + ")"); + attachment.region = region; } - var attachment:RegionAttachment = new RegionAttachment(name); - attachment.rendererObject = region; - attachment.setUVs(region.u, region.v, region.u2, region.v2, region.degrees); - attachment.regionOffsetX = region.offsetX; - attachment.regionOffsetY = region.offsetY; - attachment.regionWidth = region.width; - attachment.regionHeight = region.height; - attachment.regionOriginalWidth = region.originalWidth; - attachment.regionOriginalHeight = region.originalHeight; return attachment; } - public function newMeshAttachment(skin:Skin, name:String, path:String):MeshAttachment { - var region = atlas.findRegion(path); - if (region == null) { - trace("Region not found in atlas: " + path + " (mesh attachment: " + name + ")"); - return null; + public function newMeshAttachment(skin:Skin, name:String, path:String, sequence:Sequence):MeshAttachment { + var attachment = new MeshAttachment(name, path); + if (sequence != null) { + this.loadSequence(name, path, sequence); + } else { + var region = atlas.findRegion(path); + if (region == null) + throw new SpineException("Region not found in atlas: " + path + " (mesh attachment: " + name + ")"); + attachment.region = region; } - - var attachment:MeshAttachment = new MeshAttachment(name); - attachment.rendererObject = region; - attachment.regionU = region.u; - attachment.regionV = region.v; - attachment.regionU2 = region.u2; - attachment.regionV2 = region.v2; - attachment.regionDegrees = region.degrees; - attachment.regionOffsetX = region.offsetX; - attachment.regionOffsetY = region.offsetY; - attachment.regionWidth = region.width; - attachment.regionHeight = region.height; - attachment.regionOriginalWidth = region.originalWidth; - attachment.regionOriginalHeight = region.originalHeight; return attachment; } @@ -81,14 +66,4 @@ class AtlasAttachmentLoader implements AttachmentLoader { public function newClippingAttachment(skin:Skin, name:String):ClippingAttachment { return new ClippingAttachment(name); } - - static public function nextPOT(value:Int):Int { - value--; - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - return value + 1; - } } diff --git a/spine-haxe/spine-haxe/spine/attachments/MeshAttachment.hx b/spine-haxe/spine-haxe/spine/attachments/MeshAttachment.hx index 872f285a3..8269248d3 100644 --- a/spine-haxe/spine-haxe/spine/attachments/MeshAttachment.hx +++ b/spine-haxe/spine-haxe/spine/attachments/MeshAttachment.hx @@ -27,7 +27,7 @@ class MeshAttachment extends VertexAttachment implements HasTextureRegion { public function updateRegion():Void { if (region == null) { - trace("Region not set."); + throw new SpineException("Region not set."); return; } var regionUVs = this.regionUVs; diff --git a/spine-haxe/spine-haxe/spine/attachments/RegionAttachment.hx b/spine-haxe/spine-haxe/spine/attachments/RegionAttachment.hx index 3a4d1c7d9..f3b4ff6a1 100644 --- a/spine-haxe/spine-haxe/spine/attachments/RegionAttachment.hx +++ b/spine-haxe/spine-haxe/spine/attachments/RegionAttachment.hx @@ -38,7 +38,7 @@ class RegionAttachment extends Attachment implements HasTextureRegion { public function updateRegion():Void { if (region == null) { - trace("Region not set."); + throw new SpineException("Region not set."); uvs[0] = 0; uvs[1] = 0; uvs[2] = 0; diff --git a/spine-haxe/spine-haxe/spine/starling/SkeletonAnimation.hx b/spine-haxe/spine-haxe/spine/starling/SkeletonAnimation.hx index c54870a28..99bce0d6f 100644 --- a/spine-haxe/spine-haxe/spine/starling/SkeletonAnimation.hx +++ b/spine-haxe/spine-haxe/spine/starling/SkeletonAnimation.hx @@ -18,7 +18,6 @@ class SkeletonAnimation extends SkeletonSprite implements IAnimatable { public function advanceTime(time:Float):Void { var stage = Starling.current.stage; - skeleton.update(time); state.update(time); state.apply(skeleton); skeleton.updateWorldTransform(); diff --git a/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx b/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx index 31d43086c..dcbbd9a02 100644 --- a/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx +++ b/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx @@ -80,7 +80,7 @@ class SkeletonSprite extends DisplayObject { verticesCount = verticesLength >> 1; if (worldVertices.length < verticesLength) worldVertices.length = verticesLength; - region.computeWorldVertices(slot.bone, worldVertices, 0, 2); + region.computeWorldVertices(slot, worldVertices, 0, 2); mesh = null; if (Std.isOfType(region.rendererObject, SkeletonMesh)) { @@ -220,7 +220,7 @@ class SkeletonSprite extends DisplayObject { if (Std.isOfType(attachment, RegionAttachment)) { var region:RegionAttachment = cast(slot.attachment, RegionAttachment); verticesLength = 8; - region.computeWorldVertices(slot.bone, worldVertices, 0, 2); + region.computeWorldVertices(slot, worldVertices, 0, 2); } else if (Std.isOfType(attachment, MeshAttachment)) { var mesh:MeshAttachment = cast(attachment, MeshAttachment); verticesLength = mesh.worldVerticesLength;