浏览代码

added UV animation support through loadXtra

ncannasse 11 年之前
父节点
当前提交
fe686ec0c3
共有 6 个文件被更改,包括 139 次插入42 次删除
  1. 8 0
      h3d/anim/FrameAnimation.hx
  2. 19 0
      h3d/anim/LinearAnimation.hx
  3. 104 39
      h3d/fbx/Library.hx
  4. 1 1
      hxd/res/Any.hx
  5. 5 1
      hxd/res/FbxModel.hx
  6. 2 1
      hxd/res/FileTree.hx

+ 8 - 0
h3d/anim/FrameAnimation.hx

@@ -4,11 +4,13 @@ import h3d.anim.Animation;
 class FrameObject extends AnimatedObject {
 	public var frames : haxe.ds.Vector<h3d.Matrix>;
 	public var alphas : haxe.ds.Vector<Float>;
+	public var uvs : haxe.ds.Vector<Float>;
 	
 	override function clone() : AnimatedObject {
 		var o = new FrameObject(objectName);
 		o.frames = frames;
 		o.alphas = alphas;
+		o.uvs = uvs;
 		return o;
 	}
 }
@@ -33,6 +35,12 @@ class FrameAnimation extends Animation {
 		f.alphas = alphas;
 		objects.push(f);
 	}
+
+	public function addUVCurve( objName, uvs ) {
+		var f = new FrameObject(objName);
+		f.uvs = uvs;
+		objects.push(f);
+	}
 	
 	inline function getFrames() : Array<FrameObject> {
 		return cast objects;

+ 19 - 0
h3d/anim/LinearAnimation.hx

@@ -21,6 +21,7 @@ class LinearObject extends AnimatedObject {
 	public var hasScale : Bool;
 	public var frames : haxe.ds.Vector<LinearFrame>;
 	public var alphas : haxe.ds.Vector<Float>;
+	public var uvs : haxe.ds.Vector<Float>;
 	public var matrix : h3d.Matrix;
 	override function clone() : AnimatedObject {
 		var o = new LinearObject(objectName);
@@ -28,6 +29,7 @@ class LinearObject extends AnimatedObject {
 		o.hasScale = hasScale;
 		o.frames = frames;
 		o.alphas = alphas;
+		o.uvs = uvs;
 		return o;
 	}
 }
@@ -54,6 +56,12 @@ class LinearAnimation extends Animation {
 		f.alphas = alphas;
 		objects.push(f);
 	}
+
+	public function addUVCurve( objName, uvs ) {
+		var f = new LinearObject(objName);
+		f.uvs = uvs;
+		objects.push(f);
+	}
 	
 	inline function getFrames() : Array<LinearObject> {
 		return cast objects;
@@ -101,6 +109,17 @@ class LinearAnimation extends Animation {
 				mat.colorMul.w = o.alphas[frame1] * k1 + o.alphas[frame2] * k2;
 				continue;
 			}
+			if( o.uvs != null ) {
+				var mat = o.targetObject.toMesh().material;
+				if( mat.uvDelta == null ) {
+					mat.uvDelta = new Vector();
+					mat.texture.wrap = Repeat;
+				}
+				mat.uvDelta.x = o.uvs[frame1 << 1] * k1 + o.uvs[frame2 << 1] * k2;
+				mat.uvDelta.y = o.uvs[(frame1 << 1) | 1] * k1 + o.uvs[(frame2 << 1) | 1] * k2;
+				trace(frame1, mat.uvDelta);
+				continue;
+			}
 			var f1 = o.frames[frame1], f2 = o.frames[frame2];
 			
 			var m = o.matrix;

+ 104 - 39
h3d/fbx/Library.hx

@@ -7,6 +7,20 @@ enum AnimationMode {
 	LinearAnim;
 }
 
+private class AnimCurve {
+	public var def : DefaultMatrixes;
+	public var object : String;
+	public var t : { t : Array<Float>, x : Array<Float>, y : Array<Float>, z : Array<Float> };
+	public var r : { t : Array<Float>, x : Array<Float>, y : Array<Float>, z : Array<Float> };
+	public var s : { t : Array<Float>, x : Array<Float>, y : Array<Float>, z : Array<Float> };
+	public var a : { t : Array<Float>, v : Array<Float> };
+	public var uv : Array<{ t : Float, u : Float, v : Float }>;
+	public function new(def, object) {
+		this.def = def;
+		this.object = object;
+	}
+}
+
 class DefaultMatrixes {
 	public var trans : Null<Point>;
 	public var scale : Null<Point>;
@@ -50,6 +64,7 @@ class Library {
 	var invConnect : Map<Int,Array<Int>>;
 	var leftHand : Bool;
 	var defaultModelMatrixes : Map<String,DefaultMatrixes>;
+	var uvAnims : Map<String, Array<{ t : Float, u : Float, v : Float }>>;
 
 	/**
 		Allows to prevent some terminal unskinned joints to be removed, for instance if we want to track their position
@@ -101,6 +116,16 @@ class Library {
 			init(c);
 	}
 	
+	public function loadXtra( data : String ) {
+		var xml = Xml.parse(data).firstElement();
+		if( uvAnims == null ) uvAnims = new Map();
+		for( e in new haxe.xml.Fast(xml).elements ) {
+			var obj = e.att.name;
+			var frames = [for( f in e.elements ) { var f = f.innerData.split(" ");  { t : Std.parseFloat(f[0]) * 9622116.25, u : Std.parseFloat(f[1]), v : Std.parseFloat(f[2]) }} ];
+			uvAnims.set(obj, frames);
+		}
+	}
+	
 	function convertPoints( a : Array<Float> ) {
 		var p = 0;
 		for( i in 0...Std.int(a.length / 3) ) {
@@ -222,6 +247,38 @@ class Library {
 		}
 	}
 
+	function getObjectCurve( curves : Map < Int, AnimCurve > , model : FbxNode, curveName : String, animName : String ) : AnimCurve {
+		var c = curves.get(model.getId());
+		if( c != null )
+			return c;
+		var name = model.getName();
+		if( skipObjects.get(name) )
+			return null;
+		// if it's an empty model with no sub nodes, let's ignore it (ex : Camera)
+		if( model.getType() == "Null" && getChilds(model, "Model").length == 0 )
+			return null;
+		var def = defaultModelMatrixes.get(name);
+		if( def == null )
+			throw "Object "+name+" used in anim "+animName+" was not found in library";
+		// if it's a move animation on a terminal unskinned joint, let's skip it
+		if( def.wasRemoved != null ) {
+			if( curveName != "Visibility" && curveName != "UV" )
+				return null;
+			// apply it on the skin instead
+			model = ids.get(def.wasRemoved);
+			name = model.getName();
+			c = curves.get(def.wasRemoved);
+			def = defaultModelMatrixes.get(name);
+			// todo : change behavior not to remove the mesh but the skin instead!
+			if( def == null ) throw "assert";
+		}
+		if( c == null ) {
+			c = new AnimCurve(def, name);
+			curves.set(model.getId(), c);
+		}
+		return c;
+	}
+	
 	public function loadAnimation( mode : AnimationMode, ?animName : String, ?root : FbxNode, ?lib : Library ) : h3d.anim.Animation {
 		if( lib != null ) {
 			lib.defaultModelMatrixes = defaultModelMatrixes;
@@ -254,34 +311,8 @@ class Library {
 		var allTimes = new Map();
 		for( cn in getChilds(animNode, "AnimationCurveNode") ) {
 			var model = getParent(cn, "Model");
-			var c = curves.get(model.getId());
-			if( c == null ) {
-				var name = model.getName();
-				if( skipObjects.get(name) )
-					continue;
-				// if it's an empty model with no sub nodes, let's ignore it (ex : Camera)
-				if( model.getType() == "Null" && getChilds(model, "Model").length == 0 )
-					continue;
-				var def = defaultModelMatrixes.get(name);
-				if( def == null )
-					throw "Object "+name+" used in anim "+animName+" was not found in library";
-				// if it's a move animation on a terminal unskinned joint, let's skip it
-				if( def.wasRemoved != null ) {
-					if( cn.getName() != "Visibility" )
-						continue;
-					// apply it on the skin instead
-					model = ids.get(def.wasRemoved);
-					name = model.getName();
-					c = curves.get(def.wasRemoved);
-					def = defaultModelMatrixes.get(name);
-					// todo : change behavior not to remove the mesh but the skin instead!
-					if( def == null ) throw "assert";
-				}
-				if( c == null ) {
-					c = { def : def, t : null, r : null, s : null, a : null, name : name };
-					curves.set(model.getId(), c);
-				}
-			}
+			var c = getObjectCurve(curves, model, cn.getName(), animName);
+			if( c == null ) continue;
 			var data = getChilds(cn, "AnimationCurve");
 			var cname = cn.getName();
 			// collect all the timestamps
@@ -359,11 +390,25 @@ class Library {
 			default: throw "assert";
 			}
 		}
+
+		// process UVs
+		if( uvAnims != null ) {
+			var modelByName = new Map();
+			for( obj in this.root.getAll("Objects.Model") )
+				modelByName.set(obj.getName(), obj);
+			for( obj in uvAnims.keys() ) {
+				var frames = uvAnims.get(obj);
+				var model = modelByName.get(obj);
+				if( model == null ) throw "Missing model '" + obj + "' requires by UV animation";
+				var c = getObjectCurve(curves, model, "UV", animName);
+				if( c == null ) continue;
+				c.uv = frames;
+				for( f in frames )
+					allTimes.set(Std.int(f.t / 200000), f.t);
+			}
+		}
 		
-		var times = [];
-		for( a in allTimes )
-			times.push(a);
-		var allTimes = times;
+		var allTimes = [for( a in allTimes ) a];
 		allTimes.sort(sortDistinctFloats);
 		var maxTime = allTimes[allTimes.length - 1];
 		var minDT = maxTime;
@@ -384,8 +429,9 @@ class Library {
 			for( c in curves ) {
 				var frames = c.t == null && c.r == null && c.s == null ? null : new haxe.ds.Vector(numFrames);
 				var alpha = c.a == null ? null : new haxe.ds.Vector(numFrames);
+				var uvs = c.uv == null ? null : new haxe.ds.Vector(numFrames * 2);
 				// skip empty curves
-				if( frames == null && alpha == null )
+				if( frames == null && alpha == null && uvs == null )
 					continue;
 				var ctx = c.t == null ? null : c.t.x;
 				var cty = c.t == null ? null : c.t.y;
@@ -401,8 +447,9 @@ class Library {
 				var cst = c.s == null ? [ -1.] : c.s.t;
 				var cav = c.a == null ? null : c.a.v;
 				var cat = c.a == null ? null : c.a.t;
+				var cuv = c.uv;
 				var def = c.def;
-				var tp = 0, rp = 0, sp = 0, ap = 0;
+				var tp = 0, rp = 0, sp = 0, ap = 0, uvp = 0;
 				var curMat = null;
 				for( f in 0...numFrames ) {
 					var changed = curMat == null;
@@ -454,12 +501,20 @@ class Library {
 							ap++;
 						alpha[f] = cav[ap - 1];
 					}
+					if( uvs != null ) {
+						if( allTimes[f] == cuv[uvp].t )
+							uvp++;
+						uvs[f<<1] = cuv[uvp - 1].u;
+						uvs[(f<<1)|1] = cuv[uvp - 1].v;
+					}
 				}
 				
 				if( frames != null )
-					anim.addCurve(c.name, frames);
+					anim.addCurve(c.object, frames);
 				if( alpha != null )
-					anim.addAlphaCurve(c.name, alpha);
+					anim.addAlphaCurve(c.object, alpha);
+				if( uvs != null )
+					anim.addUVCurve(c.object, uvs);
 			}
 			return anim;
 			
@@ -471,6 +526,7 @@ class Library {
 			for( c in curves ) {
 				var frames = c.t == null && c.r == null && c.s == null ? null : new haxe.ds.Vector(numFrames);
 				var alpha = c.a == null ? null : new haxe.ds.Vector(numFrames);
+				var uvs = c.uv == null ? null : new haxe.ds.Vector(numFrames * 2);
 				// skip empty curves
 				if( frames == null && alpha == null )
 					continue;
@@ -488,8 +544,9 @@ class Library {
 				var cst = c.s == null ? [ -1.] : c.s.t;
 				var cav = c.a == null ? null : c.a.v;
 				var cat = c.a == null ? null : c.a.t;
+				var cuv = c.uv;
 				var def = c.def;
-				var tp = 0, rp = 0, sp = 0, ap = 0;
+				var tp = 0, rp = 0, sp = 0, ap = 0, uvp = 0;
 				var curFrame = null;
 				for( f in 0...numFrames ) {
 					var changed = curFrame == null;
@@ -572,12 +629,20 @@ class Library {
 							ap++;
 						alpha[f] = cav[ap - 1];
 					}
+					if( uvs != null ) {
+						if( uvp < cuv.length && allTimes[f] == cuv[uvp].t )
+							uvp++;
+						uvs[f<<1] = cuv[uvp - 1].u;
+						uvs[(f<<1)|1] = cuv[uvp - 1].v;
+					}
 				}
 				
 				if( frames != null )
-					anim.addCurve(c.name, frames, c.r != null || def.rotate != null, c.s != null || def.scale != null);
+					anim.addCurve(c.object, frames, c.r != null || def.rotate != null, c.s != null || def.scale != null);
 				if( alpha != null )
-					anim.addAlphaCurve(c.name, alpha);
+					anim.addAlphaCurve(c.object, alpha);
+				if( uvs != null )
+					anim.addUVCurve(c.object, uvs);
 			}
 			return anim;
 			

+ 1 - 1
hxd/res/Any.hx

@@ -28,7 +28,7 @@ class Any extends Resource {
 	}
 	
 	public function toFbx() {
-		return loader.loadFbxModel(entry.path).toFbx();
+		return loader.loadFbxModel(entry.path).toFbx(loader);
 	}
 	
 	public function toAwd() {

+ 5 - 1
hxd/res/FbxModel.hx

@@ -4,7 +4,7 @@ class FbxModel extends Resource {
 	
 	public static var isLeftHanded = true;
 	
-	public function toFbx() : h3d.fbx.Library {
+	public function toFbx( ?loader : Loader ) : h3d.fbx.Library {
 		var lib = new h3d.fbx.Library();
 		switch( entry.getSign() & 0xFF ) {
 		case ';'.code: // FBX
@@ -14,6 +14,10 @@ class FbxModel extends Resource {
 			var xbx = new h3d.fbx.XBXReader(f).read();
 			lib.load(xbx);
 			f.close();
+		case '<'.code: // XTRA
+			if( loader == null ) throw "Loader parameter required for XTRA";
+			lib = loader.load(entry.path.substr(0, -4) + "FBX").toFbx();
+			lib.loadXtra(entry.getBytes().toString());
 		default:
 			throw "Unsupported model format " + entry.path;
 		}

+ 2 - 1
hxd/res/FileTree.hx

@@ -30,6 +30,7 @@ class FileTree {
 		pairedExt.set("fnt", ["png"]);
 		pairedExt.set("fbx", ["png"]);
 		pairedExt.set("cdb", ["img"]);
+		pairedExt.set("xtra", ["fbx"]);
 		isFlash = Context.defined("flash");
 		isJS = Context.defined("js");
 	}
@@ -310,7 +311,7 @@ class FileTree {
 		switch( ext.toLowerCase() ) {
 		case "jpg", "png":
 			return { e : macro loader.loadImage($epath), t : macro : hxd.res.Image };
-		case "fbx", "xbx":
+		case "fbx", "xbx", "xtra":
 			return { e : macro loader.loadFbxModel($epath), t : macro : hxd.res.FbxModel };
 		case "awd":
 			return { e : macro loader.loadAwdModel($epath), t : macro : hxd.res.AwdModel };