PageSourceSearch

https://vhpt.org/js/GLTFLoader.js

js vhpt.org collected 2026-09-25 12:29:12 UTC 103,493 bytes, 3,944 lines download raw bytes

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

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.