/ examples / bno055_webgl_demo / static / js / MTLLoader.js
MTLLoader.js
  1  /**
  2   * Loads a Wavefront .mtl file specifying materials
  3   *
  4   * @author angelxuanchang
  5   */
  6  
  7  THREE.MTLLoader = function( baseUrl, options, crossOrigin ) {
  8  
  9  	this.baseUrl = baseUrl;
 10  	this.options = options;
 11  	this.crossOrigin = crossOrigin;
 12  
 13  };
 14  
 15  THREE.MTLLoader.prototype = {
 16  
 17  	constructor: THREE.MTLLoader,
 18  
 19  	load: function ( url, onLoad, onProgress, onError ) {
 20  
 21  		var scope = this;
 22  
 23  		var loader = new THREE.XHRLoader();
 24  		loader.setCrossOrigin( this.crossOrigin );
 25  		loader.load( url, function ( text ) {
 26  
 27  			onLoad( scope.parse( text ) );
 28  
 29  		}, onProgress, onError );
 30  
 31  	},
 32  
 33  	/**
 34  	 * Parses loaded MTL file
 35  	 * @param text - Content of MTL file
 36  	 * @return {THREE.MTLLoader.MaterialCreator}
 37  	 */
 38  	parse: function ( text ) {
 39  
 40  		var lines = text.split( "\n" );
 41  		var info = {};
 42  		var delimiter_pattern = /\s+/;
 43  		var materialsInfo = {};
 44  
 45  		for ( var i = 0; i < lines.length; i ++ ) {
 46  
 47  			var line = lines[ i ];
 48  			line = line.trim();
 49  
 50  			if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
 51  
 52  				// Blank line or comment ignore
 53  				continue;
 54  
 55  			}
 56  
 57  			var pos = line.indexOf( ' ' );
 58  
 59  			var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
 60  			key = key.toLowerCase();
 61  
 62  			var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : "";
 63  			value = value.trim();
 64  
 65  			if ( key === "newmtl" ) {
 66  
 67  				// New material
 68  
 69  				info = { name: value };
 70  				materialsInfo[ value ] = info;
 71  
 72  			} else if ( info ) {
 73  
 74  				if ( key === "ka" || key === "kd" || key === "ks" ) {
 75  
 76  					var ss = value.split( delimiter_pattern, 3 );
 77  					info[ key ] = [ parseFloat( ss[0] ), parseFloat( ss[1] ), parseFloat( ss[2] ) ];
 78  
 79  				} else {
 80  
 81  					info[ key ] = value;
 82  
 83  				}
 84  
 85  			}
 86  
 87  		}
 88  
 89  		var materialCreator = new THREE.MTLLoader.MaterialCreator( this.baseUrl, this.options );
 90  		materialCreator.crossOrigin = this.crossOrigin
 91  		materialCreator.setMaterials( materialsInfo );
 92  		return materialCreator;
 93  
 94  	}
 95  
 96  };
 97  
 98  /**
 99   * Create a new THREE-MTLLoader.MaterialCreator
100   * @param baseUrl - Url relative to which textures are loaded
101   * @param options - Set of options on how to construct the materials
102   *                  side: Which side to apply the material
103   *                        THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide
104   *                  wrap: What type of wrapping to apply for textures
105   *                        THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
106   *                  normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
107   *                                Default: false, assumed to be already normalized
108   *                  ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
109   *                                  Default: false
110   *                  invertTransparency: If transparency need to be inverted (inversion is needed if d = 0 is fully opaque)
111   *                                      Default: false (d = 1 is fully opaque)
112   * @constructor
113   */
114  
115  THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) {
116  
117  	this.baseUrl = baseUrl;
118  	this.options = options;
119  	this.materialsInfo = {};
120  	this.materials = {};
121  	this.materialsArray = [];
122  	this.nameLookup = {};
123  
124  	this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide;
125  	this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping;
126  
127  };
128  
129  THREE.MTLLoader.MaterialCreator.prototype = {
130  
131  	constructor: THREE.MTLLoader.MaterialCreator,
132  
133  	setMaterials: function( materialsInfo ) {
134  
135  		this.materialsInfo = this.convert( materialsInfo );
136  		this.materials = {};
137  		this.materialsArray = [];
138  		this.nameLookup = {};
139  
140  	},
141  
142  	convert: function( materialsInfo ) {
143  
144  		if ( !this.options ) return materialsInfo;
145  
146  		var converted = {};
147  
148  		for ( var mn in materialsInfo ) {
149  
150  			// Convert materials info into normalized form based on options
151  
152  			var mat = materialsInfo[ mn ];
153  
154  			var covmat = {};
155  
156  			converted[ mn ] = covmat;
157  
158  			for ( var prop in mat ) {
159  
160  				var save = true;
161  				var value = mat[ prop ];
162  				var lprop = prop.toLowerCase();
163  
164  				switch ( lprop ) {
165  
166  					case 'kd':
167  					case 'ka':
168  					case 'ks':
169  
170  						// Diffuse color (color under white light) using RGB values
171  
172  						if ( this.options && this.options.normalizeRGB ) {
173  
174  							value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
175  
176  						}
177  
178  						if ( this.options && this.options.ignoreZeroRGBs ) {
179  
180  							if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 1 ] === 0 ) {
181  
182  								// ignore
183  
184  								save = false;
185  
186  							}
187  						}
188  
189  						break;
190  
191  					case 'd':
192  
193  						// According to MTL format (http://paulbourke.net/dataformats/mtl/):
194  						//   d is dissolve for current material
195  						//   factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent)
196  
197  						if ( this.options && this.options.invertTransparency ) {
198  
199  							value = 1 - value;
200  
201  						}
202  
203  						break;
204  
205  					default:
206  
207  						break;
208  				}
209  
210  				if ( save ) {
211  
212  					covmat[ lprop ] = value;
213  
214  				}
215  
216  			}
217  
218  		}
219  
220  		return converted;
221  
222  	},
223  
224  	preload: function () {
225  
226  		for ( var mn in this.materialsInfo ) {
227  
228  			this.create( mn );
229  
230  		}
231  
232  	},
233  
234  	getIndex: function( materialName ) {
235  
236  		return this.nameLookup[ materialName ];
237  
238  	},
239  
240  	getAsArray: function() {
241  
242  		var index = 0;
243  
244  		for ( var mn in this.materialsInfo ) {
245  
246  			this.materialsArray[ index ] = this.create( mn );
247  			this.nameLookup[ mn ] = index;
248  			index ++;
249  
250  		}
251  
252  		return this.materialsArray;
253  
254  	},
255  
256  	create: function ( materialName ) {
257  
258  		if ( this.materials[ materialName ] === undefined ) {
259  
260  			this.createMaterial_( materialName );
261  
262  		}
263  
264  		return this.materials[ materialName ];
265  
266  	},
267  
268  	createMaterial_: function ( materialName ) {
269  
270  		// Create material
271  
272  		var mat = this.materialsInfo[ materialName ];
273  		var params = {
274  
275  			name: materialName,
276  			side: this.side
277  
278  		};
279  
280  		for ( var prop in mat ) {
281  
282  			var value = mat[ prop ];
283  
284  			switch ( prop.toLowerCase() ) {
285  
286  				// Ns is material specular exponent
287  
288  				case 'kd':
289  
290  					// Diffuse color (color under white light) using RGB values
291  
292  					params[ 'diffuse' ] = new THREE.Color().fromArray( value );
293  
294  					break;
295  
296  				case 'ka':
297  
298  					// Ambient color (color under shadow) using RGB values
299  
300  					break;
301  
302  				case 'ks':
303  
304  					// Specular color (color when light is reflected from shiny surface) using RGB values
305  					params[ 'specular' ] = new THREE.Color().fromArray( value );
306  
307  					break;
308  
309  				case 'map_kd':
310  
311  					// Diffuse texture map
312  
313  					params[ 'map' ] = this.loadTexture( this.baseUrl + value );
314  					params[ 'map' ].wrapS = this.wrap;
315  					params[ 'map' ].wrapT = this.wrap;
316  
317  					break;
318  
319  				case 'ns':
320  
321  					// The specular exponent (defines the focus of the specular highlight)
322  					// A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
323  
324  					params['shininess'] = value;
325  
326  					break;
327  
328  				case 'd':
329  
330  					// According to MTL format (http://paulbourke.net/dataformats/mtl/):
331  					//   d is dissolve for current material
332  					//   factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent)
333  
334  					if ( value < 1 ) {
335  
336  						params['transparent'] = true;
337  						params['opacity'] = value;
338  
339  					}
340  
341  					break;
342  
343  				case 'map_bump':
344  				case 'bump':
345  
346  					// Bump texture map
347  
348  					if ( params[ 'bumpMap' ] ) break; // Avoid loading twice.
349  
350  					params[ 'bumpMap' ] = this.loadTexture( this.baseUrl + value );
351  					params[ 'bumpMap' ].wrapS = this.wrap;
352  					params[ 'bumpMap' ].wrapT = this.wrap;
353  
354  					break;
355  
356  				default:
357  					break;
358  
359  			}
360  
361  		}
362  
363  		if ( params[ 'diffuse' ] ) {
364  
365  			params[ 'color' ] = params[ 'diffuse' ];
366  
367  		}
368  
369  		this.materials[ materialName ] = new THREE.MeshPhongMaterial( params );
370  		return this.materials[ materialName ];
371  
372  	},
373  
374  
375  	loadTexture: function ( url, mapping, onLoad, onError ) {
376  
377  		var texture;
378  		var loader = THREE.Loader.Handlers.get( url );
379  
380  		if ( loader !== null ) {
381  
382  			texture = loader.load( url, onLoad );
383  
384  		} else {
385  
386  			texture = new THREE.Texture();
387  
388  			loader = new THREE.ImageLoader();
389  			loader.crossOrigin = this.crossOrigin;
390  			loader.load( url, function ( image ) {
391  
392  				texture.image = THREE.MTLLoader.ensurePowerOfTwo_( image );
393  				texture.needsUpdate = true;
394  
395  				if ( onLoad ) onLoad( texture );
396  
397  			} );
398  
399  		}
400  
401  		if ( mapping !== undefined ) texture.mapping = mapping;
402  
403  		return texture;
404  
405  	}
406  
407  };
408  
409  THREE.MTLLoader.ensurePowerOfTwo_ = function ( image ) {
410  
411  	if ( ! THREE.Math.isPowerOfTwo( image.width ) || ! THREE.Math.isPowerOfTwo( image.height ) ) {
412  
413  		var canvas = document.createElement( "canvas" );
414  		canvas.width = THREE.MTLLoader.nextHighestPowerOfTwo_( image.width );
415  		canvas.height = THREE.MTLLoader.nextHighestPowerOfTwo_( image.height );
416  
417  		var ctx = canvas.getContext("2d");
418  		ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );
419  		return canvas;
420  
421  	}
422  
423  	return image;
424  
425  };
426  
427  THREE.MTLLoader.nextHighestPowerOfTwo_ = function( x ) {
428  
429  	-- x;
430  
431  	for ( var i = 1; i < 32; i <<= 1 ) {
432  
433  		x = x | x >> i;
434  
435  	}
436  
437  	return x + 1;
438  
439  };
440  
441  THREE.EventDispatcher.prototype.apply( THREE.MTLLoader.prototype );