Przeglądaj źródła

fixes to reduce animation data (when constant on the frame), always store at least one position, reduce precision to ~1e-5

ncannasse 11 lat temu
rodzic
commit
545ab86c5f

+ 21 - 3
h3d/anim/LinearAnimation.hx

@@ -14,6 +14,13 @@ class LinearFrame {
 	public var sz : Float;
 	public function new() {
 	}
+	public function toMatrix() {
+		var m = new h3d.Matrix();
+		new h3d.Quat(qx, qy, qz, qw).saveToMatrix(m);
+		m.prependScale(sx, sy, sz);
+		m.translate(tx, ty, tz);
+		return m;
+	}
 }
 
 class LinearObject extends AnimatedObject {
@@ -37,6 +44,7 @@ class LinearObject extends AnimatedObject {
 class LinearAnimation extends Animation {
 
 	var syncFrame : Float;
+	var isSync : Bool;
 
 	public function new(name,frame,sampling) {
 		super(name,frame,sampling);
@@ -127,6 +135,17 @@ class LinearAnimation extends Animation {
 				s.uvDelta.y = uvLerp(o.uvs[(frame1 << 1) | 1],o.uvs[(frame2 << 1) | 1],k2);
 				continue;
 			}
+
+			var frame1 = frame1, frame2 = frame2;
+
+			// if we have a single frame
+			if( o.frames.length == 1 ) {
+				if( isSync && !decompose )
+					continue;
+				frame1 = frame2 = 0;
+				isSync = !decompose;
+			}
+
 			var f1 = o.frames[frame1], f2 = o.frames[frame2];
 
 			var m = o.matrix;
@@ -212,12 +231,11 @@ class LinearAnimation extends Animation {
 					m._33 = f1.sz * k1 + f2.sz * k2;
 				} else {
 					m._11 = 1;
-					m._12 = 1;
-					m._13 = 1;
+					m._22 = 1;
+					m._33 = 1;
 				}
 			}
 
-
 			if( o.targetSkin != null ) {
 				o.targetSkin.currentRelPose[o.targetJoint] = o.matrix;
 				o.targetSkin.jointsUpdated = true;

+ 24 - 3
h3d/anim/SmoothTransition.hx

@@ -5,6 +5,7 @@ class SmoothedObject extends Animation.AnimatedObject {
 	public var outMatrix : h3d.Matrix;
 	public var isAnim1 : Bool;
 	public var isAnim2 : Bool;
+	public var def : h3d.Matrix;
 	public function new(name) {
 		super(name);
 		outMatrix = h3d.Matrix.I();
@@ -52,12 +53,33 @@ class SmoothTransition extends Transition {
 				so.targetJoint = o.targetJoint;
 				so.targetSkin = o.targetSkin;
 				so.targetObject = o.targetObject;
-				so.tmpMatrix = mzero;
 				allObjects.set(o.objectName, so);
 				objects.push(so);
 			}
 			so.isAnim2 = true;
 		}
+		for( so in allObjects ) {
+			if( so.isAnim1 && so.isAnim2 )
+				continue;
+			if( so.targetSkin != null ) {
+				var j = @:privateAccess so.targetSkin.skinData.allJoints[so.targetJoint];
+				var q = new h3d.Quat();
+				q.initRotateMatrix(j.defMat);
+				q.normalize();
+				so.def = j.defMat.clone();
+				// todo : extract default scale from matrix
+				so.def._11 = 1;
+				so.def._22 = 1;
+				so.def._33 = 1;
+				so.def._12 = q.x;
+				so.def._13 = q.y;
+				so.def._21 = q.z;
+				so.def._23 = q.w;
+			} else
+				so.def = mzero;
+			if( !so.isAnim1 )
+				so.tmpMatrix = so.def;
+		}
 	}
 
 	override function bind( base ) {
@@ -78,11 +100,10 @@ class SmoothTransition extends Transition {
 		}
 		anim2.sync(true);
 		var a = 1 - blendFactor, b = blendFactor;
-		var mzero = MZERO;
 		var q1 = new h3d.Quat(), q2 = new h3d.Quat(), qout = new h3d.Quat();
 		for( o in objects ) {
 			var m1 = o.tmpMatrix;
-			var m2 = if( !o.isAnim2 ) mzero else if( o.targetSkin != null ) o.targetSkin.currentRelPose[o.targetJoint] else o.targetObject.defaultTransform;
+			var m2 = if( !o.isAnim2 ) o.def else if( o.targetSkin != null ) o.targetSkin.currentRelPose[o.targetJoint] else o.targetObject.defaultTransform;
 			var m = o.outMatrix;
 			// interpolate rotation
 			q1.set(m1._12, m1._13, m1._21, m1._23);

+ 84 - 33
hxd/fmt/fbx/BaseLibrary.hx

@@ -49,11 +49,11 @@ class DefaultMatrixes {
 		// in right hand we have [x,y,z] * M = [x',y',z']
 		// we need to ensure that left hand matrix convey the x axis flip,
 		// in order to have [-x,y,z] * M = [-x',y',z']
-		m._12 *= -1;
-		m._13 *= -1;
-		m._21 *= -1;
-		m._31 *= -1;
-		m._41 *= -1;
+		m._12 = -m._12;
+		m._13 = -m._13;
+		m._21 = -m._21;
+		m._31 = -m._31;
+		m._41 = -m._41;
 	}
 
 	public function toMatrix(leftHand) {
@@ -479,6 +479,29 @@ class BaseLibrary {
 		invConnect.get(nid).remove(pid);
 	}
 
+	function checkData( t : { x : Array<Float>, y :Array<Float>, z:Array<Float> } ) {
+		if( t == null )
+			return true;
+		if( t.x != null ) {
+			var v = t.x[0];
+			for( v2 in t.x )
+				if( v != v2 )
+					return false;
+		}
+		if( t.y != null ) {
+			var v = t.y[0];
+			for( v2 in t.y )
+				if( v != v2 )
+					return false;
+		}
+		if( t.z != null ) {
+			var v = t.z[0];
+			for( v2 in t.z )
+				if( v != v2 )
+					return false;
+		}
+		return true;
+	}
 
 	public function loadAnimation( mode : Mode, ?animName : String, ?root : FbxNode, ?lib : BaseLibrary ) : h3d.anim.Animation {
 		if( lib != null ) {
@@ -558,7 +581,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-10, M = 1.0;
+			var E = 1e-3, 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;
@@ -567,24 +590,29 @@ class BaseLibrary {
 				throw "Unknown curve " + model.getName()+"."+cname;
 			}
 			var hasValue = false;
-			for( v in data.x )
-				if( v*M < def.x-E || v*M > def.x+E ) {
+			for( i in 0...data.x.length ) {
+				var v = data.x[i];
+				if( Math.abs(v*M - def.x) > E )
 					hasValue = true;
-					break;
-				}
-			if( !hasValue ) {
-				for( v in data.y )
-					if( v*M < def.y-E || v*M > def.y+E ) {
-						hasValue = true;
-						break;
-					}
+				else
+					v = def.x / M;
+				data.x[i] = round(v);
 			}
-			if( !hasValue ) {
-				for( v in data.z )
-					if( v*M < def.z-E || v*M > def.z+E ) {
-						hasValue = true;
-						break;
-					}
+			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);
 			}
 			// no meaningful value found
 			if( !hasValue )
@@ -735,7 +763,19 @@ class BaseLibrary {
 			var q = new h3d.Quat(), q2 = new h3d.Quat();
 
 			for( c in curves ) {
-				var frames = c.t == null && c.r == null && c.s == null ? null : new haxe.ds.Vector(numFrames);
+				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);
 				// skip empty curves
@@ -783,7 +823,7 @@ class BaseLibrary {
 							} else {
 								f.sx = 1;
 								f.sy = 1;
-								f.sx = 1;
+								f.sz = 1;
 							}
 						} else {
 							f.sx = csx[sp - 1];
@@ -826,14 +866,14 @@ class BaseLibrary {
 						}
 
 						if( leftHand ) {
-							f.tx *= -1;
-							f.qy *= -1;
-							f.qz *= -1;
+							f.tx = -f.tx;
+							f.qy = -f.qy;
+							f.qz = -f.qz;
 						}
 
 						curFrame = f;
 					}
-					if( frames != null )
+					if( frames != null && f < frames.length )
 						frames[f] = curFrame;
 					if( alpha != null ) {
 						if( allTimes[f] == cat[ap] )
@@ -847,7 +887,6 @@ class BaseLibrary {
 						uvs[(f<<1)|1] = cuv[uvp - 1].v;
 					}
 				}
-
 				if( frames != null )
 					anim.addCurve(c.object, frames, c.r != null || def.rotate != null, c.s != null || def.scale != null);
 				if( alpha != null )
@@ -998,6 +1037,10 @@ class BaseLibrary {
 		return skin;
 	}
 
+	function round(v:Float) {
+		return std.Math.fround(v * 131072) / 131072;
+	}
+
 	function getDefaultMatrixes( model : FbxNode ) {
 		var name = model.getName();
 		var d = defaultModelMatrixes.get(name);
@@ -1010,13 +1053,21 @@ class BaseLibrary {
 			case "GeometricTranslation":
 				// handle in Geometry directly
 			case "PreRotation":
-				d.preRot = new Point(p.props[4].toFloat() * F, p.props[5].toFloat() * F, p.props[6].toFloat() * F);
+				d.preRot = new Point(round(p.props[4].toFloat() * F), round(p.props[5].toFloat() * F), round(p.props[6].toFloat() * F));
+				if( d.preRot.x == 0 && d.preRot.y == 0 && d.preRot.z == 0 )
+					d.preRot = null;
 			case "Lcl Rotation":
-				d.rotate = new Point(p.props[4].toFloat() * F, p.props[5].toFloat() * F, p.props[6].toFloat() * F);
+				d.rotate = new Point(round(p.props[4].toFloat() * F), round(p.props[5].toFloat() * F), round(p.props[6].toFloat() * F));
+				if( d.rotate.x == 0 && d.rotate.y == 0 && d.rotate.z == 0 )
+					d.rotate = null;
 			case "Lcl Translation":
-				d.trans = new Point(p.props[4].toFloat(), p.props[5].toFloat(), p.props[6].toFloat());
+				d.trans = new Point(round(p.props[4].toFloat()), round(p.props[5].toFloat()), round(p.props[6].toFloat()));
+				if( d.trans.x == 0 && d.trans.y == 0 && d.trans.z == 0 )
+					d.trans = null;
 			case "Lcl Scaling":
-				d.scale = new Point(p.props[4].toFloat(), p.props[5].toFloat(), p.props[6].toFloat());
+				d.scale = new Point(round(p.props[4].toFloat()), round(p.props[5].toFloat()), round(p.props[6].toFloat()));
+				if( d.scale.x == 1 && d.scale.y == 1 && d.scale.z == 1 )
+					d.scale = null;
 			default:
 			}
 		defaultModelMatrixes.set(name, d);

+ 25 - 5
hxd/fmt/hmd/Library.hx

@@ -37,6 +37,15 @@ class Library {
 		cachedSkin = new Map();
 	}
 
+	public function getData() {
+		var b = haxe.io.Bytes.alloc(entry.size - header.dataPosition);
+		entry.open();
+		entry.skip(header.dataPosition);
+		entry.read(b, 0, b.length);
+		entry.close();
+		return b;
+	}
+
 	@:noDebug
 	public function getBuffers( geom : Geometry, format : Array<GeometryFormat>, ?defaults : Array<h3d.Vector>, ?material : Int ) {
 
@@ -294,6 +303,16 @@ class Library {
 			if( a == null )
 				throw 'Animation $name not found !';
 		}
+
+		var l = makeAnimation(a);
+		cachedAnimations.set(a.name, l);
+		if( name == null ) cachedAnimations.set("", l);
+		return l;
+	}
+
+
+	function makeAnimation( a : Animation ) {
+
 		var l = new h3d.anim.LinearAnimation(a.name, a.frames, a.sampling);
 		l.speed = a.speed;
 		l.loop = a.loop;
@@ -304,12 +323,15 @@ class Library {
 		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 fl = new haxe.ds.Vector<h3d.anim.LinearAnimation.LinearFrame>(a.frames);
-				var size = ((pos ? 3 : 0) + (rot ? 3 : 0) + (scale?3:0)) * 4 * a.frames;
+				var frameCount = a.frames;
+				if( o.flags.has(SinglePosition) )
+					frameCount = 1;
+				var fl = new haxe.ds.Vector<h3d.anim.LinearAnimation.LinearFrame>(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...fl.length ) {
+				for( i in 0...frameCount ) {
 					var f = new h3d.anim.LinearAnimation.LinearFrame();
 					if( pos ) {
 						f.tx = data.getFloat(p); p += 4;
@@ -370,8 +392,6 @@ class Library {
 
 		entry.close();
 
-		cachedAnimations.set(a.name, l);
-		if( name == null ) cachedAnimations.set("", l);
 
 		return l;
 	}