function parseAnimations( FBXTree, connections, sceneGraph ) {

				var rawNodes = FBXTree.Objects.subNodes.AnimationCurveNode;
				var rawCurves = FBXTree.Objects.subNodes.AnimationCurve;
				var rawLayers = FBXTree.Objects.subNodes.AnimationLayer;
				var rawStacks = FBXTree.Objects.subNodes.AnimationStack;

				/**
				 * @type {{
				     curves: Map<number, {
						 T: {
							id: number;
							attr: string;
							internalID: number;
							attrX: boolean;
							attrY: boolean;
							attrZ: boolean;
							containerBoneID: number;
							containerID: number;
							curves: {
								x: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								y: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								z: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
							};
						},
						 R: {
							id: number;
							attr: string;
							internalID: number;
							attrX: boolean;
							attrY: boolean;
							attrZ: boolean;
							containerBoneID: number;
							containerID: number;
							curves: {
								x: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								y: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								z: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
							};
						},
						 S: {
							id: number;
							attr: string;
							internalID: number;
							attrX: boolean;
							attrY: boolean;
							attrZ: boolean;
							containerBoneID: number;
							containerID: number;
							curves: {
								x: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								y: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								z: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
							};
						}
					 }>,
					 layers: Map<number, {
					 	T: {
							id: number;
							attr: string;
							internalID: number;
							attrX: boolean;
							attrY: boolean;
							attrZ: boolean;
							containerBoneID: number;
							containerID: number;
							curves: {
								x: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								y: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								z: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
							},
						},
						R: {
							id: number;
							attr: string;
							internalID: number;
							attrX: boolean;
							attrY: boolean;
							attrZ: boolean;
							containerBoneID: number;
							containerID: number;
							curves: {
								x: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								y: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								z: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
							},
						},
						S: {
							id: number;
							attr: string;
							internalID: number;
							attrX: boolean;
							attrY: boolean;
							attrZ: boolean;
							containerBoneID: number;
							containerID: number;
							curves: {
								x: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								y: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								z: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
							},
						}
						}[]>,
					 stacks: Map<number, {
						 name: string,
						 layers: {
							T: {
								id: number;
								attr: string;
								internalID: number;
								attrX: boolean;
								attrY: boolean;
								attrZ: boolean;
								containerBoneID: number;
								containerID: number;
								curves: {
									x: {
										version: any;
										id: number;
										internalID: number;
										times: number[];
										values: number[];
										attrFlag: number[];
										attrData: number[];
									};
									y: {
										version: any;
										id: number;
										internalID: number;
										times: number[];
										values: number[];
										attrFlag: number[];
										attrData: number[];
									};
									z: {
										version: any;
										id: number;
										internalID: number;
										times: number[];
										values: number[];
										attrFlag: number[];
										attrData: number[];
									};
								};
							};
							R: {
								id: number;
								attr: string;
								internalID: number;
								attrX: boolean;
								attrY: boolean;
								attrZ: boolean;
								containerBoneID: number;
								containerID: number;
								curves: {
									x: {
										version: any;
										id: number;
										internalID: number;
										times: number[];
										values: number[];
										attrFlag: number[];
										attrData: number[];
									};
									y: {
										version: any;
										id: number;
										internalID: number;
										times: number[];
										values: number[];
										attrFlag: number[];
										attrData: number[];
									};
									z: {
										version: any;
										id: number;
										internalID: number;
										times: number[];
										values: number[];
										attrFlag: number[];
										attrData: number[];
									};
								};
							};
							S: {
								id: number;
								attr: string;
								internalID: number;
								attrX: boolean;
								attrY: boolean;
								attrZ: boolean;
								containerBoneID: number;
								containerID: number;
								curves: {
									x: {
										version: any;
										id: number;
										internalID: number;
										times: number[];
										values: number[];
										attrFlag: number[];
										attrData: number[];
									};
									y: {
										version: any;
										id: number;
										internalID: number;
										times: number[];
										values: number[];
										attrFlag: number[];
										attrData: number[];
									};
									z: {
										version: any;
										id: number;
										internalID: number;
										times: number[];
										values: number[];
										attrFlag: number[];
										attrData: number[];
									};
								};
							};
						}[][],
					 length: number,
					 frames: number }>,
					 length: number,
					 fps: number,
					 frames: number
				 }}
				 */
				var returnObject = {
					curves: new Map(),
					layers: new Map(),
					stacks: new Map(),
					length: 0,
					fps: 30,
					frames: 0
				};

				/**
				 * @type {Array.<{
						id: number;
						attr: string;
						internalID: number;
						attrX: boolean;
						attrY: boolean;
						attrZ: boolean;
						containerBoneID: number;
						containerID: number;
					}>}
				 */
				var animationCurveNodes = [];
				for ( var nodeID in rawNodes ) {

					if ( nodeID.match( /\d+/ ) ) {

						var animationNode = parseAnimationNode( FBXTree, rawNodes[ nodeID ], connections, sceneGraph );
						animationCurveNodes.push( animationNode );

					}

				}

				/**
				 * @type {Map.<number, {
						id: number,
						attr: string,
						internalID: number,
						attrX: boolean,
						attrY: boolean,
						attrZ: boolean,
						containerBoneID: number,
						containerID: number,
						curves: {
							x: {
								version: any,
								id: number,
								internalID: number,
								times: number[],
								values: number[],
								attrFlag: number[],
								attrData: number[],
							},
							y: {
								version: any,
								id: number,
								internalID: number,
								times: number[],
								values: number[],
								attrFlag: number[],
								attrData: number[],
							},
							z: {
								version: any,
								id: number,
								internalID: number,
								times: number[],
								values: number[],
								attrFlag: number[],
								attrData: number[],
							}
						}
					}>}
				 */
				var tmpMap = new Map();
				for ( var animationCurveNodeIndex = 0; animationCurveNodeIndex < animationCurveNodes.length; ++ animationCurveNodeIndex ) {

					if ( animationCurveNodes[ animationCurveNodeIndex ] === null ) {

						continue;

					}
					tmpMap.set( animationCurveNodes[ animationCurveNodeIndex ].id, animationCurveNodes[ animationCurveNodeIndex ] );

				}


				/**
				 * @type {{
						version: any,
						id: number,
						internalID: number,
						times: number[],
						values: number[],
						attrFlag: number[],
						attrData: number[],
					}[]}
				 */
				var animationCurves = [];
				for ( nodeID in rawCurves ) {

					if ( nodeID.match( /\d+/ ) ) {

						var animationCurve = parseAnimationCurve( rawCurves[ nodeID ] );
						animationCurves.push( animationCurve );

						var firstParentConn = connections.get( animationCurve.id ).parents[ 0 ];
						var firstParentID = firstParentConn.ID;
						var firstParentRelationship = firstParentConn.relationship;
						var axis = '';

						if ( firstParentRelationship.match( /X/ ) ) {

							axis = 'x';

						} else if ( firstParentRelationship.match( /Y/ ) ) {

							axis = 'y';

						} else if ( firstParentRelationship.match( /Z/ ) ) {

							axis = 'z';

						} else {

							continue;

						}

						tmpMap.get( firstParentID ).curves[ axis ] = animationCurve;

					}

				}

				tmpMap.forEach( function ( curveNode ) {

					var id = curveNode.containerBoneID;
					if ( ! returnObject.curves.has( id ) ) {

						returnObject.curves.set( id, { T: null, R: null, S: null } );

					}
					returnObject.curves.get( id )[ curveNode.attr ] = curveNode;

				} );

				for ( var nodeID in rawLayers ) {

					/**
					 * @type {{
					 	T: {
							id: number;
							attr: string;
							internalID: number;
							attrX: boolean;
							attrY: boolean;
							attrZ: boolean;
							containerBoneID: number;
							containerID: number;
							curves: {
								x: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								y: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								z: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
							},
						},
						R: {
							id: number;
							attr: string;
							internalID: number;
							attrX: boolean;
							attrY: boolean;
							attrZ: boolean;
							containerBoneID: number;
							containerID: number;
							curves: {
								x: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								y: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								z: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
							},
						},
						S: {
							id: number;
							attr: string;
							internalID: number;
							attrX: boolean;
							attrY: boolean;
							attrZ: boolean;
							containerBoneID: number;
							containerID: number;
							curves: {
								x: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								y: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
								z: {
									version: any;
									id: number;
									internalID: number;
									times: number[];
									values: number[];
									attrFlag: number[];
									attrData: number[];
								};
							},
						}
						}[]}
					 */
					var layer = [];
					var children = connections.get( parseInt( nodeID ) ).children;
					for ( var childIndex = 0; childIndex < children.length; childIndex ++ ) {

						// Skip lockInfluenceWeights
						if ( tmpMap.has( children[ childIndex ].ID ) ) {

							var curveNode = tmpMap.get( children[ childIndex ].ID );
							var boneID = curveNode.containerBoneID;
							if ( layer[ boneID ] === undefined ) {

								layer[ boneID ] = {
									T: null,
									R: null,
									S: null
								};

							}

							layer[ boneID ][ curveNode.attr ] = curveNode;

						}

					}

					returnObject.layers.set( parseInt( nodeID ), layer );

				}

				for ( var nodeID in rawStacks ) {

					var layers = [];
					var children = connections.get( parseInt( nodeID ) ).children;
					var maxTimeStamp = 0;
					var minTimeStamp = Number.MAX_VALUE;
					for ( var childIndex = 0; childIndex < children.length; ++ childIndex ) {

						if ( returnObject.layers.has( children[ childIndex ].ID ) ) {

							var currentLayer = returnObject.layers.get( children[ childIndex ].ID );
							layers.push( currentLayer );

							currentLayer.forEach( function ( layer ) {

								if ( layer ) {

									getCurveNodeMaxMinTimeStamps( layer );

								}

								/**
								 * Sets the maxTimeStamp and minTimeStamp variables if it has timeStamps that are either larger or smaller
								 * than the max or min respectively.
								 * @param {{
											T: {
													id: number,
													attr: string,
													internalID: number,
													attrX: boolean,
													attrY: boolean,
													attrZ: boolean,
													containerBoneID: number,
													containerID: number,
													curves: {
															x: {
																	version: any,
																	id: number,
																	internalID: number,
																	times: number[],
																	values: number[],
																	attrFlag: number[],
																	attrData: number[],
															},
															y: {
																	version: any,
																	id: number,
																	internalID: number,
																	times: number[],
																	values: number[],
																	attrFlag: number[],
																	attrData: number[],
															},
															z: {
																	version: any,
																	id: number,
																	internalID: number,
																	times: number[],
																	values: number[],
																	attrFlag: number[],
																	attrData: number[],
															},
													},
											},
											R: {
													id: number,
													attr: string,
													internalID: number,
													attrX: boolean,
													attrY: boolean,
													attrZ: boolean,
													containerBoneID: number,
													containerID: number,
													curves: {
															x: {
																	version: any,
																	id: number,
																	internalID: number,
																	times: number[],
																	values: number[],
																	attrFlag: number[],
																	attrData: number[],
															},
															y: {
																	version: any,
																	id: number,
																	internalID: number,
																	times: number[],
																	values: number[],
																	attrFlag: number[],
																	attrData: number[],
															},
															z: {
																	version: any,
																	id: number,
																	internalID: number,
																	times: number[],
																	values: number[],
																	attrFlag: number[],
																	attrData: number[],
															},
													},
											},
											S: {
													id: number,
													attr: string,
													internalID: number,
													attrX: boolean,
													attrY: boolean,
													attrZ: boolean,
													containerBoneID: number,
													containerID: number,
													curves: {
															x: {
																	version: any,
																	id: number,
																	internalID: number,
																	times: number[],
																	values: number[],
																	attrFlag: number[],
																	attrData: number[],
															},
															y: {
																	version: any,
																	id: number,
																	internalID: number,
																	times: number[],
																	values: number[],
																	attrFlag: number[],
																	attrData: number[],
															},
															z: {
																	version: any,
																	id: number,
																	internalID: number,
																	times: number[],
																	values: number[],
																	attrFlag: number[],
																	attrData: number[],
															},
													},
											},
									}} layer
								 */
								function getCurveNodeMaxMinTimeStamps( layer ) {

									/**
									 * Sets the maxTimeStamp and minTimeStamp if one of the curve's time stamps
									 * exceeds the maximum or minimum.
									 * @param {{
												x: {
														version: any,
														id: number,
														internalID: number,
														times: number[],
														values: number[],
														attrFlag: number[],
														attrData: number[],
												},
												y: {
														version: any,
														id: number,
														internalID: number,
														times: number[],
														values: number[],
														attrFlag: number[],
														attrData: number[],
												},
												z: {
														version: any,
														id: number,
														internalID: number,
														times: number[],
														values: number[],
														attrFlag: number[],
														attrData: number[],
												}
										}} curve
									 */
									function getCurveMaxMinTimeStamp( curve ) {

										/**
										 * Sets the maxTimeStamp and minTimeStamp if one of its timestamps exceeds the maximum or minimum.
										 * @param {{times: number[]}} axis
										 */
										function getCurveAxisMaxMinTimeStamps( axis ) {

											maxTimeStamp = axis.times[ axis.times.length - 1 ] > maxTimeStamp ? axis.times[ axis.times.length - 1 ] : maxTimeStamp;
											minTimeStamp = axis.times[ 0 ] < minTimeStamp ? axis.times[ 0 ] : minTimeStamp;

										}

										if ( curve.x ) {

											getCurveAxisMaxMinTimeStamps( curve.x );

										}
										if ( curve.y ) {

											getCurveAxisMaxMinTimeStamps( curve.y );

										}
										if ( curve.z ) {

											getCurveAxisMaxMinTimeStamps( curve.z );

										}

									}

									if ( layer.R ) {

										getCurveMaxMinTimeStamp( layer.R.curves );

									}
									if ( layer.S ) {

										getCurveMaxMinTimeStamp( layer.S.curves );

									}
									if ( layer.T ) {

										getCurveMaxMinTimeStamp( layer.T.curves );

									}

								}

							} );

						}

					}

					// Do we have an animation clip with actual length?
					if ( maxTimeStamp > minTimeStamp ) {

						returnObject.stacks.set( parseInt( nodeID ), {
							name: rawStacks[ nodeID ].attrName,
							layers: layers,
							length: maxTimeStamp - minTimeStamp,
							frames: ( maxTimeStamp - minTimeStamp ) * 30
						} );

					}

				}

				return returnObject;

				/**
				 * @param {Object} FBXTree
				 * @param {{id: number, attrName: string, properties: Object<string, any>}} animationCurveNode
				 * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
				 * @param {{skeleton: {bones: {FBX_ID: number}[]}}} sceneGraph
				 */
				function parseAnimationNode( FBXTree, animationCurveNode, connections, sceneGraph ) {

					var returnObject = {
						/**
						 * @type {number}
						 */
						id: animationCurveNode.id,

						/**
						 * @type {string}
						 */
						attr: animationCurveNode.attrName,

						/**
						 * @type {number}
						 */
						internalID: animationCurveNode.id,

						/**
						 * @type {boolean}
						 */
						attrX: false,

						/**
						 * @type {boolean}
						 */
						attrY: false,

						/**
						 * @type {boolean}
						 */
						attrZ: false,

						/**
						 * @type {number}
						 */
						containerBoneID: - 1,

						/**
						 * @type {number}
						 */
						containerID: - 1,

						curves: {
							x: null,
							y: null,
							z: null
						}
					};

					if ( returnObject.attr.match( /S|R|T/ ) ) {

						for ( var attributeKey in animationCurveNode.properties ) {

							if ( attributeKey.match( /X/ ) ) {

								returnObject.attrX = true;

							}
							if ( attributeKey.match( /Y/ ) ) {

								returnObject.attrY = true;

							}
							if ( attributeKey.match( /Z/ ) ) {

								returnObject.attrZ = true;

							}

						}

					} else {

						return null;

					}

					var conns = connections.get( returnObject.id );
					var containerIndices = conns.parents;

					for ( var containerIndicesIndex = containerIndices.length - 1; containerIndicesIndex >= 0; -- containerIndicesIndex ) {

						var boneID = sceneGraph.skeleton.bones.findIndex( function ( bone ) {

							return bone.FBX_ID === containerIndices[ containerIndicesIndex ].ID;

						} );
						if ( boneID > - 1 ) {

							returnObject.containerBoneID = boneID;
							returnObject.containerID = containerIndices[ containerIndicesIndex ].ID;
							break;

						}

					}

					return returnObject;

				}

				/**
				 * @param {{id: number, subNodes: {KeyTime: {properties: {a: string}}, KeyValueFloat: {properties: {a: string}}, KeyAttrFlags: {properties: {a: string}}, KeyAttrDataFloat: {properties: {a: string}}}}} animationCurve
				 */
				function parseAnimationCurve( animationCurve ) {

					return {
						version: null,
						id: animationCurve.id,
						internalID: animationCurve.id,
						times: parseFloatArray( animationCurve.subNodes.KeyTime.properties.a ).map( function ( time ) {

							return ConvertFBXTimeToSeconds( time );

						} ),
						values: parseFloatArray( animationCurve.subNodes.KeyValueFloat.properties.a ),

						attrFlag: parseIntArray( animationCurve.subNodes.KeyAttrFlags.properties.a ),
						attrData: parseFloatArray( animationCurve.subNodes.KeyAttrDataFloat.properties.a )
					};

				}

			}
Parses animation information from FBXTree and generates an AnimationInfoObject.
@param {{Objects: {subNodes: {AnimationCurveNode: any, AnimationCurve: any, AnimationLayer: any, AnimationStack: any}}}} FBXTree
@param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections