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