فهرست منبع

FBXLoader refactor animation

Lewy Blue 7 سال پیش
والد
کامیت
cbdb2e8569
1فایلهای تغییر یافته به همراه107 افزوده شده و 62 حذف شده
  1. 107 62
      examples/js/loaders/FBXLoader.js

+ 107 - 62
examples/js/loaders/FBXLoader.js

@@ -16,10 +16,9 @@
  *	NURBS (Open, Closed and Periodic forms)
  *
  * Needs Support:
- * 	Indexed Buffers
- * 	PreRotation support.
  *	Euler rotation order
  *
+ *
  * FBX format references:
  * 	https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure
  *
@@ -97,7 +96,7 @@
 
 			}
 
-			// console.log( FBXTree );
+			console.log( FBXTree );
 
 			var connections = parseConnections( FBXTree );
 			var images = parseImages( FBXTree );
@@ -1990,7 +1989,6 @@
 
 		addAnimations( sceneGraph, animations );
 
-
 		// Parse ambient color - if it's not set to black (default), create an ambient light
 		if ( 'GlobalSettings' in FBXTree && 'AmbientColor' in FBXTree.GlobalSettings.properties ) {
 
@@ -2013,10 +2011,13 @@
 	}
 
 	// Parses animation information from nodes in
-	// FBXTree.Objects.subNodes.AnimationCurve ( connected to AnimationCurveNode )
-	// FBXTree.Objects.subNodes.AnimationCurveNode ( connected to AnimationLayer and an animated property in some other node )
-	// FBXTree.Objects.subNodes.AnimationLayer ( connected to AnimationStack )
+	// FBXTree.Objects.subNodes.AnimationCurve: child of an AnimationCurveNode, holds the raw animation data (e.g. x axis rotation )
+	// FBXTree.Objects.subNodes.AnimationCurveNode: child of an AnimationLayer and connected to whichever node is being animated
+	// FBXTree.Objects.subNodes.AnimationLayer: child of an AnimationStack
 	// FBXTree.Objects.subNodes.AnimationStack
+	// Multiple animation takes are stored in AnimationLayer and AnimationStack
+	// Note: There is also FBXTree.Takes, however this seems to be left over from an older version of the
+	// format and is no longer used
 	function parseAnimations( FBXTree, connections, sceneGraph ) {
 
 		var rawNodes = FBXTree.Objects.subNodes.AnimationCurveNode;
@@ -2024,60 +2025,12 @@
 		var rawLayers = FBXTree.Objects.subNodes.AnimationLayer;
 		var rawStacks = FBXTree.Objects.subNodes.AnimationStack;
 
-		var fps = 30; // default framerate
-
-		if ( 'GlobalSettings' in FBXTree && 'TimeMode' in FBXTree.GlobalSettings.properties ) {
-
-			/* Autodesk time mode documentation can be found here:
-			*	http://docs.autodesk.com/FBX/2014/ENU/FBX-SDK-Documentation/index.html?url=cpp_ref/class_fbx_time.html,topicNumber=cpp_ref_class_fbx_time_html
-			*/
-			var timeModeEnum = [
-				30, // 0: eDefaultMode
-				120, // 1: eFrames120
-				100, // 2: eFrames100
-				60, // 3: eFrames60
-				50, // 4: eFrames50
-				48, // 5: eFrames48
-				30, // 6: eFrames30 (black and white NTSC )
-				30, // 7: eFrames30Drop
-				29.97, // 8: eNTSCDropFrame
-				29.97, // 90: eNTSCFullFrame
-				25, // 10: ePal ( PAL/SECAM )
-				24, // 11: eFrames24 (Film/Cinema)
-				1, // 12: eFrames1000 (use for date time))
-				23.976, // 13: eFilmFullFrame
-				30, // 14: eCustom: use GlobalSettings.properties.CustomFrameRate.value
-				96, // 15: eFrames96
-				72, // 16: eFrames72
-				59.94, // 17: eFrames59dot94
-			];
-
-			var eMode = FBXTree.GlobalSettings.properties.TimeMode.value;
-
-			if ( eMode === 14 ) {
-
-				if ( 'CustomFrameRate' in FBXTree.GlobalSettings.properties ) {
-
-					fps = FBXTree.GlobalSettings.properties.CustomFrameRate.value;
-
-					fps = ( fps === - 1 ) ? 30 : fps;
-
-				}
-
-			} else if ( eMode <= 17 ) { // for future proofing - if more eModes get added, they will default to 30fps
-
-				fps = timeModeEnum[ eMode ];
-
-			}
-
-		}
-
 		var returnObject = {
 			curves: new Map(),
 			layers: {},
 			stacks: {},
 			length: 0,
-			fps: fps,
+			fps: getFrameRate( FBXTree ),
 			frames: 0
 		};
 
@@ -2086,7 +2039,7 @@
 
 			if ( nodeID.match( /\d+/ ) ) {
 
-				var animationNode = parseAnimationNode( FBXTree, rawNodes[ nodeID ], connections, sceneGraph );
+				var animationNode = parseAnimationCurveNode( FBXTree, rawNodes[ nodeID ], connections, sceneGraph );
 				animationCurveNodes.push( animationNode );
 
 			}
@@ -2112,7 +2065,6 @@
 
 				var animationCurve = parseAnimationCurve( rawCurves[ nodeID ] );
 
-				// seems like this check would be necessary?
 				if ( ! connections.has( animationCurve.id ) ) continue;
 
 				animationCurves.push( animationCurve );
@@ -2308,7 +2260,7 @@
 
 	}
 
-	function parseAnimationNode( FBXTree, animationCurveNode, connections, sceneGraph ) {
+	function parseAnimationCurveNode( FBXTree, animationCurveNode, connections, sceneGraph ) {
 
 		var rawModels = FBXTree.Objects.subNodes.Model;
 
@@ -2374,6 +2326,7 @@
 				returnObject.containerBoneID = boneID;
 				returnObject.containerID = containerIndices[ containerIndicesIndex ].ID;
 				var model = rawModels[ returnObject.containerID.toString() ];
+
 				if ( 'PreRotation' in model.properties ) {
 
 					returnObject.preRotations = parseVector3( model.properties.PreRotation ).multiplyScalar( Math.PI / 180 );
@@ -2404,6 +2357,60 @@
 
 	}
 
+	function getFrameRate( FBXTree ) {
+
+		var fps = 30; // default framerate
+
+		if ( 'GlobalSettings' in FBXTree && 'TimeMode' in FBXTree.GlobalSettings.properties ) {
+
+			/* Autodesk time mode documentation can be found here:
+			*	http://docs.autodesk.com/FBX/2014/ENU/FBX-SDK-Documentation/index.html?url=cpp_ref/class_fbx_time.html,topicNumber=cpp_ref_class_fbx_time_html
+			*/
+			var timeModeEnum = [
+				30, // 0: eDefaultMode
+				120, // 1: eFrames120
+				100, // 2: eFrames100
+				60, // 3: eFrames60
+				50, // 4: eFrames50
+				48, // 5: eFrames48
+				30, // 6: eFrames30 (black and white NTSC )
+				30, // 7: eFrames30Drop
+				29.97, // 8: eNTSCDropFrame
+				29.97, // 90: eNTSCFullFrame
+				25, // 10: ePal ( PAL/SECAM )
+				24, // 11: eFrames24 (Film/Cinema)
+				1, // 12: eFrames1000 (use for date time))
+				23.976, // 13: eFilmFullFrame
+				30, // 14: eCustom: use GlobalSettings.properties.CustomFrameRate.value
+				96, // 15: eFrames96
+				72, // 16: eFrames72
+				59.94, // 17: eFrames59dot94
+			];
+
+			var eMode = FBXTree.GlobalSettings.properties.TimeMode.value;
+
+			if ( eMode === 14 ) {
+
+				if ( 'CustomFrameRate' in FBXTree.GlobalSettings.properties ) {
+
+					fps = FBXTree.GlobalSettings.properties.CustomFrameRate.value;
+
+					fps = ( fps === - 1 ) ? 30 : fps;
+
+				}
+
+			} else if ( eMode <= 17 ) { // for future proofing - if more eModes get added, they will default to 30fps
+
+				fps = timeModeEnum[ eMode ];
+
+			}
+
+		}
+
+		return fps;
+
+	}
+
 	// Sets the maxTimeStamp and minTimeStamp variables if it has timeStamps that are either larger or smaller
 	// than the max or min respectively.
 	function getCurveNodeMaxMinTimeStamps( layer, timestamps ) {
@@ -2493,6 +2500,8 @@
 
 			}
 
+			var prevKey = null;
+
 			for ( var frame = 0; frame <= stack.frames; frame ++ ) {
 
 				for ( var bonesIndex = 0, bonesLength = bones.length; bonesIndex < bonesLength; ++ bonesIndex ) {
@@ -2508,7 +2517,23 @@
 
 						if ( node.name === bone.name ) {
 
-							node.keys.push( generateKey( animations, animationNode, bone, frame ) );
+							var newKey = generateKey( animations, animationNode, bone, frame, prevKey );
+
+							// push first key
+							if ( prevKey === null ) {
+
+								node.keys.push( newKey );
+
+							}
+
+							// For subsequent keys, if the key is identical to the last key then skip it
+							else if ( ! prevKey.equals( newKey ) ) {
+
+								node.keys.push( newKey );
+
+							}
+
+							prevKey = newKey;
 
 						}
 
@@ -2527,13 +2552,22 @@
 	var euler = new THREE.Euler();
 	var quaternion = new THREE.Quaternion();
 
-	function generateKey( animations, animationNode, bone, frame ) {
+	function generateKey( animations, animationNode, bone, frame, prevKey ) {
 
 		var key = {
 			time: frame / animations.fps,
 			pos: bone.position.toArray(),
 			rot: bone.quaternion.toArray(),
-			scl: bone.scale.toArray()
+			scl: bone.scale.toArray(),
+			equals: function ( k ) {
+
+				var posEqual = arraysAreEqual( this.pos, k.pos );
+				var rotEqual = arraysAreEqual( this.rot, k.rot );
+				var sclEqual = arraysAreEqual( this.scl, k.scl );
+
+				return posEqual && rotEqual && sclEqual;
+
+			},
 		};
 
 		if ( animationNode === undefined ) return key;
@@ -3982,4 +4016,15 @@
 
 	}
 
+	function arraysAreEqual( a, b ) {
+
+		return a.reduce( function ( current, val, i ) {
+
+			if ( current === false ) return current;
+			return val === b[ i ];
+
+		} );
+
+	}
+
 } )();