PageSourceSearch

https://gcmillar.github.io/Nuenen3d/three.js/examples/js/loaders/GLTFLoader.js

js gcmillar.github.io collected 2026-10-03 09:13:56 UTC 80,972 bytes, 3,180 lines download raw bytes

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

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.