PageSourceSearch

https://zipmex.com/assets/js/vendor/GLTFLoader.js

js zipmex.com collected 2026-09-28 06:42:06 UTC 96,550 bytes, 3,629 lines download raw bytes

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

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.