Selaa lähdekoodia

Merge the PLYLoaders

Garrett Johnson 7 vuotta sitten
vanhempi
commit
5907d575e9
2 muutettua tiedostoa jossa 376 lisäystä ja 541 poistoa
  1. 0 394
      examples/js/exporters/PLYBinaryExporter.js
  2. 376 147
      examples/js/exporters/PLYExporter.js

+ 0 - 394
examples/js/exporters/PLYBinaryExporter.js

@@ -1,394 +0,0 @@
-/**
- * @author Garrett Johnson / http://gkjohnson.github.io/
- * https://github.com/gkjohnson/ply-exporter-js
- *
- * Usage:
- *  var exporter = new THREE.PLYBinaryExporter();
- *
- *  // second argument is an array of attributes to
- *  // explicitly exclude from the file:
- *  // 'color', 'uv', 'normal', 'index'
- *  var data = exporter.parse(mesh, [ 'color' ]);
- *
- * Format Definition:
- *  http://paulbourke.net/dataformats/ply/
- */
-
-THREE.PLYBinaryExporter = function () {};
-
-THREE.PLYBinaryExporter.prototype = {
-
-	constructor: THREE.PLYBinaryExporter,
-
-	parse: function ( object, excludeProperties ) {
-
-		if ( Array.isArray( excludeProperties ) !== true ) {
-
-			excludeProperties = [];
-
-		}
-
-		var geomToBufferGeom = new WeakMap();
-		var includeNormals = false;
-		var includeColors = false;
-		var includeUVs = false;
-		var includeIndices = true;
-
-		// count the number of vertices
-		var vertexCount = 0;
-		var faceCount = 0;
-
-		object.traverse( function ( child ) {
-
-			if ( child instanceof THREE.Mesh ) {
-
-				var mesh = child;
-				var geometry = mesh.geometry;
-
-				if ( geometry instanceof THREE.Geometry ) {
-
-					var bufferGeometry = geomToBufferGeom.get( geometry ) || new THREE.BufferGeometry().setFromObject( mesh );
-					geomToBufferGeom.set( geometry, bufferGeometry );
-					geometry = bufferGeometry;
-
-				}
-
-				if ( geometry instanceof THREE.BufferGeometry ) {
-
-					var vertices = geometry.getAttribute( 'position' );
-					var normals = geometry.getAttribute( 'normal' );
-					var uvs = geometry.getAttribute( 'uv' );
-					var colors = geometry.getAttribute( 'color' );
-					var indices = geometry.getIndex();
-
-					if ( vertices == null ) {
-
-						return;
-
-					}
-
-					vertexCount += vertices.count;
-					faceCount += indices ? indices.count / 3 : vertices.count / 3;
-
-					if ( normals != null ) includeNormals = true;
-
-					if ( uvs != null ) includeUVs = true;
-
-					if ( colors != null ) includeColors = true;
-
-				}
-
-			}
-
-		} );
-
-		includeNormals = includeNormals && excludeProperties.indexOf( 'normal' ) === - 1;
-		includeColors = includeColors && excludeProperties.indexOf( 'color' ) === - 1;
-		includeUVs = includeUVs && excludeProperties.indexOf( 'uv' ) === - 1;
-		includeIndices = includeIndices && excludeProperties.indexOf( 'index' ) === - 1;
-
-		if ( includeIndices && faceCount !== Math.floor( faceCount ) ) {
-
-			// point cloud meshes will not have an index array and may not have a
-			// number of vertices that is divisble by 3 (and therefore representable
-			// as triangles)
-			console.error(
-
-				'PLYBinaryExporter: Failed to generate a valid PLY file with triangle indices because the ' +
-				'number of indices is not divisible by 3.'
-
-			);
-
-			return null;
-
-		}
-
-
-		// get how many bytes will be needed to save out the faces
-		// so we can use a minimal amount of memory / data
-		var indexByteCount = 1;
-
-		if ( vertexCount > 256 ) { // 2^8 bits
-
-			indexByteCount = 2;
-
-		}
-
-		if ( vertexCount > 65536 ) { // 2^16 bits
-
-			indexByteCount = 4;
-
-		}
-
-
-		// Form the header
-		var header =
-			'ply\n' +
-			'format binary_big_endian 1.0\n' +
-			`element vertex ${vertexCount}\n` +
-
-			// position
-			'property float x\n' +
-			'property float y\n' +
-			'property float z\n';
-
-		if ( includeNormals === true ) {
-
-			// normal
-			header +=
-				'property float nx\n' +
-				'property float ny\n' +
-				'property float nz\n';
-
-		}
-
-		if ( includeUVs === true ) {
-
-			// uvs
-			header +=
-				'property float s\n' +
-				'property float t\n';
-
-		}
-
-		if ( includeColors === true ) {
-
-			// colors
-			header +=
-				'property uchar red\n' +
-				'property uchar green\n' +
-				'property uchar blue\n';
-
-		}
-
-		if ( includeIndices === true ) {
-
-			// faces
-			header +=
-				`element face ${faceCount}\n` +
-				`property list uchar uint${ indexByteCount * 8 } vertex_index\n`;
-
-		}
-
-		header += 'end_header\n';
-
-
-		var headerBin = new TextEncoder().encode( header );
-
-		// 3 position values at 4 bytes
-		// 3 normal values at 4 bytes
-		// 3 color channels with 1 byte
-		// 2 uv values at 4 bytes
-		var vertexListLength = vertexCount * ( 4 * 3 + ( includeNormals ? 4 * 3 : 0 ) + ( includeColors ? 3 : 0 ) + ( includeUVs ? 4 * 2 : 0 ) );
-
-		// 1 byte shape desciptor
-		// 3 vertex indices at ${indexByteCount} bytes
-		var faceListLength = includeIndices ? faceCount * ( indexByteCount * 3 + 1 ) : 0;
-		var output = new DataView( new ArrayBuffer( headerBin.length + vertexListLength + faceListLength ) );
-		new Uint8Array( output.buffer ).set( headerBin, 0 );
-
-
-		var vOffset = headerBin.length;
-		var fOffset = headerBin.length + vertexListLength;
-		var writtenVertices = 0;
-
-		var vertex = new THREE.Vector3();
-		var normalMatrixWorld = new THREE.Matrix3();
-		object.traverse( function ( child ) {
-
-			if ( child instanceof THREE.Mesh ) {
-
-				var mesh = child;
-				var geometry = mesh.geometry;
-
-				if ( geometry instanceof THREE.Geometry ) {
-
-					geometry = geomToBufferGeom.get( geometry );
-
-				}
-
-				if ( geometry instanceof THREE.BufferGeometry ) {
-
-					var vertices = geometry.getAttribute( 'position' );
-					var normals = geometry.getAttribute( 'normal' );
-					var uvs = geometry.getAttribute( 'uv' );
-					var colors = geometry.getAttribute( 'color' );
-					var indices = geometry.getIndex();
-
-					normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
-
-					if ( vertices == null ) {
-
-						return;
-
-					}
-
-					// form each line
-					for ( var i = 0, l = vertices.count; i < l; i ++ ) {
-
-						vertex.x = vertices.getX( i );
-						vertex.y = vertices.getY( i );
-						vertex.z = vertices.getZ( i );
-
-						vertex.applyMatrix4( mesh.matrixWorld );
-
-
-						// Position information
-						output.setFloat32( vOffset, vertex.x );
-						vOffset += 4;
-
-						output.setFloat32( vOffset, vertex.y );
-						vOffset += 4;
-
-						output.setFloat32( vOffset, vertex.z );
-						vOffset += 4;
-
-						// Normal information
-						if ( includeNormals === true ) {
-
-							if ( normals != null ) {
-
-								vertex.x = normals.getX( i );
-								vertex.y = normals.getY( i );
-								vertex.z = normals.getZ( i );
-
-								vertex.applyMatrix3( normalMatrixWorld );
-
-								output.setFloat32( vOffset, vertex.x );
-								vOffset += 4;
-
-								output.setFloat32( vOffset, vertex.y );
-								vOffset += 4;
-
-								output.setFloat32( vOffset, vertex.z );
-								vOffset += 4;
-
-							} else {
-
-								output.setFloat32( vOffset, 0 );
-								vOffset += 4;
-
-								output.setFloat32( vOffset, 0 );
-								vOffset += 4;
-
-								output.setFloat32( vOffset, 0 );
-								vOffset += 4;
-
-							}
-
-						}
-
-						// UV information
-						if ( includeUVs === true ) {
-
-							if ( uvs != null ) {
-
-								output.setFloat32( vOffset, uvs.getX( i ) );
-								vOffset += 4;
-
-								output.setFloat32( vOffset, uvs.getY( i ) );
-								vOffset += 4;
-
-							} else if ( includeUVs !== false ) {
-
-								output.setFloat32( vOffset, 0 );
-								vOffset += 4;
-
-								output.setFloat32( vOffset, 0 );
-								vOffset += 4;
-
-							}
-
-						}
-
-						// Color information
-						if ( includeColors === true ) {
-
-							if ( colors != null ) {
-
-								output.setUint8( vOffset, Math.floor( colors.getX( i ) * 255 ) );
-								vOffset += 1;
-
-								output.setUint8( vOffset, Math.floor( colors.getY( i ) * 255 ) );
-								vOffset += 1;
-
-								output.setUint8( vOffset, Math.floor( colors.getZ( i ) * 255 ) );
-								vOffset += 1;
-
-							} else {
-
-								output.setUint8( vOffset, 255 );
-								vOffset += 1;
-
-								output.setUint8( vOffset, 255 );
-								vOffset += 1;
-
-								output.setUint8( vOffset, 255 );
-								vOffset += 1;
-
-							}
-
-						}
-
-					}
-
-					if ( includeIndices === true ) {
-
-						// Create the face list
-						var faceIndexFunc = `setUint${indexByteCount * 8}`;
-						if ( indices !== null ) {
-
-							for ( var i = 0, l = indices.count; i < l; i += 3 ) {
-
-								output.setUint8( fOffset, 3 );
-								fOffset += 1;
-
-								output[ faceIndexFunc ]( fOffset, indices.getX( i + 0 ) + writtenVertices );
-								fOffset += indexByteCount;
-
-								output[ faceIndexFunc ]( fOffset, indices.getX( i + 1 ) + writtenVertices );
-								fOffset += indexByteCount;
-
-								output[ faceIndexFunc ]( fOffset, indices.getX( i + 2 ) + writtenVertices );
-								fOffset += indexByteCount;
-
-							}
-
-						} else {
-
-							for ( var i = 0, l = vertices.count; i < l; i += 3 ) {
-
-								output.setUint8( fOffset, 3 );
-								fOffset += 1;
-
-								output[ faceIndexFunc ]( fOffset, writtenVertices + i );
-								fOffset += indexByteCount;
-
-								output[ faceIndexFunc ]( fOffset, writtenVertices + i + 1 );
-								fOffset += indexByteCount;
-
-								output[ faceIndexFunc ]( fOffset, writtenVertices + i + 2 );
-								fOffset += indexByteCount;
-
-							}
-
-						}
-
-					}
-
-
-					// Save the amount of verts we've already written so we can offset
-					// the face index on the next mesh
-					writtenVertices += vertices.count;
-
-				}
-
-			}
-
-		} );
-
-		return output;
-
-	}
-
-};

+ 376 - 147
examples/js/exporters/PLYExporter.js

@@ -5,10 +5,8 @@
  * Usage:
  *  var exporter = new THREE.PLYExporter();
  *
- *  // second argument is an array of attributes to
- *  // explicitly exclude from the file:
- *  // 'color', 'uv', 'normal', 'index'
- *  var data = exporter.parse(mesh, [ 'color' ]);
+ *  // second argument is a list of options
+ *  var data = exporter.parse(mesh, { binar: true, excludeProperties: [ 'color' ] });
  *
  * Format Definition:
  *  http://paulbourke.net/dataformats/ply/
@@ -20,28 +18,67 @@ THREE.PLYExporter.prototype = {
 
 	constructor: THREE.PLYExporter,
 
-	parse: function ( object, excludeProperties ) {
+	parse: function ( object, options ) {
 
-		if ( Array.isArray( excludeProperties ) !== true ) {
+		// Iterate over the valid meshes in the object
+		function traverseMeshes( cb ) {
 
-			excludeProperties = [];
+			object.traverse( function ( child ) {
+
+				if ( child.isMesh === true ) {
+
+					var mesh = child;
+					var geometry = mesh.geometry;
+
+					if ( geometry.isGeometry === true ) {
+
+						geometry = geomToBufferGeom.get( geometry );
+
+					}
+
+					if ( geometry.isBufferGeometry === true ) {
+
+						if ( geometry.getAttribute( 'position' ) !== undefined ) {
+
+							cb( mesh, geometry );
+
+						}
+
+					}
+
+				}
+
+			} );
 
 		}
 
+		// Default options
+		var defaultOptions = {
+			binary: false,
+			excludeProperties: [] // normal, uv, color, index
+		};
+
+		options = Object.assign( defaultOptions, options );
+
+		var excludeProperties = options.excludeProperties;
 		var geomToBufferGeom = new WeakMap();
 		var includeNormals = false;
 		var includeColors = false;
 		var includeUVs = false;
 		var includeIndices = true;
 
+		// count the vertices, check which properties are used,
+		// and cache the BufferGeometry
+		var vertexCount = 0;
+		var faceCount = 0;
 		object.traverse( function ( child ) {
 
-			if ( child instanceof THREE.Mesh ) {
+			if ( child.isMesh === true ) {
 
 				var mesh = child;
 				var geometry = mesh.geometry;
 
-				if ( geometry instanceof THREE.Geometry ) {
+				if ( geometry.isGeometry === true ) {
 
 					var bufferGeometry = geomToBufferGeom.get( geometry ) || new THREE.BufferGeometry().setFromObject( mesh );
 					geomToBufferGeom.set( geometry, bufferGeometry );
@@ -49,7 +86,7 @@ THREE.PLYExporter.prototype = {
 
 				}
 
-				if ( geometry instanceof THREE.BufferGeometry ) {
+				if ( geometry.isBufferGeometry === true ) {
 
 					var vertices = geometry.getAttribute( 'position' );
 					var normals = geometry.getAttribute( 'normal' );
@@ -57,18 +94,20 @@ THREE.PLYExporter.prototype = {
 					var colors = geometry.getAttribute( 'color' );
 					var indices = geometry.getIndex();
 
-					if ( vertices == null ) {
+					if ( vertices === undefined ) {
 
 						return;
 
 					}
 
+					vertexCount += vertices.count;
+					faceCount += indices ? indices.count / 3 : vertices.count / 3;
 
-					if ( normals != null ) includeNormals = true;
+					if ( normals !== undefined ) includeNormals = true;
 
-					if ( uvs != null ) includeUVs = true;
+					if ( uvs !== undefined ) includeUVs = true;
 
-					if ( colors != null ) includeColors = true;
+					if ( colors !== undefined ) includeColors = true;
 
 				}
 
@@ -82,227 +121,417 @@ THREE.PLYExporter.prototype = {
 		includeIndices = includeIndices && excludeProperties.indexOf( 'index' ) === - 1;
 
 
-		// count the number of vertices
-		var vertexCount = 0;
-		var faceCount = 0;
-		var vertexList = '';
-		var faceList = '';
+		if ( includeIndices && faceCount !== Math.floor( faceCount ) ) {
 
+			// point cloud meshes will not have an index array and may not have a
+			// number of vertices that is divisble by 3 (and therefore representable
+			// as triangles)
+			console.error(
+
+				'PLYExporter: Failed to generate a valid PLY file with triangle indices because the ' +
+				'number of indices is not divisible by 3.'
+
+			);
+
+			return null;
+
+		}
+
+		// get how many bytes will be needed to save out the faces
+		// so we can use a minimal amount of memory / data
+		var indexByteCount = 1;
+
+		if ( vertexCount > 256 ) { // 2^8 bits
+
+			indexByteCount = 2;
+
+		}
+
+		if ( vertexCount > 65536 ) { // 2^16 bits
+
+			indexByteCount = 4;
+
+		}
+
+
+		var header =
+			'ply\n' +
+			`format ${ options.binary ? 'binary_big_endian' : 'ascii' } 1.0\n` +
+			`element vertex ${vertexCount}\n` +
+
+			// position
+			'property float x\n' +
+			'property float y\n' +
+			'property float z\n';
+
+		if ( includeNormals === true ) {
+
+			// normal
+			header +=
+				'property float nx\n' +
+				'property float ny\n' +
+				'property float nz\n';
+
+		}
+
+		if ( includeUVs === true ) {
+
+			// uvs
+			header +=
+				'property float s\n' +
+				'property float t\n';
+
+		}
+
+		if ( includeColors === true ) {
+
+			// colors
+			header +=
+				'property uchar red\n' +
+				'property uchar green\n' +
+				'property uchar blue\n';
+
+		}
+
+		if ( includeIndices === true ) {
+
+			// faces
+			header +=
+				`element face ${faceCount}\n` +
+				`property list uchar uint${ indexByteCount * 8 } vertex_index\n`;
+
+		}
+
+		header += 'end_header\n';
+
+
+		// Generate attribute data
 		var vertex = new THREE.Vector3();
 		var normalMatrixWorld = new THREE.Matrix3();
-		object.traverse( function ( child ) {
 
-			if ( child instanceof THREE.Mesh ) {
+		if ( options.binary === true ) {
 
-				var mesh = child;
-				var geometry = mesh.geometry;
+			// Binary File Generation
+			var headerBin = new TextEncoder().encode( header );
 
-				if ( geometry instanceof THREE.Geometry ) {
+			// 3 position values at 4 bytes
+			// 3 normal values at 4 bytes
+			// 3 color channels with 1 byte
+			// 2 uv values at 4 bytes
+			var vertexListLength = vertexCount * ( 4 * 3 + ( includeNormals ? 4 * 3 : 0 ) + ( includeColors ? 3 : 0 ) + ( includeUVs ? 4 * 2 : 0 ) );
 
-					geometry = geomToBufferGeom.get( geometry );
+			// 1 byte shape desciptor
+			// 3 vertex indices at ${indexByteCount} bytes
+			var faceListLength = includeIndices ? faceCount * ( indexByteCount * 3 + 1 ) : 0;
+			var output = new DataView( new ArrayBuffer( headerBin.length + vertexListLength + faceListLength ) );
+			new Uint8Array( output.buffer ).set( headerBin, 0 );
 
-				}
 
-				if ( geometry instanceof THREE.BufferGeometry ) {
+			var vOffset = headerBin.length;
+			var fOffset = headerBin.length + vertexListLength;
+			var writtenVertices = 0;
+			traverseMeshes( function ( mesh, geometry ) {
 
-					var vertices = geometry.getAttribute( 'position' );
-					var normals = geometry.getAttribute( 'normal' );
-					var uvs = geometry.getAttribute( 'uv' );
-					var colors = geometry.getAttribute( 'color' );
-					var indices = geometry.getIndex();
+				var vertices = geometry.getAttribute( 'position' );
+				var normals = geometry.getAttribute( 'normal' );
+				var uvs = geometry.getAttribute( 'uv' );
+				var colors = geometry.getAttribute( 'color' );
+				var indices = geometry.getIndex();
 
-					normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
+				normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
 
-					if ( vertices == null ) {
+				for ( var i = 0, l = vertices.count; i < l; i ++ ) {
 
-						return;
+					vertex.x = vertices.getX( i );
+					vertex.y = vertices.getY( i );
+					vertex.z = vertices.getZ( i );
 
-					}
+					vertex.applyMatrix4( mesh.matrixWorld );
 
-					// form each line
-					for ( var i = 0, l = vertices.count; i < l; i ++ ) {
 
-						vertex.x = vertices.getX( i );
-						vertex.y = vertices.getY( i );
-						vertex.z = vertices.getZ( i );
+					// Position information
+					output.setFloat32( vOffset, vertex.x );
+					vOffset += 4;
 
-						vertex.applyMatrix4( mesh.matrixWorld );
+					output.setFloat32( vOffset, vertex.y );
+					vOffset += 4;
 
+					output.setFloat32( vOffset, vertex.z );
+					vOffset += 4;
 
-						// Position information
-						var line =
-							vertex.x + ' ' +
-							vertex.y + ' ' +
-							vertex.z;
+					// Normal information
+					if ( includeNormals === true ) {
 
-						// Normal information
-						if ( includeNormals === true ) {
+						if ( normals != null ) {
 
-							if ( normals != null ) {
+							vertex.x = normals.getX( i );
+							vertex.y = normals.getY( i );
+							vertex.z = normals.getZ( i );
 
-								vertex.x = normals.getX( i );
-								vertex.y = normals.getY( i );
-								vertex.z = normals.getZ( i );
+							vertex.applyMatrix3( normalMatrixWorld );
 
-								vertex.applyMatrix3( normalMatrixWorld );
+							output.setFloat32( vOffset, vertex.x );
+							vOffset += 4;
 
-								line += ' ' +
-									vertex.x + ' ' +
-									vertex.y + ' ' +
-									vertex.z;
+							output.setFloat32( vOffset, vertex.y );
+							vOffset += 4;
+
+							output.setFloat32( vOffset, vertex.z );
+							vOffset += 4;
+
+						} else {
 
-							} else {
+							output.setFloat32( vOffset, 0 );
+							vOffset += 4;
 
-								line += ' 0 0 0';
+							output.setFloat32( vOffset, 0 );
+							vOffset += 4;
 
-							}
+							output.setFloat32( vOffset, 0 );
+							vOffset += 4;
 
 						}
 
-						// UV information
-						if ( includeUVs === true ) {
+					}
+
+					// UV information
+					if ( includeUVs === true ) {
 
-							if ( uvs != null ) {
+						if ( uvs != null ) {
 
-								line += ' ' +
-									uvs.getX( i ) + ' ' +
-									uvs.getY( i );
+							output.setFloat32( vOffset, uvs.getX( i ) );
+							vOffset += 4;
 
-							} else if ( includeUVs !== false ) {
+							output.setFloat32( vOffset, uvs.getY( i ) );
+							vOffset += 4;
 
-								line += ' 0 0';
+						} else if ( includeUVs !== false ) {
 
-							}
+							output.setFloat32( vOffset, 0 );
+							vOffset += 4;
+
+							output.setFloat32( vOffset, 0 );
+							vOffset += 4;
 
 						}
 
-						// Color information
-						if ( includeColors === true ) {
+					}
 
-							if ( colors != null ) {
+					// Color information
+					if ( includeColors === true ) {
 
-								line += ' ' +
-									Math.floor( colors.getX( i ) * 255 ) + ' ' +
-									Math.floor( colors.getY( i ) * 255 ) + ' ' +
-									Math.floor( colors.getZ( i ) * 255 );
+						if ( colors != null ) {
 
-							} else {
+							output.setUint8( vOffset, Math.floor( colors.getX( i ) * 255 ) );
+							vOffset += 1;
 
-								line += ' 255 255 255';
+							output.setUint8( vOffset, Math.floor( colors.getY( i ) * 255 ) );
+							vOffset += 1;
 
-							}
+							output.setUint8( vOffset, Math.floor( colors.getZ( i ) * 255 ) );
+							vOffset += 1;
 
-						}
+						} else {
+
+							output.setUint8( vOffset, 255 );
+							vOffset += 1;
 
-						vertexList += line + '\n';
+							output.setUint8( vOffset, 255 );
+							vOffset += 1;
+
+							output.setUint8( vOffset, 255 );
+							vOffset += 1;
+
+						}
 
 					}
 
+				}
+
+				if ( includeIndices === true ) {
+
 					// Create the face list
-					if ( includeIndices === true ) {
+					var faceIndexFunc = `setUint${indexByteCount * 8}`;
+					if ( indices !== null ) {
 
-						if ( indices !== null ) {
+						for ( var i = 0, l = indices.count; i < l; i += 3 ) {
 
-							for ( var i = 0, l = indices.count; i < l; i += 3 ) {
+							output.setUint8( fOffset, 3 );
+							fOffset += 1;
 
-								faceList += `3 ${ indices.getX( i + 0 ) + vertexCount }`;
-								faceList += ` ${ indices.getX( i + 1 ) + vertexCount }`;
-								faceList += ` ${ indices.getX( i + 2 ) + vertexCount }\n`;
+							output[ faceIndexFunc ]( fOffset, indices.getX( i + 0 ) + writtenVertices );
+							fOffset += indexByteCount;
 
-							}
+							output[ faceIndexFunc ]( fOffset, indices.getX( i + 1 ) + writtenVertices );
+							fOffset += indexByteCount;
 
-						} else {
+							output[ faceIndexFunc ]( fOffset, indices.getX( i + 2 ) + writtenVertices );
+							fOffset += indexByteCount;
+
+						}
+
+					} else {
 
-							for ( var i = 0, l = vertices.count; i < l; i += 3 ) {
+						for ( var i = 0, l = vertices.count; i < l; i += 3 ) {
 
-								faceList += `3 ${ vertexCount + i } ${ vertexCount + i + 1 } ${ vertexCount + i + 2 }\n`;
+							output.setUint8( fOffset, 3 );
+							fOffset += 1;
 
-							}
+							output[ faceIndexFunc ]( fOffset, writtenVertices + i );
+							fOffset += indexByteCount;
+
+							output[ faceIndexFunc ]( fOffset, writtenVertices + i + 1 );
+							fOffset += indexByteCount;
+
+							output[ faceIndexFunc ]( fOffset, writtenVertices + i + 2 );
+							fOffset += indexByteCount;
 
 						}
 
-						faceCount += indices ? indices.count / 3 : vertices.count / 3;
+					}
+
+				}
+
+
+				// Save the amount of verts we've already written so we can offset
+				// the face index on the next mesh
+				writtenVertices += vertices.count;
+
+			} );
+
+			return output;
+
+		} else {
+
+			// Ascii File Generation
+			// count the number of vertices
+			var writtenVertices = 0;
+			var vertexList = '';
+			var faceList = '';
+
+			traverseMeshes( function ( mesh, geometry ) {
+
+				var vertices = geometry.getAttribute( 'position' );
+				var normals = geometry.getAttribute( 'normal' );
+				var uvs = geometry.getAttribute( 'uv' );
+				var colors = geometry.getAttribute( 'color' );
+				var indices = geometry.getIndex();
+
+				normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
+
+				// form each line
+				for ( var i = 0, l = vertices.count; i < l; i ++ ) {
+
+					vertex.x = vertices.getX( i );
+					vertex.y = vertices.getY( i );
+					vertex.z = vertices.getZ( i );
+
+					vertex.applyMatrix4( mesh.matrixWorld );
+
+
+					// Position information
+					var line =
+						vertex.x + ' ' +
+						vertex.y + ' ' +
+						vertex.z;
+
+					// Normal information
+					if ( includeNormals === true ) {
+
+						if ( normals != null ) {
+
+							vertex.x = normals.getX( i );
+							vertex.y = normals.getY( i );
+							vertex.z = normals.getZ( i );
+
+							vertex.applyMatrix3( normalMatrixWorld );
+
+							line += ' ' +
+								vertex.x + ' ' +
+								vertex.y + ' ' +
+								vertex.z;
+
+						} else {
+
+							line += ' 0 0 0';
+
+						}
 
 					}
 
-					vertexCount += vertices.count;
+					// UV information
+					if ( includeUVs === true ) {
 
-				}
+						if ( uvs != null ) {
 
-			}
+							line += ' ' +
+								uvs.getX( i ) + ' ' +
+								uvs.getY( i );
 
-		} );
+						} else if ( includeUVs !== false ) {
 
-		if ( includeIndices && faceCount !== Math.floor( faceCount ) ) {
+							line += ' 0 0';
 
-			// point cloud meshes will not have an index array and may not have a
-			// number of vertices that is divisble by 3 (and therefore representable
-			// as triangles)
-			console.error(
+						}
 
-				'PLYExporter: Failed to generate a valid PLY file with triangle indices because the ' +
-				'number of indices is not divisible by 3.'
+					}
 
-			);
+					// Color information
+					if ( includeColors === true ) {
 
-			return null;
+						if ( colors != null ) {
 
-		}
+							line += ' ' +
+								Math.floor( colors.getX( i ) * 255 ) + ' ' +
+								Math.floor( colors.getY( i ) * 255 ) + ' ' +
+								Math.floor( colors.getZ( i ) * 255 );
 
-		var output =
-			'ply\n' +
-			'format ascii 1.0\n' +
-			`element vertex ${vertexCount}\n` +
+						} else {
 
-			// position
-			'property float x\n' +
-			'property float y\n' +
-			'property float z\n';
+							line += ' 255 255 255';
 
-		if ( includeNormals === true ) {
+						}
 
-			// normal
-			output +=
-				'property float nx\n' +
-				'property float ny\n' +
-				'property float nz\n';
+					}
 
-		}
+					vertexList += line + '\n';
 
-		if ( includeUVs === true ) {
+				}
 
-			// uvs
-			output +=
-				'property float s\n' +
-				'property float t\n';
+				// Create the face list
+				if ( includeIndices === true ) {
 
-		}
+					if ( indices !== null ) {
 
-		if ( includeColors === true ) {
+						for ( var i = 0, l = indices.count; i < l; i += 3 ) {
 
-			// colors
-			output +=
-				'property uchar red\n' +
-				'property uchar green\n' +
-				'property uchar blue\n';
+							faceList += `3 ${ indices.getX( i + 0 ) + writtenVertices }`;
+							faceList += ` ${ indices.getX( i + 1 ) + writtenVertices }`;
+							faceList += ` ${ indices.getX( i + 2 ) + writtenVertices }\n`;
 
-		}
+						}
 
-		if ( includeIndices === true ) {
+					} else {
 
-			// faces
-			output +=
-				`element face ${faceCount}\n` +
-				'property list uchar int vertex_index\n';
+						for ( var i = 0, l = vertices.count; i < l; i += 3 ) {
 
-		}
+							faceList += `3 ${ writtenVertices + i } ${ writtenVertices + i + 1 } ${ writtenVertices + i + 2 }\n`;
+
+						}
+
+					}
 
-		output +=
-			'end_header\n' +
-			`${vertexList}\n` +
-			( includeIndices ? `${faceList}\n` : '' );
+					faceCount += indices ? indices.count / 3 : vertices.count / 3;
 
-		return output;
+				}
+
+				writtenVertices += vertices.count;
+
+			} );
+
+			return `${ header }${vertexList}\n${ includeIndices ? `${faceList}\n` : '' }`;
+
+		}
 
 	}