Kaynağa Gözat

started spine support

ncannasse 10 yıl önce
ebeveyn
işleme
ed7574fb4c

+ 108 - 0
hxd/fmt/spine/Data.hx

@@ -0,0 +1,108 @@
+package hxd.fmt.spine;
+
+class Bone {
+
+	public var name : String;
+	public var parent : Bone;
+	public var childs : Array<Bone>;
+
+	public var x : Float;
+	public var y : Float;
+	public var rotation : Float;
+	public var scaleX : Float;
+	public var scaleY : Float;
+	public var length : Float;
+
+	public var flipX : Bool;
+	public var flipY : Bool;
+	public var inheritScale : Bool;
+	public var inheritRotation : Bool;
+
+	public function new() {
+		childs = [];
+	}
+
+}
+
+class Slot {
+
+	public var name : String;
+	public var bone : Bone;
+	public var attachment : String;
+	public var color : h3d.Vector;
+	public var blendMode : h2d.BlendMode;
+
+	public function new() {
+		color = new h3d.Vector(1, 1, 1, 1);
+	}
+}
+
+class Attachment {
+	public var skin : Skin;
+	public var slot : Slot;
+	public var color : h3d.Vector;
+	public function new() {
+		color = new h3d.Vector(1, 1, 1, 1);
+	}
+}
+
+class RegionAttachment extends Attachment {
+	public var width : Float;
+	public var height : Float;
+}
+
+class SkinnedVertice {
+	public var u : Float;
+	public var v : Float;
+	public var vx0 : Float;
+	public var vy0 : Float;
+	public var vw0 : Float;
+	public var vx1 : Float;
+	public var vy1 : Float;
+	public var vw1 : Float;
+	public var vx2 : Float;
+	public var vy2 : Float;
+	public var vw2 : Float;
+	public var bone0 : Bone;
+	public var bone1 : Bone;
+	public var bone2 : Bone;
+	public function new() {
+	}
+}
+
+class SkinnedMeshAttachment extends Attachment {
+	public var vertices : Array<SkinnedVertice> = [];
+	public var triangles : Array<Int>;
+}
+
+class Skin {
+	public var name : String;
+	public var attachments : Array<Attachment>;
+	public function new() {
+		attachments = [];
+	}
+}
+
+class AnimationCurve {
+	public function new() {
+	}
+}
+
+class BoneCurve extends AnimationCurve {
+	public var bone : Bone;
+	public var translate : haxe.ds.Vector<Float>;
+	public var scale : haxe.ds.Vector<Float>;
+	public var rotate : haxe.ds.Vector<Float>;
+	public function new(bone) {
+		super();
+		this.bone = bone;
+	}
+}
+
+class Animation {
+	public var name : String;
+	public var curves : Array<AnimationCurve>;
+	public function new() {
+		curves = [];
+	}
+}

+ 89 - 0
hxd/fmt/spine/JsonData.hx

@@ -0,0 +1,89 @@
+package hxd.fmt.spine;
+import haxe.DynamicAccess;
+
+typedef JCurve = haxe.ds.Either<String,Array<Float>>; // "stepped" | "linear" | [b1,b2,b3,b4] (bezier factors)
+
+typedef JBoneAnimation = {
+	@:optional var rotate : Array<{ time : Float, angle : Float, ?curve : JCurve }>;
+	@:optional var scale : Array<{ time : Float, x : Float, y : Float, ?curve : JCurve }>;
+	@:optional var translate : Array<{ time : Float, x : Float, y : Float, ?curve : JCurve }>;
+	@:optional var flipX : Array<{ time : Float, x : Bool }>;
+	@:optional var flipY : Array<{ time : Float, y : Bool }>;
+}
+
+typedef JAnimation = {
+	@:optional var bones : DynamicAccess<JBoneAnimation>;
+	@:optional var slots : Dynamic;
+	@:optional var ik : Dynamic;
+	@:optional var ffd : Dynamic;
+	@:optional var drawOrder : Dynamic;
+	@:optional var events : Dynamic;
+}
+
+typedef JBone = {
+	var color : String;
+	var name : String;
+	@:optional var x : Float;
+	@:optional var y : Float;
+	@:optional var scaleX : Float;
+	@:optional var scaleY : Float;
+	@:optional var rotation : Float;
+	@:optional var length : Float;
+	@:optional var parent : String;
+	@:optional var flipX : Bool;
+	@:optional var flipY : Bool;
+	@:optional var inheritScale : Bool;
+	@:optional var inheritRotation : Bool;
+}
+
+typedef JSkeleton = {
+	var hash : String;
+	var width : Float;
+	var height : Float;
+	var images : String;
+	var spine : String; // version
+}
+
+typedef JAttachment = {
+	?type : String,
+	?color : String,
+};
+
+typedef JRegionAttach = { > JAttachment,
+	var width : Float;
+	var height : Float;
+	@:optional var x : Float;
+	@:optional var y : Float;
+	@:optional var rotation : Float;
+	@:optional var scaleX : Float;
+	@:optional var scaleY : Float;
+};
+
+typedef JSkinMeshAttach = { >JAttachment,
+	var width : Int;
+	var height : Int;
+	var hull : Int;
+	var edges : Array<Int>;
+	var triangles : Array<Int>;
+	var uvs : Array<Float>;
+	var vertices : Array<Float>;
+}
+
+typedef JSkin = DynamicAccess<DynamicAccess<JAttachment>>;
+
+typedef JSlot = {
+	var name : String;
+	var attachment : String;
+	var body : String;
+	@:optional var blend : String;
+	@:optional var color : String;
+}
+
+typedef JsonData = {
+	var animations : DynamicAccess<JAnimation>;
+	var bones : Array<JBone>;
+	var ik : Dynamic;
+	var skeleton : JSkeleton;
+	var skins : DynamicAccess<JSkin>;
+	var slots : Array<JSlot>;
+}

+ 283 - 0
hxd/fmt/spine/Library.hx

@@ -0,0 +1,283 @@
+package hxd.fmt.spine;
+import hxd.fmt.spine.JsonData;
+import hxd.fmt.spine.Data;
+
+class Library {
+
+	public var bonesMap : Map<String,Bone>;
+	public var bones : Array<Bone>;
+	public var slots : Array<Slot>;
+	public var defaultSkin : Skin;
+	public var skins : Map<String,Skin>;
+	public var animations : Map<String, Animation>;
+
+	public function new() {
+		bones = [];
+		slots = [];
+		bonesMap = new Map();
+		skins = new Map();
+		animations = new Map();
+	}
+
+	public function loadText( j : String ) {
+		load(haxe.Json.parse(j));
+	}
+
+	inline function def<T>( v : Null<T>, def : T ) {
+		return v == null ? def : v;
+	}
+
+	public function load( j : JsonData ) {
+
+		if( j.bones != null )
+			for( bd in j.bones ) {
+				var b = new Bone();
+				b.name = bd.name;
+				if( bd.parent != null ) {
+					b.parent = bonesMap.get(bd.parent);
+					if( b.parent == null ) throw "Missing bone " + bd.parent;
+					b.parent.childs.push(b);
+				}
+
+				b.length = def(bd.length, 0);
+				b.x = def(bd.x,0);
+				b.y = def(bd.y,0);
+				b.rotation = def(bd.rotation,0) * Math.PI / 180;
+				b.scaleX = def(bd.scaleX, 1);
+				b.scaleY = def(bd.scaleY, 1);
+				b.flipX = bd.flipX;
+				b.flipY = bd.flipY;
+				b.inheritScale = def(bd.inheritScale, true);
+				b.inheritRotation = def(bd.inheritRotation,true);
+
+				bones.push(b);
+				bonesMap.set(b.name, b);
+			}
+
+		if( j.ik != null )
+			trace("TODO : ik");
+
+		var slotByName = new Map();
+		if( j.slots != null ) {
+			var BLENDS = new Map<String, h2d.BlendMode>();
+			for( sd in j.slots ) {
+				var s = new Slot();
+				s.name = sd.name;
+				s.attachment = sd.attachment;
+				s.blendMode = sd.blend == null ? Alpha : BLENDS.get(sd.blend);
+				if( s.blendMode == null ) throw "Unknown blend mode " + sd.blend;
+				if( sd.color != null )
+					s.color.setColor(parseColor(sd.color));
+				slotByName.set(s.name, s);
+				slots.push(s);
+			}
+		}
+
+		if( j.skins != null )
+			for( skinName in j.skins.keys() ) {
+				var sd = j.skins.get(skinName);
+				var s = new Skin();
+				s.name = skinName;
+				for( slotName in sd.keys() ) {
+					var ssd = sd.get(slotName);
+					var slot = slotByName.get(slotName);
+					if( slot == null ) throw "Unknown skin slot " + slotName;
+					for( attachName in ssd.keys() ) {
+						var a = loadAttachment(ssd.get(attachName));
+						a.skin = s;
+						a.slot = slot;
+						s.attachments.push(a);
+					}
+				}
+				skins.set(s.name, s);
+				if( s.name == "default" )
+					defaultSkin = s;
+			}
+
+		if( j.animations != null )
+			for( animName in j.animations.keys() ) {
+				var a = loadAnimation(j.animations.get(animName));
+				a.name = animName;
+				animations.set(animName, a);
+			}
+	}
+
+	function parseColor( color : String ) {
+		return Std.parseInt("0x" + color);
+	}
+
+	function loadAttachment( j : JAttachment ) {
+		var type = j.type;
+		if( type == null ) type = "region";
+		var attach : Attachment;
+		switch( type ) {
+		case "region":
+
+			var j : JRegionAttach = cast j;
+			var r = new RegionAttachment();
+
+			var x = def(j.x, 0);
+			var y = def(j.y, 0);
+			var scaleX = def(j.scaleX, 1);
+			var scaleY = def(j.scaleY, 1);
+			var rotation = def(j.rotation, 0) * Math.PI / 180;
+
+			r.width = j.width;
+			r.height = j.height;
+			// TODO
+
+			attach = r;
+
+		case "skinnedmesh":
+			var j : JSkinMeshAttach = cast j;
+			var s = new SkinnedMeshAttachment();
+			var vertices = j.vertices;
+			var uvs = j.uvs;
+			var pos = 0, uvPos = 0;
+			while( pos < vertices.length ) {
+				var nbones = Std.int(vertices[pos++]);
+				var v = new SkinnedVertice();
+				if( nbones == 0 ) throw "no bones?";
+
+				v.u = uvs[uvPos++];
+				v.v = uvs[uvPos++];
+
+				v.bone0 = bones[Std.int(vertices[pos++])];
+				v.vx0 = vertices[pos++];
+				v.vy0 = vertices[pos++];
+				v.vw0 = vertices[pos++];
+
+				if( nbones > 1 ) {
+					v.bone1 = bones[Std.int(vertices[pos++])];
+					v.vx1 = vertices[pos++];
+					v.vy1 = vertices[pos++];
+					v.vw1 = vertices[pos++];
+				}
+				if( nbones > 2 ) {
+					v.bone2 = bones[Std.int(vertices[pos++])];
+					v.vx2 = vertices[pos++];
+					v.vy2 = vertices[pos++];
+					v.vw2 = vertices[pos++];
+				}
+				if( nbones > 3 ) {
+					for( i in 0...nbones - 3 ) {
+						var bone = bones[Std.int(vertices[pos++])];
+						var vx = vertices[pos++];
+						var vy = vertices[pos++];
+						var w = vertices[pos++];
+						if( v.vw0 < v.vw1 && v.vw0 < v.vw2 ) {
+							if( w > v.vw0 ) {
+								v.bone0 = bone;
+								v.vx0 = vx;
+								v.vy0 = vy;
+								v.vw0 = w;
+							}
+						} else if( v.vw1 < v.vw0 && v.vw1 < v.vw2 ) {
+							if( w > v.vw1 ) {
+								v.bone1 = bone;
+								v.vx1 = vx;
+								v.vy1 = vy;
+								v.vw1 = w;
+							}
+						} else {
+							if( w > v.vw2 ) {
+								v.bone2 = bone;
+								v.vx2 = vx;
+								v.vy2 = vy;
+								v.vw2 = w;
+							}
+						}
+					}
+					// normalize weights
+					var tot = v.vw0 + v.vw1 + v.vw2;
+					v.vw0 /= tot;
+					v.vw1 /= tot;
+					v.vw2 /= tot;
+				}
+
+				s.vertices.push(v);
+			}
+			s.triangles = j.triangles;
+			attach = s;
+
+		default:
+			throw "Unsupported attach type " + type;
+		}
+		if( j.color != null )
+			attach.color.setColor(parseColor(j.color));
+		return attach;
+	}
+
+	function loadAnimation( j : JAnimation ) {
+		var a = new Animation();
+
+		if( j.slots != null )
+			trace("TODO : slots");
+
+		if( j.bones != null ) {
+			var boneCurves = new Map();
+			for( boneName in j.bones.keys() ) {
+				var bd = j.bones.get(boneName);
+				var bone = bonesMap.get(boneName);
+				if( bone == null ) throw "Missing bone " + boneName;
+
+				var c = boneCurves.get(boneName);
+				if( c == null ) {
+					c = new BoneCurve(bone);
+					boneCurves.set(bone.name, c);
+				}
+
+				if( bd.translate != null ) {
+					var values = new haxe.ds.Vector(bd.translate.length * 3);
+					var pos = 0;
+					for( t in bd.translate )
+						values[pos++] = t.time;
+					for( t in bd.translate ) {
+						values[pos++] = t.x;
+						values[pos++] = t.y;
+					}
+					c.translate = values;
+				}
+				if( bd.scale != null ) {
+					var values = new haxe.ds.Vector(bd.scale.length * 3);
+					var pos = 0;
+					for( t in bd.scale )
+						values[pos++] = t.time;
+					for( t in bd.scale ) {
+						values[pos++] = t.x;
+						values[pos++] = t.y;
+					}
+					c.scale = values;
+				}
+				if( bd.rotate != null ) {
+					var values = new haxe.ds.Vector(bd.rotate.length * 2);
+					var pos = 0;
+					for( t in bd.rotate )
+						values[pos++] = t.time;
+					for( t in bd.rotate ) {
+						var a = t.angle * Math.PI / 180;
+						values[pos++] = a;
+					}
+					c.rotate = values;
+				}
+				if( bd.flipX != null || bd.flipY != null )
+					trace("TODO : bone flip anim");
+			}
+		}
+
+		if( j.ik != null )
+			trace("TODO : ik");
+
+		if( j.events != null )
+			trace("TODO : events");
+
+		if( j.ffd != null )
+			trace("TODO : ffd");
+
+		if( j.drawOrder != null )
+			trace("TODO : drawOrder");
+
+		return a;
+	}
+
+}

+ 55 - 0
hxd/res/Atlas.hx

@@ -0,0 +1,55 @@
+package hxd.res;
+
+class Atlas extends Resource {
+
+	public function toAtlas() : Map<String,h2d.Tile> {
+		var tiles = new Map();
+		var lines = entry.getBytes().toString().split("\n");
+		var basePath = entry.path.split("/");
+		basePath.pop();
+		var basePath = basePath.join("/");
+		if( basePath.length > 0 ) basePath += "/";
+		while( lines.length > 0 ) {
+			var line = StringTools.trim(lines.shift());
+			if( line == "" ) continue;
+			var file = hxd.res.Loader.currentInstance.load(basePath + line).toTile();
+			while( lines.length > 0 ) {
+				var line = StringTools.trim(lines.shift());
+				if( line == "" ) break;
+				var prop = line.split(": ");
+				if( prop.length > 1 ) continue;
+				var key = line;
+				var tileX = 0, tileY = 0, tileW = 0, tileH = 0;
+				while( lines.length > 0 ) {
+					var line = StringTools.trim(lines.shift());
+					var prop = line.split(": ");
+					if( prop.length == 1 ) {
+						lines.unshift(line);
+						break;
+					}
+					var v = prop[1];
+					switch( prop[0] ) {
+					case "rotate":
+						if( v == "true" ) throw "Rotation not supported in atlas";
+					case "xy":
+						var vals = v.split(", ");
+						tileX = Std.parseInt(vals[0]);
+						tileY = Std.parseInt(vals[1]);
+					case "size":
+						var vals = v.split(", ");
+						tileW = Std.parseInt(vals[0]);
+						tileH = Std.parseInt(vals[1]);
+					case "index", "orig", "offset":
+						// ?
+					default:
+						trace("Unknown prop " + prop[0]);
+					}
+				}
+				var t = file.sub(tileX, tileY, tileW, tileH);
+				tiles.set(key, t);
+			}
+		}
+		return tiles;
+	}
+
+}

+ 18 - 0
samples/spine/Main.hx

@@ -0,0 +1,18 @@
+
+class Main extends hxd.App {
+
+	override function init() {
+		var group = new h2d.Sprite(s2d);
+		group.scale(0.5);
+		var atlas = hxd.Res.Gorilla.toAtlas();
+		var r = new hxd.fmt.spine.Library();
+		r.loadText(hxd.Res.GorillaAnim.entry.getText());
+	}
+
+	static function main() {
+		hxd.Res.initEmbed();
+		new Main();
+	}
+
+
+}

+ 6 - 0
samples/spine/spine.hxml

@@ -0,0 +1,6 @@
+-swf spine.swf
+-swf-header 800:600:60:FFFFFF
+-swf-version 15.0
+-main Main
+-lib heaps
+-dce full

+ 60 - 0
samples/spine/spine.hxproj

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project version="2">
+  <!-- Output SWF options -->
+  <output>
+    <movie outputType="Application" />
+    <movie input="" />
+    <movie path="spine.swf" />
+    <movie fps="60" />
+    <movie width="800" />
+    <movie height="600" />
+    <movie version="15" />
+    <movie minorVersion="0" />
+    <movie platform="Flash Player" />
+    <movie background="#FFFFFF" />
+  </output>
+  <!-- Other classes to be compiled into your SWF -->
+  <classpaths>
+    <!-- example: <class path="..." /> -->
+  </classpaths>
+  <!-- Build options -->
+  <build>
+    <option directives="" />
+    <option flashStrict="False" />
+    <option noInlineOnDebug="False" />
+    <option mainClass="Main" />
+    <option enabledebug="False" />
+    <option additional="-lib heaps&#xA;-dce full" />
+  </build>
+  <!-- haxelib libraries -->
+  <haxelib>
+    <!-- example: <library name="..." /> -->
+  </haxelib>
+  <!-- Class files to compile (other referenced classes will automatically be included) -->
+  <compileTargets>
+    <!-- example: <compile path="..." /> -->
+  </compileTargets>
+  <!-- Assets to embed into the output SWF -->
+  <library>
+    <!-- example: <asset path="..." id="..." update="..." glyphs="..." mode="..." place="..." sharepoint="..." /> -->
+  </library>
+  <!-- Paths to exclude from the Project Explorer tree -->
+  <hiddenPaths>
+    <hidden path="engine.hxml" />
+    <hidden path="obj" />
+    <hidden path="main.js" />
+    <hidden path="main.js.map" />
+  </hiddenPaths>
+  <!-- Executed before build -->
+  <preBuildCommand />
+  <!-- Executed after build -->
+  <postBuildCommand alwaysRun="False" />
+  <!-- Other project options -->
+  <options>
+    <option showHiddenPaths="False" />
+    <option testMovie="OpenDocument" />
+    <option testMovieCommand="spine.swf" />
+  </options>
+  <!-- Plugin storage -->
+  <storage />
+</project>