Browse Source

refactor genGeo and de-dup code

Lewy Blue 7 years ago
parent
commit
5ed9d6cf80
1 changed files with 238 additions and 249 deletions
  1. 238 249
      examples/js/loaders/FBXLoader.js

+ 238 - 249
examples/js/loaders/FBXLoader.js

@@ -448,8 +448,6 @@
 
 		var parameters = {};
 
-		parameters.morphTargets = true;
-
 		if ( properties.BumpFactor ) {
 
 			parameters.bumpScale = properties.BumpFactor.value;
@@ -858,61 +856,162 @@
 		var geo = new THREE.BufferGeometry();
 		if ( geoNode.attrName ) geo.name = geoNode.attrName;
 
-		var vertexPositions = ( geoNode.Vertices !== undefined ) ? geoNode.Vertices.a : [];
-		var vertexIndices = ( geoNode.PolygonVertexIndex !== undefined ) ? geoNode.PolygonVertexIndex.a : [];
+		var geoInfo = getGeoInfo( geoNode, skeleton );
+
+		var buffers = genBuffers( geoInfo );
+
+		var positionAttribute = new THREE.Float32BufferAttribute( buffers.vertex, 3 );
+
+		preTransform.applyToBufferAttribute( positionAttribute );
+
+		geo.addAttribute( 'position', positionAttribute );
+
+		if ( buffers.colors.length > 0 ) {
+
+			geo.addAttribute( 'color', new THREE.Float32BufferAttribute( buffers.colors, 3 ) );
+
+		}
+
+		if ( skeleton ) {
+
+			geo.addAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( buffers.weightsIndices, 4 ) );
+
+			geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( buffers.vertexWeights, 4 ) );
+
+			// used later to bind the skeleton to the model
+			geo.FBX_Deformer = skeleton;
+
+		}
+
+		if ( buffers.normal.length > 0 ) {
+
+			var normalAttribute = new THREE.Float32BufferAttribute( buffers.normal, 3 );
+
+			var normalMatrix = new THREE.Matrix3().getNormalMatrix( preTransform );
+			normalMatrix.applyToBufferAttribute( normalAttribute );
+
+			geo.addAttribute( 'normal', normalAttribute );
+
+		}
+
+		buffers.uvs.forEach( function ( uvBuffer, i ) {
+
+			// subsequent uv buffers are called 'uv1', 'uv2', ...
+			var name = 'uv' + ( i + 1 ).toString();
+
+			// the first uv buffer is just called 'uv'
+			if ( i === 0 ) {
+
+				name = 'uv';
+
+			}
+
+			geo.addAttribute( name, new THREE.Float32BufferAttribute( buffers.uvs[ i ], 2 ) );
+
+		} );
+
+		if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) {
+
+			// Convert the material indices of each vertex into rendering groups on the geometry.
+			var prevMaterialIndex = buffers.materialIndex[ 0 ];
+			var startIndex = 0;
+
+			buffers.materialIndex.forEach( function ( currentIndex, i ) {
+
+				if ( currentIndex !== prevMaterialIndex ) {
+
+					geo.addGroup( startIndex, i - startIndex, prevMaterialIndex );
+
+					prevMaterialIndex = currentIndex;
+					startIndex = i;
+
+				}
+
+			} );
+
+			// the loop above doesn't add the last group, do that here.
+			if ( geo.groups.length > 0 ) {
 
-		// create arrays to hold the final data used to build the buffergeometry
-		var vertexBuffer = [];
-		var normalBuffer = [];
-		var colorsBuffer = [];
-		var uvsBuffer = [];
-		var materialIndexBuffer = [];
-		var vertexWeightsBuffer = [];
-		var weightsIndicesBuffer = [];
+				var lastGroup = geo.groups[ geo.groups.length - 1 ];
+				var lastIndex = lastGroup.start + lastGroup.count;
+
+				if ( lastIndex !== buffers.materialIndex.length ) {
+
+					geo.addGroup( lastIndex, buffers.materialIndex.length - lastIndex, prevMaterialIndex );
+
+				}
+
+			}
+
+			// case where there are multiple materials but the whole geometry is only
+			// using one of them
+			if ( geo.groups.length === 0 ) {
+
+				geo.addGroup( 0, buffers.materialIndex.length, buffers.materialIndex[ 0 ] );
+
+			}
+
+		}
+
+		addMorphTargets( FBXTree, geo, geoNode, morphTarget, preTransform );
+
+		return geo;
+
+	}
+
+	function getGeoInfo( geoNode, skeleton ) {
+
+		var geoInfo = {};
+
+		geoInfo.vertexPositions = ( geoNode.Vertices !== undefined ) ? geoNode.Vertices.a : [];
+		geoInfo.vertexIndices = ( geoNode.PolygonVertexIndex !== undefined ) ? geoNode.PolygonVertexIndex.a : [];
 
 		if ( geoNode.LayerElementColor ) {
 
-			var colorInfo = getColors( geoNode.LayerElementColor[ 0 ] );
+			geoInfo.color = getColors( geoNode.LayerElementColor[ 0 ] );
 
 		}
 
 		if ( geoNode.LayerElementMaterial ) {
 
-			var materialInfo = getMaterials( geoNode.LayerElementMaterial[ 0 ] );
+			geoInfo.material = getMaterials( geoNode.LayerElementMaterial[ 0 ] );
 
 		}
 
 		if ( geoNode.LayerElementNormal ) {
 
-			var normalInfo = getNormals( geoNode.LayerElementNormal[ 0 ] );
+			geoInfo.normal = getNormals( geoNode.LayerElementNormal[ 0 ] );
 
 		}
 
 		if ( geoNode.LayerElementUV ) {
 
-			var uvInfo = [];
+			geoInfo.uv = [];
+
 			var i = 0;
 			while ( geoNode.LayerElementUV[ i ] ) {
 
-				uvInfo.push( getUVs( geoNode.LayerElementUV[ i ] ) );
+				geoInfo.uv.push( getUVs( geoNode.LayerElementUV[ i ] ) );
 				i ++;
 
 			}
 
 		}
 
-		var weightTable = {};
+		geoInfo.weightTable = {};
 
 		if ( skeleton !== null ) {
 
+			geoInfo.skeleton = skeleton;
+
 			skeleton.rawBones.forEach( function ( rawBone, i ) {
 
 				// loop over the bone's vertex indices and weights
 				rawBone.indices.forEach( function ( index, j ) {
 
-					if ( weightTable[ index ] === undefined ) weightTable[ index ] = [];
+					if ( geoInfo.weightTable[ index ] === undefined ) geoInfo.weightTable[ index ] = [];
 
-					weightTable[ index ].push( {
+					geoInfo.weightTable[ index ].push( {
 
 						id: i,
 						weight: rawBone.weights[ j ],
@@ -925,19 +1024,35 @@
 
 		}
 
+		return geoInfo;
+
+	}
+
+	function genBuffers( geoInfo ) {
+
+		var buffers = {
+			vertex: [],
+			normal: [],
+			colors: [],
+			uvs: [],
+			materialIndex: [],
+			vertexWeights: [],
+			weightsIndices: [],
+		};
+
 		var polygonIndex = 0;
 		var faceLength = 0;
 		var displayedWeightsWarning = false;
 
 		// these will hold data for a single face
-		var vertexPositionIndexes = [];
+		var facePositionIndexes = [];
 		var faceNormals = [];
 		var faceColors = [];
 		var faceUVs = [];
 		var faceWeights = [];
 		var faceWeightIndices = [];
 
-		vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) {
+		geoInfo.vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) {
 
 			var endOfFace = false;
 
@@ -947,7 +1062,7 @@
 			//  a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5
 			//  }
 			// Negative numbers mark the end of a face - first face here is 0, 1, 3, -3
-			// to find index of last vertex multiply by -1 and subtract 1: -3 * - 1 - 1 = 2
+			// to find index of last vertex bit shift the index: ^ - 1
 			if ( vertexIndex < 0 ) {
 
 				vertexIndex = vertexIndex ^ - 1; // equivalent to ( x * -1 ) - 1
@@ -958,21 +1073,21 @@
 			var weightIndices = [];
 			var weights = [];
 
-			vertexPositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 );
+			facePositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 );
 
-			if ( colorInfo ) {
+			if ( geoInfo.color ) {
 
-				var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, colorInfo );
+				var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.color );
 
 				faceColors.push( data[ 0 ], data[ 1 ], data[ 2 ] );
 
 			}
 
-			if ( skeleton ) {
+			if ( geoInfo.skeleton ) {
 
-				if ( weightTable[ vertexIndex ] !== undefined ) {
+				if ( geoInfo.weightTable[ vertexIndex ] !== undefined ) {
 
-					weightTable[ vertexIndex ].forEach( function ( wt ) {
+					geoInfo.weightTable[ vertexIndex ].forEach( function ( wt ) {
 
 						weights.push( wt.weight );
 						weightIndices.push( wt.id );
@@ -1038,23 +1153,23 @@
 
 			}
 
-			if ( normalInfo ) {
+			if ( geoInfo.normal ) {
 
-				var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, normalInfo );
+				var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.normal );
 
 				faceNormals.push( data[ 0 ], data[ 1 ], data[ 2 ] );
 
 			}
 
-			if ( materialInfo && materialInfo.mappingType !== 'AllSame' ) {
+			if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) {
 
-				var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, materialInfo )[ 0 ];
+				var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.material )[ 0 ];
 
 			}
 
-			if ( uvInfo ) {
+			if ( geoInfo.uv ) {
 
-				uvInfo.forEach( function ( uv, i ) {
+				geoInfo.uv.forEach( function ( uv, i ) {
 
 					var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, uv );
 
@@ -1073,124 +1188,15 @@
 
 			faceLength ++;
 
-			// we have reached the end of a face - it may have 4 sides though
-			// in which case the data is split to represent two 3 sided faces
 			if ( endOfFace ) {
 
-				for ( var i = 2; i < faceLength; i ++ ) {
-
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ 0 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ 1 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ 2 ] ] );
-
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ ( i - 1 ) * 3 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ ( i - 1 ) * 3 + 1 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ ( i - 1 ) * 3 + 2 ] ] );
-
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 + 1 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 + 2 ] ] );
-
-					if ( skeleton ) {
-
-						vertexWeightsBuffer.push( faceWeights[ 0 ] );
-						vertexWeightsBuffer.push( faceWeights[ 1 ] );
-						vertexWeightsBuffer.push( faceWeights[ 2 ] );
-						vertexWeightsBuffer.push( faceWeights[ 3 ] );
-
-						vertexWeightsBuffer.push( faceWeights[ ( i - 1 ) * 4 ] );
-						vertexWeightsBuffer.push( faceWeights[ ( i - 1 ) * 4 + 1 ] );
-						vertexWeightsBuffer.push( faceWeights[ ( i - 1 ) * 4 + 2 ] );
-						vertexWeightsBuffer.push( faceWeights[ ( i - 1 ) * 4 + 3 ] );
-
-						vertexWeightsBuffer.push( faceWeights[ i * 4 ] );
-						vertexWeightsBuffer.push( faceWeights[ i * 4 + 1 ] );
-						vertexWeightsBuffer.push( faceWeights[ i * 4 + 2 ] );
-						vertexWeightsBuffer.push( faceWeights[ i * 4 + 3 ] );
-
-						weightsIndicesBuffer.push( faceWeightIndices[ 0 ] );
-						weightsIndicesBuffer.push( faceWeightIndices[ 1 ] );
-						weightsIndicesBuffer.push( faceWeightIndices[ 2 ] );
-						weightsIndicesBuffer.push( faceWeightIndices[ 3 ] );
-
-						weightsIndicesBuffer.push( faceWeightIndices[ ( i - 1 ) * 4 ] );
-						weightsIndicesBuffer.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] );
-						weightsIndicesBuffer.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] );
-						weightsIndicesBuffer.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] );
-
-						weightsIndicesBuffer.push( faceWeightIndices[ i * 4 ] );
-						weightsIndicesBuffer.push( faceWeightIndices[ i * 4 + 1 ] );
-						weightsIndicesBuffer.push( faceWeightIndices[ i * 4 + 2 ] );
-						weightsIndicesBuffer.push( faceWeightIndices[ i * 4 + 3 ] );
-
-					}
-
-					if ( colorInfo ) {
-
-						colorsBuffer.push( faceColors[ 0 ] );
-						colorsBuffer.push( faceColors[ 1 ] );
-						colorsBuffer.push( faceColors[ 2 ] );
-
-						colorsBuffer.push( faceColors[ ( i - 1 ) * 3 ] );
-						colorsBuffer.push( faceColors[ ( i - 1 ) * 3 + 1 ] );
-						colorsBuffer.push( faceColors[ ( i - 1 ) * 3 + 2 ] );
-
-						colorsBuffer.push( faceColors[ i * 3 ] );
-						colorsBuffer.push( faceColors[ i * 3 + 1 ] );
-						colorsBuffer.push( faceColors[ i * 3 + 2 ] );
-
-					}
-
-					if ( materialInfo && materialInfo.mappingType !== 'AllSame' ) {
-
-						materialIndexBuffer.push( materialIndex );
-						materialIndexBuffer.push( materialIndex );
-						materialIndexBuffer.push( materialIndex );
-
-					}
-
-					if ( normalInfo ) {
-
-						normalBuffer.push( faceNormals[ 0 ] );
-						normalBuffer.push( faceNormals[ 1 ] );
-						normalBuffer.push( faceNormals[ 2 ] );
-
-						normalBuffer.push( faceNormals[ ( i - 1 ) * 3 ] );
-						normalBuffer.push( faceNormals[ ( i - 1 ) * 3 + 1 ] );
-						normalBuffer.push( faceNormals[ ( i - 1 ) * 3 + 2 ] );
-
-						normalBuffer.push( faceNormals[ i * 3 ] );
-						normalBuffer.push( faceNormals[ i * 3 + 1 ] );
-						normalBuffer.push( faceNormals[ i * 3 + 2 ] );
-
-					}
-
-					if ( uvInfo ) {
-
-						uvInfo.forEach( function ( uv, j ) {
-
-							if ( uvsBuffer[ j ] === undefined ) uvsBuffer[ j ] = [];
-
-							uvsBuffer[ j ].push( faceUVs[ j ][ 0 ] );
-							uvsBuffer[ j ].push( faceUVs[ j ][ 1 ] );
-
-							uvsBuffer[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] );
-							uvsBuffer[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] );
-
-							uvsBuffer[ j ].push( faceUVs[ j ][ i * 2 ] );
-							uvsBuffer[ j ].push( faceUVs[ j ][ i * 2 + 1 ] );
-
-						} );
-
-					}
-
-				}
+				genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength );
 
 				polygonIndex ++;
 				faceLength = 0;
 
 				// reset arrays for the next face
-				vertexPositionIndexes = [];
+				facePositionIndexes = [];
 				faceNormals = [];
 				faceColors = [];
 				faceUVs = [];
@@ -1201,103 +1207,122 @@
 
 		} );
 
-		var positionAttribute = new THREE.Float32BufferAttribute( vertexBuffer, 3 );
+		return buffers;
 
-		preTransform.applyToBufferAttribute( positionAttribute );
+	}
 
-		geo.addAttribute( 'position', positionAttribute );
+	// Generate data for a single face in a geometry. If the face is a quad then split it into 2 tris
+	function genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ) {
 
-		if ( colorsBuffer.length > 0 ) {
+		for ( var i = 2; i < faceLength; i ++ ) {
 
-			geo.addAttribute( 'color', new THREE.Float32BufferAttribute( colorsBuffer, 3 ) );
+			buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 0 ] ] );
+			buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 1 ] ] );
+			buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 2 ] ] );
 
-		}
+			buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 ] ] );
+			buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 1 ] ] );
+			buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 2 ] ] );
 
-		if ( skeleton ) {
-
-			geo.addAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( weightsIndicesBuffer, 4 ) );
+			buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 ] ] );
+			buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 1 ] ] );
+			buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 2 ] ] );
 
-			geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( vertexWeightsBuffer, 4 ) );
+			if ( geoInfo.skeleton ) {
 
-			// used later to bind the skeleton to the model
-			geo.FBX_Deformer = skeleton;
+				buffers.vertexWeights.push( faceWeights[ 0 ] );
+				buffers.vertexWeights.push( faceWeights[ 1 ] );
+				buffers.vertexWeights.push( faceWeights[ 2 ] );
+				buffers.vertexWeights.push( faceWeights[ 3 ] );
 
-		}
+				buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 ] );
+				buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 1 ] );
+				buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 2 ] );
+				buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 3 ] );
 
-		if ( normalBuffer.length > 0 ) {
+				buffers.vertexWeights.push( faceWeights[ i * 4 ] );
+				buffers.vertexWeights.push( faceWeights[ i * 4 + 1 ] );
+				buffers.vertexWeights.push( faceWeights[ i * 4 + 2 ] );
+				buffers.vertexWeights.push( faceWeights[ i * 4 + 3 ] );
 
-			var normalAttribute = new THREE.Float32BufferAttribute( normalBuffer, 3 );
+				buffers.weightsIndices.push( faceWeightIndices[ 0 ] );
+				buffers.weightsIndices.push( faceWeightIndices[ 1 ] );
+				buffers.weightsIndices.push( faceWeightIndices[ 2 ] );
+				buffers.weightsIndices.push( faceWeightIndices[ 3 ] );
 
-			var normalMatrix = new THREE.Matrix3().getNormalMatrix( preTransform );
-			normalMatrix.applyToBufferAttribute( normalAttribute );
+				buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 ] );
+				buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] );
+				buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] );
+				buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] );
 
-			geo.addAttribute( 'normal', normalAttribute );
+				buffers.weightsIndices.push( faceWeightIndices[ i * 4 ] );
+				buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 1 ] );
+				buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 2 ] );
+				buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 3 ] );
 
-		}
+			}
 
-		uvsBuffer.forEach( function ( uvBuffer, i ) {
+			if ( geoInfo.color ) {
 
-			// subsequent uv buffers are called 'uv1', 'uv2', ...
-			var name = 'uv' + ( i + 1 ).toString();
+				buffers.colors.push( faceColors[ 0 ] );
+				buffers.colors.push( faceColors[ 1 ] );
+				buffers.colors.push( faceColors[ 2 ] );
 
-			// the first uv buffer is just called 'uv'
-			if ( i === 0 ) {
+				buffers.colors.push( faceColors[ ( i - 1 ) * 3 ] );
+				buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 1 ] );
+				buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 2 ] );
 
-				name = 'uv';
+				buffers.colors.push( faceColors[ i * 3 ] );
+				buffers.colors.push( faceColors[ i * 3 + 1 ] );
+				buffers.colors.push( faceColors[ i * 3 + 2 ] );
 
 			}
 
-			geo.addAttribute( name, new THREE.Float32BufferAttribute( uvsBuffer[ i ], 2 ) );
-
-		} );
-
-		if ( materialInfo && materialInfo.mappingType !== 'AllSame' ) {
-
-			// Convert the material indices of each vertex into rendering groups on the geometry.
-			var prevMaterialIndex = materialIndexBuffer[ 0 ];
-			var startIndex = 0;
+			if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) {
 
-			materialIndexBuffer.forEach( function ( currentIndex, i ) {
+				buffers.materialIndex.push( materialIndex );
+				buffers.materialIndex.push( materialIndex );
+				buffers.materialIndex.push( materialIndex );
 
-				if ( currentIndex !== prevMaterialIndex ) {
+			}
 
-					geo.addGroup( startIndex, i - startIndex, prevMaterialIndex );
+			if ( geoInfo.normal ) {
 
-					prevMaterialIndex = currentIndex;
-					startIndex = i;
+				buffers.normal.push( faceNormals[ 0 ] );
+				buffers.normal.push( faceNormals[ 1 ] );
+				buffers.normal.push( faceNormals[ 2 ] );
 
-				}
+				buffers.normal.push( faceNormals[ ( i - 1 ) * 3 ] );
+				buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 1 ] );
+				buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 2 ] );
 
-			} );
+				buffers.normal.push( faceNormals[ i * 3 ] );
+				buffers.normal.push( faceNormals[ i * 3 + 1 ] );
+				buffers.normal.push( faceNormals[ i * 3 + 2 ] );
 
-			// the loop above doesn't add the last group, do that here.
-			if ( geo.groups.length > 0 ) {
+			}
 
-				var lastGroup = geo.groups[ geo.groups.length - 1 ];
-				var lastIndex = lastGroup.start + lastGroup.count;
+			if ( geoInfo.uv ) {
 
-				if ( lastIndex !== materialIndexBuffer.length ) {
+				geoInfo.uv.forEach( function ( uv, j ) {
 
-					geo.addGroup( lastIndex, materialIndexBuffer.length - lastIndex, prevMaterialIndex );
+					if ( buffers.uvs[ j ] === undefined ) buffers.uvs[ j ] = [];
 
-				}
+					buffers.uvs[ j ].push( faceUVs[ j ][ 0 ] );
+					buffers.uvs[ j ].push( faceUVs[ j ][ 1 ] );
 
-			}
+					buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] );
+					buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] );
 
-			// case where there are multiple materials but the whole geometry is only
-			// using one of them
-			if ( geo.groups.length === 0 ) {
+					buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 ] );
+					buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 + 1 ] );
 
-				geo.addGroup( 0, materialIndexBuffer.length, materialIndexBuffer[ 0 ] );
+				} );
 
 			}
 
 		}
 
-		addMorphTargets( FBXTree, geo, geoNode, morphTarget, preTransform );
-
-		return geo;
-
 	}
 
 	function addMorphTargets( FBXTree, parentGeo, parentGeoNode, morphTarget, preTransform ) {
@@ -1342,63 +1367,27 @@
 
 			var morphIndex = indices[ i ] * 3;
 
-			// since this is a blend shape rather than a morph target, the effect is
-			// gained by additively combining the morph positions with the original shape's positions
+			// FBX format uses blend shapes rather than morph targets. This can be converted
+			// by additively combining the blend shape positions with the original geometry's positions
 			vertexPositions[ morphIndex ] += morphPositions[ i * 3 ];
 			vertexPositions[ morphIndex + 1 ] += morphPositions[ i * 3 + 1 ];
 			vertexPositions[ morphIndex + 2 ] += morphPositions[ i * 3 + 2 ];
 
 		}
 
-		var vertexBuffer = [];
-
-		var faceLength = 0;
-		var vertexPositionIndexes = [];
-
-		vertexIndices.forEach( function ( vertexIndex ) {
-
-			var endOfFace = false;
-
-			if ( vertexIndex < 0 ) {
-
-				vertexIndex = vertexIndex ^ - 1;
-				endOfFace = true;
-
-			}
-
-			vertexPositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 );
-
-			faceLength ++;
-
-			if ( endOfFace ) {
-
-				for ( var i = 2; i < faceLength; i ++ ) {
-
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ 0 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ 1 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ 2 ] ] );
-
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ ( i - 1 ) * 3 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ ( i - 1 ) * 3 + 1 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ ( i - 1 ) * 3 + 2 ] ] );
-
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 + 1 ] ] );
-					vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 + 2 ] ] );
-
-				}
-
-				faceLength = 0;
-				vertexPositionIndexes = [];
-
-			}
+		// TODO: add morph normal support
+		var morphGeoInfo = {
+			vertexIndices: vertexIndices,
+			vertexPositions: vertexPositions,
+		};
 
-		} );
+		var morphBuffers = genBuffers( morphGeoInfo );
 
-		var positionAttribute = new THREE.Float32BufferAttribute( vertexBuffer, 3 );
+		var positionAttribute = new THREE.Float32BufferAttribute( morphBuffers.vertex, 3 );
 		positionAttribute.name = morphGeoNode.attrName;
 
 		preTransform.applyToBufferAttribute( positionAttribute );
+
 		parentGeo.morphAttributes.position.push( positionAttribute );
 
 	}