MTLLoader.js 9.9 KB
Newer Older
1 2 3 4 5 6
/**
 * Loads a Wavefront .mtl file specifying materials
 *
 * @author angelxuanchang
 */

7
THREE.MTLLoader = function( manager ) {
8

9
	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
10

11 12
};

13
Object.assign( THREE.MTLLoader.prototype, THREE.EventDispatcher.prototype, {
14

J
Jonne Nauha 已提交
15 16 17 18 19 20 21 22 23 24 25 26 27
	/**
	 * Loads and parses a MTL asset from a URL.
	 *
	 * @param {String} url - URL to the MTL file.
	 * @param {Function} [onLoad] - Callback invoked with the loaded object.
	 * @param {Function} [onProgress] - Callback for download progress.
	 * @param {Function} [onError] - Callback for download errors.
	 *
	 * @see setPath setTexturePath
	 *
	 * @note In order for relative texture references to resolve correctly
	 * you must call setPath and/or setTexturePath explicitly prior to load.
	 */
28
	load: function ( url, onLoad, onProgress, onError ) {
29

30
		var scope = this;
31

32
		var loader = new THREE.XHRLoader( this.manager );
M
Mr.doob 已提交
33
		loader.setPath( this.path );
34
		loader.load( url, function ( text ) {
35

36
			onLoad( scope.parse( text ) );
37

38
		}, onProgress, onError );
39

40
	},
41

J
Jonne Nauha 已提交
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
	/**
	 * Set base path for resolving references.
	 * If set this path will be prepended to each loaded and found reference.
	 *
	 * @see setTexturePath
	 * @param {String} path
	 *
	 * @example
	 *     mtlLoader.setPath( 'assets/obj/' );
	 *     mtlLoader.load( 'my.mtl', ... );
	 */
	setPath: function ( path ) {

		this.path = path;

	},

	/**
	 * Set base path for resolving texture references.
	 * If set this path will be prepended found texture reference.
	 * If not set and setPath is, it will be used as texture base path.
	 *
	 * @see setPath
	 * @param {String} path
	 *
	 * @example
	 *     mtlLoader.setPath( 'assets/obj/' );
	 *     mtlLoader.setTexturePath( 'assets/textures/' );
	 *     mtlLoader.load( 'my.mtl', ... );
	 */
	setTexturePath: function( path ) {
M
Mr.doob 已提交
73

J
Jonne Nauha 已提交
74
		this.texturePath = path;
M
Mr.doob 已提交
75 76 77

	},

J
Jonne Nauha 已提交
78
	setBaseUrl: function( path ) {
79

J
Jonne Nauha 已提交
80
		console.warn( 'THREE.MTLLoader: .setBaseUrl() is deprecated. Use .setTexturePath( path ) for texture path or .setPath( path ) for general base path instead.' );
M
Mr.doob 已提交
81

J
Jonne Nauha 已提交
82
		this.setTexturePath( path );
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

	},

	setCrossOrigin: function ( value ) {

		this.crossOrigin = value;

	},

	setMaterialOptions: function ( value ) {

		this.materialOptions = value;

	},

98
	/**
J
Jonne Nauha 已提交
99 100 101
	 * Parses a MTL file.
	 *
	 * @param {String} text - Content of MTL file
102
	 * @return {THREE.MTLLoader.MaterialCreator}
J
Jonne Nauha 已提交
103 104 105 106 107
	 *
	 * @see setPath setTexturePath
	 *
	 * @note In order for relative texture references to resolve correctly
	 * you must call setPath and/or setTexturePath explicitly prior to parse.
108
	 */
109
	parse: function ( text ) {
110

J
Jonne Nauha 已提交
111
		var lines = text.split( '\n' );
112 113 114
		var info = {};
		var delimiter_pattern = /\s+/;
		var materialsInfo = {};
115

116
		for ( var i = 0; i < lines.length; i ++ ) {
117 118

			var line = lines[ i ];
119
			line = line.trim();
120

121
			if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
122

123 124
				// Blank line or comment ignore
				continue;
125

126
			}
127

128
			var pos = line.indexOf( ' ' );
129

130
			var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
131
			key = key.toLowerCase();
132

J
Jonne Nauha 已提交
133
			var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : '';
134
			value = value.trim();
135

J
Jonne Nauha 已提交
136
			if ( key === 'newmtl' ) {
137

138
				// New material
139

140 141
				info = { name: value };
				materialsInfo[ value ] = info;
142

143
			} else if ( info ) {
144

J
Jonne Nauha 已提交
145
				if ( key === 'ka' || key === 'kd' || key === 'ks' ) {
146

147
					var ss = value.split( delimiter_pattern, 3 );
G
gero3 已提交
148
					info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ];
149

150
				} else {
151

152
					info[ key ] = value;
153

154
				}
155

156
			}
157

158
		}
159

J
Jonne Nauha 已提交
160
		var materialCreator = new THREE.MTLLoader.MaterialCreator( this.texturePath || this.path, this.materialOptions );
161 162
		materialCreator.setCrossOrigin( this.crossOrigin );
		materialCreator.setManager( this.manager );
163 164
		materialCreator.setMaterials( materialsInfo );
		return materialCreator;
165

166
	}
167

168
} );
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

/**
 * Create a new THREE-MTLLoader.MaterialCreator
 * @param baseUrl - Url relative to which textures are loaded
 * @param options - Set of options on how to construct the materials
 *                  side: Which side to apply the material
 *                        THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide
 *                  wrap: What type of wrapping to apply for textures
 *                        THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
 *                  normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
 *                                Default: false, assumed to be already normalized
 *                  ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
 *                                  Default: false
 * @constructor
 */
184

185 186
THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) {

J
Jonne Nauha 已提交
187
	this.baseUrl = baseUrl || '';
188 189 190 191 192
	this.options = options;
	this.materialsInfo = {};
	this.materials = {};
	this.materialsArray = [];
	this.nameLookup = {};
193

G
gero3 已提交
194 195
	this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide;
	this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping;
196

197 198 199
};

THREE.MTLLoader.MaterialCreator.prototype = {
200

201 202
	constructor: THREE.MTLLoader.MaterialCreator,

203 204 205 206 207 208 209 210 211 212 213 214
	setCrossOrigin: function ( value ) {

		this.crossOrigin = value;

	},

	setManager: function ( value ) {

		this.manager = value;

	},

215
	setMaterials: function( materialsInfo ) {
216

217 218 219 220
		this.materialsInfo = this.convert( materialsInfo );
		this.materials = {};
		this.materialsArray = [];
		this.nameLookup = {};
221

222
	},
223

224
	convert: function( materialsInfo ) {
225

G
gero3 已提交
226
		if ( ! this.options ) return materialsInfo;
227

228
		var converted = {};
229

230
		for ( var mn in materialsInfo ) {
231

232
			// Convert materials info into normalized form based on options
233

234
			var mat = materialsInfo[ mn ];
235

236
			var covmat = {};
237

238
			converted[ mn ] = covmat;
239

240
			for ( var prop in mat ) {
241

242 243 244
				var save = true;
				var value = mat[ prop ];
				var lprop = prop.toLowerCase();
245

246
				switch ( lprop ) {
247

248 249 250
					case 'kd':
					case 'ka':
					case 'ks':
251

252
						// Diffuse color (color under white light) using RGB values
253

254
						if ( this.options && this.options.normalizeRGB ) {
255

256
							value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
257

258
						}
259

260
						if ( this.options && this.options.ignoreZeroRGBs ) {
261

262
							if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) {
263

264
								// ignore
265

266
								save = false;
267

268
							}
G
gero3 已提交
269

270
						}
271

272
						break;
273

274
					default:
275

276 277
						break;
				}
278

279
				if ( save ) {
280

281
					covmat[ lprop ] = value;
282

283
				}
284

285
			}
286

287
		}
288

289
		return converted;
290

291
	},
292

293
	preload: function () {
294

295
		for ( var mn in this.materialsInfo ) {
296

297
			this.create( mn );
298

299
		}
300

301
	},
302

303
	getIndex: function( materialName ) {
304

305
		return this.nameLookup[ materialName ];
306

307
	},
308

309
	getAsArray: function() {
310

311
		var index = 0;
312

313
		for ( var mn in this.materialsInfo ) {
314

315 316 317
			this.materialsArray[ index ] = this.create( mn );
			this.nameLookup[ mn ] = index;
			index ++;
318

319
		}
320

321
		return this.materialsArray;
322

323
	},
324

325
	create: function ( materialName ) {
326

327
		if ( this.materials[ materialName ] === undefined ) {
328

329
			this.createMaterial_( materialName );
330

331
		}
332

333
		return this.materials[ materialName ];
334

335
	},
336

337
	createMaterial_: function ( materialName ) {
338

339
		// Create material
340

341 342
		var mat = this.materialsInfo[ materialName ];
		var params = {
343

344 345
			name: materialName,
			side: this.side
346

347
		};
348

J
Jonne Nauha 已提交
349 350 351 352 353 354 355 356 357 358 359 360 361
		var resolveURL = function ( baseUrl, url ) {

			if ( typeof url !== 'string' || url === '' )
				return '';

			// Absolute URL
			if ( /^https?:\/\//i.test( url ) ) {
				return url;
			}

			return baseUrl + url;
		};

362
		for ( var prop in mat ) {
363

364
			var value = mat[ prop ];
365

M
Mr.doob 已提交
366
			if ( value === '' ) continue;
J
Jens Arps 已提交
367

368
			switch ( prop.toLowerCase() ) {
369

370
				// Ns is material specular exponent
371

372
				case 'kd':
373 374 375

					// Diffuse color (color under white light) using RGB values

J
Jonne Nauha 已提交
376
					params.color = new THREE.Color().fromArray( value );
377 378 379

					break;

380
				case 'ks':
381

382
					// Specular color (color when light is reflected from shiny surface) using RGB values
J
Jonne Nauha 已提交
383
					params.specular = new THREE.Color().fromArray( value );
384

385
					break;
386

387
				case 'map_kd':
388

389
					// Diffuse texture map
390

J
Jonne Nauha 已提交
391 392 393 394 395
					if ( params.map ) break; // Keep the first encountered texture

					params.map = this.loadTexture( resolveURL( this.baseUrl, value ) );
					params.map.wrapS = this.wrap;
					params.map.wrapT = this.wrap;
396 397 398

					break;

D
David 已提交
399 400 401 402 403 404 405 406 407 408 409 410
				case 'map_ks':

					// Specular map

					if ( params.specularMap ) break; // Keep the first encountered texture

					params.specularMap = this.loadTexture( resolveURL( this.baseUrl, value ) );
					params.specularMap.wrapS = this.wrap;
					params.specularMap.wrapT = this.wrap;

					break;

411
				case 'ns':
412

413 414
					// 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.
415

J
Jonne Nauha 已提交
416
					params.shininess = parseFloat( value );
417 418 419

					break;

420
				case 'd':
421

422
					if ( value < 1 ) {
423

J
Jonne Nauha 已提交
424 425
						params.opacity = value;
						params.transparent = true;
426 427 428 429 430 431 432 433 434

					}

					break;

				case 'Tr':

					if ( value > 0 ) {

J
Jonne Nauha 已提交
435 436
						params.opacity = 1 - value;
						params.transparent = true;
437

438
					}
439

440
					break;
441

442 443 444 445 446
				case 'map_bump':
				case 'bump':

					// Bump texture map

J
Jonne Nauha 已提交
447
					if ( params.bumpMap ) break; // Keep the first encountered texture
448

J
Jonne Nauha 已提交
449 450 451
					params.bumpMap = this.loadTexture( resolveURL( this.baseUrl, value ) );
					params.bumpMap.wrapS = this.wrap;
					params.bumpMap.wrapT = this.wrap;
452 453 454

					break;

455 456
				default:
					break;
457

458
			}
459

460
		}
461

462 463
		this.materials[ materialName ] = new THREE.MeshPhongMaterial( params );
		return this.materials[ materialName ];
464

465
	},
466

467
	loadTexture: function ( url, mapping, onLoad, onProgress, onError ) {
468

M
Mr.doob 已提交
469 470
		var texture;
		var loader = THREE.Loader.Handlers.get( url );
471
		var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager;
472

M
Mr.doob 已提交
473
		if ( loader === null ) {
474

M
Mr.doob 已提交
475
			loader = new THREE.TextureLoader( manager );
476

477 478
		}

M
Mr.doob 已提交
479
		if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin );
M
Mr.doob 已提交
480 481
		texture = loader.load( url, onLoad, onProgress, onError );

M
Mr.doob 已提交
482
		if ( mapping !== undefined ) texture.mapping = mapping;
M
Mr.doob 已提交
483

484
		return texture;
485

486
	}
487 488

};