浏览代码

FBXLoader refactor animations

Lewy Blue 7 年之前
父节点
当前提交
ce2867d514
共有 1 个文件被更改,包括 68 次插入87 次删除
  1. 68 87
      examples/js/loaders/FBXLoader.js

+ 68 - 87
examples/js/loaders/FBXLoader.js

@@ -1458,7 +1458,7 @@
 
 
 		bindSkeleton( FBXTree, deformers, geometryMap, modelMap, connections, sceneGraph );
 		bindSkeleton( FBXTree, deformers, geometryMap, modelMap, connections, sceneGraph );
 
 
-		addAnimations( FBXTree, connections, sceneGraph );
+		addAnimations( FBXTree, connections, sceneGraph, modelMap );
 
 
 		createAmbientLight( FBXTree, sceneGraph );
 		createAmbientLight( FBXTree, sceneGraph );
 
 
@@ -2051,15 +2051,6 @@
 		//Skeleton is now bound, return objects to starting world positions.
 		//Skeleton is now bound, return objects to starting world positions.
 		sceneGraph.updateMatrixWorld( true );
 		sceneGraph.updateMatrixWorld( true );
 
 
-		// Silly hack with the animation parsing. We're gonna pretend the scene graph has a skeleton
-		// to attach animations to, since FBX treats animations as animations for the entire scene,
-		// not just for individual objects.
-		sceneGraph.skeleton = {
-
-			bones: Array.from( modelMap.values() ),
-
-		};
-
 	}
 	}
 
 
 	// Parses animation information from nodes in
 	// Parses animation information from nodes in
@@ -2070,13 +2061,17 @@
 	// Multiple animation takes are stored in AnimationLayer and 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
 	// 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
 	// format and is no longer used
-	function parseAnimations( FBXTree, connections, sceneGraph ) {
+	function parseAnimations( FBXTree, connections, modelsArray ) {
 
 
 		var rawNodes = FBXTree.Objects.subNodes.AnimationCurveNode;
 		var rawNodes = FBXTree.Objects.subNodes.AnimationCurveNode;
 		var rawCurves = FBXTree.Objects.subNodes.AnimationCurve;
 		var rawCurves = FBXTree.Objects.subNodes.AnimationCurve;
 		var rawLayers = FBXTree.Objects.subNodes.AnimationLayer;
 		var rawLayers = FBXTree.Objects.subNodes.AnimationLayer;
 		var rawStacks = FBXTree.Objects.subNodes.AnimationStack;
 		var rawStacks = FBXTree.Objects.subNodes.AnimationStack;
 
 
+		// since the actual transformation data is stored in rawCurves, if this is undefined we
+		// can safely assume there are no animations
+		if ( rawCurves === undefined ) return undefined;
+
 		var animations = {
 		var animations = {
 			takes: {},
 			takes: {},
 			fps: getFrameRate( FBXTree ),
 			fps: getFrameRate( FBXTree ),
@@ -2087,7 +2082,7 @@
 
 
 			if ( nodeID.match( /\d+/ ) ) {
 			if ( nodeID.match( /\d+/ ) ) {
 
 
-				var animationNode = parseAnimationCurveNode( FBXTree, rawNodes[ nodeID ], connections, sceneGraph );
+				var animationNode = parseAnimationCurveNode( FBXTree, rawNodes[ nodeID ], connections, modelsArray );
 				animationCurveNodes.push( animationNode );
 				animationCurveNodes.push( animationNode );
 
 
 			}
 			}
@@ -2107,15 +2102,13 @@
 		}
 		}
 
 
 		var animationCurves = [];
 		var animationCurves = [];
+
 		for ( nodeID in rawCurves ) {
 		for ( nodeID in rawCurves ) {
 
 
 			if ( nodeID.match( /\d+/ ) ) {
 			if ( nodeID.match( /\d+/ ) ) {
 
 
 				var animationCurve = parseAnimationCurve( rawCurves[ nodeID ] );
 				var animationCurve = parseAnimationCurve( rawCurves[ nodeID ] );
 
 
-				// seems like this check would be necessary?
-				if ( ! connections.has( animationCurve.id ) ) continue;
-
 				animationCurves.push( animationCurve );
 				animationCurves.push( animationCurve );
 
 
 				var firstParentConn = connections.get( animationCurve.id ).parents[ 0 ];
 				var firstParentConn = connections.get( animationCurve.id ).parents[ 0 ];
@@ -2146,46 +2139,24 @@
 			}
 			}
 
 
 		}
 		}
+		var emptyCurve = {
+
+			version: null,
+			times: [ 0.0 ],
+			values: [ 0.0 ]
 
 
+		};
+
+		// loop over rotation values, convert to radians and add any pre rotation
 		tmpMap.forEach( function ( curveNode ) {
 		tmpMap.forEach( function ( curveNode ) {
 
 
 			if ( curveNode.attr === 'R' ) {
 			if ( curveNode.attr === 'R' ) {
 
 
 				var curves = curveNode.curves;
 				var curves = curveNode.curves;
 
 
-				// Some FBX files have an AnimationCurveNode
-				// which isn't any connected to any AnimationCurve.
-				// Setting animation parameter for them here.
-
-				if ( curves.x === null ) {
-
-					curves.x = {
-						version: null,
-						times: [ 0.0 ],
-						values: [ 0.0 ]
-					};
-
-				}
-
-				if ( curves.y === null ) {
-
-					curves.y = {
-						version: null,
-						times: [ 0.0 ],
-						values: [ 0.0 ]
-					};
-
-				}
-
-				if ( curves.z === null ) {
-
-					curves.z = {
-						version: null,
-						times: [ 0.0 ],
-						values: [ 0.0 ]
-					};
-
-				}
+				if ( curves.x === null ) curves.x = emptyCurve;
+				if ( curves.y === null ) curves.y = emptyCurve;
+				if ( curves.z === null ) curves.z = emptyCurve;
 
 
 				curves.x.values = curves.x.values.map( THREE.Math.degToRad );
 				curves.x.values = curves.x.values.map( THREE.Math.degToRad );
 				curves.y.values = curves.y.values.map( THREE.Math.degToRad );
 				curves.y.values = curves.y.values.map( THREE.Math.degToRad );
@@ -2195,8 +2166,10 @@
 
 
 					var preRotations = new THREE.Euler().setFromVector3( curveNode.preRotations, 'ZYX' );
 					var preRotations = new THREE.Euler().setFromVector3( curveNode.preRotations, 'ZYX' );
 					preRotations = new THREE.Quaternion().setFromEuler( preRotations );
 					preRotations = new THREE.Quaternion().setFromEuler( preRotations );
+
 					var frameRotation = new THREE.Euler();
 					var frameRotation = new THREE.Euler();
 					var frameRotationQuaternion = new THREE.Quaternion();
 					var frameRotationQuaternion = new THREE.Quaternion();
+
 					for ( var frame = 0; frame < curves.x.times.length; ++ frame ) {
 					for ( var frame = 0; frame < curves.x.times.length; ++ frame ) {
 
 
 						frameRotation.set( curves.x.values[ frame ], curves.y.values[ frame ], curves.z.values[ frame ], 'ZYX' );
 						frameRotation.set( curves.x.values[ frame ], curves.y.values[ frame ], curves.z.values[ frame ], 'ZYX' );
@@ -2303,7 +2276,7 @@
 
 
 	}
 	}
 
 
-	function parseAnimationCurveNode( FBXTree, animationCurveNode, connections, sceneGraph ) {
+	function parseAnimationCurveNode( FBXTree, animationCurveNode, connections, modelsArray ) {
 
 
 		var rawModels = FBXTree.Objects.subNodes.Model;
 		var rawModels = FBXTree.Objects.subNodes.Model;
 
 
@@ -2359,7 +2332,7 @@
 
 
 		for ( var containerIndicesIndex = containerIndices.length - 1; containerIndicesIndex >= 0; -- containerIndicesIndex ) {
 		for ( var containerIndicesIndex = containerIndices.length - 1; containerIndicesIndex >= 0; -- containerIndicesIndex ) {
 
 
-			var boneID = findIndex( sceneGraph.skeleton.bones, function ( bone ) {
+			var boneID = findIndex( modelsArray, function ( bone ) {
 
 
 				return bone.FBX_ID === containerIndices[ containerIndicesIndex ].ID;
 				return bone.FBX_ID === containerIndices[ containerIndicesIndex ].ID;
 
 
@@ -2506,63 +2479,71 @@
 
 
 	}
 	}
 
 
-	function addAnimations( FBXTree, connections, sceneGraph ) {
+	function addAnimations( FBXTree, connections, sceneGraph, modelMap ) {
 
 
-		var animations = parseAnimations( FBXTree, connections, sceneGraph );
+		// create a flattened array of all bones and models in the scene
+		var modelsArray = Array.from( modelMap.values() );
 
 
-		if ( sceneGraph.animations === undefined ) {
+		sceneGraph.animations = [];
 
 
-			sceneGraph.animations = [];
+		var animations = parseAnimations( FBXTree, connections, modelsArray );
 
 
-		}
+		if ( animations === undefined ) return;
 
 
-		var takes = animations.takes;
+		// Silly hack with the animation parsing. We're gonna pretend the scene graph has a skeleton
+		// to attach animations to, since FBX treats animations as animations for the entire scene,
+		// not just for individual objects.
+		sceneGraph.skeleton = {
 
 
-		for ( var key in takes ) {
+			bones: modelsArray,
 
 
-			var take = takes[ key ];
+		};
 
 
-			var animationData = {
-				name: take.name,
-				fps: animations.fps,
-				length: take.length,
-				hierarchy: []
-			};
+		for ( var key in animations.takes ) {
 
 
-			var bones = sceneGraph.skeleton.bones;
+			var take = animations.takes[ key ];
 
 
-			for ( var bonesIndex = 0, bonesLength = bones.length; bonesIndex < bonesLength; ++ bonesIndex ) {
+			var clip = addTake( take, animations.fps, modelsArray );
 
 
-				var bone = bones[ bonesIndex ];
+			sceneGraph.animations.push( clip );
 
 
-				var name = bone.name.replace( /.*:/, '' );
-				var parentIndex = findIndex( bones, function ( parentBone ) {
+		}
 
 
-					return bone.parent === parentBone;
+	}
 
 
-				} );
-				animationData.hierarchy.push( { parent: parentIndex, name: name, keys: [] } );
+	function addTake( take, fps, modelsArray ) {
 
 
-			}
+		var animationData = {
+			name: take.name,
+			fps: fps,
+			length: take.length,
+			hierarchy: []
+		};
+
+		animationData.hierarchy = modelsArray.map( ( model ) => {
 
 
-			for ( var frame = 0; frame <= take.frames; frame ++ ) {
+			return { name: model.name, keys: [] };
 
 
-				for ( var bonesIndex = 0, bonesLength = bones.length; bonesIndex < bonesLength; ++ bonesIndex ) {
+		} );
 
 
-					var bone = bones[ bonesIndex ];
-					var boneIndex = bonesIndex;
 
 
-					var animationNode = take.layers[ 0 ][ boneIndex ];
+		// fill empty keys with animation data
+		// this just loops over all frames and assumes that the animation has been baked at one keyframe per frame
+		for ( var frame = 0; frame <= take.frames; frame ++ ) {
 
 
-					for ( var hierarchyIndex = 0, hierarchyLength = animationData.hierarchy.length; hierarchyIndex < hierarchyLength; ++ hierarchyIndex ) {
+			for ( var i = 0, length = modelsArray.length; i < length; ++ i ) {
 
 
-						var node = animationData.hierarchy[ hierarchyIndex ];
+				var model = modelsArray[ i ];
 
 
-						if ( node.name === bone.name ) {
+				var animationNode = take.layers[ 0 ][ i ];
 
 
-							node.keys.push( generateKey( animations, animationNode, bone, frame ) );
+				for ( var hierarchyIndex = 0, hierarchyLength = animationData.hierarchy.length; hierarchyIndex < hierarchyLength; ++ hierarchyIndex ) {
 
 
-						}
+					var node = animationData.hierarchy[ hierarchyIndex ];
+
+					if ( node.name === model.name ) {
+
+						node.keys.push( generateKey( fps, animationNode, model, frame ) );
 
 
 					}
 					}
 
 
@@ -2570,20 +2551,20 @@
 
 
 			}
 			}
 
 
-			sceneGraph.animations.push( THREE.AnimationClip.parseAnimation( animationData, bones ) );
-
 		}
 		}
 
 
+		return THREE.AnimationClip.parseAnimation( animationData, modelsArray );
+
 	}
 	}
 
 
 	var euler = new THREE.Euler();
 	var euler = new THREE.Euler();
 	var quaternion = new THREE.Quaternion();
 	var quaternion = new THREE.Quaternion();
 
 
-	function generateKey( animations, animationNode, bone, frame ) {
+	function generateKey( fps, animationNode, bone, frame ) {
 
 
 		var key = {
 		var key = {
 
 
-			time: frame / animations.fps,
+			time: frame / fps,
 			pos: bone.position.toArray(),
 			pos: bone.position.toArray(),
 			rot: bone.quaternion.toArray(),
 			rot: bone.quaternion.toArray(),
 			scl: bone.scale.toArray(),
 			scl: bone.scale.toArray(),