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