[haxe] Initial port complete. WIP.

This commit is contained in:
Mario Zechner 2023-09-12 16:21:32 +02:00
parent 9d8c32b496
commit fafb585882
18 changed files with 533 additions and 242 deletions

View File

@ -1,20 +1,24 @@
package spine; package spine;
class SequenceMode { import openfl.Vector;
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");
public static var values(default, never):Vector<SequenceMode> = 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<SequenceMode> = Vector.ofArray([hold, once, loop, pingpong, onceReverse, loopReverse, pingpongReverse]);
public var name(default, null):String; 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.name = name;
this.value = value;
} }
public static function fromName(name:String):SequenceMode { public static function fromName(name:String):SequenceMode {

View File

@ -1,5 +1,6 @@
package spine; package spine;
import spine.animation.SequenceTimeline;
import openfl.errors.ArgumentError; import openfl.errors.ArgumentError;
import openfl.errors.Error; import openfl.errors.Error;
import openfl.utils.ByteArray; import openfl.utils.ByteArray;
@ -69,6 +70,9 @@ class SkeletonBinary {
private static inline var SLOT_RGB2:Int = 4; private static inline var SLOT_RGB2:Int = 4;
private static inline var SLOT_ALPHA:Int = 5; 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_POSITION:Int = 0;
private static inline var PATH_SPACING:Int = 1; private static inline var PATH_SPACING:Int = 1;
private static inline var PATH_MIX:Int = 2; private static inline var PATH_MIX:Int = 2;
@ -253,13 +257,14 @@ class SkeletonBinary {
for (linkedMesh in linkedMeshes) { for (linkedMesh in linkedMeshes) {
var skin:Skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); var skin:Skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
if (skin == null) 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); var parent:Attachment = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
if (parent == null) if (parent == null)
throw new Error("Parent mesh not found: " + linkedMesh.parent); throw new SpineException("Parent mesh not found: " + linkedMesh.parent);
linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? cast(parent, VertexAttachment) : linkedMesh.mesh; linkedMesh.mesh.timelineAttachment = linkedMesh.inheritTimeline ? cast(parent, VertexAttachment) : linkedMesh.mesh;
linkedMesh.mesh.parentMesh = cast(parent, MeshAttachment); linkedMesh.mesh.parentMesh = cast(parent, MeshAttachment);
linkedMesh.mesh.updateUVs(); if (linkedMesh.mesh.region != null)
linkedMesh.mesh.updateRegion();
} }
linkedMeshes.length = 0; linkedMeshes.length = 0;
@ -327,6 +332,16 @@ class SkeletonBinary {
return skin; 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, private function readAttachment(input:BinaryInput, skeletonData:SkeletonData, skin:Skin, slotIndex:Int, attachmentName:String,
nonessential:Bool):Attachment { nonessential:Bool):Attachment {
var vertexCount:Int; var vertexCount:Int;
@ -357,10 +372,11 @@ class SkeletonBinary {
width = input.readFloat(); width = input.readFloat();
height = input.readFloat(); height = input.readFloat();
color = input.readInt32(); color = input.readInt32();
var sequence = readSequence(input);
if (path == null) if (path == null)
path = name; path = name;
var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, path); var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, path, sequence);
if (region == null) if (region == null)
return null; return null;
region.path = path; region.path = path;
@ -372,7 +388,9 @@ class SkeletonBinary {
region.width = width * scale; region.width = width * scale;
region.height = height * scale; region.height = height * scale;
region.color.setFromRgba8888(color); region.color.setFromRgba8888(color);
region.updateOffset(); region.sequence = sequence;
if (sequence == null)
region.updateRegion();
return region; return region;
case AttachmentType.boundingbox: case AttachmentType.boundingbox:
vertexCount = input.readInt(true); vertexCount = input.readInt(true);
@ -397,6 +415,7 @@ class SkeletonBinary {
var triangles:Vector<Int> = readShortArray(input); var triangles:Vector<Int> = readShortArray(input);
vertices = readVertices(input, vertexCount); vertices = readVertices(input, vertexCount);
var hullLength:Int = input.readInt(true); var hullLength:Int = input.readInt(true);
var sequence = readSequence(input);
var edges:Vector<Int> = null; var edges:Vector<Int> = null;
if (nonessential) { if (nonessential) {
edges = readShortArray(input); edges = readShortArray(input);
@ -406,7 +425,7 @@ class SkeletonBinary {
if (path == null) if (path == null)
path = name; path = name;
mesh = attachmentLoader.newMeshAttachment(skin, name, path); mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence);
if (mesh == null) if (mesh == null)
return null; return null;
mesh.path = path; mesh.path = path;
@ -417,8 +436,10 @@ class SkeletonBinary {
mesh.worldVerticesLength = vertexCount << 1; mesh.worldVerticesLength = vertexCount << 1;
mesh.triangles = triangles; mesh.triangles = triangles;
mesh.regionUVs = uvs; mesh.regionUVs = uvs;
mesh.updateUVs(); if (sequence == null)
mesh.updateRegion();
mesh.hullLength = hullLength << 1; mesh.hullLength = hullLength << 1;
mesh.sequence = sequence;
if (nonessential) { if (nonessential) {
mesh.edges = edges; mesh.edges = edges;
mesh.width = width * scale; mesh.width = width * scale;
@ -430,7 +451,8 @@ class SkeletonBinary {
color = input.readInt32(); color = input.readInt32();
var skinName:String = input.readStringRef(); var skinName:String = input.readStringRef();
var parent:String = input.readStringRef(); var parent:String = input.readStringRef();
var inheritDeform:Bool = input.readBoolean(); var inheritTimelines:Bool = input.readBoolean();
var sequence = readSequence(input);
if (nonessential) { if (nonessential) {
width = input.readFloat(); width = input.readFloat();
height = input.readFloat(); height = input.readFloat();
@ -438,16 +460,17 @@ class SkeletonBinary {
if (path == null) if (path == null)
path = name; path = name;
mesh = attachmentLoader.newMeshAttachment(skin, name, path); mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence);
if (mesh == null) if (mesh == null)
return null; return null;
mesh.path = path; mesh.path = path;
mesh.color.setFromRgba8888(color); mesh.color.setFromRgba8888(color);
mesh.sequence = sequence;
if (nonessential) { if (nonessential) {
mesh.width = width * scale; mesh.width = width * scale;
mesh.height = height * 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; return mesh;
case AttachmentType.path: case AttachmentType.path:
var closed:Bool = input.readBoolean(); var closed:Bool = input.readBoolean();
@ -985,13 +1008,17 @@ class SkeletonBinary {
var attachmentName:String = input.readStringRef(); var attachmentName:String = input.readStringRef();
var attachment:VertexAttachment = cast(skin.getAttachment(slotIndex, attachmentName), VertexAttachment); var attachment:VertexAttachment = cast(skin.getAttachment(slotIndex, attachmentName), VertexAttachment);
if (attachment == null) if (attachment == null)
throw new Error("Vertex attachment not found: " + attachmentName); throw new SpineException("Vertex attachment not found: " + attachmentName);
var timelineType = input.readByte();
frameCount = input.readInt(true);
frameLast = frameCount - 1;
switch (timelineType) {
case ATTACHMENT_DEFORM:
var weighted:Bool = attachment.bones != null; var weighted:Bool = attachment.bones != null;
var vertices:Vector<Float> = attachment.vertices; var vertices:Vector<Float> = attachment.vertices;
var deformLength:Int = weighted ? Std.int(vertices.length / 3 * 2) : vertices.length; var deformLength:Int = weighted ? Std.int(vertices.length / 3 * 2) : vertices.length;
frameCount = input.readInt(true);
frameLast = frameCount - 1;
bezierCount = input.readInt(true); bezierCount = input.readInt(true);
var deformTimeline:DeformTimeline = new DeformTimeline(frameCount, bezierCount, slotIndex, attachment); var deformTimeline:DeformTimeline = new DeformTimeline(frameCount, bezierCount, slotIndex, attachment);
@ -1043,6 +1070,16 @@ class SkeletonBinary {
frame++; frame++;
} }
timelines.push(deformTimeline); 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;
}
} }
} }
} }
@ -1193,13 +1230,13 @@ class LinkedMeshBinary {
public var skin(default, null):String; public var skin(default, null):String;
public var slotIndex(default, null):Int; public var slotIndex(default, null):Int;
public var mesh(default, null):MeshAttachment; 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.mesh = mesh;
this.skin = skin; this.skin = skin;
this.slotIndex = slotIndex; this.slotIndex = slotIndex;
this.parent = parent; this.parent = parent;
this.inheritDeform = inheritDeform; this.inheritTimeline = inheritTimeline;
} }
} }

View File

@ -1,5 +1,6 @@
package spine; package spine;
import spine.animation.SequenceTimeline;
import haxe.Json; import haxe.Json;
import openfl.errors.ArgumentError; import openfl.errors.ArgumentError;
import openfl.errors.Error; import openfl.errors.Error;
@ -97,7 +98,7 @@ class SkeletonJson {
if (parentName != null) { if (parentName != null) {
parent = skeletonData.findBone(parentName); parent = skeletonData.findBone(parentName);
if (parent == null) 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 = new BoneData(skeletonData.bones.length, Reflect.getProperty(boneMap, "name"), parent);
boneData.length = getFloat(Reflect.getProperty(boneMap, "length")) * scale; boneData.length = getFloat(Reflect.getProperty(boneMap, "length")) * scale;
@ -126,7 +127,7 @@ class SkeletonJson {
var boneName:String = Reflect.getProperty(slotMap, "bone"); var boneName:String = Reflect.getProperty(slotMap, "bone");
boneData = skeletonData.findBone(boneName); boneData = skeletonData.findBone(boneName);
if (boneData == null) 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 slotData:SlotData = new SlotData(skeletonData.slots.length, slotName, boneData);
var color:String = Reflect.getProperty(slotMap, "color"); var color:String = Reflect.getProperty(slotMap, "color");
@ -155,22 +156,22 @@ class SkeletonJson {
for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array<Dynamic>)) { for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array<Dynamic>)) {
var bone:BoneData = skeletonData.findBone(boneName); var bone:BoneData = skeletonData.findBone(boneName);
if (bone == null) 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.bones.push(bone);
} }
ikData.target = skeletonData.findBone(Reflect.getProperty(constraintMap, "target")); ikData.target = skeletonData.findBone(Reflect.getProperty(constraintMap, "target"));
if (ikData.target == null) 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") ikData.bendDirection = (!Reflect.hasField(constraintMap, "bendPositive")
|| cast(Reflect.getProperty(constraintMap, "bendPositive"), Bool)) ? 1 : -1; || cast(Reflect.getProperty(constraintMap, "bendPositive"), Bool)) ? 1 : -1;
ikData.compress = (Reflect.hasField(constraintMap, "compress") ikData.compress = (Reflect.hasField(constraintMap, "compress")
&& cast(Reflect.getProperty(constraintMap, "compress"), Bool)); && cast(Reflect.getProperty(constraintMap, "compress"), Bool));
ikData.stretch = (Reflect.hasField(constraintMap, "stretch") && cast(Reflect.getProperty(constraintMap, "stretch"), 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.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); skeletonData.ikConstraints.push(ikData);
} }
@ -186,13 +187,13 @@ class SkeletonJson {
for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array<Dynamic>)) { for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array<Dynamic>)) {
var bone = skeletonData.findBone(boneName); var bone = skeletonData.findBone(boneName);
if (bone == null) 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.bones.push(bone);
} }
transformData.target = skeletonData.findBone(Reflect.getProperty(constraintMap, "target")); transformData.target = skeletonData.findBone(Reflect.getProperty(constraintMap, "target"));
if (transformData.target == null) 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.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; 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<Dynamic>)) { for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array<Dynamic>)) {
var bone = skeletonData.findBone(boneName); var bone = skeletonData.findBone(boneName);
if (bone == null) 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.bones.push(bone);
} }
pathData.target = skeletonData.findSlot(Reflect.getProperty(constraintMap, "target")); pathData.target = skeletonData.findSlot(Reflect.getProperty(constraintMap, "target"));
if (pathData.target == null) 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, pathData.positionMode = Reflect.hasField(constraintMap,
"positionMode") ? PositionMode.fromName(Reflect.getProperty(constraintMap, "positionMode")) : PositionMode.percent; "positionMode") ? PositionMode.fromName(Reflect.getProperty(constraintMap, "positionMode")) : PositionMode.percent;
@ -264,7 +265,7 @@ class SkeletonJson {
for (ii in 0...bones.length) { for (ii in 0...bones.length) {
var boneData:BoneData = skeletonData.findBone(bones[ii]); var boneData:BoneData = skeletonData.findBone(bones[ii]);
if (boneData == null) 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); skin.bones.push(boneData);
} }
} }
@ -274,7 +275,7 @@ class SkeletonJson {
for (ii in 0...ik.length) { for (ii in 0...ik.length) {
var constraint:ConstraintData = skeletonData.findIkConstraint(ik[ii]); var constraint:ConstraintData = skeletonData.findIkConstraint(ik[ii]);
if (constraint == null) 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); skin.constraints.push(constraint);
} }
} }
@ -284,7 +285,7 @@ class SkeletonJson {
for (ii in 0...transform.length) { for (ii in 0...transform.length) {
var constraint:ConstraintData = skeletonData.findTransformConstraint(transform[ii]); var constraint:ConstraintData = skeletonData.findTransformConstraint(transform[ii]);
if (constraint == null) 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); skin.constraints.push(constraint);
} }
} }
@ -294,7 +295,7 @@ class SkeletonJson {
for (ii in 0...path.length) { for (ii in 0...path.length) {
var constraint:ConstraintData = skeletonData.findPathConstraint(path[ii]); var constraint:ConstraintData = skeletonData.findPathConstraint(path[ii]);
if (constraint == null) 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); skin.constraints.push(constraint);
} }
} }
@ -325,13 +326,14 @@ class SkeletonJson {
for (linkedMesh in linkedMeshes) { for (linkedMesh in linkedMeshes) {
var parentSkin:Skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); var parentSkin:Skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
if (parentSkin == null) 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); var parentMesh:Attachment = parentSkin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
if (parentMesh == null) if (parentMesh == null)
throw new Error("Parent mesh not found: " + linkedMesh.parent); throw new SpineException("Parent mesh not found: " + linkedMesh.parent);
linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? cast(parentMesh, VertexAttachment) : linkedMesh.mesh; linkedMesh.mesh.timelineAttachment = linkedMesh.inheritTimeline ? cast(parentMesh, VertexAttachment) : linkedMesh.mesh;
linkedMesh.mesh.parentMesh = cast(parentMesh, MeshAttachment); linkedMesh.mesh.parentMesh = cast(parentMesh, MeshAttachment);
linkedMesh.mesh.updateUVs(); if (linkedMesh.mesh.region != null)
linkedMesh.mesh.updateRegion();
} }
linkedMeshes.length = 0; linkedMeshes.length = 0;
@ -359,6 +361,16 @@ class SkeletonJson {
return skeletonData; 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 { private function readAttachment(map:Object, skin:Skin, slotIndex:Int, name:String, skeletonData:SkeletonData):Attachment {
if (map["name"] != null) if (map["name"] != null)
name = map["name"]; name = map["name"];
@ -366,10 +378,12 @@ class SkeletonJson {
var color:String; var color:String;
switch (AttachmentType.fromName(Reflect.hasField(map, "type") ? Reflect.getProperty(map, "type") : "region")) { switch (AttachmentType.fromName(Reflect.hasField(map, "type") ? Reflect.getProperty(map, "type") : "region")) {
case AttachmentType.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) if (region == null)
return null; return null;
region.path = map["path"] != null ? map["path"] : name; region.path = path;
region.x = getFloat(map["x"]) * scale; region.x = getFloat(map["x"]) * scale;
region.y = getFloat(map["y"]) * scale; region.y = getFloat(map["y"]) * scale;
region.scaleX = getFloat(map["scaleX"], 1); region.scaleX = getFloat(map["scaleX"], 1);
@ -377,36 +391,48 @@ class SkeletonJson {
region.rotation = getFloat(map["rotation"]); region.rotation = getFloat(map["rotation"]);
region.width = getFloat(map["width"]) * scale; region.width = getFloat(map["width"]) * scale;
region.height = getFloat(map["height"]) * scale; region.height = getFloat(map["height"]) * scale;
region.sequence = sequence;
color = Reflect.getProperty(map, "color"); color = Reflect.getProperty(map, "color");
if (color != null) { if (color != null) {
region.color.setFromString(color); region.color.setFromString(color);
} }
region.updateOffset(); if (region.region != null)
region.updateRegion();
return region; return region;
case AttachmentType.mesh, AttachmentType.linkedmesh: 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) if (mesh == null)
return null; return null;
mesh.path = map["path"] != null ? map["path"] : name; mesh.path = path;
color = Reflect.getProperty(map, "color"); color = Reflect.getProperty(map, "color");
if (color != null) { if (color != null) {
mesh.color.setFromString(color); mesh.color.setFromString(color);
} }
mesh.width = getFloat(map["width"]) * scale; mesh.width = getFloat(map["width"]) * scale;
mesh.height = getFloat(map["height"]) * scale; mesh.height = getFloat(map["height"]) * scale;
mesh.sequence = sequence;
if (map["parent"] != null) { if (map["parent"] != null) {
var inheritDeform:Bool = map.hasOwnProperty("deform") ? cast(map["deform"], Bool) : true; var inheritTimelines:Bool = map.hasOwnProperty("timelines") ? cast(map["timelines"], Bool) : true;
linkedMeshes.push(new LinkedMesh(mesh, map["skin"], slotIndex, map["parent"], inheritDeform)); linkedMeshes.push(new LinkedMesh(mesh, map["skin"], slotIndex, map["parent"], inheritTimelines));
return mesh; return mesh;
} }
var uvs:Vector<Float> = getFloatArray(map, "uvs"); var uvs:Vector<Float> = getFloatArray(map, "uvs");
readVertices(map, mesh, uvs.length); readVertices(map, mesh, uvs.length);
mesh.triangles = getIntArray(map, "triangles"); mesh.triangles = getIntArray(map, "triangles");
mesh.regionUVs = uvs; mesh.regionUVs = uvs;
mesh.updateUVs(); if (mesh.region != null)
mesh.hullLength = (getInt(map["hull"])) * 2; mesh.updateRegion();
if (map["edges"] != null) if (map["edges"] != null)
mesh.edges = getIntArray(map, "edges"); mesh.edges = getIntArray(map, "edges");
mesh.hullLength = (getInt(map["hull"])) * 2;
return mesh; return mesh;
case AttachmentType.boundingbox: case AttachmentType.boundingbox:
var box:BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name); var box:BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name);
@ -448,7 +474,7 @@ class SkeletonJson {
if (end != null) { if (end != null) {
var slot:SlotData = skeletonData.findSlot(end); var slot:SlotData = skeletonData.findSlot(end);
if (slot == null) if (slot == null)
throw new Error("Clipping end slot not found: " + end); throw new SpineException("Clipping end slot not found: " + end);
clip.endSlot = slot; clip.endSlot = slot;
} }
var vertexCount:Int = Std.parseInt(map["vertexCount"]); var vertexCount:Int = Std.parseInt(map["vertexCount"]);
@ -524,7 +550,7 @@ class SkeletonJson {
var attachmentTimeline:AttachmentTimeline = new AttachmentTimeline(timelineMap.length, slotIndex); var attachmentTimeline:AttachmentTimeline = new AttachmentTimeline(timelineMap.length, slotIndex);
for (frame in 0...timelineMap.length) { for (frame in 0...timelineMap.length) {
keyMap = timelineMap[frame]; 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); timelines.push(attachmentTimeline);
} else if (timelineName == "rgba") { } else if (timelineName == "rgba") {
@ -672,7 +698,7 @@ class SkeletonJson {
timelines.push(rgb2Timeline); timelines.push(rgb2Timeline);
} else { } 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) { for (boneName in bones) {
var boneIndex:Int = skeletonData.findBoneIndex(boneName); var boneIndex:Int = skeletonData.findBoneIndex(boneName);
if (boneIndex == -1) if (boneIndex == -1)
throw new Error("Bone not found: " + boneName); throw new SpineException("Bone not found: " + boneName);
var boneMap:Object = bones[boneName]; var boneMap:Object = bones[boneName];
for (timelineName in boneMap) { for (timelineName in boneMap) {
timelineMap = boneMap[timelineName]; timelineMap = boneMap[timelineName];
@ -719,7 +745,7 @@ class SkeletonJson {
var shearYTimeline:ShearYTimeline = new ShearYTimeline(timelineMap.length, timelineMap.length, boneIndex); var shearYTimeline:ShearYTimeline = new ShearYTimeline(timelineMap.length, timelineMap.length, boneIndex);
timelines.push(readTimeline(timelineMap, shearYTimeline, 0, 1)); timelines.push(readTimeline(timelineMap, shearYTimeline, 0, 1));
} else { } 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) { for (pathName in paths) {
var index:Int = skeletonData.findPathConstraintIndex(pathName); var index:Int = skeletonData.findPathConstraintIndex(pathName);
if (index == -1) 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 pathData:PathConstraintData = skeletonData.pathConstraints[index];
var pathMap:Object = paths[pathName]; var pathMap:Object = paths[pathName];
@ -896,28 +922,32 @@ class SkeletonJson {
} }
} }
// Deform timelines. // Attachment timelines.
var deforms:Object = Reflect.getProperty(map, "deform"); var attachments:Object = Reflect.getProperty(map, "attachments");
for (deformName in deforms) { for (attachmentsName in attachments) {
var deformMap:Object = deforms[deformName]; var attachmentsMap:Object = attachments[attachmentsName];
var skin:Skin = skeletonData.findSkin(deformName); var skin:Skin = skeletonData.findSkin(attachmentsName);
if (skin == null) if (skin == null)
throw new Error("Skin not found: " + deformName); throw new SpineException("Skin not found: " + attachmentsName);
for (slotName in deformMap) { for (slotMapName in attachmentsMap) {
slotMap = deformMap[slotName]; slotMap = attachmentsMap[slotMapName];
slotIndex = skeletonData.findSlot(slotName).index; slotIndex = skeletonData.findSlot(slotName).index;
if (slotIndex == -1) if (slotIndex == -1)
throw new Error("Slot not found: " + slotName); throw new SpineException("Slot not found: " + slotName);
for (timelineName in slotMap) { for (attachmentMapName in slotMap) {
timelineMap = slotMap[timelineName]; var attachmentMap = slotMap[attachmentMapName];
keyMap = timelineMap[0]; var attachment:VertexAttachment = cast(skin.getAttachment(slotIndex, attachmentMapName), VertexAttachment);
if (attachment == null)
throw new SpineException("Timeline attachment not found: " + timelineName);
for (timelineMapName in attachmentMap) {
var timelineMap = attachmentMap[timelineMapName];
var keyMap = timelineMap[0];
if (keyMap == null) if (keyMap == null)
continue; continue;
var attachment:VertexAttachment = cast(skin.getAttachment(slotIndex, timelineName), VertexAttachment); if (timelineMapName == "deform") {
if (attachment == null)
throw new Error("Deform attachment not found: " + timelineName);
var weighted:Bool = attachment.bones != null; var weighted:Bool = attachment.bones != null;
var vertices:Vector<Float> = attachment.vertices; var vertices:Vector<Float> = attachment.vertices;
var deformLength:Int = weighted ? Std.int(vertices.length / 3 * 2) : vertices.length; var deformLength:Int = weighted ? Std.int(vertices.length / 3 * 2) : vertices.length;
@ -968,6 +998,23 @@ class SkeletonJson {
} }
timelines.push(deformTimeline); 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);
}
}
} }
} }
} }
@ -993,7 +1040,7 @@ class SkeletonJson {
for (offsetMap in offsets) { for (offsetMap in offsets) {
slotIndex = skeletonData.findSlot(Reflect.getProperty(offsetMap, "slot")).index; slotIndex = skeletonData.findSlot(Reflect.getProperty(offsetMap, "slot")).index;
if (slotIndex == -1) 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. // Collect unchanged items.
while (originalIndex != slotIndex) { while (originalIndex != slotIndex) {
unchanged[unchangedIndex++] = originalIndex++; unchanged[unchangedIndex++] = originalIndex++;
@ -1028,7 +1075,7 @@ class SkeletonJson {
for (eventMap in eventsMap) { for (eventMap in eventsMap) {
var eventData:EventData = skeletonData.findEvent(Reflect.getProperty(eventMap, "name")); var eventData:EventData = skeletonData.findEvent(Reflect.getProperty(eventMap, "name"));
if (eventData == null) 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); 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.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; event.floatValue = Reflect.hasField(eventMap, "float") ? getFloat(Reflect.getProperty(eventMap, "float")) : eventData.floatValue;
@ -1128,6 +1175,18 @@ class SkeletonJson {
return bezier + 1; 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 { static private function getFloat(value:Object, defaultValue:Float = 0):Float {
if (Std.isOfType(value, Float)) if (Std.isOfType(value, Float))
return cast(value, Float); return cast(value, Float);
@ -1146,12 +1205,12 @@ class SkeletonJson {
return values; return values;
} }
static private function getInt(value:Object):Int { static private function getInt(value:Object, defaultValue:Int = 0):Int {
if (Std.isOfType(value, Int)) if (Std.isOfType(value, Int))
return cast(value, Int); return cast(value, Int);
var intValue:Null<Int> = Std.parseInt(value); var intValue:Null<Int> = Std.parseInt(value);
if (intValue == null) if (intValue == null)
intValue = 0; intValue = defaultValue;
return intValue; return intValue;
} }
@ -1170,13 +1229,13 @@ class LinkedMesh {
public var skin(default, null):String; public var skin(default, null):String;
public var slotIndex(default, null):Int; public var slotIndex(default, null):Int;
public var mesh(default, null):MeshAttachment; 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.mesh = mesh;
this.skin = skin; this.skin = skin;
this.slotIndex = slotIndex; this.slotIndex = slotIndex;
this.parent = parent; this.parent = parent;
this.inheritDeform = inheritDeform; this.inheritTimeline = inheritTimeline;
} }
} }

View File

@ -100,7 +100,8 @@ class Skin {
if (attachment.attachment == null) if (attachment.attachment == null)
continue; continue;
if (Std.isOfType(attachment.attachment, MeshAttachment)) { 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); setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
} else { } else {
attachment.attachment = attachment.attachment.copy(); attachment.attachment = attachment.attachment.copy();

View File

@ -0,0 +1,7 @@
package spine;
class SpineException extends haxe.Exception {
public function new(message:String) {
super(message);
}
}

View File

@ -15,18 +15,23 @@ class Animation {
public function new(name:String, timelines:Vector<Timeline>, duration:Float) { public function new(name:String, timelines:Vector<Timeline>, duration:Float) {
if (name == null) if (name == null)
throw new ArgumentError("name cannot be null."); throw new SpineException("name cannot be null.");
if (timelines == null)
throw new ArgumentError("timelines cannot be null.");
_name = name; _name = name;
setTimelines(timelines);
this.duration = duration;
}
public function setTimelines(timelines:Vector<Timeline>) {
if (timelines == null)
throw new SpineException("timelines cannot be null.");
_timelines = timelines; _timelines = timelines;
_timelineIds = new Dictionary<String, Bool>();
for (timeline in timelines) { for (timeline in timelines) {
var ids:Vector<String> = timeline.propertyIds; var ids:Vector<String> = timeline.propertyIds;
for (id in ids) { for (id in ids) {
_timelineIds[id] = true; _timelineIds[id] = true;
} }
} }
this.duration = duration;
} }
public function hasTimeline(ids:Vector<String>):Bool { public function hasTimeline(ids:Vector<String>):Bool {

View File

@ -181,16 +181,24 @@ class AnimationState {
} else { } else {
var timelineMode:Vector<Int> = current.timelineMode; var timelineMode:Vector<Int> = current.timelineMode;
var firstFrame:Bool = current.timelinesRotation.length == 0; var shortestRotation = current.shortestRotation;
var firstFrame:Bool = !shortestRotation && current.timelinesRotation.length != timelineCount << 1;
if (firstFrame) if (firstFrame)
current.timelinesRotation.length = timelineCount << 1; current.timelinesRotation.length = timelineCount << 1;
for (ii in 0...timelineCount) { for (ii in 0...timelineCount) {
var timeline:Timeline = timelines[ii]; var timeline:Timeline = timelines[ii];
var timelineBlend:MixBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup; var timelineBlend:MixBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup;
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); timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend, MixDirection.mixIn);
} }
} }
}
queueEvents(current, animationTime); queueEvents(current, animationTime);
events.length = 0; events.length = 0;
current.nextAnimationLast = animationTime; current.nextAnimationLast = animationTime;
@ -255,8 +263,9 @@ class AnimationState {
} else { } else {
var timelineMode:Vector<Int> = from.timelineMode; var timelineMode:Vector<Int> = from.timelineMode;
var timelineHoldMix:Vector<TrackEntry> = from.timelineHoldMix; var timelineHoldMix:Vector<TrackEntry> = 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) if (firstFrame)
from.timelinesRotation.length = timelineCount << 1; from.timelinesRotation.length = timelineCount << 1;
var timelinesRotation:Vector<Float> = from.timelinesRotation; var timelinesRotation:Vector<Float> = from.timelinesRotation;
@ -290,9 +299,15 @@ class AnimationState {
from.totalAlpha += alpha; from.totalAlpha += alpha;
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) if (drawOrder && Std.isOfType(timeline, DrawOrderTimeline) && timelineBlend == MixBlend.setup)
direction = MixDirection.mixIn; direction = MixDirection.mixIn;
timeline.apply(skeleton, animationLast, applyTime, applyEvents, alpha, timelineBlend, direction); timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction);
}
} }
} }
@ -305,6 +320,83 @@ class AnimationState {
return mix; 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<Float>,
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 { private function setAttachment(skeleton:Skeleton, slot:Slot, attachmentName:String, attachments:Bool):Void {
slot.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slot.data.index, attachmentName); slot.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slot.data.index, attachmentName);
if (attachments) if (attachments)
@ -519,6 +611,9 @@ class AnimationState {
entry.loop = loop; entry.loop = loop;
entry.holdPrevious = false; entry.holdPrevious = false;
entry.reverse = false;
entry.shortestRotation = false;
entry.eventThreshold = 0; entry.eventThreshold = 0;
entry.attachmentThreshold = 0; entry.attachmentThreshold = 0;
entry.drawOrderThreshold = 0; entry.drawOrderThreshold = 0;
@ -536,9 +631,10 @@ class AnimationState {
entry.timeScale = 1; entry.timeScale = 1;
entry.alpha = 1; entry.alpha = 1;
entry.interruptAlpha = 1;
entry.mixTime = 0; entry.mixTime = 0;
entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation); entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation);
entry.interruptAlpha = 1;
entry.totalAlpha = 0;
entry.mixBlend = MixBlend.replace; entry.mixBlend = MixBlend.replace;
return entry; return entry;
} }

View File

@ -106,10 +106,10 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline {
if (!slot.bone.active) if (!slot.bone.active)
return; return;
var slotAttachment:Attachment = slot.attachment; var slotAttachment:Attachment = slot.attachment;
if (slotAttachment == null)
if (!Std.isOfType(slotAttachment, VertexAttachment) || cast(slotAttachment, VertexAttachment).deformAttachment != attachment) return;
if (!Std.isOfType(slotAttachment, VertexAttachment) || cast(slotAttachment, VertexAttachment).timelineAttachment != attachment)
return; return;
var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment);
var deform:Vector<Float> = slot.deform; var deform:Vector<Float> = slot.deform;
if (deform.length == 0) if (deform.length == 0)
@ -128,6 +128,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline {
return; return;
} }
deform.length = vertexCount; deform.length = vertexCount;
var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment);
if (vertexAttachment.bones == null) { if (vertexAttachment.bones == null) {
// Unweighted vertex positions. // Unweighted vertex positions.
setupVertices = vertexAttachment.vertices; setupVertices = vertexAttachment.vertices;
@ -152,6 +153,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline {
var lastVertices:Vector<Float> = vertices[frames.length - 1]; var lastVertices:Vector<Float> = vertices[frames.length - 1];
if (alpha == 1) { if (alpha == 1) {
if (blend == MixBlend.add) { if (blend == 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; setupVertices = vertexAttachment.vertices;
@ -172,6 +174,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline {
} else { } else {
switch (blend) { switch (blend) {
case MixBlend.setup: case MixBlend.setup:
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; setupVertices = vertexAttachment.vertices;
@ -190,6 +193,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline {
deform[i] += (lastVertices[i] - deform[i]) * alpha; deform[i] += (lastVertices[i] - deform[i]) * alpha;
} }
case MixBlend.add: 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; setupVertices = vertexAttachment.vertices;
@ -215,6 +219,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline {
if (alpha == 1) { if (alpha == 1) {
if (blend == MixBlend.add) { if (blend == 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; setupVertices = vertexAttachment.vertices;
@ -238,6 +243,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline {
} else { } else {
switch (blend) { switch (blend) {
case MixBlend.setup: case MixBlend.setup:
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; setupVertices = vertexAttachment.vertices;
@ -259,6 +265,7 @@ class DeformTimeline extends CurveTimeline implements SlotTimeline {
deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha; deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha;
} }
case MixBlend.add: 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; setupVertices = vertexAttachment.vertices;

View File

@ -26,5 +26,7 @@ class Property {
public static inline var pathConstraintSpacing:Int = 17; public static inline var pathConstraintSpacing:Int = 17;
public static inline var pathConstraintMix:Int = 18; public static inline var pathConstraintMix:Int = 18;
public static inline var sequence:Int = 19;
public function new() {} public function new() {}
} }

View File

@ -6,7 +6,7 @@ import spine.Event;
import spine.Skeleton; import spine.Skeleton;
class RotateTimeline extends CurveTimeline1 implements BoneTimeline { 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) { public function new(frameCount:Int, bezierCount:Int, boneIndex:Int) {
super(frameCount, bezierCount, Vector.ofArray([Property.rotate + "|" + boneIndex])); super(frameCount, bezierCount, Vector.ofArray([Property.rotate + "|" + boneIndex]));

View File

@ -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 <code>frameCount</code>, 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<Event>, 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;
}
}

View File

@ -26,7 +26,7 @@ class Timeline {
} }
public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Vector<Event>, alpha:Float, blend:MixBlend, direction:MixDirection):Void { public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Vector<Event>, 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<Float>, time:Float):Int { public static function search1(frames:Vector<Float>, time:Float):Int {

View File

@ -42,6 +42,7 @@ class TrackEntry implements Poolable {
public var timelineMode:Vector<Int> = new Vector<Int>(); public var timelineMode:Vector<Int> = new Vector<Int>();
public var timelineHoldMix:Vector<TrackEntry> = new Vector<TrackEntry>(); public var timelineHoldMix:Vector<TrackEntry> = new Vector<TrackEntry>();
public var timelinesRotation:Vector<Float> = new Vector<Float>(); public var timelinesRotation:Vector<Float> = new Vector<Float>();
public var shortestRotation = false;
public function new() {} public function new() {}

View File

@ -20,49 +20,34 @@ class AtlasAttachmentLoader implements AttachmentLoader {
var path = sequence.getPath(basePath, i); var path = sequence.getPath(basePath, i);
var region = this.atlas.findRegion(path); var region = this.atlas.findRegion(path);
if (region == null) 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; regions[i] = region;
} }
} }
public function newRegionAttachment(skin:Skin, name:String, path:String):RegionAttachment { public function newRegionAttachment(skin:Skin, name:String, path:String, sequence:Sequence):RegionAttachment {
var region = atlas.findRegion(path); var attachment = new RegionAttachment(name, path);
if (region == null) { if (sequence != null) {
trace("Region not found in atlas: " + path + " (region attachment: " + name + ")"); this.loadSequence(name, path, sequence);
return null; } 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; return attachment;
} }
public function newMeshAttachment(skin:Skin, name:String, path:String):MeshAttachment { 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); var region = atlas.findRegion(path);
if (region == null) { if (region == null)
trace("Region not found in atlas: " + path + " (mesh attachment: " + name + ")"); throw new SpineException("Region not found in atlas: " + path + " (mesh attachment: " + name + ")");
return null; 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; return attachment;
} }
@ -81,14 +66,4 @@ class AtlasAttachmentLoader implements AttachmentLoader {
public function newClippingAttachment(skin:Skin, name:String):ClippingAttachment { public function newClippingAttachment(skin:Skin, name:String):ClippingAttachment {
return new ClippingAttachment(name); 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;
}
} }

View File

@ -27,7 +27,7 @@ class MeshAttachment extends VertexAttachment implements HasTextureRegion {
public function updateRegion():Void { public function updateRegion():Void {
if (region == null) { if (region == null) {
trace("Region not set."); throw new SpineException("Region not set.");
return; return;
} }
var regionUVs = this.regionUVs; var regionUVs = this.regionUVs;

View File

@ -38,7 +38,7 @@ class RegionAttachment extends Attachment implements HasTextureRegion {
public function updateRegion():Void { public function updateRegion():Void {
if (region == null) { if (region == null) {
trace("Region not set."); throw new SpineException("Region not set.");
uvs[0] = 0; uvs[0] = 0;
uvs[1] = 0; uvs[1] = 0;
uvs[2] = 0; uvs[2] = 0;

View File

@ -18,7 +18,6 @@ class SkeletonAnimation extends SkeletonSprite implements IAnimatable {
public function advanceTime(time:Float):Void { public function advanceTime(time:Float):Void {
var stage = Starling.current.stage; var stage = Starling.current.stage;
skeleton.update(time);
state.update(time); state.update(time);
state.apply(skeleton); state.apply(skeleton);
skeleton.updateWorldTransform(); skeleton.updateWorldTransform();

View File

@ -80,7 +80,7 @@ class SkeletonSprite extends DisplayObject {
verticesCount = verticesLength >> 1; verticesCount = verticesLength >> 1;
if (worldVertices.length < verticesLength) if (worldVertices.length < verticesLength)
worldVertices.length = verticesLength; worldVertices.length = verticesLength;
region.computeWorldVertices(slot.bone, worldVertices, 0, 2); region.computeWorldVertices(slot, worldVertices, 0, 2);
mesh = null; mesh = null;
if (Std.isOfType(region.rendererObject, SkeletonMesh)) { if (Std.isOfType(region.rendererObject, SkeletonMesh)) {
@ -220,7 +220,7 @@ class SkeletonSprite extends DisplayObject {
if (Std.isOfType(attachment, RegionAttachment)) { if (Std.isOfType(attachment, RegionAttachment)) {
var region:RegionAttachment = cast(slot.attachment, RegionAttachment); var region:RegionAttachment = cast(slot.attachment, RegionAttachment);
verticesLength = 8; verticesLength = 8;
region.computeWorldVertices(slot.bone, worldVertices, 0, 2); region.computeWorldVertices(slot, worldVertices, 0, 2);
} else if (Std.isOfType(attachment, MeshAttachment)) { } else if (Std.isOfType(attachment, MeshAttachment)) {
var mesh:MeshAttachment = cast(attachment, MeshAttachment); var mesh:MeshAttachment = cast(attachment, MeshAttachment);
verticesLength = mesh.worldVerticesLength; verticesLength = mesh.worldVerticesLength;