Selaa lähdekoodia

Merge pull request #8443 from jonnenauha/objloader

ObjLoader improvements
Mr.doob 9 vuotta sitten
vanhempi
commit
7c0b24629b
1 muutettua tiedostoa jossa 332 lisäystä ja 182 poistoa
  1. 332 182
      examples/js/loaders/OBJLoader.js

+ 332 - 182
examples/js/loaders/OBJLoader.js

@@ -8,6 +8,31 @@ THREE.OBJLoader = function ( manager ) {
 
 	this.materials = null;
 
+	this.regexp = {
+		// v float float float
+		vertex_pattern           : /^v\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/,
+		// vn float float float
+		normal_pattern           : /^vn\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/,
+		// vt float float
+		uv_pattern               : /^vt\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/,
+		// 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 = {
@@ -40,315 +65,431 @@ THREE.OBJLoader.prototype = {
 
 	},
 
-	parse: function ( text ) {
-
-		console.time( 'OBJLoader' );
+	_createParserState : function()
+	{
+		var state = {
+			objects  : [],
+			object   : {},
+
+			vertices : [],
+			normals  : [],
+			uvs      : [],
+
+			materialLibraries : [],
+
+			startObject : function(name, fromDeclaration)
+			{
+				// If the current object (initial from reset) is not from a g/o declaration in the parsed
+				// file. We need to use it for the first parsed g/o to keep things in sync.
+				if ( this.object && this.object.fromDeclaration === false ) {
+					this.object.name = name;
+					this.object.fromDeclaration = (fromDeclaration !== false);
+					return;
+				}
 
-		var objects = [];
-		var object;
-		var foundObjects = false;
-		var vertices = [];
-		var normals = [];
-		var uvs = [];
+				this.object = {
+					name : name || '',
+					geometry : {
+						vertices : [],
+						normals  : [],
+						uvs      : []
+					},
+					material : {
+						name   : '',
+						smooth : true
+					},
+					fromDeclaration : (fromDeclaration !== false)
+				};
+				this.objects.push(this.object);
+			},
 
-		function addObject( name ) {
+			parseVertexIndex : function( value, len ) {
 
-			var geometry = {
-				vertices: [],
-				normals: [],
-				uvs: []
-			};
+				var index = parseInt( value, 10 );
+				return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
 
-			var material = {
-				name: '',
-				smooth: true
-			};
+			},
 
-			object = {
-				name: name,
-				geometry: geometry,
-				material: material
-			};
+			parseNormalIndex : function( value, len ) {
 
-			objects.push( object );
+				var index = parseInt( value, 10 );
+				return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
 
-		}
+			},
 
-		function parseVertexIndex( value ) {
+			parseUVIndex : function( value, len ) {
 
-			var index = parseInt( value );
+				var index = parseInt( value, 10 );
+				return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
 
-			return ( index >= 0 ? index - 1 : index + vertices.length / 3 ) * 3;
+			},
 
-		}
+			addVertex : function( a, b, c ) {
 
-		function parseNormalIndex( value ) {
+				var src = this.vertices;
+				this.object.geometry.vertices.push(src[ a ]);
+				this.object.geometry.vertices.push(src[ a + 1 ]);
+				this.object.geometry.vertices.push(src[ a + 2 ]);
+				this.object.geometry.vertices.push(src[ b ]);
+				this.object.geometry.vertices.push(src[ b + 1 ]);
+				this.object.geometry.vertices.push(src[ b + 2 ]);
+				this.object.geometry.vertices.push(src[ c ]);
+				this.object.geometry.vertices.push(src[ c + 1 ]);
+				this.object.geometry.vertices.push(src[ c + 2 ]);
 
-			var index = parseInt( value );
+			},
 
-			return ( index >= 0 ? index - 1 : index + normals.length / 3 ) * 3;
+			addVertexLine : function( a )
+			{
 
-		}
+				var src = this.vertices;
+				this.object.geometry.vertices.push(src[ a ]);
+				this.object.geometry.vertices.push(src[ a + 1 ]);
+				this.object.geometry.vertices.push(src[ a + 2 ]);
 
-		function parseUVIndex( value ) {
+			},
 
-			var index = parseInt( value );
+			addNormal : function( a, b, c ) {
 
-			return ( index >= 0 ? index - 1 : index + uvs.length / 2 ) * 2;
+				var src = this.normals;
+				this.object.geometry.normals.push(src[ a ]);
+				this.object.geometry.normals.push(src[ a + 1 ]);
+				this.object.geometry.normals.push(src[ a + 2 ]);
+				this.object.geometry.normals.push(src[ b ]);
+				this.object.geometry.normals.push(src[ b + 1 ]);
+				this.object.geometry.normals.push(src[ b + 2 ]);
+				this.object.geometry.normals.push(src[ c ]);
+				this.object.geometry.normals.push(src[ c + 1 ]);
+				this.object.geometry.normals.push(src[ c + 2 ]);
 
-		}
+			},
 
-		function addVertex( a, b, c ) {
+			addUV : function( a, b, c ) {
 
-			object.geometry.vertices.push(
-				vertices[ a ], vertices[ a + 1 ], vertices[ a + 2 ],
-				vertices[ b ], vertices[ b + 1 ], vertices[ b + 2 ],
-				vertices[ c ], vertices[ c + 1 ], vertices[ c + 2 ]
-			);
+				var src = this.uvs;
+				this.object.geometry.uvs.push(src[ a ]);
+				this.object.geometry.uvs.push(src[ a + 1 ]);
+				this.object.geometry.uvs.push(src[ b ]);
+				this.object.geometry.uvs.push(src[ b + 1 ]);
+				this.object.geometry.uvs.push(src[ c ]);
+				this.object.geometry.uvs.push(src[ c + 1 ]);
 
-		}
+			},
 
-		function addNormal( a, b, c ) {
+			addUVLine : function( a ) {
 
-			object.geometry.normals.push(
-				normals[ a ], normals[ a + 1 ], normals[ a + 2 ],
-				normals[ b ], normals[ b + 1 ], normals[ b + 2 ],
-				normals[ c ], normals[ c + 1 ], normals[ c + 2 ]
-			);
+				var src = this.uvs;
+				this.object.geometry.uvs.push(src[ a ]);
+				this.object.geometry.uvs.push(src[ a + 1 ]);
 
-		}
+			},
 
-		function addUV( a, b, c ) {
+			addFace : function( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) {
 
-			object.geometry.uvs.push(
-				uvs[ a ], uvs[ a + 1 ],
-				uvs[ b ], uvs[ b + 1 ],
-				uvs[ c ], uvs[ c + 1 ]
-			);
+				var vLen = this.vertices.length;
 
-		}
+				var ia = this.parseVertexIndex( a, vLen );
+				var ib = this.parseVertexIndex( b, vLen );
+				var ic = this.parseVertexIndex( c, vLen );
+				var id;
 
-		function addFace( a, b, c, d,  ua, ub, uc, ud, na, nb, nc, nd ) {
+				if ( d === undefined ) {
 
-			var ia = parseVertexIndex( a );
-			var ib = parseVertexIndex( b );
-			var ic = parseVertexIndex( c );
-			var id;
+					this.addVertex( ia, ib, ic );
 
-			if ( d === undefined ) {
+				} else {
 
-				addVertex( ia, ib, ic );
+					id = this.parseVertexIndex( d, vLen );
 
-			} else {
+					this.addVertex( ia, ib, id );
+					this.addVertex( ib, ic, id );
 
-				id = parseVertexIndex( d );
+				}
 
-				addVertex( ia, ib, id );
-				addVertex( ib, ic, id );
+				if ( ua !== undefined ) {
 
-			}
+					var uvLen = this.uvs.length;
 
-			if ( ua !== undefined ) {
+					ia = this.parseUVIndex( ua, uvLen );
+					ib = this.parseUVIndex( ub, uvLen );
+					ic = this.parseUVIndex( uc, uvLen );
 
-				ia = parseUVIndex( ua );
-				ib = parseUVIndex( ub );
-				ic = parseUVIndex( uc );
+					if ( d === undefined ) {
 
-				if ( d === undefined ) {
+						this.addUV( ia, ib, ic );
 
-					addUV( ia, ib, ic );
+					} else {
 
-				} else {
+						id = this.parseUVIndex( ud, uvLen );
 
-					id = parseUVIndex( ud );
+						this.addUV( ia, ib, id );
+						this.addUV( ib, ic, id );
 
-					addUV( ia, ib, id );
-					addUV( ib, ic, id );
+					}
 
 				}
 
-			}
+				if ( na !== undefined ) {
 
-			if ( na !== undefined ) {
+					// Normals are many times the same. If so, skip function call and parseInt.
+					var nLen = this.normals.length;
+					ia = this.parseNormalIndex( na, nLen );
 
-				ia = parseNormalIndex( na );
-				ib = parseNormalIndex( nb );
-				ic = parseNormalIndex( nc );
+					if (na === nb)
+						ib = ia;
+					else
+						ib = this.parseNormalIndex( nb, nLen );
 
-				if ( d === undefined ) {
+					if (na === nc)
+						ic = ia;
+					else
+						ic = this.parseNormalIndex( nc, nLen );
 
-					addNormal( ia, ib, ic );
+					if ( d === undefined ) {
 
-				} else {
+						this.addNormal( ia, ib, ic );
+
+					} else {
+
+						id = this.parseNormalIndex( nd, nLen );
 
-					id = parseNormalIndex( nd );
+						this.addNormal( ia, ib, id );
+						this.addNormal( ib, ic, id );
 
-					addNormal( ia, ib, id );
-					addNormal( ib, ic, id );
+					}
 
 				}
 
+			},
+
+			addLineGeometry : function(vertexes, uvs)
+			{
+				this.object.geometry.type = 'Line';
+
+				var vLen = this.vertices.length;
+				var uvLen = this.uvs.length;
+
+				for (var vi = 0, l = vertexes.length; vi < l; vi++) {
+					this.addVertexLine( this.parseVertexIndex( vertexes[vi], vLen ) );
+				}
+				for (var uvi = 0, l = uvs.length; uvi < l; uvi++) {
+					this.addUVLine( this.parseUVIndex( uvs[uvi], uvLen ) );
+				}
+
 			}
+		};
 
-		}
+		state.startObject('', false);
+		return state;
+	},
+
+	parse: function ( text ) {
 
-		addObject( '' );
+		console.time( 'OBJLoader' );
 
-		// v float float float
-		var vertex_pattern = /^v\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/;
+		var state = this._createParserState();
 
-		// vn float float float
-		var normal_pattern = /^vn\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/;
+		if ( text.indexOf('\r\n') !== -1 ) {
+			// This is faster than String.split with regex that splits on both
+			text = text.replace('\r\n', '\n');
+		}
 
-		// vt float float
-		var uv_pattern = /^vt\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/;
+		var lines = text.split( '\n' );
+		var line = '', lineFirstChar = '', lineSecondChar = '';
+		var lineLength = 0;
+		var result = [];
 
-		// f vertex vertex vertex ...
-		var face_pattern1 = /^f\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?/;
+		// Faster to just trim left side of the line. Use if available.
+		var trimLeft = (typeof ''.trimLeft === 'function');
 
-		// f vertex/uv vertex/uv vertex/uv ...
-		var face_pattern2 = /^f\s+((-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+))(?:\s+((-?\d+)\/(-?\d+)))?/;
+		for ( var i = 0, l = lines.length; i < l; i ++ ) {
 
-		// f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...
-		var face_pattern3 = /^f\s+((-?\d+)\/(-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+)\/(-?\d+))(?:\s+((-?\d+)\/(-?\d+)\/(-?\d+)))?/;
+			line = lines[ i ];
+			if (trimLeft)
+				line = line.trimLeft();
+			else
+				line = line.trim();
 
-		// f vertex//normal vertex//normal vertex//normal ...
-		var face_pattern4 = /^f\s+((-?\d+)\/\/(-?\d+))\s+((-?\d+)\/\/(-?\d+))\s+((-?\d+)\/\/(-?\d+))(?:\s+((-?\d+)\/\/(-?\d+)))?/;
+			lineLength = line.length;
+			if ( lineLength === 0 ) {
+				continue;
+			}
 
-		var object_pattern = /^[og]\s*(.+)?/;
+			lineFirstChar = line.charAt( 0 );
+			if ( lineFirstChar === '#' ) {
+				// @todo invoke passed in handler if any
+				continue;
+			}
 
-		var smoothing_pattern = /^s\s+(\d+|on|off)/;
+			if ( lineFirstChar === 'v' ) {
 
-		//
+				lineSecondChar = line.charAt( 1 );
 
-		var lines = text.split( '\n' );
+				if ( lineSecondChar === ' ' && ( result = this.regexp.vertex_pattern.exec( line ) ) !== null ) {
 
-		for ( var i = 0; i < lines.length; i ++ ) {
+					// 0                  1      2      3
+					// ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
 
-			var line = lines[ i ];
-			line = line.trim();
+					state.vertices.push(
+						parseFloat( result[ 1 ] ),
+						parseFloat( result[ 2 ] ),
+						parseFloat( result[ 3 ] )
+					);
 
-			var result;
+				} else if ( lineSecondChar === 'n' && ( result = this.regexp.normal_pattern.exec( line ) ) !== null ) {
 
-			if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
+					// 0                   1      2      3
+					// ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
 
-				continue;
+					state.normals.push(
+						parseFloat( result[ 1 ] ),
+						parseFloat( result[ 2 ] ),
+						parseFloat( result[ 3 ] )
+					);
 
-			} else if ( ( result = vertex_pattern.exec( line ) ) !== null ) {
+				} else if ( lineSecondChar === 't' && ( result = this.regexp.uv_pattern.exec( line ) ) !== null ) {
 
-				// ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
+					// 0               1      2
+					// ["vt 0.1 0.2", "0.1", "0.2"]
 
-				vertices.push(
-					parseFloat( result[ 1 ] ),
-					parseFloat( result[ 2 ] ),
-					parseFloat( result[ 3 ] )
-				);
+					state.uvs.push(
+						parseFloat( result[ 1 ] ),
+						parseFloat( result[ 2 ] )
+					);
 
-			} else if ( ( result = normal_pattern.exec( line ) ) !== null ) {
+				} else {
 
-				// ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
+					throw new Error( "Unexpected vertex/normal/uv line: '" + line  + "'");
 
-				normals.push(
-					parseFloat( result[ 1 ] ),
-					parseFloat( result[ 2 ] ),
-					parseFloat( result[ 3 ] )
-				);
+				}
 
-			} else if ( ( result = uv_pattern.exec( line ) ) !== null ) {
+			} else if ( lineFirstChar === "f" ) {
 
-				// ["vt 0.1 0.2", "0.1", "0.2"]
+				if ( ( result = this.regexp.face_vertex_uv_normal.exec( line ) ) !== null ) {
 
-				uvs.push(
-					parseFloat( result[ 1 ] ),
-					parseFloat( result[ 2 ] )
-				);
+					// 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]
 
-			} else if ( ( result = face_pattern1.exec( line ) ) !== null ) {
+					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 ]
+					);
 
-				// ["f 1 2 3", "1", "2", "3", undefined]
+				} else if ( ( result = this.regexp.face_vertex_uv.exec( line ) ) !== null ) {
 
-				addFace(
-					result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ]
-				);
+					// 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]
 
-			} else if ( ( result = face_pattern2.exec( line ) ) !== null ) {
+					state.addFace(
+						result[ 1 ], result[ 3 ], result[ 5 ], result[ 7 ],
+						result[ 2 ], result[ 4 ], result[ 6 ], result[ 8 ]
+					);
 
-				// ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined]
+				} else if ( ( result = this.regexp.face_vertex_normal.exec( line ) ) !== null ) {
 
-				addFace(
-					result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ],
-					result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ]
-				);
+					// 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]
 
-			} else if ( ( result = face_pattern3.exec( line ) ) !== null ) {
+					state.addFace(
+						result[ 1 ], result[ 3 ], result[ 5 ], result[ 7 ],
+						undefined, undefined, undefined, undefined,
+						result[ 2 ], result[ 4 ], result[ 6 ], result[ 8 ]
+					);
 
-				// ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined]
+				} else if ( ( result = this.regexp.face_vertex.exec( line ) ) !== null ) {
 
-				addFace(
-					result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ],
-					result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ],
-					result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ]
-				);
+					// f vertex vertex vertex
+					// 0            1    2    3   4
+					// ["f 1 2 3", "1", "2", "3", undefined]
 
-			} else if ( ( result = face_pattern4.exec( line ) ) !== null ) {
+					state.addFace(
+						result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ]
+					);
 
-				// ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined]
+				} else {
 
-				addFace(
-					result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ],
-					undefined, undefined, undefined, undefined,
-					result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ]
-				);
+					throw new Error( "Unexpected face line: '" + line  + "'");
 
-			} else if ( ( result = object_pattern.exec( line ) ) !== null ) {
+				}
 
-				// o object_name
-				// or
-				// g group_name
+			} else if ( lineFirstChar === "l" ) {
 
-				var name = result[ 0 ].substr( 1 ).trim();
+				var lineParts = line.substring(1).trim().split(" ");
+				var lineVertexes = [], lineUVs = [];
 
-				if ( foundObjects === false ) {
+				if (line.indexOf("/") === -1) {
 
-					foundObjects = true;
-					object.name = name;
+					lineVertexes = lineParts;
 
 				} else {
+					for (var li = 0, llen = lineParts.length; li < llen; li++) {
 
-					addObject( name );
+						var parts = lineParts[li].split("/");
+						if (parts[0] !== "")
+							lineVertexes.push(parts[0]);
+						if (parts[1] !== "")
+							lineUVs.push(parts[1])
 
+					}
 				}
+				state.addLineGeometry(lineVertexes, lineUVs);
+
+			} else if ( ( result = this.regexp.object_pattern.exec( line ) ) !== null ) {
+
+				// o object_name
+				// or
+				// g group_name
+
+				var name = result[ 0 ].substr( 1 ).trim();
+				state.startObject(name);
 
-			} else if ( /^usemtl /.test( line ) ) {
+			} else if ( this.regexp.material_use_pattern.test( line ) ) {
 
 				// material
 
-				object.material.name = line.substring( 7 ).trim();
+				state.object.material.name = line.substring( 7 ).trim();
 
-			} else if ( /^mtllib /.test( line ) ) {
+			} else if ( this.regexp.material_library_pattern.test( line ) ) {
 
 				// mtl file
 
-			} else if ( ( result = smoothing_pattern.exec( line ) ) !== null ) {
+				state.materialLibraries.push( line.substring( 7 ).trim() );
+
+			} else if ( ( result = this.regexp.smoothing_pattern.exec( line ) ) !== null ) {
 
 				// smooth shading
 
-				object.material.smooth = result[ 1 ] === "1" || result[ 1 ] === "on";
+				var value = result[ 1 ].trim().toLowerCase();
+				state.object.material.smooth = ( value === '1' || value === 'on' );
 
 			} else {
 
-				throw new Error( "Unexpected line: " + line );
+				// Handle null terminated files without exception
+				if (line === '\0')
+					continue;
+
+				throw new Error( "Unexpected line: '" + line  + "'");
 
 			}
 
 		}
 
 		var container = new THREE.Group();
+		container.materialLibraries = [].concat(state.materialLibraries);
 
-		for ( var i = 0, l = objects.length; i < l; i ++ ) {
+		for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
 
-			object = objects[ i ];
+			var object = state.objects[ i ];
 			var geometry = object.geometry;
+			var isLine = (geometry.type === 'Line');
+
+			// Skip o/g line declarations that did not follow with any faces
+			if ( geometry.vertices.length === 0 )
+				continue;
 
 			var buffergeometry = new THREE.BufferGeometry();
 
@@ -376,18 +517,27 @@ THREE.OBJLoader.prototype = {
 
 				material = this.materials.create( object.material.name );
 
+				// 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;
+
+				}
+
 			}
 
 			if ( !material ) {
 
-				material = new THREE.MeshPhongMaterial();
+				material = ( !isLine ? new THREE.MeshPhongMaterial() : new THREE.LineBasicMaterial() );
 				material.name = object.material.name;
 
 			}
 
 			material.shading = object.material.smooth ? THREE.SmoothShading : THREE.FlatShading;
 
-			var mesh = new THREE.Mesh( buffergeometry, material );
+			var mesh = ( !isLine ? new THREE.Mesh( buffergeometry, material ) : new THREE.Line( buffergeometry, material ) );
 			mesh.name = object.name;
 
 			container.add( mesh );