PageSourceSearch

https://www.laserboost.com/img/3d/GLTFLoader.js

js laserboost.com collected 2026-10-02 05:36:08 UTC 103,556 bytes, 3,954 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.