PageSourceSearch

https://movimentoperpetuo.ca/wp-content/plugins/3d-scene-viewer/assets/js/3js/loader/GLTFLoader.js

js movimentoperpetuo.ca collected 2026-10-07 00:47:47 UTC 119,829 bytes, 4,860 lines download raw bytes

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

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