1( function () { 2 3 class GLTFLoader extends THREE.Loader { 4 5 constructor( manager ) { 6 7 super( manager ); 8 this.dracoLoader = null; 9 this.ktx2Loader = null; 10 this.meshoptDecoder = null; 11 this.pluginCallbacks = []; 12 this.register( function ( parser ) { 13 14 return new GLTFMaterialsClearcoatExtension( parser ); 15 16 } ); 17 this.register( function ( parser ) { 18 19 return new GLTFTextureBasisUExtension( parser ); 20 21 } ); 22 this.register( function ( parser ) { 23 24 return new GLTFTextureWebPExtension( parser ); 25 26 } ); 27 this.register( function ( parser ) { 28 29 return new GLTFMaterialsSheenExtension( parser ); 30 31 } ); 32 this.register( function ( parser ) { 33 34 return new GLTFMaterialsTransmissionExtension( parser ); 35 36 } ); 37 this.register( function ( parser ) { 38 39 return new GLTFMaterialsVolumeExtension( parser ); 40 41 } ); 42 this.register( function ( parser ) { 43 44 return new GLTFMaterialsIorExtension( parser ); 45 46 } ); 47 this.register( function ( parser ) { 48 49 return new GLTFMaterialsSpecularExtension( parser ); 50 51 } ); 52 this.register( function ( parser ) { 53 54 return new GLTFLightsExtension( parser ); 55 56 } ); 57 this.register( function ( parser ) { 58 59 return new GLTFMeshoptCompression( parser ); 60 61 } ); 62 63 } 64 65 load( url, onLoad, onProgress, onError ) { 66 67 const scope = this; 68 let resourcePath; 69 70 if ( this.resourcePath !== '' ) { 71 72 resourcePath = this.resourcePath; 73 74 } else if ( this.path !== '' ) { 75 76 resourcePath = this.path; 77 78 } else { 79 80 resourcePath = THREE.LoaderUtils.extractUrlBase( url ); 81 82 } // Tells the LoadingManager to track an extra item, which resolves after 83 // the model is fully loaded. This means the count of items loaded will 84 // be incorrect, but ensures manager.onLoad() does not fire early. 85 86 87 this.manager.itemStart( url ); 88 89 const _onError = function ( e ) { 90 91 if ( onError ) { 92 93 onError( e ); 94 95 } else { 96 97 console.error( e ); 98 99 } 100 101 scope.manager.itemError( url ); 102 scope.manager.itemEnd( url ); 103 104 }; 105 106 const loader = new THREE.FileLoader( this.manager ); 107 loader.setPath( this.path ); 108 loader.setResponseType( 'arraybuffer' ); 109 loader.setRequestHeader( this.requestHeader ); 110 loader.setWithCredentials( this.withCredentials ); 111 loader.load( url, function ( data ) { 112 113 try { 114 115 scope.parse( data, resourcePath, function ( gltf ) { 116 117 onLoad( gltf ); 118 scope.manager.itemEnd( url ); 119 120 }, _onError ); 121 122 } catch ( e ) { 123 124 _onError( e ); 125 126 } 127 128 }, onProgress, _onError ); 129 130 } 131 132 setDRACOLoader( dracoLoader ) { 133 134 this.dracoLoader = dracoLoader; 135 return this; 136 137 } 138 139 setDDSLoader() { 140 141 throw new Error( 'THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".' ); 142 143 } 144 145 setKTX2Loader( ktx2Loader ) { 146 147 this.ktx2Loader = ktx2Loader; 148 return this; 149 150 } 151 152 setMeshoptDecoder( meshoptDecoder ) { 153 154 this.meshoptDecoder = meshoptDecoder; 155 return this; 156 157 } 158 159 register( callback ) { 160 161 if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { 162 163 this.pluginCallbacks.push( callback ); 164 165 } 166 167 return this; 168 169 } 170 171 unregister( callback ) { 172 173 if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { 174 175 this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); 176 177 } 178 179 return this; 180 181 } 182 183 parse( data, path, onLoad, onError ) { 184 185 let content; 186 const extensions = {}; 187 const plugins = {}; 188 189 if ( typeof data === 'string' ) { 190 191 content = data; 192 193 } else { 194 195 const magic = THREE.LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) ); 196 197 if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { 198 199 try { 200 201 extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); 202 203 } catch ( error ) { 204 205 if ( onError ) onError( error ); 206 return; 207 208 } 209 210 content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; 211 212 } else { 213 214 content = THREE.LoaderUtils.decodeText( new Uint8Array( data ) ); 215 216 } 217 218 } 219 220 const json = JSON.parse( content ); 221 222 if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { 223 224 if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); 225 return; 226 227 } 228 229 const parser = new GLTFParser( json, { 230 path: path || this.resourcePath || '', 231 crossOrigin: this.crossOrigin, 232 requestHeader: this.requestHeader, 233 manager: this.manager, 234 ktx2Loader: this.ktx2Loader, 235 meshoptDecoder: this.meshoptDecoder 236 } ); 237 parser.fileLoader.setRequestHeader( this.requestHeader ); 238 239 for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) { 240 241 const plugin = this.pluginCallbacks[ i ]( parser ); 242 plugins[ plugin.name ] = plugin; // Workaround to avoid determining as unknown extension 243 // in addUnknownExtensionsToUserData(). 244 // Remove this workaround if we move all the existing 245 // extension handlers to plugin system 246 247 extensions[ plugin.name ] = true; 248 249 } 250 251 if ( json.extensionsUsed ) { 252 253 for ( let i = 0; i < json.extensionsUsed.length; ++ i ) { 254 255 const extensionName = json.extensionsUsed[ i ]; 256 const extensionsRequired = json.extensionsRequired || []; 257 258 switch ( extensionName ) { 259 260 case EXTENSIONS.KHR_MATERIALS_UNLIT: 261 extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); 262 break; 263 264 case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 265 extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension(); 266 break; 267 268 case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: 269 extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); 270 break; 271 272 case EXTENSIONS.KHR_TEXTURE_TRANSFORM: 273 extensions[ extensionName ] = new GLTFTextureTransformExtension(); 274 break; 275
vendor: 4,990 bytes, lines 276-458
276 case EXTENSIONS.KHR_MESH_QUANTIZATION: 277 extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); 278 break; 279 280 default: 281 if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) { 282 283 console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' ); 284 285 } 286 287 } 288 289 } 290 291 } 292 293 parser.setExtensions( extensions ); 294 parser.setPlugins( plugins ); 295 parser.parse( onLoad, onError ); 296 297 } 298 299 parseAsync( data, path ) { 300 301 const scope = this; 302 return new Promise( function ( resolve, reject ) { 303 304 scope.parse( data, path, resolve, reject ); 305 306 } ); 307 308 } 309 310 } 311 /* GLTFREGISTRY */ 312 313 314 function GLTFRegistry() { 315 316 let objects = {}; 317 return { 318 get: function ( key ) { 319 320 return objects[ key ]; 321 322 }, 323 add: function ( key, object ) { 324 325 objects[ key ] = object; 326 327 }, 328 remove: function ( key ) { 329 330 delete objects[ key ]; 331 332 }, 333 removeAll: function () { 334 335 objects = {}; 336 337 } 338 }; 339 340 } 341 /*********************************/ 342 343 /********** EXTENSIONS ***********/ 344 345 /*********************************/ 346 347 348 const EXTENSIONS = { 349 KHR_BINARY_GLTF: 'KHR_binary_glTF', 350 KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', 351 KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', 352 KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', 353 KHR_MATERIALS_IOR: 'KHR_materials_ior', 354 KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', 355 KHR_MATERIALS_SHEEN: 'KHR_materials_sheen', 356 KHR_MATERIALS_SPECULAR: 'KHR_materials_specular', 357 KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', 358 KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', 359 KHR_MATERIALS_VOLUME: 'KHR_materials_volume', 360 KHR_TEXTURE_BASISU: 'KHR_texture_basisu', 361 KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', 362 KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', 363 EXT_TEXTURE_WEBP: 'EXT_texture_webp', 364 EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression' 365 }; 366 /** 367 * Punctual Lights Extension 368 * 369 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual 370 */ 371 372 class GLTFLightsExtension { 373 374 constructor( parser ) { 375 376 this.parser = parser; 377 this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL; // THREE.Object3D instance caches 378 379 this.cache = { 380 refs: {}, 381 uses: {} 382 }; 383 384 } 385 386 _markDefs() { 387 388 const parser = this.parser; 389 const nodeDefs = this.parser.json.nodes || []; 390 391 for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { 392 393 const nodeDef = nodeDefs[ nodeIndex ]; 394 395 if ( nodeDef.extensions && nodeDef.extensions[ this.name ] && nodeDef.extensions[ this.name ].light !== undefined ) { 396 397 parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light ); 398 399 } 400 401 } 402 403 } 404 405 _loadLight( lightIndex ) { 406 407 const parser = this.parser; 408 const cacheKey = 'light:' + lightIndex; 409 let dependency = parser.cache.get( cacheKey ); 410 if ( dependency ) return dependency; 411 const json = parser.json; 412 const extensions = json.extensions && json.extensions[ this.name ] || {}; 413 const lightDefs = extensions.lights || []; 414 const lightDef = lightDefs[ lightIndex ]; 415 let lightNode; 416 const color = new THREE.Color( 0xffffff ); 417 if ( lightDef.color !== undefined ) color.fromArray( lightDef.color ); 418 const range = lightDef.range !== undefined ? lightDef.range : 0; 419 420 switch ( lightDef.type ) { 421 422 case 'directional': 423 lightNode = new THREE.DirectionalLight( color ); 424 lightNode.target.position.set( 0, 0, - 1 ); 425 lightNode.add( lightNode.target ); 426 break; 427 428 case 'point': 429 lightNode = new THREE.PointLight( color ); 430 lightNode.distance = range; 431 break; 432 433 case 'spot': 434 lightNode = new THREE.SpotLight( color ); 435 lightNode.distance = range; // Handle spotlight properties. 436 437 lightDef.spot = lightDef.spot || {}; 438 lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; 439 lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; 440 lightNode.angle = lightDef.spot.outerConeAngle; 441 lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; 442 lightNode.target.position.set( 0, 0, - 1 ); 443 lightNode.add( lightNode.target ); 444 break; 445 446 default: 447 throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type ); 448 449 } // Some lights (e.g. spot) default to a position other than the origin. Reset the position 450 // here, because node-level parsing will only override position if explicitly specified. 451 452 453 lightNode.position.set( 0, 0, 0 ); 454 lightNode.decay = 2; 455 if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; 456 lightNode.name = parser.createUniqueName( lightDef.name || 'light_' + lightIndex ); 457 dependency = Promise.resolve( lightNode ); 458 parser.cache.add( cacheKey, dependency );
459 return dependency; 460 461 } 462 463 createNodeAttachment( nodeIndex ) { 464 465 const self = this; 466 const parser = this.parser; 467 const json = parser.json; 468 const nodeDef = json.nodes[ nodeIndex ]; 469 const lightDef = nodeDef.extensions && nodeDef.extensions[ this.name ] || {}; 470 const lightIndex = lightDef.light; 471 if ( lightIndex === undefined ) return null; 472 return this._loadLight( lightIndex ).then( function ( light ) { 473 474 return parser._getNodeRef( self.cache, lightIndex, light ); 475 476 } ); 477 478 } 479 480 } 481 /** 482 * Unlit Materials Extension 483 * 484 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit 485 */ 486 487 488 class GLTFMaterialsUnlitExtension { 489 490 constructor() { 491 492 this.name = EXTENSIONS.KHR_MATERIALS_UNLIT; 493 494 } 495 496 getMaterialType() { 497 498 return THREE.MeshBasicMaterial; 499 500 } 501 502 extendParams( materialParams, materialDef, parser ) { 503 504 const pending = []; 505 materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); 506 materialParams.opacity = 1.0; 507 const metallicRoughness = materialDef.pbrMetallicRoughness; 508 509 if ( metallicRoughness ) { 510 511 if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { 512 513 const array = metallicRoughness.baseColorFactor; 514 materialParams.color.fromArray( array ); 515 materialParams.opacity = array[ 3 ]; 516 517 } 518 519 if ( metallicRoughness.baseColorTexture !== undefined ) { 520 521 pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); 522 523 } 524 525 } 526 527 return Promise.all( pending ); 528 529 } 530 531 } 532 /** 533 * Clearcoat Materials Extension 534 * 535 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat 536 */ 537 538 539 class GLTFMaterialsClearcoatExtension { 540 541 constructor( parser ) { 542 543 this.parser = parser; 544 this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT; 545 546 } 547 548 getMaterialType( materialIndex ) { 549 550 const parser = this.parser; 551 const materialDef = parser.json.materials[ materialIndex ]; 552 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; 553 return THREE.MeshPhysicalMaterial; 554 555 } 556 557 extendMaterialParams( materialIndex, materialParams ) { 558 559 const parser = this.parser; 560 const materialDef = parser.json.materials[ materialIndex ]; 561 562 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { 563 564 return Promise.resolve(); 565 566 } 567 568 const pending = []; 569 const extension = materialDef.extensions[ this.name ]; 570 571 if ( extension.clearcoatFactor !== undefined ) { 572 573 materialParams.clearcoat = extension.clearcoatFactor; 574 575 } 576 577 if ( extension.clearcoatTexture !== undefined ) { 578 579 pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); 580 581 } 582 583 if ( extension.clearcoatRoughnessFactor !== undefined ) { 584 585 materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; 586 587 } 588 589 if ( extension.clearcoatRoughnessTexture !== undefined ) { 590 591 pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); 592 593 } 594 595 if ( extension.clearcoatNormalTexture !== undefined ) { 596 597 pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); 598 599 if ( extension.clearcoatNormalTexture.scale !== undefined ) { 600 601 const scale = extension.clearcoatNormalTexture.scale; 602 materialParams.clearcoatNormalScale = new THREE.Vector2( scale, scale ); 603 604 } 605 606 } 607 608 return Promise.all( pending ); 609 610 } 611 612 } 613 /** 614 * Sheen Materials Extension 615 * 616 * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen 617 */ 618 619 620 class GLTFMaterialsSheenExtension { 621 622 constructor( parser ) { 623 624 this.parser = parser; 625 this.name = EXTENSIONS.KHR_MATERIALS_SHEEN; 626 627 } 628 629 getMaterialType( materialIndex ) { 630 631 const parser = this.parser; 632 const materialDef = parser.json.materials[ materialIndex ]; 633 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; 634 return THREE.MeshPhysicalMaterial; 635 636 } 637 638 extendMaterialParams( materialIndex, materialParams ) { 639 640 const parser = this.parser; 641 const materialDef = parser.json.materials[ materialIndex ]; 642 643 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { 644 645 return Promise.resolve(); 646 647 } 648 649 const pending = []; 650 materialParams.sheenColor = new THREE.Color( 0, 0, 0 ); 651 materialParams.sheenRoughness = 0; 652 materialParams.sheen = 1; 653 const extension = materialDef.extensions[ this.name ]; 654 655 if ( extension.sheenColorFactor !== undefined ) { 656 657 materialParams.sheenColor.fromArray( extension.sheenColorFactor ); 658 659 } 660 661 if ( extension.sheenRoughnessFactor !== undefined ) { 662 663 materialParams.sheenRoughness = extension.sheenRoughnessFactor; 664 665 } 666 667 if ( extension.sheenColorTexture !== undefined ) { 668 669 pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture ) ); 670 671 } 672 673 if ( extension.sheenRoughnessTexture !== undefined ) { 674 675 pending.push( parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) ); 676 677 } 678 679 return Promise.all( pending ); 680 681 } 682 683 } 684 /** 685 * Transmission Materials Extension 686 * 687 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission 688 * Draft: https://github.com/KhronosGroup/glTF/pull/1698 689 */ 690 691 692 class GLTFMaterialsTransmissionExtension { 693 694 constructor( parser ) { 695 696 this.parser = parser; 697 this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION; 698 699 } 700 701 getMaterialType( materialIndex ) { 702 703 const parser = this.parser; 704 const materialDef = parser.json.materials[ materialIndex ]; 705 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; 706 return THREE.MeshPhysicalMaterial; 707 708 } 709 710 extendMaterialParams( materialIndex, materialParams ) { 711 712 const parser = this.parser; 713 const materialDef = parser.json.materials[ materialIndex ]; 714 715 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { 716 717 return Promise.resolve(); 718 719 } 720 721 const pending = []; 722 const extension = materialDef.extensions[ this.name ]; 723 724 if ( extension.transmissionFactor !== undefined ) { 725 726 materialParams.transmission = extension.transmissionFactor; 727 728 } 729 730 if ( extension.transmissionTexture !== undefined ) { 731 732 pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); 733 734 } 735 736 return Promise.all( pending ); 737 738 } 739 740 } 741 /** 742 * Materials Volume Extension 743 * 744 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume 745 */ 746 747 748 class GLTFMaterialsVolumeExtension { 749 750 constructor( parser ) { 751 752 this.parser = parser; 753 this.name = EXTENSIONS.KHR_MATERIALS_VOLUME; 754 755 } 756 757 getMaterialType( materialIndex ) { 758 759 const parser = this.parser; 760 const materialDef = parser.json.materials[ materialIndex ]; 761 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; 762 return THREE.MeshPhysicalMaterial; 763 764 } 765 766 extendMaterialParams( materialIndex, materialParams ) { 767 768 const parser = this.parser; 769 const materialDef = parser.json.materials[ materialIndex ]; 770 771 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { 772 773 return Promise.resolve(); 774 775 } 776 777 const pending = []; 778 const extension = materialDef.extensions[ this.name ]; 779 materialParams.thickness = extension.thicknessFactor !== undefined ? extension.thicknessFactor : 0; 780 781 if ( extension.thicknessTexture !== undefined ) { 782 783 pending.push( parser.assignTexture( materialParams, 'thicknessMap', extension.thicknessTexture ) ); 784 785 } 786 787 materialParams.attenuationDistance = extension.attenuationDistance || 0; 788 const colorArray = extension.attenuationColor || [ 1, 1, 1 ]; 789 materialParams.attenuationColor = new THREE.Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] ); 790 return Promise.all( pending ); 791 792 } 793 794 } 795 /** 796 * Materials ior Extension 797 * 798 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior 799 */ 800 801
802 class GLTFMaterialsIorExtension { 803 804 constructor( parser ) { 805 806 this.parser = parser; 807 this.name = EXTENSIONS.KHR_MATERIALS_IOR; 808 809 } 810 811 getMaterialType( materialIndex ) { 812 813 const parser = this.parser; 814 const materialDef = parser.json.materials[ materialIndex ]; 815 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; 816 return THREE.MeshPhysicalMaterial; 817 818 } 819 820 extendMaterialParams( materialIndex, materialParams ) { 821 822 const parser = this.parser; 823 const materialDef = parser.json.materials[ materialIndex ]; 824 825 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { 826 827 return Promise.resolve(); 828 829 } 830 831 const extension = materialDef.extensions[ this.name ]; 832 materialParams.ior = extension.ior !== undefined ? extension.ior : 1.5; 833 return Promise.resolve(); 834 835 } 836 837 } 838 /** 839 * Materials specular Extension 840 * 841 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular 842 */ 843 844 845 class GLTFMaterialsSpecularExtension { 846 847 constructor( parser ) { 848 849 this.parser = parser; 850 this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR; 851 852 } 853 854 getMaterialType( materialIndex ) { 855 856 const parser = this.parser; 857 const materialDef = parser.json.materials[ materialIndex ]; 858 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; 859 return THREE.MeshPhysicalMaterial; 860 861 } 862 863 extendMaterialParams( materialIndex, materialParams ) { 864 865 const parser = this.parser; 866 const materialDef = parser.json.materials[ materialIndex ]; 867 868 if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { 869 870 return Promise.resolve(); 871 872 } 873 874 const pending = []; 875 const extension = materialDef.extensions[ this.name ]; 876 materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0; 877 878 if ( extension.specularTexture !== undefined ) { 879 880 pending.push( parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) ); 881 882 } 883 884 const colorArray = extension.specularColorFactor || [ 1, 1, 1 ]; 885 materialParams.specularColor = new THREE.Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] ); 886 887 if ( extension.specularColorTexture !== undefined ) { 888 889 pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture ).then( function ( texture ) { 890 891 texture.encoding = THREE.sRGBEncoding; 892 893 } ) ); 894 895 } 896 897 return Promise.all( pending ); 898 899 } 900 901 } 902 /** 903 * BasisU THREE.Texture Extension 904 * 905 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu 906 */ 907 908 909 class GLTFTextureBasisUExtension { 910 911 constructor( parser ) { 912 913 this.parser = parser; 914 this.name = EXTENSIONS.KHR_TEXTURE_BASISU; 915 916 } 917 918 loadTexture( textureIndex ) { 919 920 const parser = this.parser; 921 const json = parser.json; 922 const textureDef = json.textures[ textureIndex ]; 923 924 if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { 925 926 return null; 927 928 } 929 930 const extension = textureDef.extensions[ this.name ]; 931 const source = json.images[ extension.source ]; 932 const loader = parser.options.ktx2Loader; 933 934 if ( ! loader ) { 935 936 if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { 937 938 throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); 939 940 } else { 941 942 // Assumes that the extension is optional and that a fallback texture is present 943 return null; 944 945 } 946 947 } 948 949 return parser.loadTextureImage( textureIndex, source, loader ); 950 951 } 952 953 } 954 /** 955 * WebP THREE.Texture Extension 956 * 957 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp 958 */ 959 960 961 class GLTFTextureWebPExtension { 962 963 constructor( parser ) { 964 965 this.parser = parser; 966 this.name = EXTENSIONS.EXT_TEXTURE_WEBP; 967 this.isSupported = null; 968 969 } 970 971 loadTexture( textureIndex ) { 972 973 const name = this.name; 974 const parser = this.parser; 975 const json = parser.json; 976 const textureDef = json.textures[ textureIndex ]; 977 978 if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { 979 980 return null; 981 982 } 983 984 const extension = textureDef.extensions[ name ]; 985 const source = json.images[ extension.source ]; 986 let loader = parser.textureLoader; 987 988 if ( source.uri ) { 989 990 const handler = parser.options.manager.getHandler( source.uri ); 991 if ( handler !== null ) loader = handler; 992 993 } 994 995 return this.detectSupport().then( function ( isSupported ) { 996 997 if ( isSupported ) return parser.loadTextureImage( textureIndex, source, loader ); 998 999 if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { 1000 1001 throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' ); 1002 1003 } // Fall back to PNG or JPEG. 1004 1005 1006 return parser.loadTexture( textureIndex ); 1007 1008 } ); 1009 1010 } 1011 1012 detectSupport() { 1013 1014 if ( ! this.isSupported ) { 1015 1016 this.isSupported = new Promise( function ( resolve ) { 1017 1018 const image = new Image(); // Lossy test image. Support for lossy images doesn't guarantee support for all 1019 // WebP images, unfortunately. 1020 1021 image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'; 1022 1023 image.onload = image.onerror = function () { 1024 1025 resolve( image.height === 1 ); 1026 1027 }; 1028 1029 } ); 1030 1031 } 1032 1033 return this.isSupported; 1034 1035 } 1036 1037 } 1038 /** 1039 * meshopt BufferView Compression Extension 1040 * 1041 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression 1042 */ 1043 1044 1045 class GLTFMeshoptCompression { 1046 1047 constructor( parser ) { 1048 1049 this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION; 1050 this.parser = parser; 1051 1052 } 1053 1054 loadBufferView( index ) { 1055 1056 const json = this.parser.json; 1057 const bufferView = json.bufferViews[ index ]; 1058 1059 if ( bufferView.extensions && bufferView.extensions[ this.name ] ) { 1060 1061 const extensionDef = bufferView.extensions[ this.name ]; 1062 const buffer = this.parser.getDependency( 'buffer', extensionDef.buffer ); 1063 const decoder = this.parser.options.meshoptDecoder; 1064 1065 if ( ! decoder || ! decoder.supported ) { 1066 1067 if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { 1068 1069 throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' ); 1070 1071 } else { 1072 1073 // Assumes that the extension is optional and that fallback buffer data is present 1074 return null; 1075 1076 } 1077 1078 } 1079 1080 return Promise.all( [ buffer, decoder.ready ] ).then( function ( res ) { 1081 1082 const byteOffset = extensionDef.byteOffset || 0; 1083 const byteLength = extensionDef.byteLength || 0; 1084 const count = extensionDef.count; 1085 const stride = extensionDef.byteStride; 1086 const result = new ArrayBuffer( count * stride ); 1087 const source = new Uint8Array( res[ 0 ], byteOffset, byteLength ); 1088 decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter ); 1089 return result; 1090 1091 } ); 1092 1093 } else { 1094 1095 return null; 1096 1097 } 1098 1099 } 1100 1101 } 1102 /* BINARY EXTENSION */ 1103 1104 1105 const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; 1106 const BINARY_EXTENSION_HEADER_LENGTH = 12; 1107 const BINARY_EXTENSION_CHUNK_TYPES = { 1108 JSON: 0x4E4F534A, 1109 BIN: 0x004E4942 1110 }; 1111 1112 class GLTFBinaryExtension { 1113 1114 constructor( data ) { 1115 1116 this.name = EXTENSIONS.KHR_BINARY_GLTF; 1117 this.content = null; 1118 this.body = null; 1119 const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); 1120 this.header = { 1121 magic: THREE.LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ), 1122 version: headerView.getUint32( 4, true ), 1123 length: headerView.getUint32( 8, true ) 1124 }; 1125 1126 if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { 1127 1128 throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); 1129 1130 } else if ( this.header.version < 2.0 ) { 1131 1132 throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); 1133 1134 } 1135 1136 const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH; 1137 const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); 1138 let chunkIndex = 0; 1139 1140 while ( chunkIndex < chunkContentsLength ) { 1141
1142 const chunkLength = chunkView.getUint32( chunkIndex, true ); 1143 chunkIndex += 4; 1144 const chunkType = chunkView.getUint32( chunkIndex, true ); 1145 chunkIndex += 4; 1146 1147 if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { 1148 1149 const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); 1150 this.content = THREE.LoaderUtils.decodeText( contentArray ); 1151 1152 } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { 1153 1154 const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; 1155 this.body = data.slice( byteOffset, byteOffset + chunkLength ); 1156 1157 } // Clients must ignore chunks with unknown types. 1158 1159 1160 chunkIndex += chunkLength; 1161 1162 } 1163 1164 if ( this.content === null ) { 1165 1166 throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); 1167 1168 } 1169 1170 } 1171 1172 } 1173 /** 1174 * DRACO THREE.Mesh Compression Extension 1175 * 1176 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression 1177 */ 1178 1179 1180 class GLTFDracoMeshCompressionExtension { 1181 1182 constructor( json, dracoLoader ) { 1183 1184 if ( ! dracoLoader ) { 1185 1186 throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); 1187 1188 } 1189 1190 this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; 1191 this.json = json; 1192 this.dracoLoader = dracoLoader; 1193 this.dracoLoader.preload(); 1194 1195 } 1196 1197 decodePrimitive( primitive, parser ) { 1198 1199 const json = this.json; 1200 const dracoLoader = this.dracoLoader; 1201 const bufferViewIndex = primitive.extensions[ this.name ].bufferView; 1202 const gltfAttributeMap = primitive.extensions[ this.name ].attributes; 1203 const threeAttributeMap = {}; 1204 const attributeNormalizedMap = {}; 1205 const attributeTypeMap = {}; 1206 1207 for ( const attributeName in gltfAttributeMap ) { 1208 1209 const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); 1210 threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; 1211 1212 } 1213 1214 for ( const attributeName in primitive.attributes ) { 1215 1216 const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); 1217 1218 if ( gltfAttributeMap[ attributeName ] !== undefined ) { 1219 1220 const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; 1221 const componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; 1222 attributeTypeMap[ threeAttributeName ] = componentType; 1223 attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; 1224 1225 } 1226 1227 } 1228 1229 return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { 1230 1231 return new Promise( function ( resolve ) { 1232 1233 dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { 1234 1235 for ( const attributeName in geometry.attributes ) { 1236 1237 const attribute = geometry.attributes[ attributeName ]; 1238 const normalized = attributeNormalizedMap[ attributeName ]; 1239 if ( normalized !== undefined ) attribute.normalized = normalized; 1240 1241 } 1242 1243 resolve( geometry ); 1244 1245 }, threeAttributeMap, attributeTypeMap ); 1246 1247 } ); 1248 1249 } ); 1250 1251 } 1252 1253 } 1254 /** 1255 * THREE.Texture Transform Extension 1256 * 1257 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform 1258 */ 1259 1260 1261 class GLTFTextureTransformExtension { 1262 1263 constructor() { 1264 1265 this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM; 1266 1267 } 1268 1269 extendTexture( texture, transform ) { 1270 1271 if ( transform.texCoord !== undefined ) { 1272 1273 console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' ); 1274 1275 } 1276 1277 if ( transform.offset === undefined && transform.rotation === undefined && transform.scale === undefined ) { 1278 1279 // See https://github.com/mrdoob/three.js/issues/21819. 1280 return texture; 1281 1282 } 1283 1284 texture = texture.clone(); 1285 1286 if ( transform.offset !== undefined ) { 1287 1288 texture.offset.fromArray( transform.offset ); 1289 1290 } 1291 1292 if ( transform.rotation !== undefined ) { 1293 1294 texture.rotation = transform.rotation; 1295 1296 } 1297 1298 if ( transform.scale !== undefined ) { 1299 1300 texture.repeat.fromArray( transform.scale ); 1301 1302 } 1303 1304 texture.needsUpdate = true; 1305 return texture; 1306 1307 } 1308 1309 } 1310 /** 1311 * Specular-Glossiness Extension 1312 * 1313 * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness 1314 */ 1315 1316 /** 1317 * A sub class of StandardMaterial with some of the functionality 1318 * changed via the `onBeforeCompile` callback 1319 * @pailhead 1320 */ 1321 1322 1323 class GLTFMeshStandardSGMaterial extends THREE.MeshStandardMaterial { 1324 1325 constructor( params ) { 1326 1327 super(); 1328 this.isGLTFSpecularGlossinessMaterial = true; //various chunks that need replacing 1329 1330 const specularMapParsFragmentChunk = [ '#ifdef USE_SPECULARMAP', ' uniform sampler2D specularMap;', '#endif' ].join( '\n' ); 1331 const glossinessMapParsFragmentChunk = [ '#ifdef USE_GLOSSINESSMAP', ' uniform sampler2D glossinessMap;', '#endif' ].join( '\n' ); 1332 const specularMapFragmentChunk = [ 'vec3 specularFactor = specular;', '#ifdef USE_SPECULARMAP', ' vec4 texelSpecular = texture2D( specularMap, vUv );', ' texelSpecular = sRGBToLinear( texelSpecular );', ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', ' specularFactor *= texelSpecular.rgb;', '#endif' ].join( '\n' ); 1333 const glossinessMapFragmentChunk = [ 'float glossinessFactor = glossiness;', '#ifdef USE_GLOSSINESSMAP', ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', ' glossinessFactor *= texelGlossiness.a;', '#endif' ].join( '\n' ); 1334 const lightPhysicalFragmentChunk = [ 'PhysicalMaterial material;', 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );
1334', 'material.roughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', 'material.roughness += geometryRoughness;', 'material.roughness = min( material.roughness, 1.0 );', 'material.specularColor = specularFactor;' ].join( '\n' ); 1335 const uniforms = { 1336 specular: { 1337 value: new THREE.Color().setHex( 0xffffff ) 1338 }, 1339 glossiness: { 1340 value: 1 1341 }, 1342 specularMap: { 1343 value: null 1344 }, 1345 glossinessMap: { 1346 value: null 1347 } 1348 }; 1349 this._extraUniforms = uniforms; 1350 1351 this.onBeforeCompile = function ( shader ) { 1352 1353 for ( const uniformName in uniforms ) { 1354 1355 shader.uniforms[ uniformName ] = uniforms[ uniformName ]; 1356 1357 } 1358 1359 shader.fragmentShader = shader.fragmentShader.replace( 'uniform float roughness;', 'uniform vec3 specular;' ).replace( 'uniform float metalness;', 'uniform float glossiness;' ).replace( '#include <roughnessmap_pars_fragment>', specularMapParsFragmentChunk ).replace( '#include <metalnessmap_pars_fragment>', glossinessMapParsFragmentChunk ).replace( '#include <roughnessmap_fragment>', specularMapFragmentChunk ).replace( '#include <metalnessmap_fragment>', glossinessMapFragmentChunk ).replace( '#include <lights_physical_fragment>', lightPhysicalFragmentChunk ); 1360 1361 }; 1362 1363 Object.defineProperties( this, { 1364 specular: { 1365 get: function () { 1366 1367 return uniforms.specular.value; 1368 1369 }, 1370 set: function ( v ) { 1371 1372 uniforms.specular.value = v; 1373 1374 } 1375 }, 1376 specularMap: { 1377 get: function () { 1378 1379 return uniforms.specularMap.value; 1380 1381 }, 1382 set: function ( v ) { 1383 1384 uniforms.specularMap.value = v; 1385 1386 if ( v ) { 1387 1388 this.defines.USE_SPECULARMAP = ''; // USE_UV is set by the renderer for specular maps 1389 1390 } else { 1391 1392 delete this.defines.USE_SPECULARMAP; 1393 1394 } 1395 1396 } 1397 }, 1398 glossiness: { 1399 get: function () { 1400 1401 return uniforms.glossiness.value; 1402 1403 }, 1404 set: function ( v ) { 1405 1406 uniforms.glossiness.value = v; 1407 1408 } 1409 }, 1410 glossinessMap: { 1411 get: function () { 1412 1413 return uniforms.glossinessMap.value; 1414 1415 }, 1416 set: function ( v ) { 1417 1418 uniforms.glossinessMap.value = v; 1419 1420 if ( v ) { 1421 1422 this.defines.USE_GLOSSINESSMAP = ''; 1423 this.defines.USE_UV = ''; 1424 1425 } else { 1426 1427 delete this.defines.USE_GLOSSINESSMAP; 1428 delete this.defines.USE_UV; 1429 1430 } 1431 1432 } 1433 } 1434 } ); 1435 delete this.metalness; 1436 delete this.roughness; 1437 delete this.metalnessMap; 1438 delete this.roughnessMap; 1439 this.setValues( params ); 1440 1441 } 1442 1443 copy( source ) { 1444 1445 super.copy( source ); 1446 this.specularMap = source.specularMap; 1447 this.specular.copy( source.specular ); 1448 this.glossinessMap = source.glossinessMap; 1449 this.glossiness = source.glossiness; 1450 delete this.metalness; 1451 delete this.roughness; 1452 delete this.metalnessMap; 1453 delete this.roughnessMap; 1454 return this; 1455 1456 } 1457 1458 } 1459 1460 class GLTFMaterialsPbrSpecularGlossinessExtension { 1461 1462 constructor() { 1463 1464 this.name = EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS; 1465 this.specularGlossinessParams = [ 'color', 'map', 'lightMap', 'lightMapIntensity', 'aoMap', 'aoMapIntensity', 'emissive', 'emissiveIntensity', 'emissiveMap', 'bumpMap', 'bumpScale', 'normalMap', 'normalMapType', 'displacementMap', 'displacementScale', 'displacementBias', 'specularMap', 'specular', 'glossinessMap', 'glossiness', 'alphaMap', 'envMap', 'envMapIntensity', 'refractionRatio' ]; 1466 1467 } 1468 1469 getMaterialType() { 1470 1471 return GLTFMeshStandardSGMaterial; 1472 1473 } 1474 1475 extendParams( materialParams, materialDef, parser ) { 1476 1477 const pbrSpecularGlossiness = materialDef.extensions[ this.name ]; 1478 materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); 1479 materialParams.opacity = 1.0; 1480 const pending = []; 1481 1482 if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) { 1483 1484 const array = pbrSpecularGlossiness.diffuseFactor; 1485 materialParams.color.fromArray( array ); 1486 materialParams.opacity = array[ 3 ]; 1487 1488 } 1489 1490 if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) { 1491 1492 pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture ) ); 1493 1494 } 1495 1496 materialParams.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); 1497 materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0; 1498 materialParams.specular = new THREE.Color( 1.0, 1.0, 1.0 ); 1499 1500 if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) { 1501 1502 materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor ); 1503 1504 } 1505 1506 if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) { 1507 1508 const specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture; 1509 pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) ); 1510 pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef ) ); 1511 1512 } 1513 1514 return Promise.all( pending ); 1515 1516 } 1517 1518 createMaterial( materialParams ) { 1519 1520 const material = new GLTFMeshStandardSGMaterial( materialParams ); 1521 material.fog = true; 1522 material.color = materialParams.color; 1523 material.map = materialParams.map === undefined ? null : materialParams.map; 1524 material.lightMap = null; 1525 material.lightMapIntensity = 1.0; 1526 material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap; 1527 material.aoMapIntensity = 1.0; 1528 material.emissive = materialParams.emissive; 1529 material.emissiveIntensity = 1.0; 1530 material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap; 1531 material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap; 1532 material.bumpScale = 1; 1533 material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap; 1534 material.normalMapType = THREE.TangentSpaceNormalMap; 1535 if ( materialParams.normalScale ) material.normalScale = materialParams.normalScale; 1536 material.displacementMap = null; 1537 material.displacementScale = 1; 1538 material.displacementBias = 0; 1539 material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap; 1540 material.specular = materialParams.specular; 1541 material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap; 1542 material.glossiness = materialParams.glossiness; 1543 material.alphaMap = null; 1544 material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap; 1545 material.envMapIntensity = 1.0; 1546 material.refractionRatio = 0.98; 1547 return material; 1548 1549 } 1550 1551 } 1552 /** 1553 * THREE.Mesh Quantization Extension 1554 * 1555 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization 1556 */ 1557 1558 1559 class GLTFMeshQuantizationExtension { 1560 1561 constructor() { 1562
vendor: 6,172 bytes, lines 1563-1823
1563 this.name = EXTENSIONS.KHR_MESH_QUANTIZATION; 1564 1565 } 1566 1567 } 1568 /*********************************/ 1569 1570 /********** INTERPOLATION ********/ 1571 1572 /*********************************/ 1573 // Spline Interpolation 1574 // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation 1575 1576 1577 class GLTFCubicSplineInterpolant extends THREE.Interpolant { 1578 1579 constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { 1580 1581 super( parameterPositions, sampleValues, sampleSize, resultBuffer ); 1582 1583 } 1584 1585 copySampleValue_( index ) { 1586 1587 // Copies a sample value to the result buffer. See description of glTF 1588 // CUBICSPLINE values layout in interpolate_() function below. 1589 const result = this.resultBuffer, 1590 values = this.sampleValues, 1591 valueSize = this.valueSize, 1592 offset = index * valueSize * 3 + valueSize; 1593 1594 for ( let i = 0; i !== valueSize; i ++ ) { 1595 1596 result[ i ] = values[ offset + i ]; 1597 1598 } 1599 1600 return result; 1601 1602 } 1603 1604 } 1605 1606 GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; 1607 GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; 1608 1609 GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) { 1610 1611 const result = this.resultBuffer; 1612 const values = this.sampleValues; 1613 const stride = this.valueSize; 1614 const stride2 = stride * 2; 1615 const stride3 = stride * 3; 1616 const td = t1 - t0; 1617 const p = ( t - t0 ) / td; 1618 const pp = p * p; 1619 const ppp = pp * p; 1620 const offset1 = i1 * stride3; 1621 const offset0 = offset1 - stride3; 1622 const s2 = - 2 * ppp + 3 * pp; 1623 const s3 = ppp - pp; 1624 const s0 = 1 - s2; 1625 const s1 = s3 - pp + p; // Layout of keyframe output values for CUBICSPLINE animations: 1626 // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] 1627 1628 for ( let i = 0; i !== stride; i ++ ) { 1629 1630 const p0 = values[ offset0 + i + stride ]; // splineVertex_k 1631 1632 const m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) 1633 1634 const p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 1635 1636 const m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) 1637 1638 result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; 1639 1640 } 1641 1642 return result; 1643 1644 }; 1645 1646 const _q = new THREE.Quaternion(); 1647 1648 class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { 1649 1650 interpolate_( i1, t0, t, t1 ) { 1651 1652 const result = super.interpolate_( i1, t0, t, t1 ); 1653 1654 _q.fromArray( result ).normalize().toArray( result ); 1655 1656 return result; 1657 1658 } 1659 1660 } 1661 /*********************************/ 1662 1663 /********** INTERNALS ************/ 1664 1665 /*********************************/ 1666 1667 /* CONSTANTS */ 1668 1669 1670 const WEBGL_CONSTANTS = { 1671 FLOAT: 5126, 1672 //FLOAT_MAT2: 35674, 1673 FLOAT_MAT3: 35675, 1674 FLOAT_MAT4: 35676, 1675 FLOAT_VEC2: 35664, 1676 FLOAT_VEC3: 35665, 1677 FLOAT_VEC4: 35666, 1678 LINEAR: 9729, 1679 REPEAT: 10497, 1680 SAMPLER_2D: 35678, 1681 POINTS: 0, 1682 LINES: 1, 1683 LINE_LOOP: 2, 1684 LINE_STRIP: 3, 1685 TRIANGLES: 4, 1686 TRIANGLE_STRIP: 5, 1687 TRIANGLE_FAN: 6, 1688 UNSIGNED_BYTE: 5121, 1689 UNSIGNED_SHORT: 5123 1690 }; 1691 const WEBGL_COMPONENT_TYPES = { 1692 5120: Int8Array, 1693 5121: Uint8Array, 1694 5122: Int16Array, 1695 5123: Uint16Array, 1696 5125: Uint32Array, 1697 5126: Float32Array 1698 }; 1699 const WEBGL_FILTERS = { 1700 9728: THREE.NearestFilter, 1701 9729: THREE.LinearFilter, 1702 9984: THREE.NearestMipmapNearestFilter, 1703 9985: THREE.LinearMipmapNearestFilter, 1704 9986: THREE.NearestMipmapLinearFilter, 1705 9987: THREE.LinearMipmapLinearFilter 1706 }; 1707 const WEBGL_WRAPPINGS = { 1708 33071: THREE.ClampToEdgeWrapping, 1709 33648: THREE.MirroredRepeatWrapping, 1710 10497: THREE.RepeatWrapping 1711 }; 1712 const WEBGL_TYPE_SIZES = { 1713 'SCALAR': 1, 1714 'VEC2': 2, 1715 'VEC3': 3, 1716 'VEC4': 4, 1717 'MAT2': 4, 1718 'MAT3': 9, 1719 'MAT4': 16 1720 }; 1721 const ATTRIBUTES = { 1722 POSITION: 'position', 1723 NORMAL: 'normal', 1724 TANGENT: 'tangent', 1725 TEXCOORD_0: 'uv', 1726 TEXCOORD_1: 'uv2', 1727 COLOR_0: 'color', 1728 WEIGHTS_0: 'skinWeight', 1729 JOINTS_0: 'skinIndex' 1730 }; 1731 const PATH_PROPERTIES = { 1732 scale: 'scale', 1733 translation: 'position', 1734 rotation: 'quaternion', 1735 weights: 'morphTargetInfluences' 1736 }; 1737 const INTERPOLATION = { 1738 CUBICSPLINE: undefined, 1739 // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each 1740 // keyframe track will be initialized with a default interpolation type, then modified. 1741 LINEAR: THREE.InterpolateLinear, 1742 STEP: THREE.InterpolateDiscrete 1743 }; 1744 const ALPHA_MODES = { 1745 OPAQUE: 'OPAQUE', 1746 MASK: 'MASK', 1747 BLEND: 'BLEND' 1748 }; 1749 /** 1750 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material 1751 */ 1752 1753 function createDefaultMaterial( cache ) { 1754 1755 if ( cache[ 'DefaultMaterial' ] === undefined ) { 1756 1757 cache[ 'DefaultMaterial' ] = new THREE.MeshStandardMaterial( { 1758 color: 0xFFFFFF, 1759 emissive: 0x000000, 1760 metalness: 1, 1761 roughness: 1, 1762 transparent: false, 1763 depthTest: true, 1764 side: THREE.FrontSide 1765 } ); 1766 1767 } 1768 1769 return cache[ 'DefaultMaterial' ]; 1770 1771 } 1772 1773 function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { 1774 1775 // Add unknown glTF extensions to an object's userData. 1776 for ( const name in objectDef.extensions ) { 1777 1778 if ( knownExtensions[ name ] === undefined ) { 1779 1780 object.userData.gltfExtensions = object.userData.gltfExtensions || {}; 1781 object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; 1782 1783 } 1784 1785 } 1786 1787 } 1788 /** 1789 * @param {Object3D|Material|BufferGeometry} object 1790 * @param {GLTF.definition} gltfDef 1791 */ 1792 1793 1794 function assignExtrasToUserData( object, gltfDef ) { 1795 1796 if ( gltfDef.extras !== undefined ) { 1797 1798 if ( typeof gltfDef.extras === 'object' ) { 1799 1800 Object.assign( object.userData, gltfDef.extras ); 1801 1802 } else { 1803 1804 console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras ); 1805 1806 } 1807 1808 } 1809 1810 } 1811 /** 1812 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets 1813 * 1814 * @param {BufferGeometry} geometry 1815 * @param {Array<GLTF.Target>} targets 1816 * @param {GLTFParser} parser 1817 * @return {Promise<BufferGeometry>} 1818 */ 1819 1820 1821 function addMorphTargets( geometry, targets, parser ) { 1822 1823 let hasMorphPosition = false;
1824 let hasMorphNormal = false; 1825 1826 for ( let i = 0, il = targets.length; i < il; i ++ ) { 1827 1828 const target = targets[ i ]; 1829 if ( target.POSITION !== undefined ) hasMorphPosition = true; 1830 if ( target.NORMAL !== undefined ) hasMorphNormal = true; 1831 if ( hasMorphPosition && hasMorphNormal ) break; 1832 1833 } 1834 1835 if ( ! hasMorphPosition && ! hasMorphNormal ) return Promise.resolve( geometry ); 1836 const pendingPositionAccessors = []; 1837 const pendingNormalAccessors = []; 1838 1839 for ( let i = 0, il = targets.length; i < il; i ++ ) { 1840 1841 const target = targets[ i ]; 1842 1843 if ( hasMorphPosition ) { 1844 1845 const pendingAccessor = target.POSITION !== undefined ? parser.getDependency( 'accessor', target.POSITION ) : geometry.attributes.position; 1846 pendingPositionAccessors.push( pendingAccessor ); 1847 1848 } 1849 1850 if ( hasMorphNormal ) { 1851 1852 const pendingAccessor = target.NORMAL !== undefined ? parser.getDependency( 'accessor', target.NORMAL ) : geometry.attributes.normal; 1853 pendingNormalAccessors.push( pendingAccessor ); 1854 1855 } 1856 1857 } 1858 1859 return Promise.all( [ Promise.all( pendingPositionAccessors ), Promise.all( pendingNormalAccessors ) ] ).then( function ( accessors ) { 1860 1861 const morphPositions = accessors[ 0 ]; 1862 const morphNormals = accessors[ 1 ]; 1863 if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; 1864 if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; 1865 geometry.morphTargetsRelative = true; 1866 return geometry; 1867 1868 } ); 1869 1870 } 1871 /** 1872 * @param {Mesh} mesh 1873 * @param {GLTF.Mesh} meshDef 1874 */ 1875 1876 1877 function updateMorphTargets( mesh, meshDef ) { 1878 1879 mesh.updateMorphTargets(); 1880 1881 if ( meshDef.weights !== undefined ) { 1882 1883 for ( let i = 0, il = meshDef.weights.length; i < il; i ++ ) { 1884 1885 mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; 1886 1887 } 1888 1889 } // .extras has user-defined data, so check that .extras.targetNames is an array. 1890 1891 1892 if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { 1893 1894 const targetNames = meshDef.extras.targetNames; 1895 1896 if ( mesh.morphTargetInfluences.length === targetNames.length ) { 1897 1898 mesh.morphTargetDictionary = {}; 1899 1900 for ( let i = 0, il = targetNames.length; i < il; i ++ ) { 1901 1902 mesh.morphTargetDictionary[ targetNames[ i ] ] = i; 1903 1904 } 1905 1906 } else { 1907 1908 console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' ); 1909 1910 } 1911 1912 } 1913 1914 } 1915 1916 function createPrimitiveKey( primitiveDef ) { 1917 1918 const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]; 1919 let geometryKey; 1920 1921 if ( dracoExtension ) { 1922 1923 geometryKey = 'draco:' + dracoExtension.bufferView + ':' + dracoExtension.indices + ':' + createAttributesKey( dracoExtension.attributes ); 1924 1925 } else { 1926 1927 geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; 1928 1929 } 1930 1931 return geometryKey; 1932 1933 } 1934 1935 function createAttributesKey( attributes ) { 1936 1937 let attributesKey = ''; 1938 const keys = Object.keys( attributes ).sort(); 1939 1940 for ( let i = 0, il = keys.length; i < il; i ++ ) { 1941 1942 attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; 1943 1944 } 1945 1946 return attributesKey; 1947 1948 } 1949 1950 function getNormalizedComponentScale( constructor ) { 1951 1952 // Reference: 1953 // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data 1954 switch ( constructor ) { 1955 1956 case Int8Array: 1957 return 1 / 127; 1958 1959 case Uint8Array: 1960 return 1 / 255; 1961 1962 case Int16Array: 1963 return 1 / 32767; 1964 1965 case Uint16Array: 1966 return 1 / 65535; 1967 1968 default: 1969 throw new Error( 'THREE.GLTFLoader: Unsupported normalized accessor component type.' ); 1970 1971 } 1972 1973 } 1974 /* GLTF PARSER */ 1975 1976 1977 class GLTFParser { 1978 1979 constructor( json = {}, options = {} ) { 1980 1981 this.json = json; 1982 this.extensions = {}; 1983 this.plugins = {}; 1984 this.options = options; // loader object cache 1985 1986 this.cache = new GLTFRegistry(); // associations between Three.js objects and glTF elements 1987 1988 this.associations = new Map(); // THREE.BufferGeometry caching 1989 1990 this.primitiveCache = {}; // THREE.Object3D instance caches 1991 1992 this.meshCache = { 1993 refs: {}, 1994 uses: {} 1995 }; 1996 this.cameraCache = { 1997 refs: {}, 1998 uses: {} 1999 }; 2000 this.lightCache = { 2001 refs: {}, 2002 uses: {} 2003 }; 2004 this.textureCache = {}; // Track node names, to ensure no duplicates 2005 2006 this.nodeNamesUsed = {}; // Use an THREE.ImageBitmapLoader if imageBitmaps are supported. Moves much of the 2007 // expensive work of uploading a texture to the GPU off the main thread. 2008 2009 if ( typeof createImageBitmap !== 'undefined' && /Firefox|Safari/.test( navigator.userAgent ) === false ) { 2010 2011 this.textureLoader = new THREE.ImageBitmapLoader( this.options.manager ); 2012 2013 } else { 2014 2015 this.textureLoader = new THREE.TextureLoader( this.options.manager ); 2016 2017 } 2018 2019 this.textureLoader.setCrossOrigin( this.options.crossOrigin ); 2020 this.textureLoader.setRequestHeader( this.options.requestHeader ); 2021 this.fileLoader = new THREE.FileLoader( this.options.manager ); 2022 this.fileLoader.setResponseType( 'arraybuffer' ); 2023 2024 if ( this.options.crossOrigin === 'use-credentials' ) { 2025 2026 this.fileLoader.setWithCredentials( true ); 2027 2028 } 2029 2030 } 2031 2032 setExtensions( extensions ) { 2033 2034 this.extensions = extensions; 2035 2036 } 2037 2038 setPlugins( plugins ) { 2039 2040 this.plugins = plugins; 2041 2042 } 2043 2044 parse( onLoad, onError ) { 2045 2046 const parser = this; 2047 const json = this.json; 2048 const extensions = this.extensions; // Clear the loader cache 2049 2050 this.cache.removeAll(); // Mark the special nodes/meshes in json for efficient parse 2051 2052 this._invokeAll( function ( ext ) { 2053 2054 return ext._markDefs && ext._markDefs(); 2055 2056 } ); 2057 2058 Promise.all( this._invokeAll( function ( ext ) { 2059 2060 return ext.beforeRoot && ext.beforeRoot(); 2061 2062 } ) ).then( function () { 2063 2064 return Promise.all( [ parser.getDependencies( 'scene' ), parser.getDependencies( 'animation' ), parser.getDependencies( 'camera' ) ] ); 2065 2066 } ).then( function ( dependencies ) { 2067 2068 const result = {
vendor: 5,634 bytes, lines 2069-2320
2069 scene: dependencies[ 0 ][ json.scene || 0 ], 2070 scenes: dependencies[ 0 ], 2071 animations: dependencies[ 1 ], 2072 cameras: dependencies[ 2 ], 2073 asset: json.asset, 2074 parser: parser, 2075 userData: {} 2076 }; 2077 addUnknownExtensionsToUserData( extensions, result, json ); 2078 assignExtrasToUserData( result, json ); 2079 Promise.all( parser._invokeAll( function ( ext ) { 2080 2081 return ext.afterRoot && ext.afterRoot( result ); 2082 2083 } ) ).then( function () { 2084 2085 onLoad( result ); 2086 2087 } ); 2088 2089 } ).catch( onError ); 2090 2091 } 2092 /** 2093 * Marks the special nodes/meshes in json for efficient parse. 2094 */ 2095 2096 2097 _markDefs() { 2098 2099 const nodeDefs = this.json.nodes || []; 2100 const skinDefs = this.json.skins || []; 2101 const meshDefs = this.json.meshes || []; // Nothing in the node definition indicates whether it is a THREE.Bone or an 2102 // THREE.Object3D. Use the skins' joint references to mark bones. 2103 2104 for ( let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { 2105 2106 const joints = skinDefs[ skinIndex ].joints; 2107 2108 for ( let i = 0, il = joints.length; i < il; i ++ ) { 2109 2110 nodeDefs[ joints[ i ] ].isBone = true; 2111 2112 } 2113 2114 } // Iterate over all nodes, marking references to shared resources, 2115 // as well as skeleton joints. 2116 2117 2118 for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { 2119 2120 const nodeDef = nodeDefs[ nodeIndex ]; 2121 2122 if ( nodeDef.mesh !== undefined ) { 2123 2124 this._addNodeRef( this.meshCache, nodeDef.mesh ); // Nothing in the mesh definition indicates whether it is 2125 // a THREE.SkinnedMesh or THREE.Mesh. Use the node's mesh reference 2126 // to mark THREE.SkinnedMesh if node has skin. 2127 2128 2129 if ( nodeDef.skin !== undefined ) { 2130 2131 meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; 2132 2133 } 2134 2135 } 2136 2137 if ( nodeDef.camera !== undefined ) { 2138 2139 this._addNodeRef( this.cameraCache, nodeDef.camera ); 2140 2141 } 2142 2143 } 2144 2145 } 2146 /** 2147 * Counts references to shared node / THREE.Object3D resources. These resources 2148 * can be reused, or "instantiated", at multiple nodes in the scene 2149 * hierarchy. THREE.Mesh, Camera, and Light instances are instantiated and must 2150 * be marked. Non-scenegraph resources (like Materials, Geometries, and 2151 * Textures) can be reused directly and are not marked here. 2152 * 2153 * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. 2154 */ 2155 2156 2157 _addNodeRef( cache, index ) { 2158 2159 if ( index === undefined ) return; 2160 2161 if ( cache.refs[ index ] === undefined ) { 2162 2163 cache.refs[ index ] = cache.uses[ index ] = 0; 2164 2165 } 2166 2167 cache.refs[ index ] ++; 2168 2169 } 2170 /** Returns a reference to a shared resource, cloning it if necessary. */ 2171 2172 2173 _getNodeRef( cache, index, object ) { 2174 2175 if ( cache.refs[ index ] <= 1 ) return object; 2176 const ref = object.clone(); // Propagates mappings to the cloned object, prevents mappings on the 2177 // original object from being lost. 2178 2179 const updateMappings = ( original, clone ) => { 2180 2181 const mappings = this.associations.get( original ); 2182 2183 if ( mappings != null ) { 2184 2185 this.associations.set( clone, mappings ); 2186 2187 } 2188 2189 for ( const [ i, child ] of original.children.entries() ) { 2190 2191 updateMappings( child, clone.children[ i ] ); 2192 2193 } 2194 2195 }; 2196 2197 updateMappings( object, ref ); 2198 ref.name += '_instance_' + cache.uses[ index ] ++; 2199 return ref; 2200 2201 } 2202 2203 _invokeOne( func ) { 2204 2205 const extensions = Object.values( this.plugins ); 2206 extensions.push( this ); 2207 2208 for ( let i = 0; i < extensions.length; i ++ ) { 2209 2210 const result = func( extensions[ i ] ); 2211 if ( result ) return result; 2212 2213 } 2214 2215 return null; 2216 2217 } 2218 2219 _invokeAll( func ) { 2220 2221 const extensions = Object.values( this.plugins ); 2222 extensions.unshift( this ); 2223 const pending = []; 2224 2225 for ( let i = 0; i < extensions.length; i ++ ) { 2226 2227 const result = func( extensions[ i ] ); 2228 if ( result ) pending.push( result ); 2229 2230 } 2231 2232 return pending; 2233 2234 } 2235 /** 2236 * Requests the specified dependency asynchronously, with caching. 2237 * @param {string} type 2238 * @param {number} index 2239 * @return {Promise<Object3D|Material|THREE.Texture|AnimationClip|ArrayBuffer|Object>} 2240 */ 2241 2242 2243 getDependency( type, index ) { 2244 2245 const cacheKey = type + ':' + index; 2246 let dependency = this.cache.get( cacheKey ); 2247 2248 if ( ! dependency ) { 2249 2250 switch ( type ) { 2251 2252 case 'scene': 2253 dependency = this.loadScene( index ); 2254 break; 2255 2256 case 'node': 2257 dependency = this.loadNode( index ); 2258 break; 2259 2260 case 'mesh': 2261 dependency = this._invokeOne( function ( ext ) { 2262 2263 return ext.loadMesh && ext.loadMesh( index ); 2264 2265 } ); 2266 break; 2267 2268 case 'accessor': 2269 dependency = this.loadAccessor( index ); 2270 break; 2271 2272 case 'bufferView': 2273 dependency = this._invokeOne( function ( ext ) { 2274 2275 return ext.loadBufferView && ext.loadBufferView( index ); 2276 2277 } ); 2278 break; 2279 2280 case 'buffer': 2281 dependency = this.loadBuffer( index ); 2282 break; 2283 2284 case 'material': 2285 dependency = this._invokeOne( function ( ext ) { 2286 2287 return ext.loadMaterial && ext.loadMaterial( index ); 2288 2289 } ); 2290 break; 2291 2292 case 'texture': 2293 dependency = this._invokeOne( function ( ext ) { 2294 2295 return ext.loadTexture && ext.loadTexture( index ); 2296 2297 } ); 2298 break; 2299 2300 case 'skin': 2301 dependency = this.loadSkin( index ); 2302 break; 2303 2304 case 'animation': 2305 dependency = this.loadAnimation( index ); 2306 break; 2307 2308 case 'camera': 2309 dependency = this.loadCamera( index ); 2310 break; 2311 2312 default: 2313 throw new Error( 'Unknown type: ' + type ); 2314 2315 } 2316 2317 this.cache.add( cacheKey, dependency ); 2318 2319 } 2320
vendor: 5,254 bytes, lines 2321-2487
2321 return dependency; 2322 2323 } 2324 /** 2325 * Requests all dependencies of the specified type asynchronously, with caching. 2326 * @param {string} type 2327 * @return {Promise<Array<Object>>} 2328 */ 2329 2330 2331 getDependencies( type ) { 2332 2333 let dependencies = this.cache.get( type ); 2334 2335 if ( ! dependencies ) { 2336 2337 const parser = this; 2338 const defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; 2339 dependencies = Promise.all( defs.map( function ( def, index ) { 2340 2341 return parser.getDependency( type, index ); 2342 2343 } ) ); 2344 this.cache.add( type, dependencies ); 2345 2346 } 2347 2348 return dependencies; 2349 2350 } 2351 /** 2352 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views 2353 * @param {number} bufferIndex 2354 * @return {Promise<ArrayBuffer>} 2355 */ 2356 2357 2358 loadBuffer( bufferIndex ) { 2359 2360 const bufferDef = this.json.buffers[ bufferIndex ]; 2361 const loader = this.fileLoader; 2362 2363 if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { 2364 2365 throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); 2366 2367 } // If present, GLB container is required to be the first buffer. 2368 2369 2370 if ( bufferDef.uri === undefined && bufferIndex === 0 ) { 2371 2372 return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); 2373 2374 } 2375 2376 const options = this.options; 2377 return new Promise( function ( resolve, reject ) { 2378 2379 loader.load( THREE.LoaderUtils.resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () { 2380 2381 reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); 2382 2383 } ); 2384 2385 } ); 2386 2387 } 2388 /** 2389 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views 2390 * @param {number} bufferViewIndex 2391 * @return {Promise<ArrayBuffer>} 2392 */ 2393 2394 2395 loadBufferView( bufferViewIndex ) { 2396 2397 const bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; 2398 return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { 2399 2400 const byteLength = bufferViewDef.byteLength || 0; 2401 const byteOffset = bufferViewDef.byteOffset || 0; 2402 return buffer.slice( byteOffset, byteOffset + byteLength ); 2403 2404 } ); 2405 2406 } 2407 /** 2408 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors 2409 * @param {number} accessorIndex 2410 * @return {Promise<BufferAttribute|InterleavedBufferAttribute>} 2411 */ 2412 2413 2414 loadAccessor( accessorIndex ) { 2415 2416 const parser = this; 2417 const json = this.json; 2418 const accessorDef = this.json.accessors[ accessorIndex ]; 2419 2420 if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { 2421 2422 // Ignore empty accessors, which may be used to declare runtime 2423 // information about attributes coming from another source (e.g. Draco 2424 // compression extension). 2425 return Promise.resolve( null ); 2426 2427 } 2428 2429 const pendingBufferViews = []; 2430 2431 if ( accessorDef.bufferView !== undefined ) { 2432 2433 pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); 2434 2435 } else { 2436 2437 pendingBufferViews.push( null ); 2438 2439 } 2440 2441 if ( accessorDef.sparse !== undefined ) { 2442 2443 pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); 2444 pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); 2445 2446 } 2447 2448 return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { 2449 2450 const bufferView = bufferViews[ 0 ]; 2451 const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ]; 2452 const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ]; // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. 2453 2454 const elementBytes = TypedArray.BYTES_PER_ELEMENT; 2455 const itemBytes = elementBytes * itemSize; 2456 const byteOffset = accessorDef.byteOffset || 0; 2457 const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; 2458 const normalized = accessorDef.normalized === true; 2459 let array, bufferAttribute; // The buffer is not interleaved if the stride is the item size in bytes. 2460 2461 if ( byteStride && byteStride !== itemBytes ) { 2462 2463 // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own THREE.InterleavedBuffer 2464 // This makes sure that IBA.count reflects accessor.count properly 2465 const ibSlice = Math.floor( byteOffset / byteStride ); 2466 const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; 2467 let ib = parser.cache.get( ibCacheKey ); 2468 2469 if ( ! ib ) { 2470 2471 array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); // Integer parameters to IB/IBA are in array elements, not bytes. 2472 2473 ib = new THREE.InterleavedBuffer( array, byteStride / elementBytes ); 2474 parser.cache.add( ibCacheKey, ib ); 2475 2476 } 2477 2478 bufferAttribute = new THREE.InterleavedBufferAttribute( ib, itemSize, byteOffset % byteStride / elementBytes, normalized ); 2479 2480 } else { 2481 2482 if ( bufferView === null ) { 2483 2484 array = new TypedArray( accessorDef.count * itemSize ); 2485 2486 } else { 2487
vendor: 4,220 bytes, lines 2488-2627
2488 array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); 2489 2490 } 2491 2492 bufferAttribute = new THREE.BufferAttribute( array, itemSize, normalized ); 2493 2494 } // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors 2495 2496 2497 if ( accessorDef.sparse !== undefined ) { 2498 2499 const itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR; 2500 const TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ]; 2501 const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; 2502 const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; 2503 const sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); 2504 const sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); 2505 2506 if ( bufferView !== null ) { 2507 2508 // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. 2509 bufferAttribute = new THREE.BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); 2510 2511 } 2512 2513 for ( let i = 0, il = sparseIndices.length; i < il; i ++ ) { 2514 2515 const index = sparseIndices[ i ]; 2516 bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); 2517 if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); 2518 if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); 2519 if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); 2520 if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse THREE.BufferAttribute.' ); 2521 2522 } 2523 2524 } 2525 2526 return bufferAttribute; 2527 2528 } ); 2529 2530 } 2531 /** 2532 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures 2533 * @param {number} textureIndex 2534 * @return {Promise<THREE.Texture>} 2535 */ 2536 2537 2538 loadTexture( textureIndex ) { 2539 2540 const json = this.json; 2541 const options = this.options; 2542 const textureDef = json.textures[ textureIndex ]; 2543 const source = json.images[ textureDef.source ]; 2544 let loader = this.textureLoader; 2545 2546 if ( source.uri ) { 2547 2548 const handler = options.manager.getHandler( source.uri ); 2549 if ( handler !== null ) loader = handler; 2550 2551 } 2552 2553 return this.loadTextureImage( textureIndex, source, loader ); 2554 2555 } 2556 2557 loadTextureImage( textureIndex, source, loader ) { 2558 2559 const parser = this; 2560 const json = this.json; 2561 const options = this.options; 2562 const textureDef = json.textures[ textureIndex ]; 2563 const cacheKey = ( source.uri || source.bufferView ) + ':' + textureDef.sampler; 2564 2565 if ( this.textureCache[ cacheKey ] ) { 2566 2567 // See https://github.com/mrdoob/three.js/issues/21559. 2568 return this.textureCache[ cacheKey ]; 2569 2570 } 2571 2572 const URL = self.URL || self.webkitURL; 2573 let sourceURI = source.uri || ''; 2574 let isObjectURL = false; 2575 2576 if ( source.bufferView !== undefined ) { 2577 2578 // Load binary image data from bufferView, if provided. 2579 sourceURI = parser.getDependency( 'bufferView', source.bufferView ).then( function ( bufferView ) { 2580 2581 isObjectURL = true; 2582 const blob = new Blob( [ bufferView ], { 2583 type: source.mimeType 2584 } ); 2585 sourceURI = URL.createObjectURL( blob ); 2586 return sourceURI; 2587 2588 } ); 2589 2590 } else if ( source.uri === undefined ) { 2591 2592 throw new Error( 'THREE.GLTFLoader: Image ' + textureIndex + ' is missing URI and bufferView' ); 2593 2594 } 2595 2596 const promise = Promise.resolve( sourceURI ).then( function ( sourceURI ) { 2597 2598 return new Promise( function ( resolve, reject ) { 2599 2600 let onLoad = resolve; 2601 2602 if ( loader.isImageBitmapLoader === true ) { 2603 2604 onLoad = function ( imageBitmap ) { 2605 2606 const texture = new THREE.Texture( imageBitmap ); 2607 texture.needsUpdate = true; 2608 resolve( texture ); 2609 2610 }; 2611 2612 } 2613 2614 loader.load( THREE.LoaderUtils.resolveURL( sourceURI, options.path ), onLoad, undefined, reject ); 2615 2616 } ); 2617 2618 } ).then( function ( texture ) { 2619 2620 // Clean up resources and configure THREE.Texture. 2621 if ( isObjectURL === true ) { 2622 2623 URL.revokeObjectURL( sourceURI ); 2624 2625 } 2626 2627 texture.flipY = false;
2628 if ( textureDef.name ) texture.name = textureDef.name; 2629 const samplers = json.samplers || {}; 2630 const sampler = samplers[ textureDef.sampler ] || {}; 2631 texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; 2632 texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.LinearMipmapLinearFilter; 2633 texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; 2634 texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; 2635 parser.associations.set( texture, { 2636 textures: textureIndex 2637 } ); 2638 return texture; 2639 2640 } ).catch( function () { 2641 2642 console.error( 'THREE.GLTFLoader: Couldn\'t load texture', sourceURI ); 2643 return null; 2644 2645 } ); 2646 this.textureCache[ cacheKey ] = promise; 2647 return promise; 2648 2649 } 2650 /** 2651 * Asynchronously assigns a texture to the given material parameters. 2652 * @param {Object} materialParams 2653 * @param {string} mapName 2654 * @param {Object} mapDef 2655 * @return {Promise<Texture>} 2656 */ 2657 2658 2659 assignTexture( materialParams, mapName, mapDef ) { 2660 2661 const parser = this; 2662 return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { 2663 2664 // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured 2665 // However, we will copy UV set 0 to UV set 1 on demand for aoMap 2666 if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) { 2667 2668 console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' ); 2669 2670 } 2671 2672 if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) { 2673 2674 const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined; 2675 2676 if ( transform ) { 2677 2678 const gltfReference = parser.associations.get( texture ); 2679 texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); 2680 parser.associations.set( texture, gltfReference ); 2681 2682 } 2683 2684 } 2685 2686 materialParams[ mapName ] = texture; 2687 return texture; 2688 2689 } ); 2690 2691 } 2692 /** 2693 * Assigns final material to a THREE.Mesh, THREE.Line, or THREE.Points instance. The instance 2694 * already has a material (generated from the glTF material options alone) 2695 * but reuse of the same glTF material may require multiple threejs materials 2696 * to accommodate different primitive types, defines, etc. New materials will 2697 * be created if necessary, and reused from a cache. 2698 * @param {Object3D} mesh THREE.Mesh, THREE.Line, or THREE.Points instance. 2699 */ 2700 2701 2702 assignFinalMaterial( mesh ) { 2703 2704 const geometry = mesh.geometry; 2705 let material = mesh.material; 2706 const useDerivativeTangents = geometry.attributes.tangent === undefined; 2707 const useVertexColors = geometry.attributes.color !== undefined; 2708 const useFlatShading = geometry.attributes.normal === undefined; 2709 2710 if ( mesh.isPoints ) { 2711 2712 const cacheKey = 'PointsMaterial:' + material.uuid; 2713 let pointsMaterial = this.cache.get( cacheKey ); 2714 2715 if ( ! pointsMaterial ) { 2716 2717 pointsMaterial = new THREE.PointsMaterial(); 2718 THREE.Material.prototype.copy.call( pointsMaterial, material ); 2719 pointsMaterial.color.copy( material.color ); 2720 pointsMaterial.map = material.map; 2721 pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px 2722 2723 this.cache.add( cacheKey, pointsMaterial ); 2724 2725 } 2726 2727 material = pointsMaterial; 2728 2729 } else if ( mesh.isLine ) { 2730 2731 const cacheKey = 'LineBasicMaterial:' + material.uuid; 2732 let lineMaterial = this.cache.get( cacheKey ); 2733 2734 if ( ! lineMaterial ) { 2735 2736 lineMaterial = new THREE.LineBasicMaterial(); 2737 THREE.Material.prototype.copy.call( lineMaterial, material ); 2738 lineMaterial.color.copy( material.color ); 2739 this.cache.add( cacheKey, lineMaterial ); 2740 2741 } 2742 2743 material = lineMaterial; 2744 2745 } // Clone the material if it will be modified 2746 2747 2748 if ( useDerivativeTangents || useVertexColors || useFlatShading ) { 2749 2750 let cacheKey = 'ClonedMaterial:' + material.uuid + ':'; 2751 if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:'; 2752 if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:'; 2753 if ( useVertexColors ) cacheKey += 'vertex-colors:'; 2754 if ( useFlatShading ) cacheKey += 'flat-shading:'; 2755 let cachedMaterial = this.cache.get( cacheKey ); 2756 2757 if ( ! cachedMaterial ) { 2758 2759 cachedMaterial = material.clone(); 2760 if ( useVertexColors ) cachedMaterial.vertexColors = true; 2761 if ( useFlatShading ) cachedMaterial.flatShading = true; 2762 2763 if ( useDerivativeTangents ) { 2764 2765 // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 2766 if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1; 2767 if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1; 2768 2769 } 2770 2771 this.cache.add( cacheKey, cachedMaterial ); 2772 this.associations.set( cachedMaterial, this.associations.get( material ) ); 2773 2774 } 2775 2776 material = cachedMaterial; 2777 2778 } // workarounds for mesh and geometry 2779 2780 2781 if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) { 2782 2783 geometry.setAttribute( 'uv2', geometry.attributes.uv ); 2784 2785 } 2786 2787 mesh.material = material; 2788 2789 } 2790 2791 getMaterialType() { 2792 2793 return THREE.MeshStandardMaterial; 2794 2795 } 2796 /** 2797 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials 2798 * @param {number} materialIndex 2799 * @return {Promise<Material>} 2800 */ 2801 2802 2803 loadMaterial( materialIndex ) { 2804 2805 const parser = this; 2806 const json = this.json; 2807 const extensions = this.extensions; 2808 const materialDef = json.materials[ materialIndex ]; 2809 let materialType; 2810 const materialParams = {}; 2811 const materialExtensions = materialDef.extensions || {}; 2812 const pending = []; 2813 2814 if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) { 2815 2816 const sgExtension = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ]; 2817 materialType = sgExtension.getMaterialType(); 2818 pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) ); 2819 2820 } else if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) { 2821
2822 const kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ]; 2823 materialType = kmuExtension.getMaterialType(); 2824 pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); 2825 2826 } else { 2827 2828 // Specification: 2829 // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material 2830 const metallicRoughness = materialDef.pbrMetallicRoughness || {}; 2831 materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); 2832 materialParams.opacity = 1.0; 2833 2834 if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { 2835 2836 const array = metallicRoughness.baseColorFactor; 2837 materialParams.color.fromArray( array ); 2838 materialParams.opacity = array[ 3 ]; 2839 2840 } 2841 2842 if ( metallicRoughness.baseColorTexture !== undefined ) { 2843 2844 pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); 2845 2846 } 2847 2848 materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; 2849 materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; 2850 2851 if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { 2852 2853 pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); 2854 pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); 2855 2856 } 2857 2858 materialType = this._invokeOne( function ( ext ) { 2859 2860 return ext.getMaterialType && ext.getMaterialType( materialIndex ); 2861 2862 } ); 2863 pending.push( Promise.all( this._invokeAll( function ( ext ) { 2864 2865 return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); 2866 2867 } ) ) ); 2868 2869 } 2870 2871 if ( materialDef.doubleSided === true ) { 2872 2873 materialParams.side = THREE.DoubleSide; 2874 2875 } 2876 2877 const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; 2878 2879 if ( alphaMode === ALPHA_MODES.BLEND ) { 2880 2881 materialParams.transparent = true; // See: https://github.com/mrdoob/three.js/issues/17706 2882 2883 materialParams.depthWrite = false; 2884 2885 } else { 2886 2887 materialParams.format = THREE.RGBFormat; 2888 materialParams.transparent = false; 2889 2890 if ( alphaMode === ALPHA_MODES.MASK ) { 2891 2892 materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; 2893 2894 } 2895 2896 } 2897 2898 if ( materialDef.normalTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) { 2899 2900 pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); 2901 materialParams.normalScale = new THREE.Vector2( 1, 1 ); 2902 2903 if ( materialDef.normalTexture.scale !== undefined ) { 2904 2905 const scale = materialDef.normalTexture.scale; 2906 materialParams.normalScale.set( scale, scale ); 2907 2908 } 2909 2910 } 2911 2912 if ( materialDef.occlusionTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) { 2913 2914 pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); 2915 2916 if ( materialDef.occlusionTexture.strength !== undefined ) { 2917 2918 materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; 2919 2920 } 2921 2922 } 2923 2924 if ( materialDef.emissiveFactor !== undefined && materialType !== THREE.MeshBasicMaterial ) { 2925 2926 materialParams.emissive = new THREE.Color().fromArray( materialDef.emissiveFactor ); 2927 2928 } 2929 2930 if ( materialDef.emissiveTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) { 2931 2932 pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture ) ); 2933 2934 } 2935 2936 return Promise.all( pending ).then( function () { 2937 2938 let material; 2939 2940 if ( materialType === GLTFMeshStandardSGMaterial ) { 2941 2942 material = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams ); 2943 2944 } else { 2945 2946 material = new materialType( materialParams ); 2947 2948 } 2949 2950 if ( materialDef.name ) material.name = materialDef.name; // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding. 2951 2952 if ( material.map ) material.map.encoding = THREE.sRGBEncoding; 2953 if ( material.emissiveMap ) material.emissiveMap.encoding = THREE.sRGBEncoding; 2954 assignExtrasToUserData( material, materialDef ); 2955 parser.associations.set( material, { 2956 materials: materialIndex 2957 } ); 2958 if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); 2959 return material; 2960 2961 } ); 2962 2963 } 2964 /** When THREE.Object3D instances are targeted by animation, they need unique names. */ 2965 2966 2967 createUniqueName( originalName ) { 2968 2969 const sanitizedName = THREE.PropertyBinding.sanitizeNodeName( originalName || '' ); 2970 let name = sanitizedName; 2971 2972 for ( let i = 1; this.nodeNamesUsed[ name ]; ++ i ) { 2973 2974 name = sanitizedName + '_' + i; 2975 2976 } 2977 2978 this.nodeNamesUsed[ name ] = true; 2979 return name; 2980 2981 } 2982 /** 2983 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry 2984 * 2985 * Creates BufferGeometries from primitives. 2986 * 2987 * @param {Array<GLTF.Primitive>} primitives 2988 * @return {Promise<Array<BufferGeometry>>} 2989 */ 2990 2991 2992 loadGeometries( primitives ) { 2993 2994 const parser = this; 2995 const extensions = this.extensions; 2996 const cache = this.primitiveCache; 2997 2998 function createDracoPrimitive( primitive ) { 2999 3000 return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ].decodePrimitive( primitive, parser ).then( function ( geometry ) { 3001 3002 return addPrimitiveAttributes( geometry, primitive, parser ); 3003 3004 } ); 3005 3006 } 3007 3008 const pending = []; 3009 3010 for ( let i = 0, il = primitives.length; i < il; i ++ ) { 3011 3012 const primitive = primitives[ i ]; 3013 const cacheKey = createPrimitiveKey( primitive ); // See if we've already created this geometry 3014
vendor: 5,165 bytes, lines 3015-3205
3015 const cached = cache[ cacheKey ]; 3016 3017 if ( cached ) { 3018 3019 // Use the cached geometry if it exists 3020 pending.push( cached.promise ); 3021 3022 } else { 3023 3024 let geometryPromise; 3025 3026 if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) { 3027 3028 // Use DRACO geometry if available 3029 geometryPromise = createDracoPrimitive( primitive ); 3030 3031 } else { 3032 3033 // Otherwise create a new geometry 3034 geometryPromise = addPrimitiveAttributes( new THREE.BufferGeometry(), primitive, parser ); 3035 3036 } // Cache this geometry 3037 3038 3039 cache[ cacheKey ] = { 3040 primitive: primitive, 3041 promise: geometryPromise 3042 }; 3043 pending.push( geometryPromise ); 3044 3045 } 3046 3047 } 3048 3049 return Promise.all( pending ); 3050 3051 } 3052 /** 3053 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes 3054 * @param {number} meshIndex 3055 * @return {Promise<Group|Mesh|SkinnedMesh>} 3056 */ 3057 3058 3059 loadMesh( meshIndex ) { 3060 3061 const parser = this; 3062 const json = this.json; 3063 const extensions = this.extensions; 3064 const meshDef = json.meshes[ meshIndex ]; 3065 const primitives = meshDef.primitives; 3066 const pending = []; 3067 3068 for ( let i = 0, il = primitives.length; i < il; i ++ ) { 3069 3070 const material = primitives[ i ].material === undefined ? createDefaultMaterial( this.cache ) : this.getDependency( 'material', primitives[ i ].material ); 3071 pending.push( material ); 3072 3073 } 3074 3075 pending.push( parser.loadGeometries( primitives ) ); 3076 return Promise.all( pending ).then( function ( results ) { 3077 3078 const materials = results.slice( 0, results.length - 1 ); 3079 const geometries = results[ results.length - 1 ]; 3080 const meshes = []; 3081 3082 for ( let i = 0, il = geometries.length; i < il; i ++ ) { 3083 3084 const geometry = geometries[ i ]; 3085 const primitive = primitives[ i ]; // 1. create THREE.Mesh 3086 3087 let mesh; 3088 const material = materials[ i ]; 3089 3090 if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || primitive.mode === undefined ) { 3091 3092 // .isSkinnedMesh isn't in glTF spec. See ._markDefs() 3093 mesh = meshDef.isSkinnedMesh === true ? new THREE.SkinnedMesh( geometry, material ) : new THREE.Mesh( geometry, material ); 3094 3095 if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) { 3096 3097 // we normalize floating point skin weight array to fix malformed assets (see #15319) 3098 // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs 3099 mesh.normalizeSkinWeights(); 3100 3101 } 3102 3103 if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) { 3104 3105 mesh.geometry = toTrianglesDrawMode( mesh.geometry, THREE.TriangleStripDrawMode ); 3106 3107 } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) { 3108 3109 mesh.geometry = toTrianglesDrawMode( mesh.geometry, THREE.TriangleFanDrawMode ); 3110 3111 } 3112 3113 } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { 3114 3115 mesh = new THREE.LineSegments( geometry, material ); 3116 3117 } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) { 3118 3119 mesh = new THREE.Line( geometry, material ); 3120 3121 } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) { 3122 3123 mesh = new THREE.LineLoop( geometry, material ); 3124 3125 } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) { 3126 3127 mesh = new THREE.Points( geometry, material ); 3128 3129 } else { 3130 3131 throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); 3132 3133 } 3134 3135 if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { 3136 3137 updateMorphTargets( mesh, meshDef ); 3138 3139 } 3140 3141 mesh.name = parser.createUniqueName( meshDef.name || 'mesh_' + meshIndex ); 3142 assignExtrasToUserData( mesh, meshDef ); 3143 if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); 3144 parser.assignFinalMaterial( mesh ); 3145 meshes.push( mesh ); 3146 3147 } 3148 3149 for ( let i = 0, il = meshes.length; i < il; i ++ ) { 3150 3151 parser.associations.set( meshes[ i ], { 3152 meshes: meshIndex, 3153 primitives: i 3154 } ); 3155 3156 } 3157 3158 if ( meshes.length === 1 ) { 3159 3160 return meshes[ 0 ]; 3161 3162 } 3163 3164 const group = new THREE.Group(); 3165 parser.associations.set( group, { 3166 meshes: meshIndex 3167 } ); 3168 3169 for ( let i = 0, il = meshes.length; i < il; i ++ ) { 3170 3171 group.add( meshes[ i ] ); 3172 3173 } 3174 3175 return group; 3176 3177 } ); 3178 3179 } 3180 /** 3181 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras 3182 * @param {number} cameraIndex 3183 * @return {Promise<THREE.Camera>} 3184 */ 3185 3186 3187 loadCamera( cameraIndex ) { 3188 3189 let camera; 3190 const cameraDef = this.json.cameras[ cameraIndex ]; 3191 const params = cameraDef[ cameraDef.type ]; 3192 3193 if ( ! params ) { 3194 3195 console.warn( 'THREE.GLTFLoader: Missing camera parameters.' ); 3196 return; 3197 3198 } 3199 3200 if ( cameraDef.type === 'perspective' ) { 3201 3202 camera = new THREE.PerspectiveCamera( THREE.MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); 3203 3204 } else if ( cameraDef.type === 'orthographic' ) { 3205
3206 camera = new THREE.OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); 3207 3208 } 3209 3210 if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); 3211 assignExtrasToUserData( camera, cameraDef ); 3212 return Promise.resolve( camera ); 3213 3214 } 3215 /** 3216 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins 3217 * @param {number} skinIndex 3218 * @return {Promise<Object>} 3219 */ 3220 3221 3222 loadSkin( skinIndex ) { 3223 3224 const skinDef = this.json.skins[ skinIndex ]; 3225 const skinEntry = { 3226 joints: skinDef.joints 3227 }; 3228 3229 if ( skinDef.inverseBindMatrices === undefined ) { 3230 3231 return Promise.resolve( skinEntry ); 3232 3233 } 3234 3235 return this.getDependency( 'accessor', skinDef.inverseBindMatrices ).then( function ( accessor ) { 3236 3237 skinEntry.inverseBindMatrices = accessor; 3238 return skinEntry; 3239 3240 } ); 3241 3242 } 3243 /** 3244 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations 3245 * @param {number} animationIndex 3246 * @return {Promise<AnimationClip>} 3247 */ 3248 3249 3250 loadAnimation( animationIndex ) { 3251 3252 const json = this.json; 3253 const animationDef = json.animations[ animationIndex ]; 3254 const pendingNodes = []; 3255 const pendingInputAccessors = []; 3256 const pendingOutputAccessors = []; 3257 const pendingSamplers = []; 3258 const pendingTargets = []; 3259 3260 for ( let i = 0, il = animationDef.channels.length; i < il; i ++ ) { 3261 3262 const channel = animationDef.channels[ i ]; 3263 const sampler = animationDef.samplers[ channel.sampler ]; 3264 const target = channel.target; 3265 const name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated. 3266 3267 const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; 3268 const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; 3269 pendingNodes.push( this.getDependency( 'node', name ) ); 3270 pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); 3271 pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); 3272 pendingSamplers.push( sampler ); 3273 pendingTargets.push( target ); 3274 3275 } 3276 3277 return Promise.all( [ Promise.all( pendingNodes ), Promise.all( pendingInputAccessors ), Promise.all( pendingOutputAccessors ), Promise.all( pendingSamplers ), Promise.all( pendingTargets ) ] ).then( function ( dependencies ) { 3278 3279 const nodes = dependencies[ 0 ]; 3280 const inputAccessors = dependencies[ 1 ]; 3281 const outputAccessors = dependencies[ 2 ]; 3282 const samplers = dependencies[ 3 ]; 3283 const targets = dependencies[ 4 ]; 3284 const tracks = []; 3285 3286 for ( let i = 0, il = nodes.length; i < il; i ++ ) { 3287 3288 const node = nodes[ i ]; 3289 const inputAccessor = inputAccessors[ i ]; 3290 const outputAccessor = outputAccessors[ i ]; 3291 const sampler = samplers[ i ]; 3292 const target = targets[ i ]; 3293 if ( node === undefined ) continue; 3294 node.updateMatrix(); 3295 node.matrixAutoUpdate = true; 3296 let TypedKeyframeTrack; 3297 3298 switch ( PATH_PROPERTIES[ target.path ] ) { 3299 3300 case PATH_PROPERTIES.weights: 3301 TypedKeyframeTrack = THREE.NumberKeyframeTrack; 3302 break; 3303 3304 case PATH_PROPERTIES.rotation: 3305 TypedKeyframeTrack = THREE.QuaternionKeyframeTrack; 3306 break; 3307 3308 case PATH_PROPERTIES.position: 3309 case PATH_PROPERTIES.scale: 3310 default: 3311 TypedKeyframeTrack = THREE.VectorKeyframeTrack; 3312 break; 3313 3314 } 3315 3316 const targetName = node.name ? node.name : node.uuid; 3317 const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; 3318 const targetNames = []; 3319 3320 if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { 3321 3322 node.traverse( function ( object ) { 3323 3324 if ( object.morphTargetInfluences ) { 3325 3326 targetNames.push( object.name ? object.name : object.uuid ); 3327 3328 } 3329 3330 } ); 3331 3332 } else { 3333 3334 targetNames.push( targetName ); 3335 3336 } 3337 3338 let outputArray = outputAccessor.array; 3339 3340 if ( outputAccessor.normalized ) { 3341 3342 const scale = getNormalizedComponentScale( outputArray.constructor ); 3343 const scaled = new Float32Array( outputArray.length ); 3344 3345 for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { 3346 3347 scaled[ j ] = outputArray[ j ] * scale; 3348 3349 } 3350 3351 outputArray = scaled; 3352 3353 } 3354 3355 for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { 3356 3357 const track = new TypedKeyframeTrack( targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ], inputAccessor.array, outputArray, interpolation ); // Override interpolation with custom factory method. 3358 3359 if ( sampler.interpolation === 'CUBICSPLINE' ) { 3360 3361 track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { 3362 3363 // A CUBICSPLINE keyframe in glTF has three output values for each input value, 3364 // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() 3365 // must be divided by three to get the interpolant's sampleSize argument. 3366 const interpolantType = this instanceof THREE.QuaternionKeyframeTrack ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant; 3367 return new interpolantType( this.times, this.values, this.getValueSize() / 3, result ); 3368 3369 };
3369 // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. 3370 3371 3372 track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; 3373 3374 } 3375 3376 tracks.push( track ); 3377 3378 } 3379 3380 } 3381 3382 const name = animationDef.name ? animationDef.name : 'animation_' + animationIndex; 3383 return new THREE.AnimationClip( name, undefined, tracks ); 3384 3385 } ); 3386 3387 } 3388 3389 createNodeMesh( nodeIndex ) { 3390 3391 const json = this.json; 3392 const parser = this; 3393 const nodeDef = json.nodes[ nodeIndex ]; 3394 if ( nodeDef.mesh === undefined ) return null; 3395 return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { 3396 3397 const node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); // if weights are provided on the node, override weights on the mesh. 3398 3399 3400 if ( nodeDef.weights !== undefined ) { 3401 3402 node.traverse( function ( o ) { 3403 3404 if ( ! o.isMesh ) return; 3405 3406 for ( let i = 0, il = nodeDef.weights.length; i < il; i ++ ) { 3407 3408 o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; 3409 3410 } 3411 3412 } ); 3413 3414 } 3415 3416 return node; 3417 3418 } ); 3419 3420 } 3421 /** 3422 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy 3423 * @param {number} nodeIndex 3424 * @return {Promise<Object3D>} 3425 */ 3426 3427 3428 loadNode( nodeIndex ) { 3429 3430 const json = this.json; 3431 const extensions = this.extensions; 3432 const parser = this; 3433 const nodeDef = json.nodes[ nodeIndex ]; // reserve node's name before its dependencies, so the root has the intended name. 3434 3435 const nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : ''; 3436 return function () { 3437 3438 const pending = []; 3439 3440 const meshPromise = parser._invokeOne( function ( ext ) { 3441 3442 return ext.createNodeMesh && ext.createNodeMesh( nodeIndex ); 3443 3444 } ); 3445 3446 if ( meshPromise ) { 3447 3448 pending.push( meshPromise ); 3449 3450 } 3451 3452 if ( nodeDef.camera !== undefined ) { 3453 3454 pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { 3455 3456 return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); 3457 3458 } ) ); 3459 3460 } 3461 3462 parser._invokeAll( function ( ext ) { 3463 3464 return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex ); 3465 3466 } ).forEach( function ( promise ) { 3467 3468 pending.push( promise ); 3469 3470 } ); 3471 3472 return Promise.all( pending ); 3473 3474 }().then( function ( objects ) { 3475 3476 let node; // .isBone isn't in glTF spec. See ._markDefs 3477 3478 if ( nodeDef.isBone === true ) { 3479 3480 node = new THREE.Bone(); 3481 3482 } else if ( objects.length > 1 ) { 3483 3484 node = new THREE.Group(); 3485 3486 } else if ( objects.length === 1 ) { 3487 3488 node = objects[ 0 ]; 3489 3490 } else { 3491 3492 node = new THREE.Object3D(); 3493 3494 } 3495 3496 if ( node !== objects[ 0 ] ) { 3497 3498 for ( let i = 0, il = objects.length; i < il; i ++ ) { 3499 3500 node.add( objects[ i ] ); 3501 3502 } 3503 3504 } 3505 3506 if ( nodeDef.name ) { 3507 3508 node.userData.name = nodeDef.name; 3509 node.name = nodeName; 3510 3511 } 3512 3513 assignExtrasToUserData( node, nodeDef ); 3514 if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); 3515 3516 if ( nodeDef.matrix !== undefined ) { 3517 3518 const matrix = new THREE.Matrix4(); 3519 matrix.fromArray( nodeDef.matrix ); 3520 node.applyMatrix4( matrix ); 3521 3522 } else { 3523 3524 if ( nodeDef.translation !== undefined ) { 3525 3526 node.position.fromArray( nodeDef.translation ); 3527 3528 } 3529 3530 if ( nodeDef.rotation !== undefined ) { 3531 3532 node.quaternion.fromArray( nodeDef.rotation ); 3533 3534 } 3535 3536 if ( nodeDef.scale !== undefined ) { 3537 3538 node.scale.fromArray( nodeDef.scale ); 3539 3540 } 3541 3542 } 3543 3544 if ( ! parser.associations.has( node ) ) { 3545 3546 parser.associations.set( node, {} ); 3547 3548 } 3549 3550 parser.associations.get( node ).nodes = nodeIndex; 3551 return node; 3552 3553 } ); 3554 3555 } 3556 /** 3557 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes 3558 * @param {number} sceneIndex 3559 * @return {Promise<Group>} 3560 */ 3561 3562 3563 loadScene( sceneIndex ) { 3564 3565 const json = this.json; 3566 const extensions = this.extensions; 3567 const sceneDef = this.json.scenes[ sceneIndex ]; 3568 const parser = this; // THREE.Loader returns THREE.Group, not Scene. 3569 // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 3570 3571 const scene = new THREE.Group(); 3572 if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); 3573 assignExtrasToUserData( scene, sceneDef ); 3574 if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); 3575 const nodeIds = sceneDef.nodes || []; 3576 const pending = []; 3577 3578 for ( let i = 0, il = nodeIds.length; i < il; i ++ ) { 3579 3580 pending.push( buildNodeHierarchy( nodeIds[ i ], scene, json, parser ) ); 3581 3582 } 3583 3584 return Promise.all( pending ).then( function () { 3585 3586 // Removes dangling associations, associations that reference a node that 3587 // didn't make it into the scene. 3588 const reduceAssociations = node => { 3589 3590 const reducedAssociations = new Map(); 3591 3592 for ( const [ key, value ] of parser.associations ) { 3593 3594 if ( key instanceof THREE.Material || key instanceof THREE.Texture ) { 3595
3596 reducedAssociations.set( key, value ); 3597 3598 } 3599 3600 } 3601 3602 node.traverse( node => { 3603 3604 const mappings = parser.associations.get( node ); 3605 3606 if ( mappings != null ) { 3607 3608 reducedAssociations.set( node, mappings ); 3609 3610 } 3611 3612 } ); 3613 return reducedAssociations; 3614 3615 }; 3616 3617 parser.associations = reduceAssociations( scene ); 3618 return scene; 3619 3620 } ); 3621 3622 } 3623 3624 } 3625 3626 function buildNodeHierarchy( nodeId, parentObject, json, parser ) { 3627 3628 const nodeDef = json.nodes[ nodeId ]; 3629 return parser.getDependency( 'node', nodeId ).then( function ( node ) { 3630 3631 if ( nodeDef.skin === undefined ) return node; // build skeleton here as well 3632 3633 let skinEntry; 3634 return parser.getDependency( 'skin', nodeDef.skin ).then( function ( skin ) { 3635 3636 skinEntry = skin; 3637 const pendingJoints = []; 3638 3639 for ( let i = 0, il = skinEntry.joints.length; i < il; i ++ ) { 3640 3641 pendingJoints.push( parser.getDependency( 'node', skinEntry.joints[ i ] ) ); 3642 3643 } 3644 3645 return Promise.all( pendingJoints ); 3646 3647 } ).then( function ( jointNodes ) { 3648 3649 node.traverse( function ( mesh ) { 3650 3651 if ( ! mesh.isMesh ) return; 3652 const bones = []; 3653 const boneInverses = []; 3654 3655 for ( let j = 0, jl = jointNodes.length; j < jl; j ++ ) { 3656 3657 const jointNode = jointNodes[ j ]; 3658 3659 if ( jointNode ) { 3660 3661 bones.push( jointNode ); 3662 const mat = new THREE.Matrix4(); 3663 3664 if ( skinEntry.inverseBindMatrices !== undefined ) { 3665 3666 mat.fromArray( skinEntry.inverseBindMatrices.array, j * 16 ); 3667 3668 } 3669 3670 boneInverses.push( mat ); 3671 3672 } else { 3673 3674 console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[ j ] ); 3675 3676 } 3677 3678 } 3679 3680 mesh.bind( new THREE.Skeleton( bones, boneInverses ), mesh.matrixWorld ); 3681 3682 } ); 3683 return node; 3684 3685 } ); 3686 3687 } ).then( function ( node ) { 3688 3689 // build node hierachy 3690 parentObject.add( node ); 3691 const pending = []; 3692 3693 if ( nodeDef.children ) { 3694 3695 const children = nodeDef.children; 3696 3697 for ( let i = 0, il = children.length; i < il; i ++ ) { 3698 3699 const child = children[ i ]; 3700 pending.push( buildNodeHierarchy( child, node, json, parser ) ); 3701 3702 } 3703 3704 } 3705 3706 return Promise.all( pending ); 3707 3708 } ); 3709 3710 } 3711 /** 3712 * @param {BufferGeometry} geometry 3713 * @param {GLTF.Primitive} primitiveDef 3714 * @param {GLTFParser} parser 3715 */ 3716 3717 3718 function computeBounds( geometry, primitiveDef, parser ) { 3719 3720 const attributes = primitiveDef.attributes; 3721 const box = new THREE.Box3(); 3722 3723 if ( attributes.POSITION !== undefined ) { 3724 3725 const accessor = parser.json.accessors[ attributes.POSITION ]; 3726 const min = accessor.min; 3727 const max = accessor.max; // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. 3728 3729 if ( min !== undefined && max !== undefined ) { 3730 3731 box.set( new THREE.Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), new THREE.Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) ); 3732 3733 if ( accessor.normalized ) { 3734 3735 const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); 3736 box.min.multiplyScalar( boxScale ); 3737 box.max.multiplyScalar( boxScale ); 3738 3739 } 3740 3741 } else { 3742 3743 console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); 3744 return; 3745 3746 } 3747 3748 } else { 3749 3750 return; 3751 3752 } 3753 3754 const targets = primitiveDef.targets; 3755 3756 if ( targets !== undefined ) { 3757 3758 const maxDisplacement = new THREE.Vector3(); 3759 const vector = new THREE.Vector3(); 3760 3761 for ( let i = 0, il = targets.length; i < il; i ++ ) { 3762 3763 const target = targets[ i ]; 3764 3765 if ( target.POSITION !== undefined ) { 3766 3767 const accessor = parser.json.accessors[ target.POSITION ]; 3768 const min = accessor.min; 3769 const max = accessor.max; // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. 3770 3771 if ( min !== undefined && max !== undefined ) { 3772 3773 // we need to get max of absolute components because target weight is [-1,1] 3774 vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); 3775 vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); 3776 vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); 3777 3778 if ( accessor.normalized ) { 3779 3780 const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] ); 3781 vector.multiplyScalar( boxScale ); 3782 3783 } // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative 3784 // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets 3785 // are used to implement key-frame animations and as such only two are active at a time - this results in very large 3786 // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. 3787 3788 3789 maxDisplacement.max( vector ); 3790 3791 } else { 3792 3793 console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); 3794 3795 } 3796 3797 } 3798 3799 } // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. 3800 3801 3802 box.expandByVector( maxDisplacement ); 3803 3804 } 3805 3806 geometry.boundingBox = box; 3807 const sphere = new THREE.Sphere(); 3808 box.getCenter( sphere.center ); 3809 sphere.radius = box.min.distanceTo( box.max ) / 2; 3810 geometry.boundingSphere = sphere; 3811 3812 } 3813 /** 3814 * @param {BufferGeometry} geometry 3815 * @param {GLTF.Primitive} primitiveDef 3816 * @param {GLTFParser} parser 3817 * @return {Promise<BufferGeometry>} 3818 */ 3819 3820 3821 function addPrimitiveAttributes( geometry, primitiveDef, parser ) { 3822 3823 const attributes = primitiveDef.attributes; 3824 const pending = []; 3825
vendor: 2,921 bytes, lines 3826-3954
3826 function assignAttributeAccessor( accessorIndex, attributeName ) { 3827 3828 return parser.getDependency( 'accessor', accessorIndex ).then( function ( accessor ) { 3829 3830 geometry.setAttribute( attributeName, accessor ); 3831 3832 } ); 3833 3834 } 3835 3836 for ( const gltfAttributeName in attributes ) { 3837 3838 const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); // Skip attributes already provided by e.g. Draco extension. 3839 3840 if ( threeAttributeName in geometry.attributes ) continue; 3841 pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); 3842 3843 } 3844 3845 if ( primitiveDef.indices !== undefined && ! geometry.index ) { 3846 3847 const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { 3848 3849 geometry.setIndex( accessor ); 3850 3851 } ); 3852 pending.push( accessor ); 3853 3854 } 3855 3856 assignExtrasToUserData( geometry, primitiveDef ); 3857 computeBounds( geometry, primitiveDef, parser ); 3858 return Promise.all( pending ).then( function () { 3859 3860 return primitiveDef.targets !== undefined ? addMorphTargets( geometry, primitiveDef.targets, parser ) : geometry; 3861 3862 } ); 3863 3864 } 3865 /** 3866 * @param {BufferGeometry} geometry 3867 * @param {Number} drawMode 3868 * @return {BufferGeometry} 3869 */ 3870 3871 3872 function toTrianglesDrawMode( geometry, drawMode ) { 3873 3874 let index = geometry.getIndex(); // generate index if not present 3875 3876 if ( index === null ) { 3877 3878 const indices = []; 3879 const position = geometry.getAttribute( 'position' ); 3880 3881 if ( position !== undefined ) { 3882 3883 for ( let i = 0; i < position.count; i ++ ) { 3884 3885 indices.push( i ); 3886 3887 } 3888 3889 geometry.setIndex( indices ); 3890 index = geometry.getIndex(); 3891 3892 } else { 3893 3894 console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); 3895 return geometry; 3896 3897 } 3898 3899 } // 3900 3901 3902 const numberOfTriangles = index.count - 2; 3903 const newIndices = []; 3904 3905 if ( drawMode === THREE.TriangleFanDrawMode ) { 3906 3907 // gl.TRIANGLE_FAN 3908 for ( let i = 1; i <= numberOfTriangles; i ++ ) { 3909 3910 newIndices.push( index.getX( 0 ) ); 3911 newIndices.push( index.getX( i ) ); 3912 newIndices.push( index.getX( i + 1 ) ); 3913 3914 } 3915 3916 } else { 3917 3918 // gl.TRIANGLE_STRIP 3919 for ( let i = 0; i < numberOfTriangles; i ++ ) { 3920 3921 if ( i % 2 === 0 ) { 3922 3923 newIndices.push( index.getX( i ) ); 3924 newIndices.push( index.getX( i + 1 ) ); 3925 newIndices.push( index.getX( i + 2 ) ); 3926 3927 } else { 3928 3929 newIndices.push( index.getX( i + 2 ) ); 3930 newIndices.push( index.getX( i + 1 ) ); 3931 newIndices.push( index.getX( i ) ); 3932 3933 } 3934 3935 } 3936 3937 } 3938 3939 if ( newIndices.length / 3 !== numberOfTriangles ) { 3940 3941 console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); 3942 3943 } // build final geometry 3944 3945 3946 const newGeometry = geometry.clone(); 3947 newGeometry.setIndex( newIndices ); 3948 return newGeometry; 3949 3950 } 3951 3952 THREE.GLTFLoader = GLTFLoader; 3953 3954} )();
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.