function parseScene( FBXTree, connections, deformerMap, geometryMap, materialMap ) {

				var sceneGraph = new THREE.Group();

				var ModelNode = FBXTree.Objects.subNodes.Model;

				/**
				 * @type {Array.<THREE.Object3D>}
				 */
				var modelArray = [];

				/**
				 * @type {Map.<number, THREE.Object3D>}
				 */
				var modelMap = new Map();

				for ( var nodeID in ModelNode ) {

					var id = parseInt( nodeID );
					var node = ModelNode[ nodeID ];
					var conns = connections.get( id );
					var model = null;
					for ( var i = 0; i < conns.parents.length; ++ i ) {

						deformerMap.forEach( function ( deformer ) {

							if ( deformer.map.has( conns.parents[ i ].ID ) ) {

								model = new THREE.Bone();
								var index = deformer.array.findIndex( function ( subDeformer ) {

									return subDeformer.FBX_ID === conns.parents[ i ].ID;

								} );
								deformer.bones[ index ] = model;

							}

						} );

					}
					if ( ! model ) {

						switch ( node.attrType ) {

							case "Mesh":
								/**
								 * @type {?THREE.BufferGeometry}
								 */
								var geometry = null;

								/**
								 * @type {THREE.MultiMaterial|THREE.Material}
								 */
								var material = null;

								/**
								 * @type {Array.<THREE.Material>}
								 */
								var materials = [];

								conns.children.forEach( function ( child ) {

									if ( geometryMap.has( child.ID ) ) {

										geometry = geometryMap.get( child.ID );

									}

									if ( materialMap.has( child.ID ) ) {

										materials.push( materialMap.get( child.ID ) );

									}

								} );
								if ( materials.length > 1 ) {

									material = new THREE.MultiMaterial( materials );

								} else if ( materials.length > 0 ) {

									material = materials[ 0 ];

								} else {

									material = new THREE.MeshBasicMaterial( { color: 0x3300ff } );

								}
								if ( geometry.FBX_Deformer ) {

									materials.forEach( function ( material ) {

										material.skinning = true;

									} );
									material.skinning = true;
									model = new THREE.SkinnedMesh( geometry, material );

								} else {

									model = new THREE.Mesh( geometry, material );

								}
								break;

							case "NurbsCurve":
								var geometry = null;

								conns.children.forEach( function ( child ) {

									if ( geometryMap.has( child.ID ) ) {

										geometry = geometryMap.get( child.ID );

									}

								} );

								// 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;

							default:
								model = new THREE.Object3D();
								break;

						}

					}

					model.name = node.attrName.replace( /:/, '' ).replace( /_/, '' ).replace( /-/, '' );
					model.FBX_ID = id;

					modelArray.push( model );
					modelMap.set( id, model );

				}

				modelArray.forEach( function ( model ) {

					var node = ModelNode[ model.FBX_ID ];

					if ( 'Lcl_Translation' in node.properties ) {

						model.position.fromArray( parseFloatArray( node.properties.Lcl_Translation.value ) );

					}

					if ( 'Lcl_Rotation' in node.properties ) {

						var rotation = parseFloatArray( node.properties.Lcl_Rotation.value ).map( function ( value ) {

							return value * Math.PI / 180;

						} );
						rotation.push( 'ZYX' );
						model.rotation.fromArray( rotation );

					}

					if ( 'Lcl_Scaling' in node.properties ) {

						model.scale.fromArray( parseFloatArray( node.properties.Lcl_Scaling.value ) );

					}

					var conns = connections.get( model.FBX_ID );
					for ( var parentIndex = 0; parentIndex < conns.parents.length; parentIndex ++ ) {

						var pIndex = modelArray.findIndex( 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 );

					}

				} );


				// Now with the bones created, we can update the skeletons and bind them to the skinned meshes.
				sceneGraph.updateMatrixWorld( true );

				// Put skeleton into bind pose.
				var BindPoseNode = FBXTree.Objects.subNodes.Pose;
				for ( var nodeID in BindPoseNode ) {

					if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) {

						BindPoseNode = BindPoseNode[ nodeID ];
						break;

					}

				}
				if ( BindPoseNode ) {

					var PoseNode = BindPoseNode.subNodes.PoseNode;
					var worldMatrices = new Map();

					PoseNode.forEach( function ( node ) {

						var rawMatWrd = parseMatrixArray( node.subNodes.Matrix.properties.a );

						worldMatrices.set( parseInt( node.id ), rawMatWrd );

					} );

				}

				deformerMap.forEach( function ( deformer, FBX_ID ) {

					deformer.array.forEach( function ( subDeformer, subDeformerIndex ) {

						/**
						 * @type {THREE.Bone}
						 */
						var bone = deformer.bones[ subDeformerIndex ];
						if ( ! worldMatrices.has( bone.FBX_ID ) ) {

							return;

						}
						var mat = worldMatrices.get( bone.FBX_ID );
						bone.matrixWorld.copy( mat );

					} );

					// Now that skeleton is in bind pose, bind to model.
					deformer.skeleton = new THREE.Skeleton( deformer.bones );
					var conns = connections.get( FBX_ID );
					conns.parents.forEach( function ( parent ) {

						if ( geometryMap.has( parent.ID ) ) {

							var geoID = parent.ID;
							var geoConns = connections.get( geoID );
							for ( var i = 0; i < geoConns.parents.length; ++ i ) {

								if ( modelMap.has( geoConns.parents[ i ].ID ) ) {

									var model = modelMap.get( geoConns.parents[ i ].ID );
									//ASSERT model typeof SkinnedMesh
									model.bind( deformer.skeleton, model.matrixWorld );
									break;

								}

							}

						}

					} );

				} );

				// Skeleton is now bound, we are now free to set up the
				// scene graph.
				modelArray.forEach( function ( model ) {

					var node = ModelNode[ model.FBX_ID ];

					if ( 'Lcl_Translation' in node.properties ) {

						model.position.fromArray( parseFloatArray( node.properties.Lcl_Translation.value ) );

					}

					if ( 'Lcl_Rotation' in node.properties ) {

						var rotation = parseFloatArray( node.properties.Lcl_Rotation.value ).map( function ( value ) {

							return value * Math.PI / 180;

						} );
						rotation.push( 'ZYX' );
						model.rotation.fromArray( rotation );

					}

					if ( 'Lcl_Scaling' in node.properties ) {

						model.scale.fromArray( parseFloatArray( node.properties.Lcl_Scaling.value ) );

					}

				} );

				// Silly hack with the animation parsing.  We're gonna pretend the scene graph has a skeleton
				// to attach animations to, since FBXs treat 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 );

				return sceneGraph;

			}
Finally generates Scene graph and Scene graph Objects.
@param {{Objects: {subNodes: {Model: Object.<number, FBXModelNode>}}}} FBXTree
@param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
@param {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}>} deformerMap
@param {Map<number, THREE.BufferGeometry>} geometryMap
@param {Map<number, THREE.Material>} materialMap
@returns {THREE.Group}