Browse Source

Removed jquery dependencies from MTLLoader and OBJMTLLoader. Cleaned up code a bit, reformatted whitespace. Added example.

See #2399
alteredq 13 years ago
parent
commit
12eecef692
3 changed files with 612 additions and 159 deletions
  1. 246 93
      examples/js/loaders/MTLLoader.js
  2. 221 66
      examples/js/loaders/OBJMTLLoader.js
  3. 145 0
      examples/webgl_loader_obj_mtl.html

+ 246 - 93
examples/js/loaders/MTLLoader.js

@@ -4,10 +4,12 @@
  * @author angelxuanchang
  */
 
-THREE.MTLLoader = function(baseUrl, options) {
+THREE.MTLLoader = function( baseUrl, options ) {
+
     THREE.EventTarget.call( this );
     this.baseUrl = baseUrl;
     this.options = options;
+
 };
 
 THREE.MTLLoader.prototype = {
@@ -22,29 +24,42 @@ THREE.MTLLoader.prototype = {
      *
      * @param url - location of MTL file
      */
-    load: function(url) {
+    load: function( url ) {
+
         var scope = this;
         var xhr = new XMLHttpRequest();
 
-        function onloaded(event) {
-            if (event.target.status === 200 || event.target.status === 0) {
-                var materialCreator = scope.parse(event.target.responseText);
+        function onloaded( event ) {
+
+            if ( event.target.status === 200 || event.target.status === 0 ) {
+
+                var materialCreator = scope.parse( event.target.responseText );
+
                 // Notify caller, that I'm done
+
                 scope.dispatchEvent( { type: 'load', content: materialCreator } );
+
             } else {
+
                 scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']',
                     response: event.target.responseText } );
+
             }
+
         }
 
         xhr.addEventListener( 'load', onloaded, false );
 
         xhr.addEventListener( 'progress', function ( event ) {
+
             scope.dispatchEvent( { type: 'progress', loaded: event.loaded, total: event.total } );
+
         }, false );
 
         xhr.addEventListener( 'error', function () {
+
             scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } );
+
         }, false );
 
         xhr.open( 'GET', url, true );
@@ -56,41 +71,63 @@ THREE.MTLLoader.prototype = {
      * @param text - Content of MTL file
      * @return {THREE.MTLLoader.MaterialCreator}
      */
-    parse: function(text) {
-        var lines = text.split("\n");
+    parse: function( text ) {
+
+        var lines = text.split( "\n" );
         var info = {};
         var delimiter_pattern = /\s+/;
         var materialsInfo = {};
-        for (var i = 0; i < lines.length; i++) {
-            var line = lines[i];
-            line = $.trim(line);
-            if (line.length == 0 || line.charAt(0) == '#') {
+
+			for ( var i = 0; i < lines.length; i ++ ) {
+
+			var line = lines[ i ];
+            line = line.trim();
+
+            if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
+
                 // Blank line or comment ignore
                 continue;
+
             }
 
-            var pos = line.indexOf(' ');
-            var key = (pos >= 0)? line.substring(0,pos):line;
+            var pos = line.indexOf( ' ' );
+
+			var key = ( pos >= 0 ) ? line.substring( 0, pos) : line;
             key = key.toLowerCase();
-            var value = (pos >= 0)? line.substring(pos+1):"";
-            value = $.trim(value);
-            if (key === "newmtl") {
+
+            var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : "";
+            value = value.trim();
+
+            if ( key === "newmtl" ) {
+
                 // New material
+
                 info = { name: value };
-                materialsInfo[value] = info;
-            } else if (info) {
-                if (key === "ka" || key === "kd" || key === "ks") {
-                    var ss = value.split(delimiter_pattern, 3);
-                    info[key] = [ parseFloat(ss[0]), parseFloat(ss[1]), parseFloat(ss[2])];
+                materialsInfo[ value ] = info;
+
+            } else if ( info ) {
+
+                if ( key === "ka" || key === "kd" || key === "ks" ) {
+
+                    var ss = value.split( delimiter_pattern, 3 );
+                    info[ key ] = [ parseFloat( ss[0] ), parseFloat( ss[1] ), parseFloat( ss[2] ) ];
+
                 } else {
-                    info[key] = value;
+
+                    info[ key ] = value;
+
                 }
+
             }
+
         }
-        var materialCreator = new THREE.MTLLoader.MaterialCreator(this.baseUrl, this.options);
-        materialCreator.setMaterials(materialsInfo);
+
+        var materialCreator = new THREE.MTLLoader.MaterialCreator( this.baseUrl, this.options );
+        materialCreator.setMaterials( materialsInfo );
         return materialCreator;
+
     }
+
 };
 
 /**
@@ -109,7 +146,8 @@ THREE.MTLLoader.prototype = {
  *                                      Default: false (d = 1 is fully opaque)
  * @constructor
  */
-THREE.MTLLoader.MaterialCreator = function(baseUrl, options) {
+THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) {
+
     THREE.EventTarget.call( this );
     this.baseUrl = baseUrl;
     this.options = options;
@@ -118,194 +156,309 @@ THREE.MTLLoader.MaterialCreator = function(baseUrl, options) {
     this.materialsArray = [];
     this.nameLookup = {};
 
-    this.side = (this.options && this.options.side)? this.options.side: THREE.FrontSide;
-    this.wrap = (this.options && this.options.wrap)? this.options.wrap: THREE.RepeatWrapping;
+    this.side = ( this.options && this.options.side )? this.options.side: THREE.FrontSide;
+    this.wrap = ( this.options && this.options.wrap )? this.options.wrap: THREE.RepeatWrapping;
+
 };
 
 THREE.MTLLoader.MaterialCreator.prototype = {
-    setMaterials: function(materialsInfo) {
-        this.materialsInfo = this.convert(materialsInfo);
+
+    setMaterials: function( materialsInfo ) {
+
+        this.materialsInfo = this.convert( materialsInfo );
         this.materials = {};
         this.materialsArray = [];
         this.nameLookup = {};
+
     },
 
-    convert: function(materialsInfo) {
-        if (!this.options) return materialsInfo;
+    convert: function( materialsInfo ) {
+
+        if ( !this.options ) return materialsInfo;
+
         var converted = {};
-        for (var mn in materialsInfo) {
+
+        for ( var mn in materialsInfo ) {
+
             // Convert materials info into normalized form based on options
-            var mat = materialsInfo[mn];
+
+            var mat = materialsInfo[ mn ];
+
             var covmat = {};
-            converted[mn] = covmat;
-            for (var prop in mat) {
+
+            converted[ mn ] = covmat;
+
+            for ( var prop in mat ) {
+
                 var save = true;
-                var value = mat[prop];
+                var value = mat[ prop ];
                 var lprop = prop.toLowerCase();
-                switch (lprop) {
+
+                switch ( lprop ) {
+
                     case 'kd':
                     case 'ka':
                     case 'ks':
+
                         // Diffuse color (color under white light) using RGB values
-                        if (this.options && this.options.normalizeRGB) {
+
+                        if ( this.options && this.options.normalizeRGB ) {
+
                             value =  [ value[0]/255, value[1]/255, value[2]/255 ];
+
                         }
-                        if (this.options && this.options.ignoreZeroRGBs) {
-                            if (value[0] === 0.0 && value[1] === 0.0 && value[1] === 0.0) {
+
+                        if ( this.options && this.options.ignoreZeroRGBs ) {
+
+                            if ( value[0] === 0.0 && value[1] === 0.0 && value[1] === 0.0 ) {
+
                                 // ignore
+
                                 save = false;
+
                             }
                         }
+
                         break;
+
                     case 'd':
+
                         // According to MTL format (http://paulbourke.net/dataformats/mtl/):
                         //   d is dissolve for current material
                         //   factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent)
-                        if (this.options && this.options.invertTransparency) {
+
+                        if ( this.options && this.options.invertTransparency ) {
+
                             value = 1 - value;
+
                         }
+
                         break;
+
                     default:
+
                         break;
                 }
-                if (save) {
+
+                if ( save ) {
+
                     covmat[lprop] = value;
+
                 }
+
             }
+
         }
+
         return converted;
+
     },
 
     preload: function () {
-        for (var mn in this.materialsInfo) {
-            this.create(mn);
+
+        for ( var mn in this.materialsInfo ) {
+
+            this.create( mn );
+
         }
+
     },
 
-    getIndex: function(materialName) {
-        return this.nameLookup[materialName];
+    getIndex: function( materialName ) {
+
+        return this.nameLookup[ materialName ];
+
     },
 
     getAsArray: function() {
+
         var index = 0;
-        for (var mn in this.materialsInfo) {
-            this.materialsArray[index] = this.create(mn);
-            this.nameLookup[mn] = index;
-            index++;
+
+        for ( var mn in this.materialsInfo ) {
+
+            this.materialsArray[ index ] = this.create( mn );
+            this.nameLookup[ mn ] = index;
+            index ++;
+
         }
+
         return this.materialsArray;
+
     },
 
-    create: function (materialName) {
-        if (this.materials[materialName] == undefined) {
-            this.createMaterial_(materialName);
+    create: function ( materialName ) {
+
+        if ( this.materials[ materialName ] === undefined ) {
+
+            this.createMaterial_( materialName );
+
         }
-        return this.materials[materialName];
+
+        return this.materials[ materialName ];
+
     },
 
-    createMaterial_: function (materialName) {
+    createMaterial_: function ( materialName ) {
+
         // Create material
-        var mat = this.materialsInfo[materialName];
+
+        var mat = this.materialsInfo[ materialName ];
         var params = {
+
             name: materialName,
             side: this.side
+
         };
-        for (var prop in mat) {
-            var value = mat[prop];
-            switch (prop.toLowerCase()) {
+
+        for ( var prop in mat ) {
+
+            var value = mat[ prop ];
+
+            switch ( prop.toLowerCase() ) {
+
                 // Ns is material specular exponent
+
                 case 'kd':
-                    // Diffuse color (color under white light) using RGB values
-                    params['diffuse'] = new THREE.Color().setRGB(value[0], value[1], value[2]);
-                    break;
+
+					// Diffuse color (color under white light) using RGB values
+
+					params[ 'diffuse' ] = new THREE.Color().setRGB( value[0], value[1], value[2] );
+
+					break;
+
                 case 'ka':
+
                     // Ambient color (color under shadow) using RGB values
-                    params['ambient'] = new THREE.Color().setRGB(value[0], value[1], value[2]);
-                    break;
+
+                    params[ 'ambient' ] = new THREE.Color().setRGB( value[0], value[1], value[2] );
+
+					break;
+
                 case 'ks':
-                    // specular color (color when light is reflected from shiny surface) using RGB values
-                    params['specular'] = new THREE.Color().setRGB(value[0], value[1], value[2]);
+
+                    // Specular color (color when light is reflected from shiny surface) using RGB values
+                    params[ 'specular' ] = new THREE.Color().setRGB( value[0], value[1], value[2] );
+
                     break;
+
                 case 'map_kd':
+
                     // Diffuse texture map
-                    params['map'] = THREE.MTLLoader.loadTexture( this.baseUrl + value );
-                    params['map'].wrapS = this.wrap;
-                    params['map'].wrapT = this.wrap;
-                    break;
+
+                    params[ 'map' ] = THREE.MTLLoader.loadTexture( this.baseUrl + value );
+                    params[ 'map' ].wrapS = this.wrap;
+                    params[ 'map' ].wrapT = this.wrap;
+
+					break;
+
                 case 'ns':
+
                     // The specular exponent (defines the focus of the specular highlight)
                     // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
-                    params['shininess'] = value;
-                    break;
+
+					params['shininess'] = value;
+
+					break;
+
                 case 'd':
+
                     // According to MTL format (http://paulbourke.net/dataformats/mtl/):
                     //   d is dissolve for current material
                     //   factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent)
-                    if (value < 1) {
+
+                    if ( value < 1 ) {
+
                         params['transparent'] = true;
                         params['opacity'] = value;
+
                     }
+
                     break;
+
                 default:
                     break;
+
             }
+
         }
-        if (params['diffuse']) {
-            if (!params['ambient']) params['ambient'] = params['diffuse'];
-            params['color'] = params['diffuse'];
+
+        if ( params[ 'diffuse' ] ) {
+
+            if ( !params[ 'ambient' ]) params[ 'ambient' ] = params[ 'diffuse' ];
+            params[ 'color' ] = params[ 'diffuse' ];
+
         }
-        this.materials[materialName] = new THREE.MeshPhongMaterial(params);
-        return this.materials[materialName];
+
+        this.materials[ materialName ] = new THREE.MeshPhongMaterial( params );
+        return this.materials[ materialName ];
+
     }
+
 };
 
 THREE.MTLLoader.loadTexture = function ( url, mapping, onLoad, onError ) {
+
         var image = new Image();
         var texture = new THREE.Texture( image, mapping );
 
         var loader = new THREE.ImageLoader();
 
         loader.addEventListener( 'load', function ( event ) {
-            texture.image = THREE.MTLLoader.ensurePowerOfTwo_(event.content);
+
+            texture.image = THREE.MTLLoader.ensurePowerOfTwo_( event.content );
             texture.needsUpdate = true;
             if ( onLoad ) onLoad( texture );
+
         } );
 
         loader.addEventListener( 'error', function ( event ) {
+
             if ( onError ) onError( event.message );
+
         } );
 
         loader.crossOrigin = this.crossOrigin;
         loader.load( url, image );
 
         return texture;
+
     };
 
-THREE.MTLLoader.ensurePowerOfTwo_ = function (image)
-{
-    if (!THREE.MTLLoader.isPowerOfTwo_(image.width) || !THREE.MTLLoader.isPowerOfTwo_(image.height))
-    {
-        var canvas = document.createElement("canvas");
-        canvas.width = THREE.MTLLoader.nextHighestPowerOfTwo_(image.width);
-        canvas.height = THREE.MTLLoader.nextHighestPowerOfTwo_(image.height);
+THREE.MTLLoader.ensurePowerOfTwo_ = function ( image ) {
+
+    if ( ! THREE.MTLLoader.isPowerOfTwo_( image.width ) || ! THREE.MTLLoader.isPowerOfTwo_( image.height ) ) {
+
+        var canvas = document.createElement( "canvas" );
+        canvas.width = THREE.MTLLoader.nextHighestPowerOfTwo_( image.width );
+        canvas.height = THREE.MTLLoader.nextHighestPowerOfTwo_( image.height );
+
         var ctx = canvas.getContext("2d");
-        ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
+        ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );
         return canvas;
+
     }
+
     return image;
+
 };
 
-THREE.MTLLoader.isPowerOfTwo_ = function (x)
-{
-    return (x & (x - 1)) == 0;
+THREE.MTLLoader.isPowerOfTwo_ = function ( x ) {
+
+	return ( x & ( x - 1 ) ) === 0;
+
 };
 
-THREE.MTLLoader.nextHighestPowerOfTwo_ = function(x)
-{
+THREE.MTLLoader.nextHighestPowerOfTwo_ = function( x ) {
+
     --x;
-    for (var i = 1; i < 32; i <<= 1) {
+
+    for ( var i = 1; i < 32; i <<= 1 ) {
+
         x = x | x >> i;
+
     }
+
     return x + 1;
+
 };
 

+ 221 - 66
examples/js/loaders/OBJMTLLoader.js

@@ -6,7 +6,9 @@
  */
 
 THREE.OBJMTLLoader = function ( ) {
+
 	THREE.EventTarget.call( this );
+
 };
 
 THREE.OBJMTLLoader.prototype = {
@@ -27,6 +29,7 @@ THREE.OBJMTLLoader.prototype = {
      * @param options - Options on how to interpret the material (see THREE.MTLLoader.MaterialCreator )
      *
      */
+
 	load: function ( url, mtlfileurl, options ) {
 
 		var scope = this;
@@ -37,78 +40,125 @@ THREE.OBJMTLLoader.prototype = {
         var materialsCreator;  // Material creator is created when MTL file is loaded
 
         // Loader for MTL
-        var mtlLoader = new THREE.MTLLoader(url.substr(0,url.lastIndexOf("/")+1), options);
+
+        var mtlLoader = new THREE.MTLLoader( url.substr( 0, url.lastIndexOf( "/" ) + 1 ), options );
         mtlLoader.addEventListener( 'load', waitReady );
         mtlLoader.addEventListener( 'error', waitReady );
+
         // Try to load mtlfile
-        if (mtlfileurl) {
-            mtlLoader.load(mtlfileurl);
+
+        if ( mtlfileurl ) {
+
+            mtlLoader.load( mtlfileurl );
             mtlDone = false;
+
         } else {
+
             mtlDone = true;
+
         }
 
-        function waitReady(event) {
-            if (event.type === 'load') {
-                if (event.content instanceof THREE.MTLLoader.MaterialCreator) {
+        function waitReady( event ) {
+
+            if ( event.type === 'load' ) {
+
+                if ( event.content instanceof THREE.MTLLoader.MaterialCreator ) {
+
                     // MTL file is loaded
+
                     mtlDone = true;
                     materialsCreator = event.content;
                     materialsCreator.preload();
+
                 } else {
+
                     // OBJ file is loaded
-                    if (event.target.status == 200 || event.target.status == 0) {
+
+                    if ( event.target.status === 200 || event.target.status === 0 ) {
+
                         var objContent = event.target.responseText;
-                        if (mtlfileurl) {
+
+                        if ( mtlfileurl ) {
+
                             // Parse with passed in MTL file
-                            obj3d = scope.parse(objContent);
+
+                            obj3d = scope.parse( objContent );
+
                         } else {
+
                             // No passed in MTL file, look for mtlfile in obj file
-                            obj3d = scope.parse(objContent, function(mtlfile) {
+
+                            obj3d = scope.parse( objContent, function( mtlfile ) {
+
                                 mtlDone = false;
-                                mtlLoader.load(mtlLoader.baseUrl + mtlfile);
-                            });
+                                mtlLoader.load( mtlLoader.baseUrl + mtlfile );
+
+                            } );
+
                         }
+
                     } else {
+
                         // Error loading OBJ file....
+
                         scope.dispatchEvent( {
                             type: 'error',
                             message: 'Couldn\'t load URL [' + url + ']',
                             response: event.target.responseText } );
+
                     }
+
                 }
-            } else if (event.type === 'error') {
-                // MTL failed to load -- oh well, we will just not have material...
+
+            } else if ( event.type === 'error' ) {
+
+                // MTL failed to load -- oh well, we will just not have material ...
+
                 mtlDone = true;
+
             }
-            if (mtlDone && obj3d) {
+
+            if ( mtlDone && obj3d ) {
+
                 // MTL file is loaded and OBJ file is loaded
                 // Apply materials to model
-                if (materialsCreator) {
-                    THREE.SceneUtils.traverseHierarchy(
-                        obj3d, function(node) {
-                            if (node instanceof THREE.Mesh) {
-                                if (node.material.name) {
-                                    var material = materialsCreator.create(node.material.name);
-                                    if (material) node.material = material;
-                                }
-                            }
-                        }
-                    )
+
+                if ( materialsCreator ) {
+
+                    THREE.SceneUtils.traverseHierarchy( obj3d, function( node ) {
+
+						if ( node instanceof THREE.Mesh ) {
+
+							if ( node.material.name ) {
+
+								var material = materialsCreator.create( node.material.name );
+								if  (material ) node.material = material;
+							}
+						}
+
+					} );
+
                 }
+
                 // Notify listeners
+
                 scope.dispatchEvent( { type: 'load', content: obj3d } );
             }
+
         }
 
         xhr.addEventListener( 'load', waitReady, false );
 
 		xhr.addEventListener( 'progress', function ( event ) {
+
 			scope.dispatchEvent( { type: 'progress', loaded: event.loaded, total: event.total } );
+
 		}, false );
 
 		xhr.addEventListener( 'error', function () {
+
 			scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } );
+
 		}, false );
 
 		xhr.open( 'GET', url, true );
@@ -125,26 +175,36 @@ THREE.OBJMTLLoader.prototype = {
 	parse: function ( data, mtllibCallback ) {
 
         function vector( x, y, z ) {
+
 			return new THREE.Vector3( x, y, z );
+
 		}
 
 		function uv( u, v ) {
+
 			return new THREE.UV( u, v );
+
 		}
 
 		function face3( a, b, c, normals ) {
+
 			return new THREE.Face3( a, b, c, normals );
+
 		}
 
 		function face4( a, b, c, d, normals ) {
+
 			return new THREE.Face4( a, b, c, d, normals );
+
 		}
 
         function finalize_mesh( group, mesh_info ) {
+
             mesh_info.geometry.computeCentroids();
             mesh_info.geometry.computeFaceNormals();
             mesh_info.geometry.computeBoundingSphere();
             group.add( new THREE.Mesh( mesh_info.geometry, mesh_info.material ) );
+
         }
 
 		var vertices = [];
@@ -152,79 +212,124 @@ THREE.OBJMTLLoader.prototype = {
 		var uvs = [];
 
         // v float float float
+
         var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( [\d|\.|\+|\-|e]+)( [\d|\.|\+|\-|e]+)/;
-        // vn float float float
+
+		// vn float float float
+
         var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( [\d|\.|\+|\-|e]+)( [\d|\.|\+|\-|e]+)/;
-        // vt float float
+
+		// vt float float
+
         var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( [\d|\.|\+|\-|e]+)/;
-        // f vertex vertex vertex ...
+
+		// f vertex vertex vertex ...
+
         var face_pattern1 = /f( +[\d]+)( [\d]+)( [\d]+)( [\d]+)?/;
-        // f vertex/uv vertex/uv vertex/uv ...
+
+		// f vertex/uv vertex/uv vertex/uv ...
+
         var face_pattern2 = /f( +([\d]+)\/([\d]+))( ([\d]+)\/([\d]+))( ([\d]+)\/([\d]+))( ([\d]+)\/([\d]+))?/;
-        // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...
+
+		// f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...
+
         var face_pattern3 = /f( +([\d]+)\/([\d]+)\/([\d]+))( ([\d]+)\/([\d]+)\/([\d]+))( ([\d]+)\/([\d]+)\/([\d]+))( ([\d]+)\/([\d]+)\/([\d]+))?/;
-        // f vertex//normal vertex//normal vertex//normal ...
+
+		// f vertex//normal vertex//normal vertex//normal ...
+
         var face_pattern4 = /f( +([\d]+)\/\/([\d]+))( ([\d]+)\/\/([\d]+))( ([\d]+)\/\/([\d]+))( ([\d]+)\/\/([\d]+))?/;
 
         var final_model = new THREE.Object3D();
-        var geometry = new THREE.Geometry();
+
+		var geometry = new THREE.Geometry();
         geometry.vertices = vertices;
-        var cur_mesh = {
+
+		var cur_mesh = {
+
             material: new THREE.MeshLambertMaterial(),
             geometry: geometry
+
         };
-        var lines = data.split("\n");
-        for (var i = 0; i < lines.length; i++) {
-            var line = lines[i];
-            line = $.trim(line);
+
+        var lines = data.split( "\n" );
+
+        for ( var i = 0; i < lines.length; i ++ ) {
+
+            var line = lines[ i ];
+            line = line.trim();
+
             // temporary variable storing pattern matching result
+
             var result;
-            if (line.length === 0 || line.charAt(0) === '#') {
+
+            if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
+
                 continue;
-            } else if ((result = vertex_pattern.exec(line)) != null) {
+
+            } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) {
+
                 // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
+
                 vertices.push( vector(
                     parseFloat( result[ 1 ] ),
                     parseFloat( result[ 2 ] ),
                     parseFloat( result[ 3 ] )
                 ) );
-            } else if ((result = normal_pattern.exec(line)) != null) {
-                // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
-                normals.push( vector(
+
+            } else if ( ( result = normal_pattern.exec( line ) ) !== null ) {
+
+				// ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
+
+				normals.push( vector(
                     parseFloat( result[ 1 ] ),
                     parseFloat( result[ 2 ] ),
                     parseFloat( result[ 3 ] )
                 ) );
-            } else if (( result = uv_pattern.exec(line)) != null) {
+
+            } else if ( ( result = uv_pattern.exec( line ) ) !== null ) {
+
                 // ["vt 0.1 0.2", "0.1", "0.2"]
+
                 uvs.push( uv(
                     parseFloat( result[ 1 ] ),
                     parseFloat( result[ 2 ] )
                 ) );
-            } else if (( result = face_pattern1.exec( line ) ) != null ) {
+
+            } else if ( ( result = face_pattern1.exec( line ) ) !== null ) {
+
                 // ["f 1 2 3", "1", "2", "3", undefined]
+
                 if ( result[ 4 ] === undefined ) {
+
                     geometry.faces.push( face3(
                         parseInt( result[ 1 ] ) - 1,
                         parseInt( result[ 2 ] ) - 1,
                         parseInt( result[ 3 ] ) - 1
                     ) );
+
                 } else {
+
                     geometry.faces.push( face4(
                         parseInt( result[ 1 ] ) - 1,
                         parseInt( result[ 2 ] ) - 1,
                         parseInt( result[ 3 ] ) - 1,
                         parseInt( result[ 4 ] ) - 1
                     ) );
+
                 }
-            } else if ( ( result = face_pattern2.exec( line ) ) != null ) {
+
+            } else if ( ( result = face_pattern2.exec( line ) ) !== null ) {
+
                 // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined]
+
                 if ( result[ 10 ] === undefined ) {
+
                     geometry.faces.push( face3(
                         parseInt( result[ 2 ] ) - 1,
                         parseInt( result[ 5 ] ) - 1,
                         parseInt( result[ 8 ] ) - 1
                     ) );
+
                     geometry.faceVertexUvs[ 0 ].push( [
                         uvs[ parseInt( result[ 3 ] ) - 1 ],
                         uvs[ parseInt( result[ 6 ] ) - 1 ],
@@ -232,22 +337,29 @@ THREE.OBJMTLLoader.prototype = {
                     ] );
 
                 } else {
+
                     geometry.faces.push( face4(
                         parseInt( result[ 2 ] ) - 1,
                         parseInt( result[ 5 ] ) - 1,
                         parseInt( result[ 8 ] ) - 1,
                         parseInt( result[ 11 ] ) - 1
                     ) );
+
                     geometry.faceVertexUvs[ 0 ].push( [
                         uvs[ parseInt( result[ 3 ] ) - 1 ],
                         uvs[ parseInt( result[ 6 ] ) - 1 ],
                         uvs[ parseInt( result[ 9 ] ) - 1 ],
                         uvs[ parseInt( result[ 12 ] ) - 1 ]
                     ] );
+
                 }
-            } else if ( ( result = face_pattern3.exec( line ) ) != null ) {
+
+            } else if ( ( result = face_pattern3.exec( line ) ) !== null ) {
+
                 // ["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]
+
                 if ( result[ 13 ] === undefined ) {
+
                     geometry.faces.push( face3(
                         parseInt( result[ 2 ] ) - 1,
                         parseInt( result[ 6 ] ) - 1,
@@ -258,12 +370,15 @@ THREE.OBJMTLLoader.prototype = {
                             normals[ parseInt( result[ 12 ] ) - 1 ]
                         ]
                     ) );
+
                     geometry.faceVertexUvs[ 0 ].push( [
                         uvs[ parseInt( result[ 3 ] ) - 1 ],
                         uvs[ parseInt( result[ 7 ] ) - 1 ],
                         uvs[ parseInt( result[ 11 ] ) - 1 ]
                     ] );
+
                 } else {
+
                     geometry.faces.push( face4(
                         parseInt( result[ 2 ] ) - 1,
                         parseInt( result[ 6 ] ) - 1,
@@ -276,6 +391,7 @@ THREE.OBJMTLLoader.prototype = {
                             normals[ parseInt( result[ 16 ] ) - 1 ]
                         ]
                     ) );
+
                     geometry.faceVertexUvs[ 0 ].push( [
                         uvs[ parseInt( result[ 3 ] ) - 1 ],
                         uvs[ parseInt( result[ 7 ] ) - 1 ],
@@ -284,9 +400,13 @@ THREE.OBJMTLLoader.prototype = {
                     ] );
 
                 }
-            } else if ( ( result = face_pattern4.exec( line ) ) != null ) {
+
+            } else if ( ( result = face_pattern4.exec( line ) ) !== null ) {
+
                 // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined]
+
                 if ( result[ 10 ] === undefined ) {
+
                     geometry.faces.push( face3(
                         parseInt( result[ 2 ] ) - 1,
                         parseInt( result[ 5 ] ) - 1,
@@ -297,7 +417,9 @@ THREE.OBJMTLLoader.prototype = {
                             normals[ parseInt( result[ 9 ] ) - 1 ]
                         ]
                     ) );
+
                 } else {
+
                     geometry.faces.push( face4(
                         parseInt( result[ 2 ] ) - 1,
                         parseInt( result[ 5 ] ) - 1,
@@ -310,31 +432,44 @@ THREE.OBJMTLLoader.prototype = {
                             normals[ parseInt( result[ 12 ] ) - 1 ]
                         ]
                     ) );
+
                 }
-            } else if (line.startsWith("usemtl ")) {
-                var material_name = line.substring(7);
-                material_name = $.trim(material_name);
+
+            } else if ( line.startsWith( "usemtl " ) ) {
+
+                var material_name = line.substring( 7 );
+                material_name = material_name.trim();
+
                 var material = new THREE.MeshLambertMaterial();
                 material.name = material_name;
-                if (geometry.faces.length > 0) {
+
+                if ( geometry.faces.length > 0 ) {
+
                     // Finalize previous geometry and add to model
-                    finalize_mesh(final_model, cur_mesh);
+
+                    finalize_mesh( final_model, cur_mesh );
                     geometry = new THREE.Geometry();
                     geometry.vertices = vertices;
-                    cur_mesh = {
-                        geometry: geometry
-                    };
+
+                    cur_mesh = {  geometry: geometry };
+
                 }
+
                 cur_mesh.material = material;
-                //material_index = materialsCreator.getIndex(material_name);
-            } else if (line.startsWith("g ")) {
+                //material_index = materialsCreator.getIndex( material_name );
+
+            } else if ( line.startsWith( "g " ) ) {
+
                 // Polygon group for object
-                var group_name = line.substring(2);
-                group_name = $.trim(group_name);
-            } else if (line.startsWith("o ")) {
+
+                var group_name = line.substring( 2 );
+                group_name = group_name.trim();
+
+            } else if ( line.startsWith( "o " ) ) {
+
                 // Object
                 var object_name = line.substring(2);
-                object_name = $.trim(object_name);
+                //object_name = $.trim(object_name);
             } else if (line.startsWith("s ")) {
                 // Smooth shading
             } else if (line.startsWith("mtllib ")) {
@@ -353,8 +488,28 @@ THREE.OBJMTLLoader.prototype = {
 	}
 };
 
-if (typeof String.prototype.startsWith != 'function') {
-    String.prototype.startsWith = function (str){
-        return this.slice(0, str.length) == str;
+// Shims of "startsWith" and "trim" for old browsers
+// not sure we should have this, or at least not have it here
+
+// http://stackoverflow.com/questions/646628/javascript-startswith
+// http://stackoverflow.com/questions/498970/how-do-i-trim-a-string-in-javascript
+
+if ( ! String.prototype.startsWith ) {
+
+    String.prototype.startsWith = function ( str ) {
+
+        return this.slice( 0, str.length ) === str;
+
     };
-}
+
+}
+
+if ( ! String.prototype.trim ) {
+
+   String.prototype.trim = function () {
+
+	   return this.replace( /^\s+|\s+$/g, "" );
+
+	};
+
+}

+ 145 - 0
examples/webgl_loader_obj_mtl.html

@@ -0,0 +1,145 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - loaders - OBJ MTL loader</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #000;
+				color: #fff;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display:block;
+			}
+			#info a, .button { color: #f00; font-weight: bold; text-decoration: underline; cursor: pointer }
+		</style>
+	</head>
+
+	<body>
+		<div id="info">
+		<a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a> - OBJMTLLoader test
+		</div>
+
+		<script src="../build/three.min.js"></script>
+
+		<script src="js/loaders/MTLLoader.js"></script>
+		<script src="js/loaders/OBJMTLLoader.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/Stats.js"></script>
+
+		<script>
+
+			var container, stats;
+
+			var camera, scene, renderer;
+
+			var mouseX = 0, mouseY = 0;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+
+
+			init();
+			animate();
+
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
+				camera.position.z = 100;
+
+				// scene
+
+				scene = new THREE.Scene();
+
+				var ambient = new THREE.AmbientLight( 0x101030 );
+				scene.add( ambient );
+
+				var directionalLight = new THREE.DirectionalLight( 0xffeedd );
+				directionalLight.position.set( 0, 0, 1 ).normalize();
+				scene.add( directionalLight );
+
+				// model
+
+				var loader = new THREE.OBJMTLLoader();
+				loader.addEventListener( 'load', function ( event ) {
+
+					var object = event.content;
+
+					object.position.y = - 80;
+					scene.add( object );
+
+				});
+				loader.load( 'obj/male02/male02.obj', 'obj/male02/male02.mtl' );
+
+				//
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				windowHalfX = window.innerWidth / 2;
+				windowHalfY = window.innerHeight / 2;
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function onDocumentMouseMove( event ) {
+
+				mouseX = ( event.clientX - windowHalfX ) / 2;
+				mouseY = ( event.clientY - windowHalfY ) / 2;
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+				render();
+
+			}
+
+			function render() {
+
+				camera.position.x += ( mouseX - camera.position.x ) * .05;
+				camera.position.y += ( - mouseY - camera.position.y ) * .05;
+
+				camera.lookAt( scene.position );
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>