1/** 2 * Loads a Wavefront .mtl file specifying materials 3 * 4 * @author angelxuanchang 5 */ 6 7THREE3DP.MTLLoader = function ( manager ) { 8 9 this.manager = ( manager !== undefined ) ? manager : THREE3DP.DefaultLoadingManager; 10 11}; 12 13THREE3DP.MTLLoader.prototype = { 14 15 constructor: THREE3DP.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 ) ? THREE3DP.LoaderUtils.extractUrlBase( url ) : this.path; 35 36 var loader = new THREE3DP.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 {THREE3DP.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 {THREE3DP.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( 'THREE3DP.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 {THREE3DP.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 THREE3DP.MTLLoader.MaterialCreator( this.resourcePath || path, this.materialOptions ); 168 materialCreator.setCrossOrigin( this.crossOrigin ); 169 materialCreator.setManager( this.manager ); 170 materialCreator.setMaterials( materialsInfo ); 171 return materialCreator; 172 173 } 174 175}; 176 177/** 178 * Create a new THREE3DP.-MTLLoader.MaterialCreator 179 * @param baseUrl - Url relative to which textures are loaded 180 * @param options - Set of options on how to construct the materials 181 * side: Which side to apply the material 182 * THREE3DP.FrontSide (default), THREE3DP.BackSide, THREE3DP.DoubleSide 183 * wrap: What type of wrapping to apply for textures 184 * THREE3DP.RepeatWrapping (default), THREE3DP.ClampToEdgeWrapping, THREE3DP.MirroredRepeatWrapping 185 * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 186 * Default: false, assumed to be already normalized 187 * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's 188 * Default: false 189 * @constructor 190 */ 191 192THREE3DP.MTLLoader.MaterialCreator = function ( baseUrl, options ) { 193 194 this.baseUrl = baseUrl || ''; 195 this.options = options; 196 this.materialsInfo = {}; 197 this.materials = {}; 198 this.materialsArray = []; 199 this.nameLookup = {}; 200 201 this.side = ( this.options && this.options.side ) ? this.options.side : THREE3DP.FrontSide; 202 this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE3DP.RepeatWrapping; 203 204}; 205 206THREE3DP.MTLLoader.MaterialCreator.prototype = { 207 208 constructor: THREE3DP.MTLLoader.MaterialCreator, 209 210 crossOrigin: 'anonymous', 211 212 setCrossOrigin: function ( value ) { 213 214 this.crossOrigin = value; 215 return this; 216 217 }, 218 219 setManager: function ( value ) { 220 221 this.manager = value; 222 223 }, 224 225 setMaterials: function ( materialsInfo ) { 226 227 this.materialsInfo = this.convert( materialsInfo ); 228 this.materials = {}; 229 this.materialsArray = []; 230 this.nameLookup = {}; 231 232 }, 233 234 convert: function ( materialsInfo ) { 235 236 if ( ! this.options ) return materialsInfo; 237 238 var converted = {}; 239 240 for ( var mn in materialsInfo ) { 241 242 // Convert materials info into normalized form based on options 243 244 var mat = materialsInfo[ mn ]; 245 246 var covmat = {}; 247 248 converted[ mn ] = covmat; 249 250 for ( var prop in mat ) { 251 252 var save = true; 253 var value = mat[ prop ]; 254 var lprop = prop.toLowerCase(); 255 256 switch ( lprop ) { 257 258 case 'kd': 259 case 'ka': 260 case 'ks': 261 262 // Diffuse color (color under white light) using RGB values 263 264 if ( this.options && this.options.normalizeRGB ) { 265 266 value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; 267 268 } 269 270 if ( this.options && this.options.ignoreZeroRGBs ) { 271 272 if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) { 273 274 // ignore 275 276 save = false;
277 278 } 279 280 } 281 282 break; 283 284 default: 285 286 break; 287 288 } 289 290 if ( save ) { 291 292 covmat[ lprop ] = value; 293 294 } 295 296 } 297 298 } 299 300 return converted; 301 302 }, 303 304 preload: function () { 305 306 for ( var mn in this.materialsInfo ) { 307 308 this.create( mn ); 309 310 } 311 312 }, 313 314 getIndex: function ( materialName ) { 315 316 return this.nameLookup[ materialName ]; 317 318 }, 319 320 getAsArray: function () { 321 322 var index = 0; 323 324 for ( var mn in this.materialsInfo ) { 325 326 this.materialsArray[ index ] = this.create( mn ); 327 this.nameLookup[ mn ] = index; 328 index ++; 329 330 } 331 332 return this.materialsArray; 333 334 }, 335 336 create: function ( materialName ) { 337 338 if ( this.materials[ materialName ] === undefined ) { 339 340 this.createMaterial_( materialName ); 341 342 } 343 344 return this.materials[ materialName ]; 345 346 }, 347 348 createMaterial_: function ( materialName ) { 349 350 // Create material 351 352 var scope = this; 353 var mat = this.materialsInfo[ materialName ]; 354 var params = { 355 356 name: materialName, 357 side: this.side 358 359 }; 360 361 function resolveURL( baseUrl, url ) { 362 363 if ( typeof url !== 'string' || url === '' ) 364 return ''; 365 366 // Absolute URL 367 if ( /^https?:\/\//i.test( url ) ) return url; 368 369 return baseUrl + url; 370 371 } 372 373 function setMapForType( mapType, value ) { 374 375 if ( params[ mapType ] ) return; // Keep the first encountered texture 376 377 var texParams = scope.getTextureParams( value, params ); 378 var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) ); 379 380 map.repeat.copy( texParams.scale ); 381 map.offset.copy( texParams.offset ); 382 383 map.wrapS = scope.wrap; 384 map.wrapT = scope.wrap; 385 386 params[ mapType ] = map; 387 388 } 389 390 for ( var prop in mat ) { 391 392 var value = mat[ prop ]; 393 var n; 394 395 if ( value === '' ) continue; 396 397 switch ( prop.toLowerCase() ) { 398 399 // Ns is material specular exponent 400 401 case 'kd': 402 403 // Diffuse color (color under white light) using RGB values 404 405 params.color = new THREE3DP.Color().fromArray( value ); 406 407 break; 408 409 case 'ks': 410 411 // Specular color (color when light is reflected from shiny surface) using RGB values 412 params.specular = new THREE3DP.Color().fromArray( value ); 413 414 break; 415 416 case 'ke': 417 418 // Emissive using RGB values 419 params.emissive = new THREE3DP.Color().fromArray( value ); 420 421 break; 422 423 case 'map_kd': 424 425 // Diffuse texture map 426 427 setMapForType( "map", value ); 428 429 break; 430 431 case 'map_ks': 432 433 // Specular map 434 435 setMapForType( "specularMap", value ); 436 437 break; 438 439 case 'map_ke': 440 441 // Emissive map 442 443 setMapForType( "emissiveMap", value ); 444 445 break; 446 447 case 'norm': 448 449 setMapForType( "normalMap", value ); 450 451 break; 452 453 case 'map_bump': 454 case 'bump': 455 456 // Bump texture map 457 458 setMapForType( "bumpMap", value ); 459 460 break; 461 462 case 'map_d': 463 464 // Alpha map 465 466 setMapForType( "alphaMap", value ); 467 params.transparent = true; 468 469 break; 470 471 case 'ns': 472 473 // The specular exponent (defines the focus of the specular highlight) 474 // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. 475 476 params.shininess = parseFloat( value ); 477 478 break; 479 480 case 'd': 481 n = parseFloat( value ); 482 483 if ( n < 1 ) { 484 485 params.opacity = n; 486 params.transparent = true; 487 488 } 489 490 break; 491 492 case 'tr': 493 n = parseFloat( value ); 494 495 if ( this.options && this.options.invertTrProperty ) n = 1 - n; 496 497 if ( n > 0 ) { 498 499 params.opacity = 1 - n; 500 params.transparent = true; 501 502 } 503 504 break; 505 506 default: 507 break; 508 509 } 510 511 } 512 params.precision='mediump'; 513 this.materials[ materialName ] = new THREE3DP.MeshPhongMaterial( params ); 514 return this.materials[ materialName ]; 515 516 }, 517 518 getTextureParams: function ( value, matParams ) { 519 520 var texParams = { 521 522 scale: new THREE3DP.Vector2( 1, 1 ), 523 offset: new THREE3DP.Vector2( 0, 0 ) 524 525 }; 526 527 var items = value.split( /\s+/ ); 528 var pos; 529 530 pos = items.indexOf( '-bm' ); 531 532 if ( pos >= 0 ) { 533 534 matParams.bumpScale = parseFloat( items[ pos + 1 ] ); 535 items.splice( pos, 2 ); 536 537 } 538 539 pos = items.indexOf( '-s' ); 540 541 if ( pos >= 0 ) { 542 543 texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) ); 544 items.splice( pos, 4 ); // we expect 3 parameters here! 545 546 } 547
548 pos = items.indexOf( '-o' ); 549 550 if ( pos >= 0 ) { 551 552 texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) ); 553 items.splice( pos, 4 ); // we expect 3 parameters here! 554 555 } 556 557 texParams.url = items.join( ' ' ).trim(); 558 return texParams; 559 560 }, 561 562 loadTexture: function ( url, mapping, onLoad, onProgress, onError ) { 563 564 var texture; 565 var loader = THREE3DP.Loader.Handlers.get( url ); 566 var manager = ( this.manager !== undefined ) ? this.manager : THREE3DP.DefaultLoadingManager; 567 568 if ( loader === null ) { 569 570 loader = new THREE3DP.TextureLoader( manager ); 571 572 } 573 574 if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin ); 575 texture = loader.load( url, onLoad, onProgress, onError ); 576 577 if ( mapping !== undefined ) texture.mapping = mapping; 578 579 return texture; 580 581 } 582 583};
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.