Bläddra i källkod

added camera fov animation support in HMD, removed old frame animation support

ncannasse 10 år sedan
förälder
incheckning
a6f263bfde

+ 14 - 0
h3d/Camera.hx

@@ -118,6 +118,20 @@ class Camera {
 			target.set(0, 0, 0);
 			follow.pos.localToGlobal(pos);
 			follow.target.localToGlobal(target);
+			// Animate FOV
+			if( follow.pos.name != null ) {
+				var p = follow.pos;
+				while( p != null ) {
+					if( p.currentAnimation != null ) {
+						var v = p.currentAnimation.getPropValue(follow.pos.name, "FOVY");
+						if( v != null ) {
+							fovY = v;
+							break;
+						}
+					}
+					p = p.parent;
+				}
+			}
 		}
 		makeCameraMatrix(mcam);
 		makeFrustumMatrix(mproj);

+ 7 - 0
h3d/anim/Animation.hx

@@ -170,6 +170,13 @@ class Animation {
 	}
 	#end
 
+	/**
+		Returns the current value of animation property for the given object, or null if not found.
+	**/
+	public function getPropValue( objectName : String, propName : String ) : Null<Float> {
+		return null;
+	}
+
 	/**
 		Synchronize the target object matrix.
 		If decompose is true, then the rotation quaternion is stored in [m12,m13,m21,m23] instead of mixed with the scale.

+ 0 - 87
h3d/anim/FrameAnimation.hx

@@ -1,87 +0,0 @@
-package h3d.anim;
-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;
-	}
-}
-
-class FrameAnimation extends Animation {
-
-	var syncFrame : Int;
-
-	public function new(name,frame,sampling) {
-		super(name,frame,sampling);
-		syncFrame = -1;
-	}
-
-	public function addCurve( objName, frames ) {
-		var f = new FrameObject(objName);
-		f.frames = frames;
-		objects.push(f);
-	}
-
-	public function addAlphaCurve( objName, alphas ) {
-		var f = new FrameObject(objName);
-		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;
-	}
-
-	override function clone(?a:Animation) {
-		if( a == null )
-			a = new FrameAnimation(name, frameCount, sampling);
-		super.clone(a);
-		return a;
-	}
-
-	#if !(dataOnly || macro)
-
-	override function initInstance() {
-		super.initInstance();
-		for( a in getFrames() )
-			if( a.alphas != null && (a.targetObject == null || !a.targetObject.isMesh()) )
-				throw a.objectName + " should be a mesh";
-	}
-
-	@:access(h3d.scene.Skin)
-	override function sync( decompose = false ) {
-		if( decompose ) throw "Decompose not supported on Frame Animation";
-		var frame = Std.int(frame);
-		if( frame < 0 ) frame = 0 else if( frame >= frameCount ) frame = frameCount - 1;
-		if( frame == syncFrame )
-			return;
-		syncFrame = frame;
-		for( o in getFrames() ) {
-			if( o.alphas != null ) {
-				var mat = o.targetObject.toMesh().material;
-				if( mat.blendMode == None ) mat.blendMode = Alpha;
-				mat.color.w = o.alphas[frame];
-			} else if( o.targetSkin != null ) {
-				o.targetSkin.currentRelPose[o.targetJoint] = o.frames[frame];
-				o.targetSkin.jointsUpdated = true;
-			} else
-				o.targetObject.defaultTransform = o.frames[frame];
-		}
-	}
-	#end
-
-}

+ 30 - 2
h3d/anim/LinearAnimation.hx

@@ -29,7 +29,10 @@ class LinearObject extends AnimatedObject {
 	public var frames : haxe.ds.Vector<LinearFrame>;
 	public var alphas : haxe.ds.Vector<Float>;
 	public var uvs : haxe.ds.Vector<Float>;
+	public var propName:  String;
+	public var propValues : haxe.ds.Vector<Float>;
 	public var matrix : h3d.Matrix;
+	public var propCurrentValue : Float;
 	override function clone() : AnimatedObject {
 		var o = new LinearObject(objectName);
 		o.hasRotation = hasRotation;
@@ -37,6 +40,8 @@ class LinearObject extends AnimatedObject {
 		o.frames = frames;
 		o.alphas = alphas;
 		o.uvs = uvs;
+		o.propName = propName;
+		o.propValues = propValues;
 		return o;
 	}
 }
@@ -70,6 +75,20 @@ class LinearAnimation extends Animation {
 		objects.push(f);
 	}
 
+	public function addPropCurve( objName, propName, values ) {
+		var f = new LinearObject(objName);
+		f.propName = propName;
+		f.propValues = values;
+		objects.push(f);
+	}
+
+	override function getPropValue( objName, propName ) : Null<Float> {
+		for( o in getFrames() )
+			if( o.objectName == objName && o.propName == propName )
+				return o.propCurrentValue;
+		return null;
+	}
+
 	inline function getFrames() : Array<LinearObject> {
 		return cast objects;
 	}
@@ -91,10 +110,15 @@ class LinearAnimation extends Animation {
 		super.initInstance();
 		var frames = getFrames();
 		for( a in frames ) {
-			a.matrix = new h3d.Matrix();
-			a.matrix.identity();
+			if( a.propValues != null ) {
+				a.propCurrentValue = a.propValues[0];
+				continue;
+			}
 			if( a.alphas != null && (a.targetObject == null || !a.targetObject.isMesh()) )
 				throw a.objectName + " should be a mesh (for alpha animation)";
+			if( a.uvs != null || a.alphas != null ) continue;
+			a.matrix = new h3d.Matrix();
+			a.matrix.identity();
 		}
 		// makes sure that all single frame anims are at the end so we can break early when isSync=true
 		frames.sort(sortByFrameCountDesc);
@@ -147,6 +171,10 @@ class LinearAnimation extends Animation {
 				s.uvDelta.y = uvLerp(o.uvs[(frame1 << 1) | 1],o.uvs[(frame2 << 1) | 1],k2);
 				continue;
 			}
+			if( o.propValues != null ) {
+				o.propCurrentValue = o.propValues[frame1] * k1 + o.propValues[frame2] * k2;
+				continue;
+			}
 
 			var frame1 = frame1, frame2 = frame2;
 

+ 0 - 6
h3d/anim/Mode.hx

@@ -1,6 +0,0 @@
-package h3d.anim;
-
-enum Mode {
-	FrameAnim;
-	LinearAnim;
-}

+ 209 - 253
hxd/fmt/fbx/BaseLibrary.hx

@@ -1,7 +1,6 @@
 package hxd.fmt.fbx;
 using hxd.fmt.fbx.Data;
 import h3d.col.Point;
-import h3d.anim.Mode;
 
 class TmpObject {
 	public var index : Int;
@@ -27,6 +26,8 @@ private class AnimCurve {
 	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 fov : { t : Array<Float>, v : Array<Float> };
+	public var roll : { t : Array<Float>, v : Array<Float> };
 	public var uv : Array<{ t : Float, u : Float, v : Float }>;
 	public function new(def, object) {
 		this.def = def;
@@ -561,17 +562,30 @@ class BaseLibrary {
 		return true;
 	}
 
-	public function loadAnimation( mode : Mode, ?animName : String, ?root : FbxNode, ?lib : BaseLibrary ) : h3d.anim.Animation {
+	function roundValues( data : Array<Float>, def : Float, mult : Float = 1. ) {
+		var hasValue = false;
+		for( i in 0...data.length ) {
+			var v = data[i] * mult;
+			if( Math.abs(v - def) > 1e-3 )
+				hasValue = true;
+			else
+				v = def;
+			data[i] = round(v);
+		}
+		return hasValue;
+	}
+
+	public function loadAnimation( ?animName : String, ?root : FbxNode, ?lib : BaseLibrary ) : h3d.anim.Animation {
 		if( lib != null ) {
 			lib.defaultModelMatrixes = defaultModelMatrixes;
-			return lib.loadAnimation(mode,animName);
+			return lib.loadAnimation(animName);
 		}
 		if( root != null ) {
 			var l = new BaseLibrary();
 			l.load(root);
 			if( leftHand ) l.leftHandConvert();
 			l.defaultModelMatrixes = defaultModelMatrixes;
-			return l.loadAnimation(mode,animName);
+			return l.loadAnimation(animName);
 		}
 		var animNode = null;
 		for( a in this.root.getAll("Objects.AnimationStack") )
@@ -596,10 +610,22 @@ class BaseLibrary {
 
 		if( animNode != null ) for( cn in getChilds(animNode, "AnimationCurveNode") ) {
 			var model = getParent(cn, "Model",true);
-			if(model==null) continue; //morph support
+			if( model == null ) {
+				switch( cn.getName() ) {
+				case "Roll", "FieldOfView":
+					// the parent is not a Model but a NodeAttribute
+					var nattr = getParent(cn, "NodeAttribute", true);
+					model = nattr == null ? null : getParent(nattr, "Model", true);
+					if( model == null ) continue;
+				default:
+					continue; //morph support
+				}
+			}
 
 			var c = getObjectCurve(curves, model, cn.getName(), animName);
-			if( c == null ) continue;
+			if( c == null )
+				continue;
+
 			var data = getChilds(cn, "AnimationCurve");
 			if( data.length == 0 ) continue;
 			var cname = cn.getName();
@@ -618,10 +644,42 @@ class BaseLibrary {
 				}
 			// handle special curves
 			if( data.length != 3 ) {
+				var values = data[0].get("KeyValueFloat").getFloats();
 				switch( cname ) {
 				case "Visibility":
+					if( !roundValues(values, 1) )
+						continue;
 					c.a = {
-						v : data[0].get("KeyValueFloat").getFloats(),
+						v : values,
+						t : times,
+					};
+					continue;
+				case "Roll":
+					if( !roundValues(values, 0) )
+						continue;
+					c.roll = {
+						v : values,
+						t : times,
+					};
+					continue;
+				case "FieldOfView":
+					var ratio = 16/9, fov = 45.;
+					for( p in getChild(model, "NodeAttribute").getAll("Properties70.P") ) {
+						switch( p.props[0].toString() ) {
+						case "FilmAspectRatio": ratio = p.props[4].toFloat();
+						case "FieldOfView": fov = p.props[4].toFloat();
+						default:
+						}
+					}
+					inline function fovXtoY(v:Float) {
+						return 2 * Math.atan( Math.tan(v * 0.5 * Math.PI / 180) / ratio ) * 180 / Math.PI;
+					}
+					for( i in 0...values.length )
+						values[i] = fovXtoY(values[i]);
+					if( !roundValues(values, fovXtoY(fov)) )
+						continue;
+					c.fov = {
+						v : values,
 						t : times,
 					};
 					continue;
@@ -640,7 +698,7 @@ class BaseLibrary {
 			//if( data.y.length != times.length || data.z.length != times.length )
 			//	throw "Unsynchronized curve components on " + model.getName()+"."+cname+" (" + data.x.length + "/" + data.y.length + "/" + data.z.length + ")";
 			// optimize empty animations out
-			var E = 1e-3, M = 1.0;
+			var M = 1.0;
 			var def = switch( cname ) {
 			case "T": if( c.def.trans == null ) P0 else c.def.trans;
 			case "R": M = F; if( c.def.rotate == null ) P0 else c.def.rotate;
@@ -649,30 +707,12 @@ class BaseLibrary {
 				throw "Unknown curve " + model.getName()+"."+cname;
 			}
 			var hasValue = false;
-			for( i in 0...data.x.length ) {
-				var v = data.x[i];
-				if( Math.abs(v*M - def.x) > E )
-					hasValue = true;
-				else
-					v = def.x / M;
-				data.x[i] = round(v);
-			}
-			for( i in 0...data.y.length ) {
-				var v = data.y[i];
-				if( Math.abs(v*M - def.y) > E )
-					hasValue = true;
-				else
-					v = def.y / M;
-				data.y[i] = round(v);
-			}
-			for( i in 0...data.z.length ) {
-				var v = data.z[i];
-				if( Math.abs(v*M - def.z) > E )
-					hasValue = true;
-				else
-					v = def.z / M;
-				data.z[i] = round(v);
-			}
+			if( roundValues(data.x, def.x, M) )
+				hasValue = true;
+			if( roundValues(data.y, def.y, M) )
+				hasValue = true;
+			if( roundValues(data.z, def.z, M) )
+				hasValue = true;
 			// no meaningful value found
 			if( !hasValue )
 				continue;
@@ -732,242 +772,158 @@ class BaseLibrary {
 			if( allTimes.length != numFrames ) throw "assert";
 		}
 
-		switch( mode ) {
-		case FrameAnim:
-			var anim = new h3d.anim.FrameAnimation(animName, numFrames, sampling);
-
-			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 && uvs == null )
-					continue;
-				var ctx = c.t == null ? null : c.t.x;
-				var cty = c.t == null ? null : c.t.y;
-				var ctz = c.t == null ? null : c.t.z;
-				var ctt = c.t == null ? [-1.] : c.t.t;
-				var crx = c.r == null ? null : c.r.x;
-				var cry = c.r == null ? null : c.r.y;
-				var crz = c.r == null ? null : c.r.z;
-				var crt = c.r == null ? [-1.] : c.r.t;
-				var csx = c.s == null ? null : c.s.x;
-				var csy = c.s == null ? null : c.s.y;
-				var csz = c.s == null ? null : c.s.z;
-				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, uvp = 0;
-				var curMat = null;
-				for( f in 0...numFrames ) {
-					var changed = curMat == null;
-					if( allTimes[f] == ctt[tp] ) {
-						changed = true;
-						tp++;
-					}
-					if( allTimes[f] == crt[rp] ) {
-						changed = true;
-						rp++;
-					}
-					if( allTimes[f] == cst[sp] ) {
-						changed = true;
-						sp++;
-					}
-					if( changed ) {
-						var m = new h3d.Matrix();
-						m.identity();
-						if( c.s == null || sp == 0 ) {
-							if( def.scale != null )
-								m.scale(def.scale.x, def.scale.y, def.scale.z);
-						} else
-							m.scale(csx[sp-1], csy[sp-1], csz[sp-1]);
-
-						if( c.r == null || rp == 0 ) {
-							if( def.rotate != null )
-								m.rotate(def.rotate.x, def.rotate.y, def.rotate.z);
-						} else
-							m.rotate(crx[rp-1] * F, cry[rp-1] * F, crz[rp-1] * F);
-
-						if( def.preRot != null )
-							m.rotate(def.preRot.x, def.preRot.y, def.preRot.z);
-
-						if( c.t == null || tp == 0 ) {
-							if( def.trans != null )
-								m.translate(def.trans.x, def.trans.y, def.trans.z);
-						} else
-							m.translate(ctx[tp-1], cty[tp-1], ctz[tp-1]);
-
-						if( leftHand )
-							DefaultMatrixes.rightHandToLeft(m);
-
-						curMat = m;
-					}
-					if( frames != null )
-						frames[f] = curMat;
-					if( alpha != null ) {
-						if( allTimes[f] == cat[ap] )
-							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.object, frames);
-				if( alpha != null )
-					anim.addAlphaCurve(c.object, alpha);
-				if( uvs != null )
-					anim.addUVCurve(c.object, uvs);
+		var anim = new h3d.anim.LinearAnimation(animName, numFrames, sampling);
+		var q = new h3d.Quat(), q2 = new h3d.Quat();
+
+		for( c in curves ) {
+			var numFrames = numFrames;
+			var sameData = true;
+			if( c.t == null && c.r == null && c.s == null && c.a == null && c.uv == null && c.roll == null && c.fov == null )
+				numFrames = 1;
+			else {
+				if( sameData )
+					sameData = checkData(c.t);
+				if( sameData )
+					sameData = checkData(c.r);
+				if( sameData )
+					sameData = checkData(c.s);
 			}
-			return anim;
-
-		case LinearAnim:
-
-			var anim = new h3d.anim.LinearAnimation(animName, numFrames, sampling);
-			var q = new h3d.Quat(), q2 = new h3d.Quat();
-
-			for( c in curves ) {
-				var numFrames = numFrames;
-				var sameData = true;
-				if( c.t == null && c.r == null && c.s == null && c.a == null && c.uv == null )
-					numFrames = 1;
-				else {
-					if( sameData )
-						sameData = checkData(c.t);
-					if( sameData )
-						sameData = checkData(c.r);
-					if( sameData )
-						sameData = checkData(c.s);
+			var frames = new haxe.ds.Vector(sameData ? 1 : numFrames);
+			var alpha = c.a == null ? null : new haxe.ds.Vector(numFrames);
+			var uvs = c.uv == null ? null : new haxe.ds.Vector(numFrames * 2);
+			var roll = c.roll == null ? null : new haxe.ds.Vector(numFrames);
+			var fov = c.fov == null ? null : new haxe.ds.Vector(numFrames);
+			// skip empty curves
+			if( frames == null && alpha == null && uvs == null && roll == null && fov == null )
+				continue;
+			var ctx = c.t == null ? null : c.t.x;
+			var cty = c.t == null ? null : c.t.y;
+			var ctz = c.t == null ? null : c.t.z;
+			var ctt = c.t == null ? [-1.] : c.t.t;
+			var crx = c.r == null ? null : c.r.x;
+			var cry = c.r == null ? null : c.r.y;
+			var crz = c.r == null ? null : c.r.z;
+			var crt = c.r == null ? [-1.] : c.r.t;
+			var csx = c.s == null ? null : c.s.x;
+			var csy = c.s == null ? null : c.s.y;
+			var csz = c.s == null ? null : c.s.z;
+			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, uvp = 0, fovp = 0, rollp = 0;
+			var curFrame = null;
+			for( f in 0...numFrames ) {
+				var changed = curFrame == null;
+				if( allTimes[f] == ctt[tp] ) {
+					changed = true;
+					tp++;
 				}
-				var frames = new haxe.ds.Vector(sameData ? 1 : 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 && uvs == null )
-					continue;
-				var ctx = c.t == null ? null : c.t.x;
-				var cty = c.t == null ? null : c.t.y;
-				var ctz = c.t == null ? null : c.t.z;
-				var ctt = c.t == null ? [-1.] : c.t.t;
-				var crx = c.r == null ? null : c.r.x;
-				var cry = c.r == null ? null : c.r.y;
-				var crz = c.r == null ? null : c.r.z;
-				var crt = c.r == null ? [-1.] : c.r.t;
-				var csx = c.s == null ? null : c.s.x;
-				var csy = c.s == null ? null : c.s.y;
-				var csz = c.s == null ? null : c.s.z;
-				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, uvp = 0;
-				var curFrame = null;
-				for( f in 0...numFrames ) {
-					var changed = curFrame == null;
-					if( allTimes[f] == ctt[tp] ) {
-						changed = true;
-						tp++;
-					}
-					if( allTimes[f] == crt[rp] ) {
-						changed = true;
-						rp++;
-					}
-					if( allTimes[f] == cst[sp] ) {
-						changed = true;
-						sp++;
-					}
-					if( changed ) {
-						var f = new h3d.anim.LinearAnimation.LinearFrame();
-						if( c.s == null || sp == 0 ) {
-							if( def.scale != null ) {
-								f.sx = def.scale.x;
-								f.sy = def.scale.y;
-								f.sz = def.scale.z;
-							} else {
-								f.sx = 1;
-								f.sy = 1;
-								f.sz = 1;
-							}
+				if( allTimes[f] == crt[rp] ) {
+					changed = true;
+					rp++;
+				}
+				if( allTimes[f] == cst[sp] ) {
+					changed = true;
+					sp++;
+				}
+				if( changed ) {
+					var f = new h3d.anim.LinearAnimation.LinearFrame();
+					if( c.s == null || sp == 0 ) {
+						if( def.scale != null ) {
+							f.sx = def.scale.x;
+							f.sy = def.scale.y;
+							f.sz = def.scale.z;
 						} else {
-							f.sx = csx[sp - 1];
-							f.sy = csy[sp - 1];
-							f.sz = csz[sp - 1];
+							f.sx = 1;
+							f.sy = 1;
+							f.sz = 1;
 						}
+					} else {
+						f.sx = csx[sp - 1];
+						f.sy = csy[sp - 1];
+						f.sz = csz[sp - 1];
+					}
 
-						if( c.r == null || rp == 0 ) {
-							if( def.rotate != null ) {
-								q.initRotate(def.rotate.x, def.rotate.y, def.rotate.z);
-							} else
-								q.identity();
+					if( c.r == null || rp == 0 ) {
+						if( def.rotate != null ) {
+							q.initRotate(def.rotate.x, def.rotate.y, def.rotate.z);
 						} else
-							q.initRotate(crx[rp-1] * F, cry[rp-1] * F, crz[rp-1] * F);
+							q.identity();
+					} else
+						q.initRotate(crx[rp-1], cry[rp-1], crz[rp-1]);
 
-						if( def.preRot != null ) {
-							q2.initRotate(def.preRot.x, def.preRot.y, def.preRot.z);
-							q.multiply(q,q2);
-						}
+					if( def.preRot != null ) {
+						q2.initRotate(def.preRot.x, def.preRot.y, def.preRot.z);
+						q.multiply(q,q2);
+					}
 
-						f.qx = q.x;
-						f.qy = q.y;
-						f.qz = q.z;
-						f.qw = q.w;
-
-						if( c.t == null || tp == 0 ) {
-							if( def.trans != null ) {
-								f.tx = def.trans.x;
-								f.ty = def.trans.y;
-								f.tz = def.trans.z;
-							} else {
-								f.tx = 0;
-								f.ty = 0;
-								f.tz = 0;
-							}
-						} else {
-							f.tx = ctx[tp - 1];
-							f.ty = cty[tp - 1];
-							f.tz = ctz[tp - 1];
-						}
+					f.qx = q.x;
+					f.qy = q.y;
+					f.qz = q.z;
+					f.qw = q.w;
 
-						if( leftHand ) {
-							f.tx = -f.tx;
-							f.qy = -f.qy;
-							f.qz = -f.qz;
+					if( c.t == null || tp == 0 ) {
+						if( def.trans != null ) {
+							f.tx = def.trans.x;
+							f.ty = def.trans.y;
+							f.tz = def.trans.z;
+						} else {
+							f.tx = 0;
+							f.ty = 0;
+							f.tz = 0;
 						}
-
-						curFrame = f;
-					}
-					if( frames != null && f < frames.length )
-						frames[f] = curFrame;
-					if( alpha != null ) {
-						if( allTimes[f] == cat[ap] )
-							ap++;
-						alpha[f] = cav[ap - 1];
+					} else {
+						f.tx = ctx[tp - 1];
+						f.ty = cty[tp - 1];
+						f.tz = ctz[tp - 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( leftHand ) {
+						f.tx = -f.tx;
+						f.qy = -f.qy;
+						f.qz = -f.qz;
 					}
+
+					curFrame = f;
+				}
+				if( frames != null && f < frames.length )
+					frames[f] = curFrame;
+				if( alpha != null ) {
+					if( allTimes[f] == cat[ap] )
+						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( roll != null ) {
+					if( allTimes[f] == c.roll.t[rollp] )
+						rollp++;
+					roll[f] = c.roll.v[rollp - 1];
+				}
+				if( fov != null ) {
+					if( allTimes[f] == c.fov.t[fovp] )
+						fovp++;
+					fov[f] = c.fov.v[fovp - 1];
 				}
-				if( frames != null )
-					anim.addCurve(c.object, frames, c.r != null || def.rotate != null, c.s != null || def.scale != null);
-				if( alpha != null )
-					anim.addAlphaCurve(c.object, alpha);
-				if( uvs != null )
-					anim.addUVCurve(c.object, uvs);
 			}
-			return anim;
-
+			if( frames != null )
+				anim.addCurve(c.object, frames, c.r != null || def.rotate != null, c.s != null || def.scale != null);
+			if( alpha != null )
+				anim.addAlphaCurve(c.object, alpha);
+			if( uvs != null )
+				anim.addUVCurve(c.object, uvs);
+			if( roll != null )
+				anim.addPropCurve(c.object, "Roll", roll);
+			if( fov != null )
+				anim.addPropCurve(c.object, "FOVY", fov);
 		}
+		return anim;
 	}
 
 	function sortDistinctFloats( a : Float, b : Float ) {

+ 8 - 1
hxd/fmt/fbx/HMDOut.hx

@@ -577,6 +577,7 @@ class HMDOut extends BaseLibrary {
 			var o = new AnimationObject();
 			o.name = obj.objectName;
 			o.flags = new haxe.EnumFlags();
+			o.props = [];
 			if( obj.frames != null ) {
 				o.flags.set(HasPosition);
 				if( obj.hasRotation )
@@ -615,6 +616,12 @@ class HMDOut extends BaseLibrary {
 				for( f in obj.alphas )
 					writeFloat(f);
 			}
+			if( obj.propValues != null ) {
+				o.flags.set(HasProps);
+				o.props.push(obj.propName);
+				for( f in obj.propValues )
+					writeFloat(f);
+			}
 			a.objects.push(o);
 		}
 		return a;
@@ -648,7 +655,7 @@ class HMDOut extends BaseLibrary {
 
 		addModels(includeGeometry);
 
-		var anim = loadAnimation(LinearAnim);
+		var anim = loadAnimation();
 		if( anim != null )
 			d.animations.push(makeAnimation(anim));
 

+ 3 - 0
hxd/fmt/hmd/Data.hx

@@ -170,11 +170,14 @@ enum AnimationFlag {
 	HasUV;
 	HasAlpha;
 	SinglePosition;
+	HasProps;
+	Reserved;
 }
 
 class AnimationObject {
 	public var name : String;
 	public var flags : haxe.EnumFlags<AnimationFlag>;
+	public var props : Array<String>;
 	public function new() {
 	}
 }

+ 5 - 0
hxd/fmt/hmd/Dump.hx

@@ -130,6 +130,7 @@ class Dump {
 					if( o.flags.has(f) ) {
 						var n = f.getName();
 						if( StringTools.startsWith(n, "Has") ) n = n.substr(3);
+						if( f == HasProps ) n += o.props;
 						flags.push(n);
 					}
 				add('${o.name} : ${flags.join(",")}');
@@ -181,6 +182,10 @@ class Dump {
 					add('${o.name} UV : '+Std.string([for( i in 0...a.frames ) [for( j in 0...2 ) d.readFloat()]]));
 				if( o.flags.has(HasAlpha) )
 					add('${o.name} Alpha : '+Std.string([for( i in 0...a.frames ) d.readFloat()]));
+				if( o.flags.has(HasProps) ) {
+					for( p in o.props )
+						add('${o.name} $p : '+Std.string([for( i in 0...a.frames ) d.readFloat()]));
+				}
 			}
 			prefix = '';
 		}

+ 12 - 92
hxd/fmt/hmd/Library.hx

@@ -339,10 +339,7 @@ class Library {
 		return objs[0];
 	}
 
-	public function loadAnimation( ?mode : h3d.anim.Mode, ?name : String ) : h3d.anim.Animation {
-
-		if( mode == null )
-			mode = LinearAnim;
+	public function loadAnimation( ?name : String ) : h3d.anim.Animation {
 
 		var a = cachedAnimations.get(name == null ? "" : name);
 		if( a != null )
@@ -363,12 +360,7 @@ class Library {
 				throw 'Animation $name not found !';
 		}
 
-		var l = switch( mode ) {
-		case FrameAnim:
-			makeFrameAnimation(a);
-		case LinearAnim:
-			makeAnimation(a);
-		}
+		var l = makeAnimation(a);
 		cachedAnimations.set(a.name, l);
 		if( name == null ) cachedAnimations.set("", l);
 		return l;
@@ -452,93 +444,21 @@ class Library {
 				l.addAlphaCurve(o.name, fl);
 				hxd.impl.Tmp.saveBytes(data);
 			}
-		}
-
-		entry.close();
-
-
-		return l;
-	}
-
-	function makeFrameAnimation( a : Animation ) {
-
-		var l = new h3d.anim.FrameAnimation(a.name, a.frames, a.sampling);
-		l.speed = a.speed;
-		l.loop = a.loop;
-
-		entry.open();
-		entry.skip(header.dataPosition + a.dataPosition);
-
-		var q = new h3d.Quat(), mrot = new h3d.Matrix();
-		for( o in a.objects ) {
-			var pos = o.flags.has(HasPosition), rot = o.flags.has(HasRotation), scale = o.flags.has(HasScale);
-			if( pos || rot || scale ) {
-				var frameCount = a.frames;
-				if( o.flags.has(SinglePosition) )
-					frameCount = 1;
-				var fl = new haxe.ds.Vector<h3d.Matrix>(frameCount);
-				var size = ((pos ? 3 : 0) + (rot ? 3 : 0) + (scale?3:0)) * 4 * frameCount;
-				var data = hxd.impl.Tmp.getBytes(size);
-				entry.read(data, 0, size);
-				var p = 0;
-				for( i in 0...frameCount ) {
-					var m = new h3d.Matrix();
-					var tx = 0., ty = 0., tz = 0.;
-					m.identity();
-					if( pos ) {
-						tx = data.getFloat(p); p += 4;
-						ty = data.getFloat(p); p += 4;
-						tz = data.getFloat(p); p += 4;
-					}
-					if( rot ) {
-						var qx = data.getFloat(p); p += 4;
-						var qy = data.getFloat(p); p += 4;
-						var qz = data.getFloat(p); p += 4;
-						var qw = 1 - (qx * qx + qy * qy + qz * qz);
-						qw = qw < 0 ? -Math.sqrt( -qw) : Math.sqrt(qw);
-						q.set(qx, qy, qz, qw);
-						q.saveToMatrix(scale ? mrot : m);
-					}
-					if( scale ) {
-						var sx = data.getFloat(p); p += 4;
-						var sy = data.getFloat(p); p += 4;
-						var sz = data.getFloat(p); p += 4;
-						m.initScale(sx, sy, sz);
-						if( rot ) m.multiply3x4(m, mrot);
-					}
-					m._41 = tx;
-					m._42 = ty;
-					m._43 = tz;
-					fl[i] = m;
+			if( o.flags.has(HasProps) ) {
+				for( p in o.props ) {
+					var fl = new haxe.ds.Vector(a.frames);
+					var size = 4 * a.frames;
+					var data = hxd.impl.Tmp.getBytes(size);
+					entry.read(data, 0, size);
+					for( i in 0...fl.length )
+						fl[i] = data.getFloat(i * 4);
+					l.addPropCurve(o.name, p, fl);
+					hxd.impl.Tmp.saveBytes(data);
 				}
-				l.addCurve(o.name, fl);
-				hxd.impl.Tmp.saveBytes(data);
-			}
-			if( o.flags.has(HasUV) ) {
-				var fl = new haxe.ds.Vector(a.frames*2);
-				var size = 2 * 4 * a.frames;
-				var data = hxd.impl.Tmp.getBytes(size);
-				entry.read(data, 0, size);
-				for( i in 0...fl.length )
-					fl[i] = data.getFloat(i * 4);
-				l.addUVCurve(o.name, fl);
-				hxd.impl.Tmp.saveBytes(data);
-			}
-			if( o.flags.has(HasAlpha) ) {
-				var fl = new haxe.ds.Vector(a.frames);
-				var size = 4 * a.frames;
-				var data = hxd.impl.Tmp.getBytes(size);
-				entry.read(data, 0, size);
-				for( i in 0...fl.length )
-					fl[i] = data.getFloat(i * 4);
-				l.addAlphaCurve(o.name, fl);
-				hxd.impl.Tmp.saveBytes(data);
 			}
 		}
 
 		entry.close();
-
-
 		return l;
 	}
 

+ 2 - 0
hxd/fmt/hmd/Reader.hx

@@ -179,6 +179,8 @@ class Reader {
 				o.name = readName();
 				o.flags = haxe.EnumFlags.ofInt(i.readByte());
 				a.objects.push(o);
+				if( o.flags.has(HasProps) )
+					o.props = [for( i in 0...i.readByte() ) readName()];
 			}
 			d.animations.push(a);
 		}

+ 5 - 0
hxd/fmt/hmd/Writer.hx

@@ -166,6 +166,11 @@ class Writer {
 			for( o in a.objects ) {
 				writeName(o.name);
 				out.writeByte(o.flags.toInt());
+				if( o.flags.has(HasProps) ) {
+					out.writeByte(o.props.length);
+					for( n in o.props )
+						writeName(n);
+				}
 			}
 		}
 

+ 5 - 7
tools/fbx/Viewer.hx

@@ -36,10 +36,10 @@ class Viewer extends hxd.App {
 
 	var curFbx : hxd.fmt.fbx.Library;
 	var curHmd : hxd.fmt.hmd.Library;
+	static public var loadAnim = true;
 	static public var curData : haxe.io.Bytes;
 	static public var curDataSize : Int;
 	static public var props : Props;
-	static public var animMode : Null<h3d.anim.Mode> = LinearAnim;
 
 	var rightHand : Bool;
 	var playAnim : Bool;
@@ -272,9 +272,7 @@ class Viewer extends hxd.App {
 			else
 				props.speed = props.speed == 1 ? 0.1 : 1;
 		case "A".code:
-			var cst = h3d.anim.Mode.createAll();
-			cst.push(null);
-			animMode = cst[(Lambda.indexOf(cst, animMode) + 1) % cst.length];
+			loadAnim = !loadAnim;
 			reload = true;
 		case "L".code:
 			props.loop = !props.loop;
@@ -557,12 +555,12 @@ class Viewer extends hxd.App {
 
 	function setAnim() {
 		var anim;
-		if( animMode == null )
+		if( !loadAnim )
 			anim = null;
 		else if( curHmd != null )
 			anim = (ahmd == null ? curHmd : ahmd).loadAnimation();
 		else
-			anim = curFbx.loadAnimation(animMode, null, null, alib);
+			anim = curFbx.loadAnimation(null, null, alib);
 		if( anim == null )
 			return;
 		var prev = s3d.currentAnimation;
@@ -668,7 +666,7 @@ class Viewer extends hxd.App {
 		tf_keys.text = [
 			"[F1] Load model",
 			"[F2] Load animation",
-			"[A] Animation = " + animMode,
+			"[A] Animation = " + loadAnim,
 			"[L] Loop = "+props.loop,
 			"[Y] Axis = "+props.showAxis,
 			"[K] Bones = "+props.showBones,