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