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