Pārlūkot izejas kodu

Merge pull request #12701 from looeee/FBXLoader_split_prasescene

Fbx loader split monolithic parseScene method
Mr.doob 7 gadi atpakaļ
vecāks
revīzija
f6158adedd
1 mainītis faili ar 322 papildinājumiem un 274 dzēšanām
  1. 322 274
      examples/js/loaders/FBXLoader.js

+ 322 - 274
examples/js/loaders/FBXLoader.js

@@ -1434,22 +1434,66 @@
 
 	}
 
-
-	// parse nodes in FBXTree.Objects.subNodes.Model and generate a THREE.Group
+	// create the main THREE.Group() to be returned by the loader
 	function parseScene( FBXTree, connections, deformers, geometryMap, materialMap ) {
 
 		var sceneGraph = new THREE.Group();
 
-		var ModelNode = FBXTree.Objects.subNodes.Model;
+		var modelMap = parseModels( FBXTree, deformers, geometryMap, materialMap, connections );
+
+		var modelNodes = FBXTree.Objects.subNodes.Model;
+
+		modelMap.forEach( function ( model ) {
+
+			var modelNode = modelNodes[ model.FBX_ID ];
+
+			setModelTransforms( FBXTree, model, modelNode, connections, sceneGraph );
+
+			var conns = connections.get( model.FBX_ID );
+			for ( var parentIndex = 0; parentIndex < conns.parents.length; parentIndex ++ ) {
+
+				var modelArray = Array.from( modelMap.values() );
+				var pIndex = findIndex( modelArray, function ( mod ) {
+
+					return mod.FBX_ID === conns.parents[ parentIndex ].ID;
+
+				} );
+				if ( pIndex > - 1 ) {
+
+					modelArray[ pIndex ].add( model );
+					break;
+
+				}
+
+			}
+			if ( model.parent === null ) {
+
+				sceneGraph.add( model );
+
+			}
+
+		} );
 
-		var modelArray = [];
+		bindSkeleton( FBXTree, deformers, geometryMap, modelMap, connections, sceneGraph );
+
+		addAnimations( FBXTree, connections, sceneGraph );
+
+		createAmbientLight( FBXTree, sceneGraph );
+
+		return sceneGraph;
+
+	}
+
+	// parse nodes in FBXTree.Objects.subNodes.Model
+	function parseModels( FBXTree, deformers, geometryMap, materialMap, connections ) {
 
 		var modelMap = new Map();
+		var modelNodes = FBXTree.Objects.subNodes.Model;
 
-		for ( var nodeID in ModelNode ) {
+		for ( var nodeID in modelNodes ) {
 
 			var id = parseInt( nodeID );
-			var node = ModelNode[ nodeID ];
+			var node = modelNodes[ nodeID ];
 			var conns = connections.get( id );
 			var model = null;
 
@@ -1481,433 +1525,457 @@
 
 				switch ( node.attrType ) {
 
-					// create a THREE.PerspectiveCamera or THREE.OrthographicCamera
 					case 'Camera':
+						model = createCamera( FBXTree, conns );
+						break;
+					case 'Light':
+						model = createLight( FBXTree, conns );
+						break;
+					case 'Mesh':
+						model = createMesh( FBXTree, conns, geometryMap, materialMap );
+						break;
+					case 'NurbsCurve':
+						model = createCurve( conns, geometryMap );
+						break;
+					default:
+						model = new THREE.Group();
+						break;
 
-						var cameraAttribute;
-
-						for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
+				}
 
-							var childID = conns.children[ childrenIndex ].ID;
+			}
 
-							var attr = FBXTree.Objects.subNodes.NodeAttribute[ childID ];
+			model.name = THREE.PropertyBinding.sanitizeNodeName( node.attrName );
+			model.FBX_ID = id;
 
-							if ( attr !== undefined && attr.properties !== undefined ) {
+			modelMap.set( id, model );
 
-								cameraAttribute = attr.properties;
+		}
 
-							}
+		return modelMap;
 
-						}
+	}
 
-						if ( cameraAttribute === undefined ) {
+	// create a THREE.PerspectiveCamera or THREE.OrthographicCamera
+	function createCamera( FBXTree, conns ) {
 
-							model = new THREE.Object3D();
+		var model;
+		var cameraAttribute;
 
-						} else {
+		for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
 
-							var type = 0;
-							if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) {
+			var childID = conns.children[ childrenIndex ].ID;
 
-								type = 1;
+			var attr = FBXTree.Objects.subNodes.NodeAttribute[ childID ];
 
-							}
+			if ( attr !== undefined && attr.properties !== undefined ) {
 
-							var nearClippingPlane = 1;
-							if ( cameraAttribute.NearPlane !== undefined ) {
+				cameraAttribute = attr.properties;
 
-								nearClippingPlane = cameraAttribute.NearPlane.value / 1000;
+			}
 
-							}
+		}
 
-							var farClippingPlane = 1000;
-							if ( cameraAttribute.FarPlane !== undefined ) {
+		if ( cameraAttribute === undefined ) {
 
-								farClippingPlane = cameraAttribute.FarPlane.value / 1000;
+			model = new THREE.Object3D();
 
-							}
+		} else {
 
+			var type = 0;
+			if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) {
 
-							var width = window.innerWidth;
-							var height = window.innerHeight;
+				type = 1;
 
-							if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) {
+			}
 
-								width = cameraAttribute.AspectWidth.value;
-								height = cameraAttribute.AspectHeight.value;
+			var nearClippingPlane = 1;
+			if ( cameraAttribute.NearPlane !== undefined ) {
 
-							}
+				nearClippingPlane = cameraAttribute.NearPlane.value / 1000;
 
-							var aspect = width / height;
+			}
 
-							var fov = 45;
-							if ( cameraAttribute.FieldOfView !== undefined ) {
+			var farClippingPlane = 1000;
+			if ( cameraAttribute.FarPlane !== undefined ) {
 
-								fov = cameraAttribute.FieldOfView.value;
+				farClippingPlane = cameraAttribute.FarPlane.value / 1000;
 
-							}
+			}
 
-							switch ( type ) {
 
-								case 0: // Perspective
-									model = new THREE.PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane );
-									break;
+			var width = window.innerWidth;
+			var height = window.innerHeight;
 
-								case 1: // Orthographic
-									model = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane );
-									break;
+			if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) {
 
-								default:
-									console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' );
-									model = new THREE.Object3D();
-									break;
+				width = cameraAttribute.AspectWidth.value;
+				height = cameraAttribute.AspectHeight.value;
 
-							}
+			}
 
-						}
+			var aspect = width / height;
 
-						break;
+			var fov = 45;
+			if ( cameraAttribute.FieldOfView !== undefined ) {
 
+				fov = cameraAttribute.FieldOfView.value;
 
-					// Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight
-					case 'Light':
+			}
 
-						var lightAttribute;
+			switch ( type ) {
 
-						for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
+				case 0: // Perspective
+					model = new THREE.PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane );
+					break;
 
-							var childID = conns.children[ childrenIndex ].ID;
+				case 1: // Orthographic
+					model = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane );
+					break;
 
-							var attr = FBXTree.Objects.subNodes.NodeAttribute[ childID ];
+				default:
+					console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' );
+					model = new THREE.Object3D();
+					break;
 
-							if ( attr !== undefined && attr.properties !== undefined ) {
+			}
 
-								lightAttribute = attr.properties;
+		}
 
-							}
+		return model;
 
-						}
+	}
 
-						if ( lightAttribute === undefined ) {
+	// Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight
+	function createLight( FBXTree, conns ) {
 
-							model = new THREE.Object3D();
+		var model;
+		var lightAttribute;
 
-						} else {
+		for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
 
-							var type;
+			var childID = conns.children[ childrenIndex ].ID;
 
-							// LightType can be undefined for Point lights
-							if ( lightAttribute.LightType === undefined ) {
+			var attr = FBXTree.Objects.subNodes.NodeAttribute[ childID ];
 
-								type = 0;
+			if ( attr !== undefined && attr.properties !== undefined ) {
 
-							} else {
+				lightAttribute = attr.properties;
 
-								type = lightAttribute.LightType.value;
+			}
 
-							}
+		}
 
-							var color = 0xffffff;
+		if ( lightAttribute === undefined ) {
 
-							if ( lightAttribute.Color !== undefined ) {
+			model = new THREE.Object3D();
 
-								color = parseColor( lightAttribute.Color );
+		} else {
 
-							}
+			var type;
 
-							var intensity = ( lightAttribute.Intensity === undefined ) ? 1 : lightAttribute.Intensity.value / 100;
+			// LightType can be undefined for Point lights
+			if ( lightAttribute.LightType === undefined ) {
 
-							// light disabled
-							if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) {
+				type = 0;
 
-								intensity = 0;
+			} else {
 
-							}
+				type = lightAttribute.LightType.value;
 
-							var distance = 0;
-							if ( lightAttribute.FarAttenuationEnd !== undefined ) {
+			}
 
-								if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) {
+			var color = 0xffffff;
 
-									distance = 0;
+			if ( lightAttribute.Color !== undefined ) {
 
-								} else {
+				color = parseColor( lightAttribute.Color );
 
-									distance = lightAttribute.FarAttenuationEnd.value / 1000;
+			}
 
-								}
+			var intensity = ( lightAttribute.Intensity === undefined ) ? 1 : lightAttribute.Intensity.value / 100;
 
-							}
+			// light disabled
+			if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) {
 
-							// TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd?
-							var decay = 1;
+				intensity = 0;
 
-							switch ( type ) {
+			}
 
-								case 0: // Point
-									model = new THREE.PointLight( color, intensity, distance, decay );
-									break;
+			var distance = 0;
+			if ( lightAttribute.FarAttenuationEnd !== undefined ) {
 
-								case 1: // Directional
-									model = new THREE.DirectionalLight( color, intensity );
-									break;
+				if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) {
 
-								case 2: // Spot
-									var angle = Math.PI / 3;
+					distance = 0;
 
-									if ( lightAttribute.InnerAngle !== undefined ) {
+				} else {
 
-										angle = THREE.Math.degToRad( lightAttribute.InnerAngle.value );
+					distance = lightAttribute.FarAttenuationEnd.value / 1000;
 
-									}
+				}
 
-									var penumbra = 0;
-									if ( lightAttribute.OuterAngle !== undefined ) {
+			}
 
-										// TODO: this is not correct - FBX calculates outer and inner angle in degrees
-										// with OuterAngle > InnerAngle && OuterAngle <= Math.PI
-										// while three.js uses a penumbra between (0, 1) to attenuate the inner angle
-										penumbra = THREE.Math.degToRad( lightAttribute.OuterAngle.value );
-										penumbra = Math.max( penumbra, 1 );
+			// TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd?
+			var decay = 1;
 
-									}
+			switch ( type ) {
 
-									model = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay );
-									break;
+				case 0: // Point
+					model = new THREE.PointLight( color, intensity, distance, decay );
+					break;
 
-								default:
-									console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.' );
-									model = new THREE.PointLight( color, intensity );
-									break;
+				case 1: // Directional
+					model = new THREE.DirectionalLight( color, intensity );
+					break;
 
-							}
+				case 2: // Spot
+					var angle = Math.PI / 3;
 
-							if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) {
+					if ( lightAttribute.InnerAngle !== undefined ) {
 
-								model.castShadow = true;
+						angle = THREE.Math.degToRad( lightAttribute.InnerAngle.value );
 
-							}
+					}
 
-						}
+					var penumbra = 0;
+					if ( lightAttribute.OuterAngle !== undefined ) {
 
-						break;
+						// TODO: this is not correct - FBX calculates outer and inner angle in degrees
+						// with OuterAngle > InnerAngle && OuterAngle <= Math.PI
+						// while three.js uses a penumbra between (0, 1) to attenuate the inner angle
+						penumbra = THREE.Math.degToRad( lightAttribute.OuterAngle.value );
+						penumbra = Math.max( penumbra, 1 );
 
-					case 'Mesh':
+					}
 
-						var geometry = null;
-						var material = null;
-						var materials = [];
+					model = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay );
+					break;
 
-						for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
+				default:
+					console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.' );
+					model = new THREE.PointLight( color, intensity );
+					break;
 
-							var child = conns.children[ childrenIndex ];
+			}
 
-							if ( geometryMap.has( child.ID ) ) {
+			if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) {
 
-								geometry = geometryMap.get( child.ID );
+				model.castShadow = true;
 
-							}
+			}
 
-							if ( materialMap.has( child.ID ) ) {
+		}
 
-								materials.push( materialMap.get( child.ID ) );
+		return model;
 
-							}
+	}
 
-						}
-						if ( materials.length > 1 ) {
+	function createMesh( FBXTree, conns, geometryMap, materialMap ) {
 
-							material = materials;
+		var model;
+		var geometry = null;
+		var material = null;
+		var materials = [];
 
-						} else if ( materials.length > 0 ) {
+		for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
 
-							material = materials[ 0 ];
+			var child = conns.children[ childrenIndex ];
 
-						} else {
+			if ( geometryMap.has( child.ID ) ) {
 
-							material = new THREE.MeshPhongMaterial( { color: 0xcccccc } );
-							materials.push( material );
+				geometry = geometryMap.get( child.ID );
 
-						}
-						if ( 'color' in geometry.attributes ) {
+			}
 
-							for ( var materialIndex = 0, numMaterials = materials.length; materialIndex < numMaterials; ++ materialIndex ) {
+			if ( materialMap.has( child.ID ) ) {
 
-								materials[ materialIndex ].vertexColors = THREE.VertexColors;
+				materials.push( materialMap.get( child.ID ) );
 
-							}
+			}
 
-						}
-						if ( geometry.FBX_Deformer ) {
+		}
+		if ( materials.length > 1 ) {
 
-							for ( var materialsIndex = 0, materialsLength = materials.length; materialsIndex < materialsLength; ++ materialsIndex ) {
+			material = materials;
 
-								materials[ materialsIndex ].skinning = true;
+		} else if ( materials.length > 0 ) {
 
-							}
-							model = new THREE.SkinnedMesh( geometry, material );
+			material = materials[ 0 ];
 
-						} else {
+		} else {
 
-							model = new THREE.Mesh( geometry, material );
+			material = new THREE.MeshPhongMaterial( { color: 0xcccccc } );
+			materials.push( material );
 
-						}
-						break;
+		}
+		if ( 'color' in geometry.attributes ) {
 
-					case 'NurbsCurve':
-						var geometry = null;
+			for ( var materialIndex = 0, numMaterials = materials.length; materialIndex < numMaterials; ++ materialIndex ) {
 
-						for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
+				materials[ materialIndex ].vertexColors = THREE.VertexColors;
 
-							var child = conns.children[ childrenIndex ];
+			}
 
-							if ( geometryMap.has( child.ID ) ) {
+		}
+		if ( geometry.FBX_Deformer ) {
 
-								geometry = geometryMap.get( child.ID );
+			for ( var materialsIndex = 0, materialsLength = materials.length; materialsIndex < materialsLength; ++ materialsIndex ) {
 
-							}
+				materials[ materialsIndex ].skinning = true;
 
-						}
+			}
 
-						// FBX does not list materials for Nurbs lines, so we'll just put our own in here.
-						material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 5 } );
-						model = new THREE.Line( geometry, material );
-						break;
+			model = new THREE.SkinnedMesh( geometry, material );
 
-					default:
-						model = new THREE.Group();
-						break;
+		} else {
 
-				}
+			model = new THREE.Mesh( geometry, material );
 
-			}
+		}
 
-			model.name = THREE.PropertyBinding.sanitizeNodeName( node.attrName );
-			model.FBX_ID = id;
+		return model;
 
-			modelArray.push( model );
-			modelMap.set( id, model );
+	}
 
-		}
+	function createCurve( conns, geometryMap ) {
 
-		for ( var modelArrayIndex = 0, modelArrayLength = modelArray.length; modelArrayIndex < modelArrayLength; ++ modelArrayIndex ) {
+		var geometry = null;
 
-			var model = modelArray[ modelArrayIndex ];
+		for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
 
-			var node = ModelNode[ model.FBX_ID ];
+			var child = conns.children[ childrenIndex ];
 
-			if ( 'Lcl_Translation' in node.properties ) {
+			if ( geometryMap.has( child.ID ) ) {
 
-				model.position.fromArray( node.properties.Lcl_Translation.value );
+				geometry = geometryMap.get( child.ID );
 
 			}
 
-			if ( 'Lcl_Rotation' in node.properties ) {
+		}
 
-				var rotation = node.properties.Lcl_Rotation.value.map( THREE.Math.degToRad );
-				rotation.push( 'ZYX' );
-				model.rotation.fromArray( rotation );
+		// FBX does not list materials for Nurbs lines, so we'll just put our own in here.
+		var material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 1 } );
+		return new THREE.Line( geometry, material );
 
-			}
+	}
+
+	// Parse ambient color in FBXTree.GlobalSettings.properties - if it's not set to black (default), create an ambient light
+	function createAmbientLight( FBXTree, sceneGraph ) {
+
+		if ( 'GlobalSettings' in FBXTree && 'AmbientColor' in FBXTree.GlobalSettings.properties ) {
 
-			if ( 'Lcl_Scaling' in node.properties ) {
+			var ambientColor = FBXTree.GlobalSettings.properties.AmbientColor.value;
+			var r = ambientColor[ 0 ];
+			var g = ambientColor[ 1 ];
+			var b = ambientColor[ 2 ];
 
-				model.scale.fromArray( node.properties.Lcl_Scaling.value );
+			if ( r !== 0 || g !== 0 || b !== 0 ) {
+
+				var color = new THREE.Color( r, g, b );
+				sceneGraph.add( new THREE.AmbientLight( color, 1 ) );
 
 			}
 
-			if ( 'PreRotation' in node.properties ) {
+		}
 
-				var array = node.properties.PreRotation.value.map( THREE.Math.degToRad );
-				array[ 3 ] = 'ZYX';
+	}
 
-				var preRotations = new THREE.Euler().fromArray( array );
+	// parse the model node for transform details and apply them to the model
+	function setModelTransforms( FBXTree, model, modelNode, connections, sceneGraph ) {
 
-				preRotations = new THREE.Quaternion().setFromEuler( preRotations );
-				var currentRotation = new THREE.Quaternion().setFromEuler( model.rotation );
-				preRotations.multiply( currentRotation );
-				model.rotation.setFromQuaternion( preRotations, 'ZYX' );
+		if ( 'Lcl_Translation' in modelNode.properties ) {
 
-			}
+			model.position.fromArray( modelNode.properties.Lcl_Translation.value );
 
-			// allow transformed pivots - see https://github.com/mrdoob/three.js/issues/11895
-			if ( 'GeometricTranslation' in node.properties ) {
+		}
 
-				var array = node.properties.GeometricTranslation.value;
+		if ( 'Lcl_Rotation' in modelNode.properties ) {
 
-				model.traverse( function ( child ) {
+			var rotation = modelNode.properties.Lcl_Rotation.value.map( THREE.Math.degToRad );
+			rotation.push( 'ZYX' );
+			model.rotation.fromArray( rotation );
 
-					if ( child.geometry ) {
+		}
 
-						child.geometry.translate( array[ 0 ], array[ 1 ], array[ 2 ] );
+		if ( 'Lcl_Scaling' in modelNode.properties ) {
 
-					}
+			model.scale.fromArray( modelNode.properties.Lcl_Scaling.value );
 
-				} );
+		}
 
-			}
+		if ( 'PreRotation' in modelNode.properties ) {
 
-			if ( 'LookAtProperty' in node.properties ) {
+			var array = modelNode.properties.PreRotation.value.map( THREE.Math.degToRad );
+			array[ 3 ] = 'ZYX';
 
-				var conns = connections.get( model.FBX_ID );
+			var preRotations = new THREE.Euler().fromArray( array );
 
-				for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
+			preRotations = new THREE.Quaternion().setFromEuler( preRotations );
+			var currentRotation = new THREE.Quaternion().setFromEuler( model.rotation );
+			preRotations.multiply( currentRotation );
+			model.rotation.setFromQuaternion( preRotations, 'ZYX' );
 
-					var child = conns.children[ childrenIndex ];
+		}
 
-					if ( child.relationship === 'LookAtProperty' ) {
+		// allow transformed pivots - see https://github.com/mrdoob/three.js/issues/11895
+		if ( 'GeometricTranslation' in modelNode.properties ) {
 
-						var lookAtTarget = FBXTree.Objects.subNodes.Model[ child.ID ];
+			var array = modelNode.properties.GeometricTranslation.value;
 
-						if ( 'Lcl_Translation' in lookAtTarget.properties ) {
+			model.traverse( function ( child ) {
 
-							var pos = lookAtTarget.properties.Lcl_Translation.value;
+				if ( child.geometry ) {
 
-							// DirectionalLight, SpotLight
-							if ( model.target !== undefined ) {
+					child.geometry.translate( array[ 0 ], array[ 1 ], array[ 2 ] );
 
-								model.target.position.set( pos[ 0 ], pos[ 1 ], pos[ 2 ] );
-								sceneGraph.add( model.target );
+				}
 
+			} );
 
-							} else { // Cameras and other Object3Ds
+		}
 
-								model.lookAt( new THREE.Vector3( pos[ 0 ], pos[ 1 ], pos[ 2 ] ) );
+		if ( 'LookAtProperty' in modelNode.properties ) {
 
-							}
+			var conns = connections.get( model.FBX_ID );
 
-						}
+			for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
 
-					}
+				var child = conns.children[ childrenIndex ];
 
-				}
+				if ( child.relationship === 'LookAtProperty' ) {
 
-			}
+					var lookAtTarget = FBXTree.Objects.subNodes.Model[ child.ID ];
 
-			var conns = connections.get( model.FBX_ID );
-			for ( var parentIndex = 0; parentIndex < conns.parents.length; parentIndex ++ ) {
+					if ( 'Lcl_Translation' in lookAtTarget.properties ) {
 
-				var pIndex = findIndex( modelArray, function ( mod ) {
+						var pos = lookAtTarget.properties.Lcl_Translation.value;
 
-					return mod.FBX_ID === conns.parents[ parentIndex ].ID;
+						// DirectionalLight, SpotLight
+						if ( model.target !== undefined ) {
 
-				} );
-				if ( pIndex > - 1 ) {
+							model.target.position.set( pos[ 0 ], pos[ 1 ], pos[ 2 ] );
+							sceneGraph.add( model.target );
 
-					modelArray[ pIndex ].add( model );
-					break;
 
-				}
+						} else { // Cameras and other Object3Ds
 
-			}
-			if ( model.parent === null ) {
+							model.lookAt( new THREE.Vector3( pos[ 0 ], pos[ 1 ], pos[ 2 ] ) );
 
-				sceneGraph.add( model );
+						}
+
+					}
+
+				}
 
 			}
 
 		}
 
+	}
+
+	function bindSkeleton( FBXTree, deformers, geometryMap, modelMap, connections, sceneGraph ) {
 
 		// Now with the bones created, we can update the skeletons and bind them to the skinned meshes.
 		sceneGraph.updateMatrixWorld( true );
@@ -2005,32 +2073,10 @@
 		// to attach animations to, since FBX treats animations as animations for the entire scene,
 		// not just for individual objects.
 		sceneGraph.skeleton = {
-			bones: modelArray
-		};
-
-		var animations = parseAnimations( FBXTree, connections, sceneGraph );
-
-		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 ) {
-
-			var ambientColor = FBXTree.GlobalSettings.properties.AmbientColor.value;
-			var r = ambientColor[ 0 ];
-			var g = ambientColor[ 1 ];
-			var b = ambientColor[ 2 ];
-
-			if ( r !== 0 || g !== 0 || b !== 0 ) {
-
-				var color = new THREE.Color( r, g, b );
-				sceneGraph.add( new THREE.AmbientLight( color, 1 ) );
 
-			}
-
-		}
+			bones: Array.from( modelMap.values() ),
 
-		return sceneGraph;
+		};
 
 	}
 
@@ -2478,11 +2524,13 @@
 
 	}
 
-	function addAnimations( group, animations ) {
+	function addAnimations( FBXTree, connections, sceneGraph ) {
+
+		var animations = parseAnimations( FBXTree, connections, sceneGraph );
 
-		if ( group.animations === undefined ) {
+		if ( sceneGraph.animations === undefined ) {
 
-			group.animations = [];
+			sceneGraph.animations = [];
 
 		}
 
@@ -2499,7 +2547,7 @@
 				hierarchy: []
 			};
 
-			var bones = group.skeleton.bones;
+			var bones = sceneGraph.skeleton.bones;
 
 			for ( var bonesIndex = 0, bonesLength = bones.length; bonesIndex < bonesLength; ++ bonesIndex ) {
 
@@ -2540,7 +2588,7 @@
 
 			}
 
-			group.animations.push( THREE.AnimationClip.parseAnimation( animationData, bones ) );
+			sceneGraph.animations.push( THREE.AnimationClip.parseAnimation( animationData, bones ) );
 
 		}