1/** 2 * Loads a Wavefront .mtl file specifying materials 3 * 4 * @author angelxuanchang 5 */ 6 7THREEW.MTLLoader = function ( manager ) { 8 9 this.manager = ( manager !== undefined ) ? manager : THREEW.DefaultLoadingManager; 10 11}; 12 13THREEW.MTLLoader.prototype = { 14 15 constructor: THREEW.MTLLoader, 16 17 /** 18 * Loads and parses a MTL asset from a URL. 19 * 20 * @param {String} url - URL to the MTL file. 21 * @param {Function} [onLoad] - Callback invoked with the loaded object. 22 * @param {Function} [onProgress] - Callback for download progress. 23 * @param {Function} [onError] - Callback for download errors. 24 * 25 * @see setPath setResourcePath 26 * 27 * @note In order for relative texture references to resolve correctly 28 * you must call setResourcePath() explicitly prior to load. 29 */ 30 load: function ( url, onLoad, onProgress, onError ) { 31 32 var scope = this; 33 34 var path = ( this.path === undefined ) ? THREEW.LoaderUtils.extractUrlBase( url ) : this.path; 35 36 var loader = new THREEW.FileLoader( this.manager ); 37 loader.setPath( this.path ); 38 loader.load( url, function ( text ) { 39 40 onLoad( scope.parse( text, path ) ); 41 42 }, onProgress, onError ); 43 44 }, 45 46 /** 47 * Set base path for resolving references. 48 * If set this path will be prepended to each loaded and found reference. 49 * 50 * @see setResourcePath 51 * @param {String} path 52 * @return {THREEW.MTLLoader} 53 * 54 * @example 55 * mtlLoader.setPath( 'assets/obj/' ); 56 * mtlLoader.load( 'my.mtl', ... ); 57 */ 58 setPath: function ( path ) { 59 60 this.path = path; 61 return this; 62 63 }, 64 65 /** 66 * Set base path for additional resources like textures. 67 * 68 * @see setPath 69 * @param {String} path 70 * @return {THREEW.MTLLoader} 71 * 72 * @example 73 * mtlLoader.setPath( 'assets/obj/' ); 74 * mtlLoader.setResourcePath( 'assets/textures/' ); 75 * mtlLoader.load( 'my.mtl', ... ); 76 */
77 setResourcePath: function ( path ) { 78 79 this.resourcePath = path; 80 return this; 81 82 }, 83 84 setTexturePath: function ( path ) { 85 86 console.warn( 'THREEW.MTLLoader: .setTexturePath() has been renamed to .setResourcePath().' ); 87 return this.setResourcePath( path ); 88 89 }, 90 91 setCrossOrigin: function ( value ) { 92 93 this.crossOrigin = value; 94 return this; 95 96 }, 97 98 setMaterialOptions: function ( value ) { 99 100 this.materialOptions = value; 101 return this; 102 103 }, 104 105 /** 106 * Parses a MTL file. 107 * 108 * @param {String} text - Content of MTL file 109 * @return {THREEW.MTLLoader.MaterialCreator} 110 * 111 * @see setPath setResourcePath 112 * 113 * @note In order for relative texture references to resolve correctly 114 * you must call setResourcePath() explicitly prior to parse. 115 */ 116 parse: function ( text, path ) { 117 118 var lines = text.split( '\n' ); 119 var info = {}; 120 var delimiter_pattern = /\s+/; 121 var materialsInfo = {}; 122 123 for ( var i = 0; i < lines.length; i ++ ) { 124 125 var line = lines[ i ]; 126 line = line.trim(); 127 128 if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 129 130 // Blank line or comment ignore 131 continue; 132 133 } 134 135 var pos = line.indexOf( ' ' ); 136 137 var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line; 138 key = key.toLowerCase(); 139 140 var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : ''; 141 value = value.trim(); 142 143 if ( key === 'newmtl' ) { 144 145 // New material 146 147 info = { name: value }; 148 materialsInfo[ value ] = info; 149 150 } else { 151 152 if ( key === 'ka' || key === 'kd' || key === 'ks' || key ==='ke' ) { 153 154 var ss = value.split( delimiter_pattern, 3 ); 155 info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ]; 156 157 } else { 158 159 info[ key ] = value; 160 161 } 162 163 } 164 165 } 166 167 var materialCreator = new THREEW.MTLLoader.MaterialCreator( this.resourcePath || path, this.materialOptions ); 168 materialCreator.setCrossOrigin( this.crossOrigin ); 169 materialCreator.setManager( this.manager ); 170 materialCreator.setMaterials( materialsInfo ); 171 172 return materialCreator; 173 174 } 175 176}; 177 178/** 179 * Create a new THREE-MTLLoader.MaterialCreator 180 * @param baseUrl - Url relative to which textures are loaded 181 * @param options - Set of options on how to construct the materials 182 * side: Which side to apply the material 183 * THREEW.FrontSide (default), THREEW.BackSide, THREEW.DoubleSide 184 * wrap: What type of wrapping to apply for textures 185 * THREEW.RepeatWrapping (default), THREEW.ClampToEdgeWrapping, THREEW.MirroredRepeatWrapping 186 * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 187 * Default: false, assumed to be already normalized 188 * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's 189 * Default: false 190 * @constructor 191 */ 192 193THREEW.MTLLoader.MaterialCreator = function ( baseUrl, options ) { 194 195 this.baseUrl = baseUrl || ''; 196 this.options = options; 197 this.materialsInfo = {}; 198 this.materials = {}; 199 this.materialsArray = []; 200 this.nameLookup = {}; 201 202 this.side = ( this.options && this.options.side ) ? this.options.side : THREEW.FrontSide; 203 this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREEW.RepeatWrapping; 204 205}; 206 207THREEW.MTLLoader.MaterialCreator.prototype = { 208 209 constructor: THREEW.MTLLoader.MaterialCreator, 210 211 crossOrigin: 'anonymous', 212 213 setCrossOrigin: function ( value ) { 214 215 this.crossOrigin = value; 216 return this; 217 218 }, 219 220 setManager: function ( value ) { 221 222 this.manager = value; 223 224 }, 225 226 setMaterials: function ( materialsInfo ) { 227 228 this.materialsInfo = this.convert( materialsInfo ); 229 this.materials = {}; 230 this.materialsArray = []; 231 this.nameLookup = {}; 232 233 }, 234 235 convert: function ( materialsInfo ) { 236 237 if ( ! this.options ) return materialsInfo; 238 239 var converted = {}; 240 241 for ( var mn in materialsInfo ) { 242 243 // Convert materials info into normalized form based on options 244 245 var mat = materialsInfo[ mn ]; 246 247 var covmat = {}; 248 249 converted[ mn ] = covmat; 250 251 for ( var prop in mat ) { 252 253 var save = true; 254 var value = mat[ prop ]; 255 var lprop = prop.toLowerCase(); 256 257 switch ( lprop ) { 258 259 case 'kd': 260 case 'ka': 261 case 'ks': 262 263 // Diffuse color (color under white light) using RGB values 264 265 if ( this.options && this.options.normalizeRGB ) { 266 267 value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; 268 269 } 270 271 if ( this.options && this.options.ignoreZeroRGBs ) { 272 273 if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) { 274 275 // ignore 276 277 save = false;
278 279 } 280 281 } 282 283 break; 284 285 default: 286 287 break; 288 289 } 290 291 if ( save ) { 292 293 covmat[ lprop ] = value; 294 295 } 296 297 } 298 299 } 300 301 return converted; 302 303 }, 304 305 preload: function () { 306 307 for ( var mn in this.materialsInfo ) { 308 309 this.create( mn ); 310 311 } 312 313 }, 314 315 getIndex: function ( materialName ) { 316 317 return this.nameLookup[ materialName ]; 318 319 }, 320 321 getAsArray: function () { 322 323 var index = 0; 324 325 for ( var mn in this.materialsInfo ) { 326 327 this.materialsArray[ index ] = this.create( mn ); 328 this.nameLookup[ mn ] = index; 329 index ++; 330 331 } 332 333 return this.materialsArray; 334 335 }, 336 337 create: function ( materialName ) { 338 339 if ( this.materials[ materialName ] === undefined ) { 340 341 this.createMaterial_( materialName ); 342 343 } 344 345 return this.materials[ materialName ]; 346 347 }, 348 349 createMaterial_: function ( materialName ) { 350 351 // Create material 352 353 var scope = this; 354 var mat = this.materialsInfo[ materialName ]; 355 var params = { 356 357 name: materialName, 358 side: this.side 359 360 }; 361 362 function resolveURL( baseUrl, url ) { 363 364 if ( typeof url !== 'string' || url === '' ) 365 return ''; 366 367 // Absolute URL 368 if ( /^https?:\/\//i.test( url ) ) return url; 369 370 return baseUrl + url; 371 372 } 373 374 function setMapForType( mapType, value ) { 375 376 if ( params[ mapType ] ) return; // Keep the first encountered texture 377 378 var texParams = scope.getTextureParams( value, params ); 379 var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) ); 380 381 map.repeat.copy( texParams.scale ); 382 map.offset.copy( texParams.offset ); 383 384 map.wrapS = scope.wrap; 385 map.wrapT = scope.wrap; 386 387 params[ mapType ] = map; 388 389 } 390 391 for ( var prop in mat ) { 392 393 var value = mat[ prop ]; 394 var n; 395 396 if ( value === '' ) continue; 397 398 switch ( prop.toLowerCase() ) { 399 400 // Ns is material specular exponent 401 402 case 'kd': 403 404 // Diffuse color (color under white light) using RGB values 405 406 params.color = new THREEW.Color().fromArray( value ); 407 408 break; 409 410 case 'ks': 411 412 // Specular color (color when light is reflected from shiny surface) using RGB values 413 params.specular = new THREEW.Color().fromArray( value ); 414 415 break; 416 417 case 'ke': 418 419 // Emissive using RGB values 420 params.emissive = new THREEW.Color().fromArray( value ); 421 422 break; 423 424 case 'map_kd': 425 426 // Diffuse texture map 427 428 setMapForType( "map", value ); 429 430 break; 431 432 case 'map_ks': 433 434 // Specular map 435 436 setMapForType( "specularMap", value ); 437 438 break; 439 440 case 'map_ke': 441 442 // Emissive map 443 444 setMapForType( "emissiveMap", value ); 445 446 break; 447 448 case 'norm': 449 450 setMapForType( "normalMap", value ); 451 452 break; 453 454 case 'map_bump': 455 case 'bump': 456 457 // Bump texture map 458 459 setMapForType( "bumpMap", value ); 460 461 break; 462 463 case 'map_d': 464 465 // Alpha map 466 467 setMapForType( "alphaMap", value ); 468 params.transparent = true; 469 470 break; 471 472 case 'ns': 473 474 // The specular exponent (defines the focus of the specular highlight) 475 // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. 476 477 params.shininess = parseFloat( value ); 478 479 break; 480 481 case 'd': 482 n = parseFloat( value ); 483 484 if ( n < 1 ) { 485 486 params.opacity = n; 487 params.transparent = true; 488 489 } 490 491 break; 492 493 case 'tr': 494 n = parseFloat( value ); 495 496 if ( this.options && this.options.invertTrProperty ) n = 1 - n; 497 498 if ( n > 0 ) { 499 500 params.opacity = 1 - n; 501 params.transparent = true; 502 503 } 504 505 break; 506 507 default: 508 break; 509 510 } 511 512 } 513 params.precision='mediump'; 514 this.materials[ materialName ] = new THREEW.MeshPhongMaterial( params ); 515 return this.materials[ materialName ]; 516 517 }, 518 519 getTextureParams: function ( value, matParams ) { 520 521 var texParams = { 522 523 scale: new THREEW.Vector2( 1, 1 ), 524 offset: new THREEW.Vector2( 0, 0 ) 525 526 }; 527 528 var items = value.split( /\s+/ ); 529 var pos; 530 531 pos = items.indexOf( '-bm' ); 532 533 if ( pos >= 0 ) { 534 535 matParams.bumpScale = parseFloat( items[ pos + 1 ] ); 536 items.splice( pos, 2 ); 537 538 } 539 540 pos = items.indexOf( '-s' ); 541 542 if ( pos >= 0 ) { 543 544 texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) ); 545 items.splice( pos, 4 ); // we expect 3 parameters here! 546 547 } 548
549 pos = items.indexOf( '-o' ); 550 551 if ( pos >= 0 ) { 552 553 texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) ); 554 items.splice( pos, 4 ); // we expect 3 parameters here! 555 556 } 557 558 texParams.url = items.join( ' ' ).trim(); 559 return texParams; 560 561 }, 562 563 loadTexture: function ( url, mapping, onLoad, onProgress, onError ) { 564 565 var texture; 566 var loader = THREEW.Loader.Handlers.get( url ); 567 var manager = ( this.manager !== undefined ) ? this.manager : THREEW.DefaultLoadingManager; 568 569 if ( loader === null ) { 570 571 loader = new THREEW.TextureLoader( manager ); 572 573 } 574 575 if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin ); 576 texture = loader.load( url, onLoad, onProgress, onError ); 577 578 if ( mapping !== undefined ) texture.mapping = mapping; 579 580 return texture; 581 582 } 583 584};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.