瀏覽代碼

OBJLoader: Clean up.

Mr.doob 8 年之前
父節點
當前提交
8919ca3266
共有 1 個文件被更改,包括 284 次插入296 次删除
  1. 284 296
      examples/js/loaders/OBJLoader.js

+ 284 - 296
examples/js/loaders/OBJLoader.js

@@ -2,70 +2,32 @@
  * @author mrdoob / http://mrdoob.com/
  */
 
-THREE.OBJLoader = function ( manager ) {
-
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-	this.materials = null;
-
-	this.regexp = {
-		// v float float float
-		vertex_pattern           : /^v\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)/,
-		// vn float float float
-		normal_pattern           : /^vn\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)/,
-		// vt float float
-		uv_pattern               : /^vt\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)/,
-		// f vertex vertex vertex
-		face_vertex              : /^f\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?/,
-		// f vertex/uv vertex/uv vertex/uv
-		face_vertex_uv           : /^f\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+))?/,
-		// f vertex/uv/normal vertex/uv/normal vertex/uv/normal
-		face_vertex_uv_normal    : /^f\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+)\/(-?\d+))?/,
-		// f vertex//normal vertex//normal vertex//normal
-		face_vertex_normal       : /^f\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)(?:\s+(-?\d+)\/\/(-?\d+))?/,
-		// o object_name | g group_name
-		object_pattern           : /^[og]\s*(.+)?/,
-		// s boolean
-		smoothing_pattern        : /^s\s+(\d+|on|off)/,
-		// mtllib file_reference
-		material_library_pattern : /^mtllib /,
-		// usemtl material_name
-		material_use_pattern     : /^usemtl /
-	};
-
-};
-
-THREE.OBJLoader.prototype = {
-
-	constructor: THREE.OBJLoader,
-
-	load: function ( url, onLoad, onProgress, onError ) {
-
-		var scope = this;
-
-		var loader = new THREE.FileLoader( scope.manager );
-		loader.setPath( this.path );
-		loader.load( url, function ( text ) {
-
-			onLoad( scope.parse( text ) );
-
-		}, onProgress, onError );
-
-	},
-
-	setPath: function ( value ) {
-
-		this.path = value;
-
-	},
-
-	setMaterials: function ( materials ) {
-
-		this.materials = materials;
-
-	},
-
-	_createParserState : function () {
+THREE.OBJLoader = ( function () {
+
+	// v float float float
+	var vertex_pattern           = /^v\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)/;
+	// vn float float float
+	var normal_pattern           = /^vn\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)/;
+	// vt float float
+	var uv_pattern               = /^vt\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)/;
+	// f vertex vertex vertex
+	var face_vertex              = /^f\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?/;
+	// f vertex/uv vertex/uv vertex/uv
+	var face_vertex_uv           = /^f\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+))?/;
+	// f vertex/uv/normal vertex/uv/normal vertex/uv/normal
+	var face_vertex_uv_normal    = /^f\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+)\/(-?\d+))?/;
+	// f vertex//normal vertex//normal vertex//normal
+	var face_vertex_normal       = /^f\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)(?:\s+(-?\d+)\/\/(-?\d+))?/;
+	// o object_name | g group_name
+	var object_pattern           = /^[og]\s*(.+)?/;
+	// s boolean
+	var smoothing_pattern        = /^s\s+(\d+|on|off)/;
+	// mtllib file_reference
+	var material_library_pattern = /^mtllib /;
+	// usemtl material_name
+	var material_use_pattern     = /^usemtl /;
+
+	function ParserState() {
 
 		var state = {
 			objects  : [],
@@ -178,7 +140,7 @@ THREE.OBJLoader.prototype = {
 						if ( end && this.materials.length > 1 ) {
 
 							for ( var mi = this.materials.length - 1; mi >= 0; mi-- ) {
-								if ( this.materials[mi].groupCount <= 0 ) {
+								if ( this.materials[ mi ].groupCount <= 0 ) {
 									this.materials.splice( mi, 1 );
 								}
 							}
@@ -254,15 +216,9 @@ THREE.OBJLoader.prototype = {
 				var src = this.vertices;
 				var dst = this.object.geometry.vertices;
 
-				dst.push( src[ a + 0 ] );
-				dst.push( src[ a + 1 ] );
-				dst.push( src[ a + 2 ] );
-				dst.push( src[ b + 0 ] );
-				dst.push( src[ b + 1 ] );
-				dst.push( src[ b + 2 ] );
-				dst.push( src[ c + 0 ] );
-				dst.push( src[ c + 1 ] );
-				dst.push( src[ c + 2 ] );
+				dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
+				dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
+				dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
 
 			},
 
@@ -271,9 +227,7 @@ THREE.OBJLoader.prototype = {
 				var src = this.vertices;
 				var dst = this.object.geometry.vertices;
 
-				dst.push( src[ a + 0 ] );
-				dst.push( src[ a + 1 ] );
-				dst.push( src[ a + 2 ] );
+				dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
 
 			},
 
@@ -282,15 +236,9 @@ THREE.OBJLoader.prototype = {
 				var src = this.normals;
 				var dst = this.object.geometry.normals;
 
-				dst.push( src[ a + 0 ] );
-				dst.push( src[ a + 1 ] );
-				dst.push( src[ a + 2 ] );
-				dst.push( src[ b + 0 ] );
-				dst.push( src[ b + 1 ] );
-				dst.push( src[ b + 2 ] );
-				dst.push( src[ c + 0 ] );
-				dst.push( src[ c + 1 ] );
-				dst.push( src[ c + 2 ] );
+				dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
+				dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
+				dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
 
 			},
 
@@ -299,12 +247,9 @@ THREE.OBJLoader.prototype = {
 				var src = this.uvs;
 				var dst = this.object.geometry.uvs;
 
-				dst.push( src[ a + 0 ] );
-				dst.push( src[ a + 1 ] );
-				dst.push( src[ b + 0 ] );
-				dst.push( src[ b + 1 ] );
-				dst.push( src[ c + 0 ] );
-				dst.push( src[ c + 1 ] );
+				dst.push( src[ a + 0 ], src[ a + 1 ] );
+				dst.push( src[ b + 0 ], src[ b + 1 ] );
+				dst.push( src[ c + 0 ], src[ c + 1 ] );
 
 			},
 
@@ -313,8 +258,7 @@ THREE.OBJLoader.prototype = {
 				var src = this.uvs;
 				var dst = this.object.geometry.uvs;
 
-				dst.push( src[ a + 0 ] );
-				dst.push( src[ a + 1 ] );
+				dst.push( src[ a + 0 ], src[ a + 1 ] );
 
 			},
 
@@ -416,338 +360,382 @@ THREE.OBJLoader.prototype = {
 
 		return state;
 
-	},
+	}
 
-	parse: function ( text ) {
+	//
 
-		console.time( 'OBJLoader' );
+	function OBJLoader( manager ) {
 
-		var state = this._createParserState();
+		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
-		if ( text.indexOf( '\r\n' ) !== - 1 ) {
+		this.materials = null;
 
-			// This is faster than String.split with regex that splits on both
-			text = text.replace( /\r\n/g, '\n' );
+	};
 
-		}
+	OBJLoader.prototype = {
 
-		if ( text.indexOf( '\\\n' ) !== - 1) {
+		constructor: OBJLoader,
 
-			// join lines separated by a line continuation character (\)
-			text = text.replace( /\\\n/g, '' );
+		load: function ( url, onLoad, onProgress, onError ) {
 
-		}
+			var scope = this;
 
-		var lines = text.split( '\n' );
-		var line = '', lineFirstChar = '', lineSecondChar = '';
-		var lineLength = 0;
-		var result = [];
+			var loader = new THREE.FileLoader( scope.manager );
+			loader.setPath( this.path );
+			loader.load( url, function ( text ) {
 
-		// Faster to just trim left side of the line. Use if available.
-		var trimLeft = ( typeof ''.trimLeft === 'function' );
+				onLoad( scope.parse( text ) );
 
-		for ( var i = 0, l = lines.length; i < l; i ++ ) {
+			}, onProgress, onError );
 
-			line = lines[ i ];
+		},
 
-			line = trimLeft ? line.trimLeft() : line.trim();
+		setPath: function ( value ) {
 
-			lineLength = line.length;
+			this.path = value;
 
-			if ( lineLength === 0 ) continue;
+		},
 
-			lineFirstChar = line.charAt( 0 );
+		setMaterials: function ( materials ) {
 
-			// @todo invoke passed in handler if any
-			if ( lineFirstChar === '#' ) continue;
+			this.materials = materials;
 
-			if ( lineFirstChar === 'v' ) {
+		},
 
-				lineSecondChar = line.charAt( 1 );
+		parse: function ( text ) {
 
-				if ( lineSecondChar === ' ' && ( result = this.regexp.vertex_pattern.exec( line ) ) !== null ) {
+			console.time( 'OBJLoader' );
 
-					// 0                  1      2      3
-					// ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
+			var state = new ParserState();
 
-					state.vertices.push(
-						parseFloat( result[ 1 ] ),
-						parseFloat( result[ 2 ] ),
-						parseFloat( result[ 3 ] )
-					);
+			if ( text.indexOf( '\r\n' ) !== - 1 ) {
 
-				} else if ( lineSecondChar === 'n' && ( result = this.regexp.normal_pattern.exec( line ) ) !== null ) {
+				// This is faster than String.split with regex that splits on both
+				text = text.replace( /\r\n/g, '\n' );
 
-					// 0                   1      2      3
-					// ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
+			}
 
-					state.normals.push(
-						parseFloat( result[ 1 ] ),
-						parseFloat( result[ 2 ] ),
-						parseFloat( result[ 3 ] )
-					);
+			if ( text.indexOf( '\\\n' ) !== - 1) {
 
-				} else if ( lineSecondChar === 't' && ( result = this.regexp.uv_pattern.exec( line ) ) !== null ) {
+				// join lines separated by a line continuation character (\)
+				text = text.replace( /\\\n/g, '' );
 
-					// 0               1      2
-					// ["vt 0.1 0.2", "0.1", "0.2"]
+			}
 
-					state.uvs.push(
-						parseFloat( result[ 1 ] ),
-						parseFloat( result[ 2 ] )
-					);
+			var lines = text.split( '\n' );
+			var line = '', lineFirstChar = '', lineSecondChar = '';
+			var lineLength = 0;
+			var result = [];
 
-				} else {
+			// Faster to just trim left side of the line. Use if available.
+			var trimLeft = ( typeof ''.trimLeft === 'function' );
 
-					throw new Error( "Unexpected vertex/normal/uv line: '" + line  + "'" );
+			for ( var i = 0, l = lines.length; i < l; i ++ ) {
 
-				}
+				line = lines[ i ];
 
-			} else if ( lineFirstChar === "f" ) {
+				line = trimLeft ? line.trimLeft() : line.trim();
 
-				if ( ( result = this.regexp.face_vertex_uv_normal.exec( line ) ) !== null ) {
+				lineLength = line.length;
 
-					// f vertex/uv/normal vertex/uv/normal vertex/uv/normal
-					// 0                        1    2    3    4    5    6    7    8    9   10         11         12
-					// ["f 1/1/1 2/2/2 3/3/3", "1", "1", "1", "2", "2", "2", "3", "3", "3", undefined, undefined, undefined]
+				if ( lineLength === 0 ) continue;
 
-					state.addFace(
-						result[ 1 ], result[ 4 ], result[ 7 ], result[ 10 ],
-						result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ],
-						result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ]
-					);
+				lineFirstChar = line.charAt( 0 );
 
-				} else if ( ( result = this.regexp.face_vertex_uv.exec( line ) ) !== null ) {
+				// @todo invoke passed in handler if any
+				if ( lineFirstChar === '#' ) continue;
 
-					// f vertex/uv vertex/uv vertex/uv
-					// 0                  1    2    3    4    5    6   7          8
-					// ["f 1/1 2/2 3/3", "1", "1", "2", "2", "3", "3", undefined, undefined]
+				if ( lineFirstChar === 'v' ) {
 
-					state.addFace(
-						result[ 1 ], result[ 3 ], result[ 5 ], result[ 7 ],
-						result[ 2 ], result[ 4 ], result[ 6 ], result[ 8 ]
-					);
+					lineSecondChar = line.charAt( 1 );
 
-				} else if ( ( result = this.regexp.face_vertex_normal.exec( line ) ) !== null ) {
+					if ( lineSecondChar === ' ' && ( result = vertex_pattern.exec( line ) ) !== null ) {
 
-					// f vertex//normal vertex//normal vertex//normal
-					// 0                     1    2    3    4    5    6   7          8
-					// ["f 1//1 2//2 3//3", "1", "1", "2", "2", "3", "3", undefined, undefined]
+						// 0                  1      2      3
+						// ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
 
-					state.addFace(
-						result[ 1 ], result[ 3 ], result[ 5 ], result[ 7 ],
-						undefined, undefined, undefined, undefined,
-						result[ 2 ], result[ 4 ], result[ 6 ], result[ 8 ]
-					);
+						state.vertices.push(
+							parseFloat( result[ 1 ] ),
+							parseFloat( result[ 2 ] ),
+							parseFloat( result[ 3 ] )
+						);
 
-				} else if ( ( result = this.regexp.face_vertex.exec( line ) ) !== null ) {
+					} else if ( lineSecondChar === 'n' && ( result = normal_pattern.exec( line ) ) !== null ) {
 
-					// f vertex vertex vertex
-					// 0            1    2    3   4
-					// ["f 1 2 3", "1", "2", "3", undefined]
+						// 0                   1      2      3
+						// ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
 
-					state.addFace(
-						result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ]
-					);
+						state.normals.push(
+							parseFloat( result[ 1 ] ),
+							parseFloat( result[ 2 ] ),
+							parseFloat( result[ 3 ] )
+						);
 
-				} else {
+					} else if ( lineSecondChar === 't' && ( result = uv_pattern.exec( line ) ) !== null ) {
 
-					throw new Error( "Unexpected face line: '" + line  + "'" );
+						// 0               1      2
+						// ["vt 0.1 0.2", "0.1", "0.2"]
 
-				}
+						state.uvs.push(
+							parseFloat( result[ 1 ] ),
+							parseFloat( result[ 2 ] )
+						);
 
-			} else if ( lineFirstChar === "l" ) {
+					} else {
 
-				var lineParts = line.substring( 1 ).trim().split( " " );
-				var lineVertices = [], lineUVs = [];
+						throw new Error( "Unexpected vertex/normal/uv line: '" + line  + "'" );
 
-				if ( line.indexOf( "/" ) === - 1 ) {
+					}
 
-					lineVertices = lineParts;
+				} else if ( lineFirstChar === "f" ) {
 
-				} else {
+					if ( ( result = face_vertex_uv_normal.exec( line ) ) !== null ) {
+
+						// f vertex/uv/normal vertex/uv/normal vertex/uv/normal
+						// 0                        1    2    3    4    5    6    7    8    9   10         11         12
+						// ["f 1/1/1 2/2/2 3/3/3", "1", "1", "1", "2", "2", "2", "3", "3", "3", undefined, undefined, undefined]
+
+						state.addFace(
+							result[ 1 ], result[ 4 ], result[ 7 ], result[ 10 ],
+							result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ],
+							result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ]
+						);
+
+					} else if ( ( result = face_vertex_uv.exec( line ) ) !== null ) {
+
+						// f vertex/uv vertex/uv vertex/uv
+						// 0                  1    2    3    4    5    6   7          8
+						// ["f 1/1 2/2 3/3", "1", "1", "2", "2", "3", "3", undefined, undefined]
+
+						state.addFace(
+							result[ 1 ], result[ 3 ], result[ 5 ], result[ 7 ],
+							result[ 2 ], result[ 4 ], result[ 6 ], result[ 8 ]
+						);
+
+					} else if ( ( result = face_vertex_normal.exec( line ) ) !== null ) {
+
+						// f vertex//normal vertex//normal vertex//normal
+						// 0                     1    2    3    4    5    6   7          8
+						// ["f 1//1 2//2 3//3", "1", "1", "2", "2", "3", "3", undefined, undefined]
+
+						state.addFace(
+							result[ 1 ], result[ 3 ], result[ 5 ], result[ 7 ],
+							undefined, undefined, undefined, undefined,
+							result[ 2 ], result[ 4 ], result[ 6 ], result[ 8 ]
+						);
+
+					} else if ( ( result = face_vertex.exec( line ) ) !== null ) {
 
-					for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
+						// f vertex vertex vertex
+						// 0            1    2    3   4
+						// ["f 1 2 3", "1", "2", "3", undefined]
 
-						var parts = lineParts[ li ].split( "/" );
+						state.addFace(
+							result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ]
+						);
 
-						if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] );
-						if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] );
+					} else {
+
+						throw new Error( "Unexpected face line: '" + line  + "'" );
 
 					}
 
-				}
-				state.addLineGeometry( lineVertices, lineUVs );
+				} else if ( lineFirstChar === "l" ) {
+
+					var lineParts = line.substring( 1 ).trim().split( " " );
+					var lineVertices = [], lineUVs = [];
+
+					if ( line.indexOf( "/" ) === - 1 ) {
 
-			} else if ( ( result = this.regexp.object_pattern.exec( line ) ) !== null ) {
+						lineVertices = lineParts;
 
-				// o object_name
-				// or
-				// g group_name
+					} else {
 
-				// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
-				// var name = result[ 0 ].substr( 1 ).trim();
-				var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
+						for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
 
-				state.startObject( name );
+							var parts = lineParts[ li ].split( "/" );
 
-			} else if ( this.regexp.material_use_pattern.test( line ) ) {
+							if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] );
+							if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] );
 
-				// material
+						}
 
-				state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
+					}
+					state.addLineGeometry( lineVertices, lineUVs );
 
-			} else if ( this.regexp.material_library_pattern.test( line ) ) {
+				} else if ( ( result = object_pattern.exec( line ) ) !== null ) {
 
-				// mtl file
+					// o object_name
+					// or
+					// g group_name
 
-				state.materialLibraries.push( line.substring( 7 ).trim() );
+					// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
+					// var name = result[ 0 ].substr( 1 ).trim();
+					var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
 
-			} else if ( ( result = this.regexp.smoothing_pattern.exec( line ) ) !== null ) {
+					state.startObject( name );
 
-				// smooth shading
+				} else if ( material_use_pattern.test( line ) ) {
 
-				// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
-				// but does not define a usemtl for each face set.
-				// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
-				// This requires some care to not create extra material on each smooth value for "normal" obj files.
-				// where explicit usemtl defines geometry groups.
-				// Example asset: examples/models/obj/cerberus/Cerberus.obj
+					// material
 
-				var value = result[ 1 ].trim().toLowerCase();
-				/*
-				 * http://paulbourke.net/dataformats/obj/
-				 * or
-				 * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
-				 *
-				 * From chapter "Grouping" Syntax explanation "s group_number":
-				 * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
-				 * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
-				 * surfaces, smoothing groups are either turned on or off; there is no difference between values greater
-				 * than 0."
-				 */
-				state.object.smooth = ( value !== '0' && value !== 'off' );
+					state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
 
-				var material = state.object.currentMaterial();
-				if ( material ) {
+				} else if ( material_library_pattern.test( line ) ) {
 
-					material.smooth = state.object.smooth;
+					// mtl file
 
-				}
+					state.materialLibraries.push( line.substring( 7 ).trim() );
+
+				} else if ( ( result = smoothing_pattern.exec( line ) ) !== null ) {
 
-			} else {
+					// smooth shading
 
-				// Handle null terminated files without exception
-				if ( line === '\0' ) continue;
+					// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
+					// but does not define a usemtl for each face set.
+					// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
+					// This requires some care to not create extra material on each smooth value for "normal" obj files.
+					// where explicit usemtl defines geometry groups.
+					// Example asset: examples/models/obj/cerberus/Cerberus.obj
 
-				throw new Error( "Unexpected line: '" + line  + "'" );
+					var value = result[ 1 ].trim().toLowerCase();
+					/*
+					 * http://paulbourke.net/dataformats/obj/
+					 * or
+					 * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
+					 *
+					 * From chapter "Grouping" Syntax explanation "s group_number":
+					 * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
+					 * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
+					 * surfaces, smoothing groups are either turned on or off; there is no difference between values greater
+					 * than 0."
+					 */
+					state.object.smooth = ( value !== '0' && value !== 'off' );
+
+					var material = state.object.currentMaterial();
+					if ( material ) {
+
+						material.smooth = state.object.smooth;
+
+					}
+
+				} else {
+
+					// Handle null terminated files without exception
+					if ( line === '\0' ) continue;
+
+					throw new Error( "Unexpected line: '" + line  + "'" );
+
+				}
 
 			}
 
-		}
+			state.finalize();
 
-		state.finalize();
+			var container = new THREE.Group();
+			container.materialLibraries = [].concat( state.materialLibraries );
 
-		var container = new THREE.Group();
-		container.materialLibraries = [].concat( state.materialLibraries );
+			for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
 
-		for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
+				var object = state.objects[ i ];
+				var geometry = object.geometry;
+				var materials = object.materials;
+				var isLine = ( geometry.type === 'Line' );
 
-			var object = state.objects[ i ];
-			var geometry = object.geometry;
-			var materials = object.materials;
-			var isLine = ( geometry.type === 'Line' );
+				// Skip o/g line declarations that did not follow with any faces
+				if ( geometry.vertices.length === 0 ) continue;
 
-			// Skip o/g line declarations that did not follow with any faces
-			if ( geometry.vertices.length === 0 ) continue;
+				var buffergeometry = new THREE.BufferGeometry();
 
-			var buffergeometry = new THREE.BufferGeometry();
+				buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) );
 
-			buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) );
+				if ( geometry.normals.length > 0 ) {
 
-			if ( geometry.normals.length > 0 ) {
+					buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) );
 
-				buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) );
+				} else {
 
-			} else {
+					buffergeometry.computeVertexNormals();
 
-				buffergeometry.computeVertexNormals();
+				}
 
-			}
+				if ( geometry.uvs.length > 0 ) {
 
-			if ( geometry.uvs.length > 0 ) {
+					buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) );
 
-				buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) );
+				}
 
-			}
+				// Create materials
 
-			// Create materials
+				var createdMaterials = [];
 
-			var createdMaterials = [];
+				for ( var mi = 0, miLen = materials.length; mi < miLen ; mi++ ) {
 
-			for ( var mi = 0, miLen = materials.length; mi < miLen ; mi++ ) {
+					var sourceMaterial = materials[ mi ];
+					var material = undefined;
 
-				var sourceMaterial = materials[mi];
-				var material = undefined;
+					if ( this.materials !== null ) {
 
-				if ( this.materials !== null ) {
+						material = this.materials.create( sourceMaterial.name );
 
-					material = this.materials.create( sourceMaterial.name );
+						// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
+						if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
 
-					// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
-					if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
+							var materialLine = new THREE.LineBasicMaterial();
+							materialLine.copy( material );
+							material = materialLine;
 
-						var materialLine = new THREE.LineBasicMaterial();
-						materialLine.copy( material );
-						material = materialLine;
+						}
 
 					}
 
-				}
+					if ( ! material ) {
 
-				if ( ! material ) {
+						material = ( ! isLine ? new THREE.MeshPhongMaterial() : new THREE.LineBasicMaterial() );
+						material.name = sourceMaterial.name;
 
-					material = ( ! isLine ? new THREE.MeshPhongMaterial() : new THREE.LineBasicMaterial() );
-					material.name = sourceMaterial.name;
+					}
+
+					material.shading = sourceMaterial.smooth ? THREE.SmoothShading : THREE.FlatShading;
+
+					createdMaterials.push(material);
 
 				}
 
-				material.shading = sourceMaterial.smooth ? THREE.SmoothShading : THREE.FlatShading;
+				// Create mesh
 
-				createdMaterials.push(material);
+				var mesh;
 
-			}
+				if ( createdMaterials.length > 1 ) {
 
-			// Create mesh
+					for ( var mi = 0, miLen = materials.length; mi < miLen ; mi++ ) {
 
-			var mesh;
+						var sourceMaterial = materials[ mi ];
+						buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
 
-			if ( createdMaterials.length > 1 ) {
+					}
 
-				for ( var mi = 0, miLen = materials.length; mi < miLen ; mi++ ) {
+					mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials ) : new THREE.LineSegments( buffergeometry, createdMaterials ) );
 
-					var sourceMaterial = materials[mi];
-					buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
+				} else {
 
+					mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] ) : new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] ) );
 				}
 
-				mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials ) : new THREE.LineSegments( buffergeometry, createdMaterials ) );
+				mesh.name = object.name;
 
-			} else {
+				container.add( mesh );
 
-				mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] ) : new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] ) );
 			}
 
-			mesh.name = object.name;
+			console.timeEnd( 'OBJLoader' );
 
-			container.add( mesh );
+			return container;
 
 		}
 
-		console.timeEnd( 'OBJLoader' );
-
-		return container;
+	};
 
-	}
+	return OBJLoader;
 
-};
+} )();