PageSourceSearch

https://dcult.ru/assets/vendor/GLTFLoader.js

js dcult.ru collected 2026-10-07 22:31:23 UTC 108,534 bytes, 4,663 lines download raw bytes

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

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